特征检索总结

总述

特征检索算法,也叫ANN(Approximate Nearest Neighbor)搜索,主流方法可分为以下几种:基于树的方法(kdTree、ANNOY)、哈希方法(LSH、FALCONN)、矢量量化方法(PQ、IVF)、基于图的方法(NHSW)等,矢量量化工业界应用最多。
参考链接

1 Product Quantization乘积量化

PQ系列的算法大致的套路分三个阶段:训练、量化、查询

1.1 训练

假设特征向量维度D=64维,将原始的D维向量分成M=8段,那么每段的子维度subD=8。对每个段的sub特征进行聚类,一般使用KNN,假设聚类中心数k=256,这样也就得到了M*k个聚类中心,也叫码本。
聚类训练过程

1.2 量化

训练阶段得到了得到了M*k个聚类中心即码本,每个聚类中心都是一个sub向量,这个码本太长了不好表示,我们想有一个类似索引的东西,代替这个sub向量,这个索引如何建立呢,就是量化的过程。
原始D维向量对应的码本就是C=C1×C2×…×Cm(乘号是笛卡尔积符号,笛卡尔积可以得到整个向量的聚类中心)。可以看到m=1或者m=D是PQ算法的2种极端情况,对m=1,PQ算法就回退到vector quantization,对m=D,PQ算法相当于对原始向量的每一维都用kmeans算出码本。k=256和M=8,像这样一种默认配置,相当于用 m×logk=8×log256=64bits=8bytes来表示一个原始向量(码本大小或量化长度之和聚类中心数k和段数M有关
也可以用一个长度为8的数组表示编码后的信息(34,87,250,88,129……)
可参考别人的python实现
补充知识,笛卡尔积:A×B={(x,y)|x∈A∧y∈B},例如,A={a,b}, B={0,1,2},则
A×B={(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}
B×A={(0, a), (0, b), (1, a), (1, b), (2, a), (2, b)}

1.3 查询

PQ有两种查询方法,一种是SDC(symmetric distance computation),另一种是ADC(asymmetric distance computation)。对称和非对称的区别在于是否要对查询向量x做量化。SDC是将查询向量进行量化,然后计算量化子和数据库中量化子的距离;ADC是直接计算查询向量和数据库中量化子的距离。
在这里插入图片描述

SDC:向量x和向量y之间的距离直接用x和y量化时对应的聚类中心之间的距离来表示,各量化子之间的距离可以提前算好存到表里,因此SDC较快,但是由于对query做了量化,误差较大。查询向量query也要量化到聚类中心点,假设为{21, 46, 98, 128}
ADC:向量x和向量y的距离用x的量化结果和向量y对应的聚类中心间的距离来表示,较耗时,但是误差也相对较小,实际应用中通常采用ADC,步骤如下:
1)首先计算查询向量和类中心的距离,得到距离表
2)遍历样本库中的向量,根据距离表,计算每个样本与查询向量的距离和
3)返回k个距离最接近的样本
在这里插入图片描述

2 倒排乘积量化

对所有的样本进行距离计算,其实是做了非常多的无用功。那么我们只计算“有潜力”的那几个样本不就好了吗?问题就是如何寻找出“有潜力”的那几个样本呢?最常见的方式就是聚类+倒排(倒排见文末解释)。但凡是一个检索系统,倒排必不可少。通过聚类后构建当前类别下所有的样本

2.1 流程

在PQ乘积量化之前,增加了一个粗量化过程。具体如下
1、粗聚类:先对N个训练样本采用KMeans进行聚类,这里聚类的数目一般设置得不应过大,一般设置为1024差不多,这种可以以比较快的速度完成聚类过程。
2、残差PQ:得到了聚类中心后,针对每一个样本x_i,找到其距离最近的类中心c_i后,两者相减得到样本x_i的残差向量(x_i-c_i),后面剩下的过程,就是针对(x_i-c_i)的PQ乘积量化过程(属于不同聚类中心的残差分别PQ),此过程不再赘述。
在这里插入图片描述

2.2 查询

在查询的时候,通过相同的粗量化,可以快速定位到查询向量属于哪个c_i(即在哪一个感兴趣区域),然后在该感兴趣区域按上面所述的PQ乘积量化距离计算方式计算距离。

2.3 分析

核心是粗量化和残差计算,粗量化就是通过聚类粗分类成N个感兴趣区域,查询的时候只到感兴趣区域里去查找,降低计算量。另外就是残差,之所以要残差,个人理解是为了后面的pq步骤准备,数据归一化后,有助于提升聚类效果。
倒排可以结合PQ或其他算法组合使用,也可以单独使用,faiss库中集成了IVF(Iverted File)算法。

3 LSH局部感知哈希

先记下参考链接,回头再继续总结
添加链接描述
添加链接描述

3.1 Min Hashing

3.2 LSH

局部敏感什么意思?
当距离相近的样本点对比相远的样本点对更容易发生碰撞,我们称这个哈希函数是局部敏感的

LSH的思想也是数据分区,分成不同的hash桶,hash函数如何设计,针对不同的特征向量,没有一个特定的规则。如下图所示,将特征分为4个band,如果两个向量的其中一个或多个band相同,那么这两个向量就可能相似度较高;相同的band数越多,其相似度高的可能性越大。所以LSH的做法就是对各个用户的向量在每一个band上分别进行哈希分桶(如取前两位作为hashKey),在任意一个band上被分到同一个桶内的用户就互为candidate相似用户。
在这里插入图片描述
多表哈希
对于单表哈希,当哈希函数桶的数目K取得太大,查询样本与其对应的最近邻落入同一个桶中的可能性会变得很微弱,针对这个问题,我们可以重复这个过程L次,从而增加最近邻的召回率。这个重复L次的过程,可以转化为构建L个哈希表,这样在给定查询样本时,我们可以找到L个哈希桶(每个表找到一个哈希桶),然后我们在这L个哈希表中进行遍历。这个过程相当于构建了K*L个哈希函数(注意是“相当”,不要做“等价”理解)。

Multiprobe LSH
多probe LSH主要是为了提高查找准确率而引入的一种策略。首先解释一下什么是Multiprobe。对于构建的L个哈希表,我们在每一个哈希表中找到查询样本落入的哈希桶,然后再在这个哈希桶中做遍历,而Multiprobe指的是我们不止在查询样本所在的哈希桶中遍历,还会找到其他的一些哈希桶,然后这些找到的T个哈希桶中进行遍历。这些其他哈希桶的选取准则是:跟查询样本所在的哈希桶邻近的哈希桶,“邻近”指的是汉明距离度量下的邻近。

通常,如果不使用Multiprobe,我们需要的哈希表数目L在100到1000之间,在处理大数据集的时候,其空间的消耗会非常的高,幸运地是,因为有了上面的Multiprobe的策略,LSH在任意一个哈希表中查找到最近邻的概率变得更高,从而使得我们能到减少哈希表的构建数目。

综上,对于LSH,涉及到的主要的参数有三个:
K,每一个哈希表的哈希函数(空间划分)数目,K越大,检索越快
L,哈希表(每一个哈希表有K个哈希函数)的数目,L越大检索越慢
T,近邻哈希桶的数目,即the number of probes,T越大检索越慢
哈希函数
哈希函数
哈希函数

4 ANNOY

ANNOY和KD树都是基于树的检索算法,建立二叉搜索树来使得每个点查找时间复杂度是O(log n),与KD树不停的切分子空间不同,ANNOY是通过聚类划分超平面,将数据不停的切分,映射到二叉树

4.1 建立索引过程

随机选择两个点,以这两个节点为初始中心节点,执行聚类数为2的kmeans过程,最终产生收敛后两个聚类中心点。这两个聚类中心点之间连一条线段(灰色短线),建立一条垂直于这条灰线,并且通过灰线中心点的线(黑色粗线)。这条黑色粗线把数据空间分成两部分。在多维空间的话,这条黑色粗线可以看成等距垂直超平面。
在这里插入图片描述
在划分的子空间内进行不停的递归迭代继续划分,知道每个子空间最多只剩下K个数据节点
在这里插入图片描述

通过多次递归迭代划分的话,最终原始数据会形成类似下面这样一个二叉树结构。二叉树底层是叶子节点记录原始数据节点,其他中间节点记录的是分割超平面的信息。Annoy建立这样的二叉树结构是希望满足这样的一个假设: 相似的数据节点应该在二叉树上位置更接近,一个分割超平面不应该把相似的数据节点分割二叉树的不同分支上。
在这里插入图片描述

4.2 查询过程

查找的过程就是不断看他在分割超平面的哪一边。从二叉树索引结构来看,就是从根节点不停的往叶子节点遍历的过程。通过对二叉树每个中间节点(分割超平面相关信息)和查询数据节点进行相关计算来确定二叉树遍历过程是往这个中间节点左孩子节点走还是右孩子节点走。通过以上方式完成查询过程。
在这里插入图片描述

4.2 优化

上述过程可能存在两个问题:
(1)查询过程最终落到叶子节点的数据节点数小于 我们需要的Top N相似邻居节点数目怎么办?
(2)两个相近的数据节点划分到二叉树不同分支上怎么办?

优化方法一:如果分割超平面的两边都很相似,那可以两边都遍历,反应到二叉树,就是二叉树的两个分支都往下走,最后会得到2个或多个子空间
优化方法二:建立多个二叉树,构成二叉树森林,对以上两个问题,都能有所缓解。当然,此时获得的多个子空间要合并去重,然后进行查询排序。
优化方法三:采用优先队列机制,采用一个优先队列来遍历二叉树,从根节点往下的路径,根据查询节点与当前分割超平面距离(margin)进行排序。

ANNOY参考博客

5 HNSW

HNSW(Hierarchcal Navigable Small World graphs)是基于图的检索算法。

5.1 朴素想法

在这里插入图片描述
假设我们搜索上图粉色点的邻近向量,朴素想法是这样的,选择图中任意一点出发,向外遍历扩展,直至找到一个点,它离粉色点的距离小于它所有邻接点到粉色点的距离,遍历终止,我们认为该点是距离最近的点。
为了保证朴素想法的得以实现,我们在构建图时需要满足几个条件:
①不存在孤点,所有的点必须有友点
②距离近似的点互为友点
③尽量减少每个节点的友点数量,友点太多,影响检索速度

5.2 NSW

在图论中有一个很好的剖分法则专门解决上一节中提到的朴素想法的缺陷问题------德劳内(Delaunay)三角剖分算法,这个算法可以满足上述几个条件,但是德劳内算法构图的时间复杂度高,因此NSW算法并使用,而是使用了我们上面的朴素想法来构图。
朴素构图:向图中逐个插入点,插图一个全新点时,通过朴素想法中的朴素查找法(通过计算“友点”和待插入点的距离来判断下一个进入点是哪个点)查找到与这个全新点最近的m个点(m由用户设置),连接全新点到m个点的连线。完了
高速通道:为了保证检索效率,出了满足上述条件外,在图结构中,我们还想存在高速通道,假设我们要从上海到北京,常规思路是买火车票,火车可能会途径苏州、无锡、徐州、南京、济南等地,最终达到北京,但是我们买票的时候,发现有些列车只会在南京和济南两站停留,显然种列车更能快更节约时间,这样的列车就是高速通道。
我们分析下朴素构图,是否存在高速公路。首先,我们的构图算法是逐点随机插入的,这就意味着在图构建的早期,很有可能构建出“高速公路”。假设我们现在要构成10000个点组成的图,设置m=4(每个点至少有4个“友点”),这10000个点中有两个点,p和q,他们俩坐标完全一样。假设在插入过程中我们分别在第10次插入p,在第9999次插入q,请问p和q谁更容易具有“高速公路”?答:因为在第10次插入时,只见过前9个点,故只能在前9个点中选出距离最近的4个点(m=4)作为“友点”,而q的选择就多了,前9998个点都能选,所以q的“友点”更接近q,p的早期“友点”不一定接近p,所以p更容易具有“高速公路”。
结论:一个点,越早插入就越容易形成与之相关的“高速公路”连接,越晚插入就越难形成与之相关的“高速公路”连接。所以这个算法设计的妙处就在于扔掉德劳内三角构图法,改用“无脑添加”(NSW朴素插入算法),降低了构图算法时间复杂度的同时还带来了数量有限的“高速公路”,加速了查找。

