Package: autofaiss
AutoFaissAutomatically create Faiss knn indices with the most optimal similarity search parameters.It selects the best indexing parameters to achieve the highest recalls given memory and query speed constraints.Doc and posts and notebooksUsingfaissefficient indices, binary search, and heuristics, Autofaiss makes it possible toautomaticallybuild in 3 hours a large (200 million vectors, 1TB) KNN index in a low amount of memory (15 GB) with latency in milliseconds (10ms).Get started by running thiscolab notebook, then check thefull documentation.Get some insights on the automatic index selection function with thiscolab notebook.Then you can check ourmultimodal search example(using OpenAI Clip model).Read themedium postto learn more about it!InstallationTo install runpip install autofaissIt's probably best to create a virtual env:python-mvenv.venv/autofaiss_envsource.venv/autofaiss_env/bin/activate
pipinstall-Upip
pipinstallautofaissUsing autofaiss in pythonIf you want to use autofaiss directly from python, check theAPI documentationand theexamplesIn particular you can use autofaiss with on memory or on disk embeddings collections:Using in memory numpy arraysIf you have a few embeddings, you can use autofaiss with in memory numpy arrays:fromautofaissimportbuild_indeximportnumpyasnpembeddings=np.float32(np.random.rand(100,512))index,index_infos=build_index(embeddings,save_on_disk=False)query=np.float32(np.random.rand(1,512))_,I=index.search(query,1)print(I)Using numpy arrays saved as .npy filesIf you have many embeddings file, it is preferred to save them on disk as .npy files then use autofaiss like this:fromautofaissimportbuild_indexbuild_index(embeddings="embeddings",index_path="my_index_folder/knn.index",index_infos_path="my_index_folder/index_infos.json",max_index_memory_usage="4G",current_memory_available="4G")Memory-mapped indicesFaiss makes it possible to use memory-mapped indices. This is useful when you don't need a fast search time (>50ms)
and still want to reduce the memory footprint to the minimum.We provide the should_be_memory_mappable boolean in build_index function to generate memory-mapped indices only.
Note: Only IVF indices can be memory-mapped in faiss, so the output index will be a IVF index.To load an index in memory mapping mode, use the following code:importfaissfaiss.read_index("my_index_folder/knn.index",faiss.IO_FLAG_MMAP|faiss.IO_FLAG_READ_ONLY)You can have a look to theexamplesto see how to use it.Technical note: You can create a direct map on IVF indices with index.make_direct_map() (or directly from the
build_index function by passing the make_direct_map boolean). Doing this speeds up a lot
the .reconstruct() method, function that gives you the value of one of your vector given its rank.
However, this mapping will be stored in RAM... We advise you to create your own direct map in a memory-mapped
numpy array and then call .reconstruct_from_offset() with your custom direct_map.Using autofaiss with pysparkAutofaiss allows you to build indices with Spark for the following two use cases:To build a big index in a distributed wayGiven a partitioned dataset of embeddings, building one index per partition in parallel and in a distributed way.Prerequisities:Install pyspark:pip install pyspark.Prepare your embeddings files (partitioned or not).Create a Spark session before calling autofaiss. If no Spark session exists, a default session will be creaed with a minimum configuration.Creating a big index in a distributed waySeedistributed_autofaiss.mdfor a complete guide.It is possible to generate an index that would require more memory than what's available. To do so, you can control the number of index splits that will compose your index withnb_indices_to_keep.
For example, ifnb_indices_to_keepis 10 andindex_pathisknn.index, the final index will be decomposed into 10 smaller indexes:knn.index01knn.index02knn.index03...knn.index10Aconcrete exampleshows how to produce N indices and how to use them.Creating partitioned indexesGiven a partitioned dataset of embeddings, it is possible to create one index per partition by calling the methodbuild_partitioned_indexes.See thisexamplethat shows how to create partitioned indexes.Using the command lineCreate embeddingsimportosimportnumpyasnpembeddings=np.random.rand(1000,100)os.mkdir("embeddings")np.save("embeddings/part1.npy",embeddings)os.mkdir("my_index_folder")Generate a Knn indexautofaissbuild_index--embeddings="embeddings"--index_path="my_index_folder/knn.index"--index_infos_path="my_index_folder/index_infos.json"--metric_type="ip"Try the indeximportfaissimportglobimportnumpyasnpmy_index=faiss.read_index(glob.glob("my_index_folder/*.index")[0])query_vector=np.float32(np.random.rand(1,100))k=5distances,indices=my_index.search(query_vector,k)print(list(zip(distances[0],indices[0])))How are indices selected ?To understand better why indices are selected and what are their characteristics, check theindex selection demoCommand quick overviewQuick description of theautofaiss build_indexcommand:embeddings-> Source path of the embeddings in numpy.index_path-> Destination path of the created index.index_infos_path-> Destination path of the index infos.save_on_disk-> Save the index on the disk.metric_type-> Similarity distance for the queries.index_key-> (optional) Describe the index to build.index_param-> (optional) Describe the hyperparameters of the index.current_memory_available-> (optional) Describe the amount of memory available on the machine.use_gpu-> (optional) Whether to use GPU or not (not tested).Command detailsTheautofaiss build_indexcommand takes the following parameters:Flag availableDefaultDescription--embeddingsrequireddirectory (or list of directories) containing your .npy embedding files. If there are several files, they are read in the lexicographical order. This can be a local path or a path in another Filesystem e.g.hdfs://root/...ors3://...--index_pathrequiredDestination path of the faiss index on local machine.--index_infos_pathrequiredDestination path of the faiss index infos on local machine.--save_on_diskrequiredSave the index on the disk.--file_format"npy"File format of the files in embeddings Can be eithernpyfor numpy matrix files orparquetfor parquet serialized tables--embedding_column_name"embeddings"Only necessary when file_format=parquetIn this case this is the name of the column containing the embeddings (one vector per row)--id_columnsNoneCan only be used when file_format=parquet. In this case these are the names of the columns containing the Ids of the vectors, and separate files will be generated to map these ids to indices in the KNN index--ids_pathNoneOnly useful when id_columns is not None and file_format=parquet. This will be the path (in any filesystem) where the mapping files Ids->vector index will be store in parquet format--metric_type"ip"(Optional) Similarity function used for query: ("ip" for inner product, "l2" for euclidian distance)--max_index_memory_usage"32GB"(Optional) Maximum size in GB of the created index, this bound is strict.--current_memory_available"32GB"(Optional) Memory available (in GB) on the machine creating the index, having more memory is a boost because it reduces the swipe between RAM and disk.--max_index_query_time_ms10(Optional) Bound on the query time for KNN search, this bound is approximative.--min_nearest_neighbors_to_retrieve20(Optional) Minimum number of nearest neighbors to retrieve when querying the index. Parameter used only during index hyperparameter finetuning step, it is not taken into account to select the indexing algorithm. This parameter has the priority over the max_index_query_time_ms constraint.--index_keyNone(Optional) If present, the Faiss index will be build using this description string in the index_factory, more detail in theFaiss documentation--index_paramNone(Optional) If present, the Faiss index will be set using this description string of hyperparameters, more detail in theFaiss documentation--use_gpuFalse(Optional) Experimental, gpu training can be faster, but this feature is not tested so far.--nb_coresNone(Optional) The number of cores to use, by default will use all cores--make_direct_mapFalse(Optional) Create a direct map allowing reconstruction of embeddings. This is only needed for IVF indices. Note that might increase the RAM usage (approximately 8GB for 1 billion embeddings).--should_be_memory_mappableFalse(Optional) Boolean used to force the index to be selected among indices having an on-disk memory-mapping implementation.--distributedNone(Optional) If "pyspark", create the index using pyspark. Otherwise, the index is created on your local machine.--temporary_indices_folder"hdfs://root/tmp/distributed_autofaiss_indices"(Optional) Folder to save the temporary small indices, only used when distributed = "pyspark"--verbose20(Optional) Set verbosity of logging output: DEBUG=10, INFO=20, WARN=30, ERROR=40, CRITICAL=50--nb_indices_to_keep1(Optional) Number of indices to keep at most when distributed is "pyspark".Install from sourceFirst, create a virtual env and install dependencies:python3 -m venv .env
source .env/bin/activate
make installpython -m pytest -x -s -v tests -k "test_get_optimal_hyperparameters"to run a specific test