-
Notifications
You must be signed in to change notification settings - Fork 136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEATURE] Enable setting ef_search parameter as part of k-NN Query #1537
Comments
@navneet1v does nmslib support passing this at search time? I dont think it does. |
Also, IVF would benefit from this as well for the nprobe parameter. The nprobe should be easier to do once we have #1527 checked in as well. We can then just say for the nprobe parameter passed in at training is just the default. |
The way it works is you need to unload the graphs and then load again with new values. I would say its not true query parameter for nmslib. But we should move towards making the change in nmslib to avoid making ef_search as a graph parameter. |
Picking this up |
[RFC] Adding efSearch as query parameter for kNNOverviewThe efSearch parameter is utilized in the HNSW (Hierarchical Navigable Small World) algorithm to facilitate the exploration of more neighboring candidates, which in turn enhances the potential recall. A higher value for efSearch can often result in improved recall. Opensearch k-NN plugin offers three engines options for HNSW-based kNN: NMSLIB, FAISS and Lucene. Currently customers can specify the efSearch option for NMSLIB and FAISS implementations as an index setting. This setting is established during index creation process . FAISS ef_search value cannot be changed after the index is created. NMSLIB value can be changed. In the case of the Lucene implementation, the efSearch parameter is currently tied to the k parameter. The objective of the feature is to enable efSearch as a query parameter across all three engines. This documents outlines the challenges associated with the goal, along with potential solutions and recommendations. API Contract changesAn additional parameter support will be added in the query ef_search which will take an integer value indicating number of neighbors to explore. The value will be valid if its greater than 0; example query
Engine supportOpensearch will maintain support for index settings for efSearch in FAISS and NMSLIB engines. The setting will serve as defaults. However, if the query time efSearch parameter is provided, it will take precedence over the index settings. FAISSThe FAISS engine is the simplest among the three. We can forward the efSearch parameter to the FAISS engine to get the intended results. NMSLIBNMSLIB implementation lacks support for efSearch as a query parameter. Instead, It is stored as a member variable of the index, which makes the implementation not-thread-safe with respect to query time efSearch. The k-NN plugin caches the native memory address when it loads the NMSLIB (and FAISS) index. A simplistic approach would be to load a separate index with a distinct efSearch value from the query. The efSearch value will be appended as a the cache key for the native memory address. However, this approach can potentially turn into a memory bottleneck and can lead to OOM issues for customers if large number of queries with different efSearch parameters are executed. The memory bottleneck will be particularly more evident if there are multiple indexes in a node. Approach 1: Adding support for efSearch in NMSLIB [Recommended]The approach entails adding support for efSearch query parameter in the NMSLIB implementation of HNSW. It mirrors FAISS’ implementation, where the efSearch query parameter value supersedes the default index setting. The implementation will in turn make NMSLIB thread safe for query time efSearch. Given that KNNQuery is utilized for algorithms beyond HNSW in NMSLIB, we introduce HnswKNNQuery which inherits KNNQuery . This can accommodate HNSW specific parameters such as efSearch. Consequently efSearch value will be utilized psuedo code:
The discussion has been initiated in the this github feature request and is awaiting feedback. Approach 2: Synchronize on Index Allocation ObjectCurrently the system permits multiple threads to query the same index simultaneously. This approach will block multiple threads from querying the same index. An index allocation object, stored in cache, which holds the native memory address of the index graph. By synchronizing the call to query method of the library on the index allocation object acts as a work around to the thread safety problem. Note that, in this approach, we will manipulate the member variable of efSearch from the query and set it back to the index setting. As a consequence, it can lead to contention in the system if large number of queries are executed at once. It's also worth noting that implementing this approach impacts the FAISS' implementation. Therefore, a refactor is necessary to ensure that FAISS querying remains unaffected, making sure we don't block threads for FAISS'. Pros and cons
LuceneThe Lucene implementation currently uses k as the equivalent of efSearch. It does not have a mechanism to differentiate between the k and efSearch parameters. Lucene extracts the top similar documents for KNN by overriding the rewrite phase of the query. The rewrite of the query is managed in the class AbstractKnnVectorQuery . As a part of rewrite, AbstractKnnVectorQuery iterates over each segment in parallel and retrieves the top k docs, exploring k neighbors as candidates from each segment. It utilizes TopDocsKnnCollector to collect results from each segment, subsequently merging the top k documents from the segment results. Approach 1: Add support for efSearch parameter in Lucene [Recommended]The approach requires introducing a parameter efSearch, alongside k in AbstractKnnVectorQuery . This parameter then needs to passed on to TopDocsKnnCollector while its being initialized. This makes sure that the underlying HNSW implementation utilizes efSearch to explore the candidates similarities. Meanwhile AbstractKnnVectorQuery continues to utilize the k value to return the results. The k value will be used to merge the top efSearch docs for each segment. The approach requires us to submit a pull request which add Approach 2: Add custom queriesThis introduces four new queries which will inherit KnnVectorFloatQuery , KnnVectorByteQuery , psuedo code example public class EfSearchKnnVectorFloatQuery extends KnnVectorFloatQuery {
private int final efSearch;
private int final k;
public EfSearchKnnVectorFloatQuery(String field, float[] target,
int k, int efSearch, Query filter) {
super(field, efSearch, filter);
this.topk = k;
}
protected TopDocs mergeLeafResults(TopDocs[] perLeafResults) {
return TopDocs.merge(this.k, perLeafResults);
}
} Pros and cons
efSearch BoundsWhile efSearch value can be used to get a better recall, the cost can be higher latencies if the parameter value is recklessly large and can turn into a literal k-NN instead of approximate, especially if the value of efSearch is not adjusted per segment. The current bound for k is 10000 in opensearch k-NN plugin, similarly a recommendation is to put a upper bound on the efSearch value, the upper bound value needs to be decided based on testing. Note: After a discussion we deceide not to go ahead with bounds as there are no bounds on current ef_search values while indexing AppendixOOM issues calculationsAssumptions Memory: 128 GB out of which 32GB is used by heap and 90GB as native memory
|
@shatejas thanks for putting up the RFC. For this feature I have created this feature branch: https://github.com/opensearch-project/k-NN/tree/feature/ef-search. The branch cut out of main branch just now. |
For radial search in Lucene engine HNSW, there is no the |
Thanks @junqiu-lei for doing the deep-dive and providing the information. |
Is your feature request related to a problem?
While using HNSW algorithm currently there is no support for sending the
ef_search
parameter as part of query. The ask here is to support functionality of setting the ef_search in the k-NN query.Even though the downstream libraries like Faiss and Nmslib has support for this.
Faiss: https://github.com/facebookresearch/faiss/blob/dafdff110489db7587b169a0afee8470f220d295/faiss/impl/HNSW.h#L46
What solution would you like?
While doing a k-NN query as a user I should be able to provide the
ef_search
value.What alternatives have you considered?
Currently in Nmslib I can update this value per index by updating the index settings. But this functionality is not there for faiss.
Do you have any additional context?
NA
The text was updated successfully, but these errors were encountered: