今天要讲博客博客都围绕一个问题主题展开:有一个包含了N个元素的集合,在向量化的参数空间里,给出任何一个节点i,如何在最短的时间复杂度的前提下找到该节点 i i i的 k k k临近向量子集。

当然第一直觉是最少也要把元素 i i i和集合里其余 N − 1 N-1 N−1个元素两两比较,这样才能从这 N − 1 N-1 N−1个元素中找到k个最大的元素。当然如果在N不是特别大的时候,这种方法也是可以的。但是如果是类似应用在搜索推荐系统的召回环节,往往候选集规模在千万量级,这种全部遍历的方法往往效率就不够了。这时候就需要对检索结构进行特殊的设计,甚至在一定程度上会牺牲部分“计算精度”来换取“检索时间”。今天准备将几种在工业界中使用的比较多的算法模型。(PS:今天的博客更侧重于从“what”和“how”这个角度来进行介绍,更多“why”等维度的内容会在未来抽空总结介绍)

HNSW检索框架

这种框架是目前比较火的向量k临街检索框架之一,阿里的“二向箔”检索技术就是在该框架的基础之上构建起来的。
其实HNSW(Hierarchcal Navigable Small World graphs)是NSW( Navigable Small World graphs)算法在高维空间的扩展版本,可以在NSW算法的基础上理解HNSW算法。

上图是NSW算法的构图结构。
为了加快k临街节点的搜索效率,首先需要构建一张图。这种图中每一个点代表了一个数据点,数据点会和自己最多 M M M个最近的邻居之间有边相连(M为构图流程中的超参数,用来权衡搜索过程的精度和速度),这些邻居节点也称之为“友点”:

构图之后需要保证:
1 所有数据向量节点都必须有友点;(必选项)
2 每个节点的友点个数最多不超过M个;(必选项)
3 距离相近的点最好互为“友点”;(必选项)(PS:无向图天然就满足这点)
4 最好有高速公路机制,可以加快搜索速度。(可选项)

知道了这个图的需要满足的性质,那么在得到图后如何检索距离某一个query节点(图中绿色节点)的k临近节点呢,下面来讲解一下这个搜索检索过程:(后续的构图过程也依赖该流程)
1 随机从图中选择一个节点作为enter point;
2 从计算enter point节点以及其友点和 query之间的距离,并把top n(n一般大于等于k)节点存入中间结果动态缓存列表中;同时在查找的过程中,为了提高效率,我们可以建立一个废弃列表,在一次查找任务中遍历过的点不再遍历。
3 根据中间结果动态缓存列表中的结果,并行地对这n个点进行同时计算“友点”(前提是这些友点没有在废弃列表中出现)和待查找点的距离,在这些“友点”中选择n个点与动态列表中的n个点进行并集操作,在并集中选出n个最近的友点,更新动态列表和废弃列表;
4 当根据中间结果无法继续扩展出新的节点的时候,整个算法结束。取动态列表中的top s元素即可作为最终的检索结果。

在了解了基于NSW算法的top S检索策略之后,那么整个构图的流程就更简单了:
向图中逐个插入点,插图一个全新点时,通过朴素想法中的朴素查找法(通过计算“友点”和待插入点的距离来判断下一个进入点是哪个点)查找到与这个全新点最近的m个点(m由用户设置),连接全新点到m个点的连线。(PS:但是在节点插入环节有一个细节需要注意下,由于连接的边是无向的。在当前节点和m个临近节点在进行连边操作的时候,有可能会导致这m个临近节点中的某一个节点u的临邻居会超过m个约束。这时候就需要将u的m+1个临近节点全部拿出来按照距离u的距离进行排序,只保留前m个节点的边)

上述构图过程虽然看着简单,但其实仔细思考的话,是能够满足上面的几个必选项和那一个可选项的。同时如果对整个流程熟悉的话,会发现“高速公路机制”的连边往往会在构图的早期生成。

上面讲解完了NSW,在讲HNSW之前,可以先了解一种数据结构:跳表。

图中的跳表的底层level 1 是原始的明细数据层,上面的level 2 和level 3分别是“索引”层。假设原始数据序列是按照顺序并且底层的数据结构采用的是链表这种链式的访问结构的话,使用跳表,尤其是整个结构中的层和层之间的“跳转”能够大大的节约跳转时间。
如果理解了跳表的结构和工作原理,再加上对NSW工作流程的熟悉。那么对HNSW=NSW+跳表。

上图就是HNSW的模型结构示意图,其中layer 1 ,layer 2 和 layer 3每一层都可以看做是NSW,最底层的Layer 0中的节点包含了所有图中的节点,layer 1 和layer 2只包含了图中的一部分节点。同时从图中不难看出,layer 2 的节点数量比layer 1的节点数量更少。

至于每一个节点最多能够到达第几层,是按照公式 P = f l o o r ( − l n ( u n i f o r m ( 0 , 1 ) ) ∗ l a y e r N u m ) P=floor(-ln(uniform(0,1))*layerNum) P=floor(−ln(uniform(0,1))∗layerNum)算出来的。该公式的执行每一次之间都是独立的。

