本系列文章基于Faiss 1.5.3版本的代码进行分析。
相似性搜索介绍
Faiss是Facebook AI团队开源的针对聚类和相似性搜索库,为稠密向量提供高效相似度搜索和聚类,是目前比较成熟的近似近邻搜索库。以图片搜索为例,所谓相似度搜索,就是在给定的图片中,寻找出指定的图片最像的K张图片,本质上为KNN(K近邻)问题。
为了解决KNN问题,在工程上需要对现有图片库的特征向量进行存储。当用户指定检索图片后,需要知道如何从存储的图片库中找到最为相似的K张图片。Faiss为解决这种问题,除具备添加和搜索的功能外,还需要提供相应的修改和删除功能。从本质上将,Faiss属于向量(矢量)数据库。
Faiss的github首页上对相似性搜索做了一个简要的介绍,What is similarity search?。在这里把这部分内容摘抄过来,以对Faiss提供的功能有基本的了解:
Given a set of vectors x_i in dimension d, Faiss build a data structure from it. After the structure is constructed, when given a new vector x in dimension d it performs efficiently the operation:
i = argmin_i ||x - x_i||
where ||.|| is the Euclidean distance (L2).
If Faiss terms, the data structure is an index, an object that has an add method to add x_i vectors. Note that the x_i's are assumed to be fixed.
Computing the argmin is the search operation on the index.
This is all what Faiss is about. It can also:
1. return not just the nearest neighbor, but also the 2nd nearest, 3rd, ..., k-th nearest neighbor
2. search several vectors at a time rather than one (batch processing). For many index types, this is faster than searching one vector after another
3. trade precision for speed, ie. give an incorrect result 10% of the time with a method that's 10x faster or uses 10x less memory
4. perform maximum inner product search argmax_i <x, x_i> instead of minimum Euclidean search.
5. return all elements that are within a given radius of the query point (range search)
相似性计算方式
既然Faiss是一种相似性搜索库,那就来看一下Faiss提供了哪些方式来衡量相似度度。在Faiss 1.5.0版本之前,仅支持常用的内积(归一化后与余弦等同)和欧式距离两种,新版本中支持了更多的计算方式,可供用户选择。
/// Some algorithms support both an inner product version and a L2 search version.
enum MetricType {
METRIC_INNER_PRODUCT = 0, ///< maximum inner product search
METRIC_L2 = 1, ///< squared L2 search
METRIC_L1, ///< L1 (aka cityblock)
METRIC_Linf, ///< infinity distance
METRIC_Lp, ///< L_p distance, p is given by metric_arg
/// some additional metrics defined in scipy.spatial.distance
METRIC_Canberra = 20,
METRIC_BrayCurtis,
METRIC_JensenShannon,
};
Index抽象类
对于传统数据库,时空的优化是永恒的主题,即在存储上如何以更少的空间来存储更多的信息,在搜索上如何以更快的速度来搜索出更准确的信息,如何减少搜索所需的时间?在数据库中比较常见的便是各种索引,把各种加速搜索算法功能或空间换时间的策略封装成各种索引,来满足各种不同的引用场景。Faiss作为向量(矢量)数据库,也需要考虑时空优化问题。本系列文章会对每种类型的向量搜索算法进行详尽的分析,本文先来分析Faiss基类的设计。
Faiss实现中有两个基础索引抽象类Index和IndexBinary,本文仅对Index基类进行介绍。Faiss作为向量数据库,需要提供向量的增删改查功能,那先来看一下向量参数如何传递的。
向量参数传递
在整个Faiss的实现中,向量参数的传递按照下面的方式进行: