背景
以神经网络对样本进行特征的提取,然后在海量的特征库里进行特征相似度的检索、匹配已经是 AI 技术落地的一大领域;使用机器学习、深度学习领域中的方法提取出样本的特征,最终目标就是用一个embedding向量更准确、丰富的表达原始样本。在此基础上出现了许多开源的向量相似性检索库,比如 Faiss: Facebook AI Similarity Search
安装
源码安装
待补充
nvidia docker
待补充
知识准备
相似性度量
-
欧式距离
- 即L2距离,x与y每维分量之差的平方和,距离越小代表越相近
-
内积
- x与y每维分量的点积之和,值越大代表越相近(越大夹角越小,方向越一致)
-
汉明距离
- Hamming距离,指两个相同长度的二进制数据中相同位置处比特位值不同的个数
相似性的度量方式还有许多,如:cosine距离、Jaccard距离等等
Kmeans
Kmeans算法属于聚类算法,把n个对象根据他们的属性分为k个聚类使得所获得的聚类满足:同一聚类中的对象相似度较高;不同聚类中的对象相似度较小。算法的过程是通过多次迭代,最终形成k个聚类,每个聚类有一个中心
KNN
K-Nearest Neighbor,这是一种分类算法,它的思路为:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。该算法法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别
向量近似检索通常分几类:基于树的搜索算法、向量量化的编码算法、基于哈希的空间划分法、基于图的搜索方法。它们都是将原来需要在整个高维向量空间内的搜索,转换为在小范围空间或者相对低维的向量空间内搜索的算法
PQ
Product Quantization,乘积量化,它是一种编码方式,例如128维向量,先把向量分成4段,对每一段数据做 kmeans 聚类,每段聚类出256个聚类中心(假设样本数量远大于256),这样,原始向量的每段就可以使用该段上的聚类中心的编号重新编码,因此表示一个原始向量只需要用4个字节。聚类中心的编号与分段的向量的映射被称之为码本
LSH
Local Sensitive Hashing,局部敏感哈希,与一般的哈希算法不同的是其位置敏感性,也就是哈希前类似的点(距离近的点),在哈希后仍然能够以一定的概率保证一定程度上的相似
HNSW
Hierarchical Navigable Small World,分层可导航小世界
首先是朴素想法,将向量当作一个点,把某些点和点之间连上线,构成一个图;当查找与目标点最近的点时,可以从任意一个图中的点出发,计算它和它所有相邻结点与目标结点的距离,选择与目标结点距离最小的点作为下一个进入点,继续按照上面的规则查找下去,如果当前结点与目标结点的距离最小则停止查找,认为这个点就是所有点中距离目标结点最近的点
朴素想法简单易懂,但有如准确度低、性能较差的缺点。NSW 在朴素想法基础上加上一些限制来提高准确度和性能,它的基本思想是:向图中逐个插入点,插图一个全新点时,通过上述朴素查找法找到与这个全新点最近的m个点,连接全新点到m个点的连线。HNSW 是对 NSW 进行优化的版本,它类似跳表数据结构,进一步提高了查找性能
Faiss 索引
Faiss 提供根据输入向量,给出top K个最相似的向量(的index和distance)功能
暴力搜索
方案:遍历所有建库向量与输入向量计算相似度,使用堆排序选择top K个最相似的。暴力搜索是使用其他索引进行搜索的准确度进行对比的基准,Faiss内部对其进行了一些性能优化如:使用openmp并行计算、高效矩阵计算库BLAS、采用avx2指令进行计算等
此外,还有多种以牺牲些许精度换取检索性能的方案
IndexIVF
倒排索引,传统搜索中的倒排索引中的索引结点通常为关键词,包含这个关键词的文章id构成倒排列表。对于向量的倒排索引也是类似的思想:通过聚类算法建立倒排结构,聚类中心为索引结点,聚类中的向量构成倒排列表;检索时选择若干个最近的聚类中心,然后在这若干个对应的聚类里选择距离最近的top K个向量
Faiss内部将索引的使用分为三个阶段:
- 训练:在初始建库向量上执行聚类算法(Kmeans)过程,生成倒排索引结构
- 入库:将每个入库向量添加到最近的聚类中
- 检索:选择最近的若干个聚类中心,在这些对应的聚类中选择距离最近的top K个向量
倒排索引方法先将建库数据划分成多个聚类,检索时只需要在若干个聚类内对比数据,通常检索时指定的聚类个数都小于建库时设置的聚类个数,从而加速了检索过程
IndexPQ
量化索引,建库的原始向量的每个分段向量都可以用该分段上的聚类中心向量表示,因此这是一种对原始向量的近似表示。检索时仍然要遍历所有数据,计算输入向量与量化后的建库向量的距离(非对称距离),选择出top K个
量化大大减少了索引的数据量,并且遍历计算输入向量与量化后的各建库向量距离时,只需要计算一次输入向量各分段向量与对应聚类中心向量距离并保存下来,之后查表即可,加速检索过程
IndexIVFPQ
倒排+量化,即上述两种索引的组合。这种索引中Faiss提供了另一种精度更高的 PQ 方案:对残差向量进行量化。它的基本思想是首先对建库数据构建倒排索引,每个建库向量与相应聚类中心的差为残差向量,然后对残差向量进行量化,因此输入向量与建库向量的距离为:输入向量 - 对应聚类中心向量 - 残差向量,近似为:输入向量 - 对应聚类中心向量 - 量化后的残差向量。在检索时首先按照倒排索引检索方式找到若干个最近的聚类中心,然后在对应的聚类中选择出top K个
三个阶段:
-
训练
- 在初始建库向量上执行Kmeans聚类算法生成倒排索引结构(得到粗聚类中心点)
- 默认情况下计算残差向量,即每个建库向量与其最近粗聚类中心点的差
- 对残差向量分段,对每个分段下的所有子向量进行Kmeans聚类算法,计算中心点与码本
- 生成预计算表
-
入库
- 将每个入库库向量都添加到最近的粗聚类中
- 计算每个入库向量与其粗聚类中心的残差
- 计算每个分段向量最近的分段残差聚类中心,加入到其聚类中
-
检索
- 选择最近的若干个粗聚类
- 根据预计算表在每个粗聚类中选择top K个最近的向量,然后整体上再选出top K个最近的向量
IndexIVFPQ类型的索引进一步以精度换取了检索性能上的提升
IndexLSH
局部敏感哈希索引,向量被哈希为一串二进制,并且使用汉明距离计算(近似的)相似度,详细分析待补充
IndexHNSW
分层可导航小世界索引,详细分析待补充
索引操作
增量添加、更新、删除数据
Faiss并没有提供并发安全的上述操作,因此执行上述操作需要用户保证并发安全,这可能会对检索性能造成影响。此外上述操作可能会改变建库数据的分布,进而影响检索精度
导入、导出
索引文件支持从训练服务中导出,在检索服务中加载,因此可以更高效的利用资源
合并、拆分
当使用多个 GPU 以提高训练速度,然后合并索加载到内存
应用
应用场景
单机无法保存全量索引
对建库数据进行分片分别构建索引,检索结果进行合并
实时索引
少量的增量数据采用暴力搜索,全量数据离线建库和切换,全量的检索结果与增量的检索结果进行合并
问题记录
查看gpu以及类型 lspci | grep -i vga; lspci |grep -i nvidia
查看nvidia显卡状态 nvidia-smi
参考
Faiss:Facebook开源的相似性搜索类库
向量搜索
向量检索算法
IVFPQ算法原理
图像检索(6):局部敏感哈希索引(LSH)
局部敏感哈希(Locality-Sensitive Hashing,LSH)
图像检索:OPQ索引与HNSW索引
一文看懂HNSW算法理论的来龙去脉
使用Faiss进行海量特征的相似度匹配
do search while at the same time adding incrementally