5.2 跳表结构

我们知道,在一个有序链表中查找特定元素的时间复杂度是O(N),但如果链表中存在“高速通道”呢,如下图所示,最上面的链表稀疏,粒度也比较大,这就是高速通道,中间的是次高速通道,假设找68这个数字,先走高速通道,确定站点后,再走此高速,直至找到该节点。
在这里插入图片描述
这样的跳表很好,我们该如何构建呢,三个字,抛硬币。对于sorted_link链表中的每个节点进行抛硬币,如抛正,则该节点进入上一层有序链 表,每个sorted_link中的节点有50%的概率进入上一层有序链表。将上一层有序链表中和sorted_link链表中相同的元素做一一对应的指针链接。再从sorted_link上一层链表中再抛硬币,sorted_link上一层链表中的节点有50%的可能进入最表层,相当于sorted_link中的每个节点有25%的概率进入最表层。以此类推

5.3 HNSW

将跳表思路应用于NSW算法,就是它的升级版HNSW了。在朴素构建图的时候,我们兼带使用跳表思路。
在这里插入图片描述
HNSW的细节补充:
①检索或插入过程:从表层(上图中编号为Layer=2)任意点开始查找,选择进入点最邻近的一些友点,把它们存储在定长的动态列表中,别忘了把它们也同样在废弃表中存一份,以防后面走冤枉路。一般地,在第x次查找时,先计算动态列表中所有点的友点距离待查找点(上图绿色点)的距离,在废弃列表中记录过的友点不要计算,计算完后更新废弃列表,不走冤枉路,再把这些计算完的友点存入动态列表,去重排序,保留前k个点,看看这k个点和更新前的k个点是不是一样的,如果不是一样的,继续查找,如果是一样的,返回前m个结果。
②构图的时候,应确定该节点插入到哪一层,然后找到该层的m个最近邻点,进行连接
③参数:插入的时候,上述的动态列表越长,图的质量越好,但是也会降低检索或查找效率。其次每个节点的友点越多,图的质量越高,查找越精准,但同样也会降低检索效率

参考链接

6 倒排索引(补充)

倒排索引是搜索引擎经常使用的一种搜索算法,个人理解,常规的搜索是,在文档中搜索某个关键词(文档->关键词),而倒排索引是首先知道了每个关键词都出现在了哪些文档里,从关键词搜文档(关键词→文档),正好目的反过来,和“颠倒搜索”没什么关系

6.1 好处

普通的搜索,到文档中去找单词,非常耗时。
倒排索引先记录了每个关键词出现在了哪些文档里,需要哪个关键词,把含有的文档直接拎出来就可以,这样就快多了
主流的搜索工具,ElasticSearch都是基于倒排索引的方式

6.2 如何建立倒排索引

此处仅简单介绍其大概原理,假设有以下一些文档
在这里插入图片描述分词后可以得到这16个关键词,倒排列表中记录的是哪些文档包含这个关键词
在这里插入图片描述

附录 数据集及算法评估

数据集

在这里插入图片描述

工具及评估结果

知乎参考文章
原网址

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值