nsw & hnsw

参考了很多该博客 https://blog.csdn.net/u011233351/article/details/85116719,感谢博主。

参考论文《Approximate nearest neighbor algorithm based on navigable small world graphs》

《Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs》

----------------------------------先了解NSW------------------------------------

 

NSW论文中配了这样一张图,黑色是近邻点的连线,红色线就是“高速公路机制”了。我们从enter point点进入查找,查找绿色点临近节点的时候,就可以用过红色连线“高速公路机制”快速查找到结果。

为什么会形成“高速公路”呢?来看下面的例子

我们对7个二维点进行构图,用户设置m=3(每个点在插入时找3个紧邻友点)。首先初始点是A点(随机出来的),A点插入图中只有它自己,所以无法挑选“友点”。然后是B点,B点只有A点可选,所以连接BA,此为第1次构造。然后插入F点,F只有A和B可以选,所以连接FA,FB,此为第2此构造。然后插入了C点,同样地,C点只有A,B,F可选,连接CA,CB,CF,此为第3次构造。重点来了,然后插入了E点,E点在A,B,F,C中只能选择3个点(m=3)作为“友点”,根据我们前面讲规则,要选最近的三个,怎么确定最近呢?朴素查找!从A,B,C,F任意一点出发,计算出发点与E的距离和出发点的所有“友点”和E的距离,选出最近的一点作为新的出发点,如果选出的点就是出发点本身,那么看我们的m等于几,如果不够数,就继续找第二近的点或者第三近的点,本着不找重复点的原则,直到找到3个近点为止。由此,我们找到了E的三个近点,连接EA,EC,EF,此为第四次构造。第5次构造和第6次与E点的插入一模一样,都是在“现成”的图中查找到3个最近的节点作为“友点”,并做连接。

        图画完了,请关注E点和A点的连线,如果我再这个图的基础上再插入6个点,这6个点有3个和E很近,有3个和A很近,那么距离E最近的3个点中没有A,距离A最近的3个点中也没有E,但因为A和E是构图早期添加的点,A和E有了连线,我们管这种连线叫“高速公路”,在查找时可以提高查找效率(当进入点为E,待查找距离A很近时,我们可以通过AE连线从E直接到达A,而不是一小步一小步分多次跳转到A)。

-------------------------------------划重点:越早插入的点越容易产生高速公路-------------------------

对于查询来说,这里有三个点集合:candidates、visitedset、results 。其中candidates为当前要考察的点集合,visitedset为已经访问过的点集合,results为当前距离查询点最近的点集合;前两者为变长,最后为定长。

查询流程如下:

  1. 随机选择一个点作为查询起始点entry_point,把该点加入candidates中,同时加入visitedset
  2. 遍历candidates,从candidates中选择距离查询点最近的点c,和results中距离查询点最远的点d进行比较,如果c和查询点q的距离大于d和查询点q的距离,则结束查询,说明当前图中所有距离查询点最近的点已经都找到了,或者candidates为空
  3. 从candidates中删除点c
  4. 查询c的所有邻居e,如果e已经在visitedset中存在则跳过,不存在则加入visitedset
  5. 把比d和q距离更近的e加入candidates、results中,如果results未满,则把所有的e都加入candidates、results。如果results已满,则弹出和q距离最远的点
  6. 循环2-5     

论文中的伪码:

K-NNSearch(object q, integer: m, k)
TreeSet [object] tempRes, candidates, visitedSet, result 
// 进行m次循环,避免随机性
for (i←0; i < m; i++) do:
    put random entry point in candidates
    tempRes←null
    repeat:
        // 利用上述提到的贪婪搜索算法找到距离q最近的点c
        get element c closest from candidates to q
        remove c from candidates
        // 判断结束条件
        if c is further than k-th element from result then
            break repeat
        // 更新后选择列表
        for every element e from friends of c do:
            if e is not in visitedSet then
                add e to visitedSet, candidates, tempRes
    end repeat
    // 汇总结果
    add objects from tempRes to result 