这样在插入的过程中,针对目标待插入节点u。可以先计算得到其最终能够到达的最大层次,然后在每一层能够到达的层次中按照NSW的算法进行节点的插入。检索过程如下所示:

但是这里有一点可能需要注意的是,为了避免构造的图产生local minimum现象,降低搜索效率:

在从中间结果动态缓存列表中选择top r元素作为邻居的时候:
假设当前出队的是节点A,x为待搜索query。最终的结果集合是R,初始化为空。
对于R中任意一个节点n,如果dist(A,n) < dist(A,x),说明节点A和已有的结果集中的节点离的太近了,这样的节点会被丢弃,正如上文所说,要避免只有邻近的cluster被选中做邻居。
如果R中的每个节点都和A离的比较远的话,A就可以进入R了。
当然这样的做法可能会导致过滤得到的合适节点数量小于目标值r。算法会在最后加一个兜底策略,从那些被淘汰掉的节点集合中选择距离x更近的进行补充。

基于HNSW算法的好处是泛化能力强,整个算法框架可以灵活的和各种距离计算函数(甚至是复杂的NN打分模型)进行结合,非常方便。

PQ检索框架

所谓PQ即为Product quantization(乘积量化)的简写。facebook著名的开源框架faiss就用了该方法进行加快检索。(PS:faiss库中另一个向量降维的方法为PCA)。

PQ是一种建立索引的方式,假设原始向量是1024维,可以把它拆解成8个子向量,每个子向量128维。对于这8组子向量的每组子向量,使用 kmeans 方法聚成k=256类。也就是说,每个子向量有256个中心点(centroids)。如下图。


在product quantization方法中,这256个中心点构成一个码本。这些码本的笛卡尔积就是原始D维向量对应的码本。这样在计算两个向量 v e c t o r A vector_A vectorA​和 v e c t o r B vector_B vectorB​的距离的时候,可以按照向量维度进行分成8段,分别计算两个向量不同分段对应中心点的码本距离,然后将8段得到的码本距离加和就得到了最终向量A和向量B的近似距离。

注意到每组子向量有其256个中心点,我们可以中心点的 ID 来表示每组子向量中的每个向量。中心点的 ID只需要 8位(= l o g 2 256 log_2{256} log2​256)来保存即可。这样,初始一个由32位浮点数组成的1,024维向量,可以转化为8个8位整数组成。如下图。


对向量压缩后,有2种方法作相似搜索。一种是SDC(symmetric distance computation),另一种是ADC(asymmetric distance computation)。SDC算法和ADC算法的区别在于是否要对查询向量x做量化,参见公式1和公式2。如下图所示,x是查询向量(query vector),y是数据集中的某个向量,目标是要在数据集中找到x的相似向量。

SDC算法:先用PQ量化器对x和y表示为对应的中心点q(x)和q(y),然后用公式1来近似d(x,y)。这里 q 表示 PQ量化过程。

ADC算法:只对y表示为对应的中心点q(y),然后用下述公式2来近似d(x,y)。

KD-Tree检索框架

kd(k-dimensional)树的概念自1975年提出,试图解决的是在k维空间为数据集建立索引的问题。依上文所述,已知样本空间如何快速查询得到其近邻?唯有以空间换时间,建立索引便是计算机世界的解决之道。但是索引建立的方式各有不同,kd树只是是其中一种。它的思想如同分治法,即:利用已有数据对k维空间进行切分。该算法在sikit-learn中的KNN算法中有所使用。

其实整个kd树的构造流程还是比较简洁的,整体结构可以表现为一颗二叉树:

针对每一个节点内的数据,计算k个维度中每一个维度数据的方差,选取方差最大的维度为分裂维度(PS:方差值越大说明数据在该维度上越分散,则更适合用来作为分裂的维度)。然后将数据按照该目标维度取值进行排序,选择排序后的目标中间值作为节点分裂值。

值得注意的是,在kd树的实现过程中,切分域的选择并不一定采用方差选择方式,而可能只是简单的以 d mod n 维度的方式,来决定该层树的切分域(d代表树节点的深度,n代表维度数量,求模来确定切分域)。这样实现简单,效果也一般不会太差。

其查找过程如下所示:
1 寻找近似点-寻找最近邻的叶子节点作为目标数据的近似最近点。
2 回溯-以目标数据和最近邻的近似点的距离沿树根部进行回溯和迭代。

由于整个KD-Tree的构建过程和查找过程比较清晰简单,符合直觉,这里就不再赘述。具体的其他相关细节可以看:https://zhuanlan.zhihu.com/p/53826008。

但从上面三个算法本身来看。HNSW模型框架本身并没有和任何一种距离计算范式耦合,能够支持任意定制的距离计算函数。而Product quantization和KD-Tree框架本身和欧式距离的计算范式耦合较为紧密。个人感觉在一定程度上限制了算法模式的应用场景。

