前言
本文是根据Faiss发布的官方wiki,在学习过程中写的中文版记录。有表意不清晰的地方可查看英文原版,如有侵权,请联系作者删除。英文教程:Faiss-Facebook
一、简介
Faiss是一个用于对向量进行高效相似向量搜索(Similarity Search)
和聚类的库。它同时支持验证和调参。它是C++写的,同时支持Python调用,一些有用的算法也实现了GPU版本。
二、向量检索(Similarity Search)
1.索引
给定一个d维的向量集合x_i,Faiss在内存上建立一个数据结构。当这个结构被建立之后,给定一个新的d维向量x时,这个结构可以高效地返回与x最相近的Topk个向量。这个结构就叫做索引(index)
。这个对象有一个add
方法来添加x_i向量,计算topk个最相近向量的操作就叫做搜索(search)
。
2.最近邻算法(KNN,K-NearestNeighbor)
1中提到的问题,其实就是为1个或者多个向量找它的k个最近邻的向量,可以认为是一个最近邻问题
。第一反应的解决办法可能是计算x和向量集合中所有向量的欧式距离(或者cos距离,点积等),然后从小到大排序,这看上去似乎并不需要很复杂的算法,但在现实场景中往往面临海量的数据,当数据量在上亿级别的时候,这种暴力搜索的方法就并不可取了。所以在进行大规模向量检索时,通常采用的是近似最近邻算法(Approximate Nearest Neighbor,ANN)
。
3.近似最近邻算法(ANN,Approximate Nearest Neighbor)
和暴力解法不同的是,ANN
算法不需要和集合中的所有向量计算距离,而是有选择地和部分向量计算距离,这样得到的答案往往不是最精确的,但是在效率上却大大提高了。所以向量检索其实是一个精度和效率的权衡问题。
ANN算法主要分为基于空间划分的方法
和基于图
的方法。
基于划分的方法
的主要思路是将向量划分到不同的空间中(可以通过聚类等方法),在检索时先确定最相近的一个或多个空间,然后只在这些空间中搜索最近的向量。主要有以下几种:
- 基于树的方法
-比较典型的有KDTree、BallTree、VPTree。KDTree会选取向量中某个方差最大的维度取中值作为判定标准,也就是以超平面去划分空间。基于树的方法还有很多其他类型,但万变不离其宗,无非就是按照某个判定标准,对向量空间进行划分,但不管怎么划分,由于需要回溯的,都决定了基于树的方法在性能上要稍逊一筹。 - 局部敏感哈希(Locality Sensitive Hashing,LSH)
-高维空间的两点若距离很近,那么设计一种哈希函数对这两点进行哈希值计算,使得他们哈希值有很大的概率是一样的,若两点之间的距离较远,他们哈希值相同的概率会很小。不同距离度量的哈希函数不同,不是所有距离度量(如内积)都能找到对应局部敏感哈希。 - 基于倒排的方法
-向量的倒排索引不同于传统的倒排索引(根据文档中包含某个词,就把这篇文档放到这个词的倒排索引结构中),向量的倒排索引是通过聚类把向量空间划分为K个子空间,每个向量将归入距离自己最近的聚类中心的倒排结构中。
基于图的方法
的主要思路是邻居的邻居也可能是邻居,这样把最近邻的查找转化为图的遍历,由于其连通性,可以针对性的考察部分向量而不是按区域来考察,因此可以大幅降低向量的考察范围。典型的如HNSW方法。
- 其他加速技术:
上述方法缩小了向量检索的候选空间,但是对于高维向量来说,两两向量之间的计算量还是很大,向量量化就是从这个角度来减少计算量,比较典型的是乘积量化方法。通过把高维向量切分成不同的子段,原始向量就可以表示成由各个子段索引值构成的压缩向量。计算压缩向量之间的距离可以大大减小计算量。
(以上只是粗略地描述了一下几类方法(参考【一文纵览KNN(ANN)向量检索】),其中细节还需要寻找对应的资料进一步理解,后续有时间会补充上详细的算法描述)
以上的算法在后续的Faiss使用介绍中可能会陆续用到。
三、Faiss中文教程
上面简单介绍了向量检索的背景,后面会基本按照Tutorial的顺序来介绍Faiss的使用。
* * Tutorial
(一)Getting started
假设Faiss已经安装好了,且后面的代码演示主要以Python为主,C++版本可以去官方github上查看。
1.构造数据
Faiss处理固定维度d维的向量集合,通常从10维到100维均可。这些集合可以被存储在矩阵中。我们假设按行存储,向量i的第j维元素被存储在矩阵的第i行第j列。Faiss使用的是32位的浮点数矩阵。
我们需要两个矩阵:
xb
用于构建数据库,包含我们要被构建索引的,即将被检索