end for 
return best k elements from result

-----------------------------------------装个逼,写一句自己的理解,欢迎大佬修正--------------------------------

         其实搜索过程还是图搜索里面open-closed表那一套吧,所以NSW最重要的是构图机制(伪德劳内,其实跟德劳内没有版毛钱关系)产生了高速公路,而且算法复杂度要比德劳内算法更小

----------------------------------------------NSW部分完,接下来是HNSW------------------------------------------------

HNSW

-----------------------------------------------先来了解跳表---------------------------------------------

正常查找需要遍历八个节点,现在只需要遍历七个节点,这个数据量比较小,优势不太明显。数据量越大有优势越明显

这样就保证了表层是“高速通道”,底层是精细查找,这个思想被应用到了NSW算法中,变成了其升级版-----HNSW

 

---------------------------------------------------先看查找最近邻算法,如下图----------------------------------

输入:q是待插入点,ep是enter point(s),ef是返回最近邻的数量,lc是根据公式计算的q在哪些层

输出:q的ef个最近邻

c是候选点里面的某一点

f是上一步找到的最近邻点里面的最远点

7,8:如果c和q的距离大于f和q的距离,重新提取c

9,如果小于,就访问c的neighbours,更新已访问点v的数组和最远点f的值

13-17:如果e和q的距离小于f和q的距离,把e更新到候选点C的数组和返回最近邻点W的数组,如果W数组的长度大于ef的个数,就把最远点删除,从这可以看出W是一个动态链表,它的长度随着ef的个数而变化。

 

-----------------------------------------------------接下来是hnsw算法构建graph过程--------------------------------------------------------------

 

HNSW里面的多层结构不同于传统意义上的跳表,它的每一层不是顺序表,而是近邻图

这个算法总的来说干了个啥:遍历需要搜索的所有点,把每个点依次插入之后,确定该点所在的层数,建立该点与graph中其他点的连接关系(就是确定该点的邻居都有谁),举个例子:如果该点的层数l是3,那么算法会依次遍历3,2,1,0层的节点,分别在每一层用上面提到的SEARCH-LAYER算法找到自己的邻居,与这些邻居建立连接关系,每一层的每个节点的邻居数量是有限的(Mmax),如果大于这个数,就会丢弃最远的邻居,直到把所有点都insert到graph里面。

输入:graph ,q是待插入点,Mmax是每个点在某一层最多的连接数

输出:插入q,更新hnsw

第一步,L是top layer, l是根据公式(详见论文)算出来的layer,ef=1,就是简单的greedy search ,搜索到一个最接近的节点;

第二步,把第一步中获取的近邻值ep作为enter point,并用上面提到的SEARCH-LAYER依次在每一层查找最近邻,搜索到的最近邻会通过SELECT-NEIGHBOURS选取前几个到neighbours中,中间这几部比较简单,就不多说了,最后把搜索到的最近邻作为下一次的enter points。这里并不是从顶层开始遍历每一层,而是从min(L,l)到第0层

第二步开始以后,返回的近邻数量就从1变为efconstruction个了,这是为了保证检索的召回率;找到的最近邻值也作为插入值的候选,the found closest neighbors on each layer are also used as candidates for the connections of the inserted element。

第11行,找到这些邻居之后,将待插入点q和这些邻居之间建立联系,

Mmax是第0层以外的每一层的每个节点的最大边数,Mmax0是第0层的每个节点的最大边数(connections)

 

插入点q的层数l是个随机数,从l层到0层都会建立连接关系,第8-11行说明了该问题

 

----------------------------接下来就是利用KNN-SEARCH在建好的HNSW graph进行检索q的过程---------------------------

这个就比较好懂了,

先遍历top layer L到第1层,找到一个最近邻值作为下一步搜索的enter point

然后在第0层搜索,得到K个最近邻值。

 

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值