A k-nearest neighbor algorithm using the hnswlib library (https://github.com/nmslib/hnswlib).
Usage
hnsw_knn(
X,
k = 10,
distance = "euclidean",
M = 16,
ef_construction = 200,
ef = 10,
verbose = FALSE,
progress = "bar",
n_threads = 0,
grain_size = 1,
byrow = TRUE,
random_seed = 100
)Arguments
- X
A numeric matrix of
nitems to search for neighbors. Ifbyrow = TRUE(the default) then each row ofXstores an item to be searched. Otherwise, each item should be stored in the columns ofX.- k
Positive whole number of neighbors to return. It cannot exceed the number of items in
X.- distance
Type of distance to calculate. One of:
"l2"Squared L2, i.e. squared Euclidean."euclidean"Euclidean."cosine"One minus cosine similarity."ip"One minus inner product:1 - sum(a * b). Values can be negative and need not satisfy metric properties.
- M
Controls the number of bi-directional links created for each element during index construction. Higher values lead to better results at the expense of memory consumption. Typical values are
2 - 100, but for most datasets a range of12 - 48is suitable. Can't be smaller than 2.- ef_construction
Size of the dynamic list used during construction. A larger value means a better quality index, but increases build time. Must be a positive whole number. It is raised to at least
kand is not bounded by the size of the dataset.- ef
Size of the dynamic list used during search. Higher values lead to improved recall at the expense of longer search time. Must be positive; the effective value is at least
kand may be greater or smaller thanef_construction. Typical values are100 - 2000.- verbose
If
TRUE, log messages to the console.- progress
defunct and has no effect.
- n_threads
Maximum number of threads to use. Zero and one both select serial execution. For larger values, the exact number is determined by
grain_sizeand the amount of work.- grain_size
Minimum number of items in
Xto add or search per thread. Zero is treated as one. If the number of items inXisn't sufficient, then fewer thann_threadswill be used. This is useful in cases where the overhead of context switching with too many threads outweighs the gains due to parallelism.- byrow
If
TRUE(the default), this indicates that the items to be processed inXare stored in each row ofX. Otherwise, the items are stored in the columns ofX. Storing items in each column reduces the overhead of copying data to a form that can be used by thehnswlibrary. Note that ifbyrow = FALSE, any matrices returned from this function will also store the items by column.- random_seed
Seed passed to hnswlib for index construction. The default,
100, is the underlying hnswlib default. This seed belongs to hnswlib: callingset.seed()does not affect index construction.
Value
a list containing:
idxa matrix containing the nearest neighbor indices.dista matrix containing the nearest neighbor distances.
The dimensions of the matrices respect the storage (row or column-based) of
X as indicated by the byrow parameter. If byrow = TRUE (the default)
each row of idx and dist contain the neighbor information for the item
passed in the equivalent row of X, i.e. the dimensions are n x k where
n is the number of items in X. If byrow = FALSE, then each column of
idx and dist contain the neighbor information for the item passed in
the equivalent column of X, i.e. the dimensions are k x n.
Hnswlib Parameters
Some details on the parameters used for index construction and search, based on https://github.com/nmslib/hnswlib/blob/master/ALGO_PARAMS.md:
MControls the number of bi-directional links created for each element during index construction. Higher values lead to better results at the expense of memory consumption, which is aroundM * 8-10bytes per stored element. High intrinsic dimensionalities will require higher values ofM. A range of2 - 100is typical, but12 - 48is ok for most use cases.ef_constructionSize of the dynamic list used during construction. A larger value means a better quality index, but increases build time. It must be a positive whole number, but is not bounded by the size of the dataset. A typical range is100 - 2000. Beyond a certain point, increasingef_constructionhas no effect. A sufficient value ofef_constructioncan be determined by searching withef = ef_construction, and ensuring that the recall is at least 0.9.efSize of the dynamic list used during index search. Can differ fromef_construction. The effective value is at leastk, and it is not bounded by the number of elements in the index.
Numeric data and reproducibility
Coordinates are stored as single-precision floating-point values. The
package rejects non-finite or out-of-range coordinates and, for cosine
distance, vectors with zero norm after conversion. Parallel index
construction may be nondeterministic even for a fixed random_seed. Use
serial construction for repeatable reconstruction. Save the constructed
index to reuse the exact graph.
HNSW search is approximate. For L2, Euclidean, and cosine distance, an item queried against its source data has distance zero from itself, but it can be omitted when recall is insufficient. Under inner-product distance, an item need not be its own nearest neighbor.
References
Malkov, Y. A., & Yashunin, D. A. (2020). Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs. IEEE Transactions on Pattern Analysis and Machine Intelligence, 42(4), 824-836. doi:10.1109/TPAMI.2018.2889473 .
Examples
iris_nn_data <- hnsw_knn(as.matrix(iris[, -5]), k = 10)