常见的k临近向量检索算法相关推荐

  1. 13 种高维向量检索算法全解析!数据库顶会 VLDB 2021 论文作者干货分享

    编者按: 以图搜图.商品推荐.社交推荐等社会场景中潜藏了大量非结构化数据,这些数据被工程师们表达为具有隐式语义的高维向量.为了更好应对高维向量检索这一关键问题,杭州电子科技大学计算机专业硕士王梦召等人 ...

  2. annoy向量检索算法

    一:算法目标 annoy 算法的目标是建立一个数据结构能够在较短的时间内找到任何查询点的最近点,在精度允许的条件下通过牺牲准确率来换取比暴力搜索要快的多的搜索速度. 二:算法流程 1:建立索引 Ann ...

  3. KNN(K临近分类)算法

    1.算法描述 主要功能:根据所有的已知类别的实例来判断未知实例的类别 主要过程:首先确定参数K,然后计算未知实例与所有已知实例的距离,选择其中最近的K个已知实例,根据少数服从多数的投票法则,让未知实例 ...

  4. 算法工程 # 深度学习算法落地最后一公里:工业界中的大规模向量检索

    前言:现代深度学习实践中很多场景其实都是对输入数据进行处理.嵌入,最终获得一个 embedding,然后对 embedding 进行相似度检索,而工业界中的被检索数据往往是海量的,因此深度学习模型落地 ...

  5. 向量检索(一)Faiss 在工业界的应用和常见问题解决

    一.向量检索的场景 传统的搜索,使用关键做精确的查找,利用倒排索引在索引库中搜索.日常在用的百度,Google都属于关键词搜索. 在 AI 时代,我们需要查找一张相似的图片,一个问题的答案,或者根据一 ...

  6. 蚂蚁金服 ZSearch 在向量检索上的探索

    图为 ZSearch 基础架构负责人十倍 2019 Elastic Dev Day 现场分享 引言 ElasticSearch(简称 ES)是一个非常受欢迎的分布式全文检索系统,常用于数据分析,搜索, ...

  7. 向量检索的索引构建算法综述

    目录 VectorRetrieval github 落地场景 背景 框架 检索模型--暴力搜索.倒排索引 检索模型--kd-Tree.Annoy KD-Tree: Annoy: 检索模型--LSH 检 ...

  8. KNN算法(K临近算法)及使用KNN算法实现手写数字0-9识别

    首先感谢博主倔强的小彬雅,本文使用的素材及部分代码来源其博文机器学习入门-用KNN实现手写数字图片识别(包含自己图片转化),需要下载素材的可以到其博文最后进行下载. 关于KNN算法 knn算法也叫K临 ...

  9. 【机器学习】实验一 K临近算法

    K临近算法 1.什么是K临近算法? 所谓K近邻算法,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(也就是上面所说的K个邻居), 这K个实例的多数属于某个类,就把该 ...

最新文章

  1. iOS下bound,center和frame
  2. JBPM executionService.deleteProcessInstanceCascade(id)报错
  3. 四针角oled屏连接arduino_使用Arduino开发板连接OLED显示屏制作一款智能手表
  4. USACO 1.2 Milking Cows (枚举)
  5. noip2004普及组第2题 花生采摘
  6. 输入一个字符串,删除汉字字符或者西文字符(C语言)
  7. 滴滴这车值不值得上?前Google全球技术总监郄小虎说来来来
  8. JavaScript-Date类的getMonth方法释疑
  9. linux usb有线网卡驱动_linux系统下安装usb网卡驱动图文?
  10. Confluence 6 后台中的默认空间模板设置
  11. 2022电大国家开放大学网上形考任务-大学语文非免费(非答案)
  12. sql server查看密码使用天数和剩余天数
  13. word转freemarker和修改的步骤
  14. Visustin(自动生成流程图)使用说明
  15. proteus中继电器怎么找_proteus中这个开关在哪
  16. 词法分析器设计与实现
  17. 【运维】记一次yapi安全漏洞导致服务器被木马入侵的处理过程
  18. 在pycharm中%matplotlib inline报错!!!
  19. 633_AUTOSAR_AUTOSAR_TR_Glossary_文档阅读3
  20. 罗技K580键盘快捷键(ipad)

热门文章

  1. anaconda 安装 PuLP
  2. 清除console密码重起后,部分版本又会要输入默认console密码
  3. 考研数学三真题1987-2022年所有历年真题及解析(高清无水印)
  4. iOS开发之Xcode8打印台[NWConcrete_nw_endpoint_proxy cancelWithHandler:forced:] [7 api.weibo.com:443 cancell
  5. 如何删除CSDN博客
  6. 央企79号文助推信创,江民科技持续占据终端安全市场第一梯队
  7. 使用Python开发一个超级简单的接水果小游戏,零基础也可以学会
  8. Crawling is going on - Beta版本测试报告
  9. 使用matlab画可以旋转的太极图
  10. 前端性能优化-服务端和网络优化-极客时间