Skip to content

Commit

Permalink
add annoy update hnsw
Browse files Browse the repository at this point in the history
  • Loading branch information
vichayturen committed Jan 30, 2024
1 parent 706bffe commit 8eca6ed
Show file tree
Hide file tree
Showing 14 changed files with 72 additions and 8 deletions.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
67 changes: 67 additions & 0 deletions src/zh/posts/rag/Annoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
author: 最后的开神-wkyc
icon: blog
date: 2024-01-30
shortTitle: Annoy向量检索算法
category:
- rag
tag:
- 向量检索
- rag
# sticky: 10
---

# 近似最近邻搜索算法Annoy

本文以 $R^2$ 中的点集来作为案例,介绍 Annoy(APPROXIMATE NEAREST NEIGHBORS OH YEAH)算法的基本思想和算法原理。

<!-- more -->

用 n 表示现有的文档个数,如果采用暴力搜索的方式,那么每次查询的耗时是 O(n), 采用合适的数据结构可以有效地减少查询的耗时,在 annoy 算法中,作者采用了二叉树这个数据结构来提升查询的效率,目标是把查询的耗时减少至 O(ln⁡(n)).

## 1 构建二叉树索引

刚开始的时候,在数据集中随机选择两个点,然后用它们的中垂线来切分整个数据集,于是数据集就被分成了蓝绿两个部分。然后再随机两个平面中各选出一个顶点,再用中垂线进行切分,于是,整个平面就被切成了四份。

![](/assets/images/rag/annoy_1.webp "图1.1 切分前示意图")

![](/assets/images/rag/annoy_2.webp "图1.2 一次切分后示意图")

用一颗二叉树来表示这个被切分的平面就是:

![](/assets/images/rag/annoy_4.webp "图1.3 两次切分二叉树示意图")

后续继续采用同样的方式进行切分,直到每一个平面区域最多拥有 K 个点为止。当 K = 10 时,其相应的切分平面和二叉树如下图所示。

![](/assets/images/rag/annoy_6.webp "图1.4 K=10切分平面示意图")

![](/assets/images/rag/annoy_7.webp "图1.5 K=10二叉树示意图")

下面,新来的一个点(用红色的叉表示),通过对二叉树的查找,我们可以找到所在的子平面,然后里面最多有 K = 10 个点。从二叉树的叶子节点来看,该区域只有 7 个点。

![](/assets/images/rag/annoy_8.webp "图1.6 查询点示意图")

在 ANN 领域,最常见的两个问题是:
(1)如果我们想要 Top K 的点,但是该区域的点集数量不足 K,该怎么办?
(2)如果真实的 Top K 中部分点不在这个区域,该怎么办?
作者用了两个技巧来解决这个问题:
1.使用优先队列(priority queue):将多棵树放入优先队列,逐一处理;并且通过阈值设定的方式,如果查询的点与二叉树中某个节点比较相似,那么就同时走两个分支,而不是只走一个分支;
2.使用森林(forest of trees):构建多棵树,采用多个树同时搜索的方式,得到候选集 Top M(M > K),然后对这 M 个候选集计算其相似度或者距离,最终进行排序就可以得到近似 Top K 的结果。
同时走两个分支的的示意图:

![](/assets/images/rag/annoy_10.webp "图1.7 查询平面示意图")

![](/assets/images/rag/annoy_11.webp "图1.8 查询二叉树示意图")

随机生成多棵树,构建森林的示意图:

![](/assets/images/rag/annoy_12.webp "图1.9 构建森林示意图")

## 2 算法原理:
构建索引:建立多颗二叉树,每颗二叉树都是随机切分的;
查询方法:
(1)将每一颗树的根节点插入优先队列;
(2)搜索优先队列中的每一颗二叉树,每一颗二叉树都可以得到最多 Top K 的候选集;
(3)删除重复的候选集;
(4)计算候选集与查询点的相似度或者距离;
(5)返回 Top K 的集合。
13 changes: 5 additions & 8 deletions src/zh/posts/rag/HNSW.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,23 @@ ANN最近邻搜索广泛应用在各类搜索、分类任务中,在超大的
(4)基于图:NSW,HNSW。
HNSW(Hierarchical Navigable Small World)是ANN搜索领域基于图的算法,我们要做的是把D维空间中所有的向量构建成一张相互联通的图,并基于这张图搜索某个顶点的K个最近邻,如下图所示:

![](/assets/images/rag/hnsw_1.webp "图1.1 示意图")
![](/assets/images/rag/hnsw_1.webp "图1.1 D维空间中的点进行构图示意图")

D维空间中的点进行构图
## 1 Delaunay graph
说起HNSW,就不能不提前作NSW,在NSW中作者建立了一个类Delaunay graph,所以这里先看了下Delaunay graph。
Delaunay graph的构建流程如下:
(1)构造一个超级三角形,包含所有散点,放入三角形链表;
(2)将点集中的散点依次插入,在三角形链表中找出其外接圆包含插入点的三角形(称为该点的影响三角形),删除影响三角形的公共边,将插入点同影响三角形的全部顶点连接起来,从而完成一个点在 Delaunay 三角形链表中的插入;
(3)根据优化准则对局部新形成的三角形进行优化。将形成的三角形放入 Delaunay 三角形链表;
(4)循环执行上述第 2 步,直到所有散点插入完毕。
第2步如下图所示:

![](/assets/images/rag/hnsw_2.webp "图1.2 示意图")
![](/assets/images/rag/hnsw_2.webp "图1.2 Delaunay graph构建第2步示意图")

形成的Delaunay graph有这些优点:图中每个点都有“友点”,不会有“孤点”存在,导致无法搜索到;相近的点都互为“友点”,保证了搜索结果的准确性;图中所有连接(线段)的数量最少,保证了搜索的效率。
## 2 NSW Graph
NSW没有采用这种方式进行建图,原因是这种建图方式建图和搜索的时间复杂度太高,NSW的建图方式非常简单,就是向图中插入新点时,通过随机存在的一个点出发查找到距离新点最近的m个点(m由用户设置),连接新点到这最近的m个点。

![](/assets/images/rag/hnsw_3.webp "图2.1 示意图")
![](/assets/images/rag/hnsw_3.webp "图2.1 NSW Graph插入节点示意图")

NSW论文中配了这样一张图,黑色是近邻点的连线,红色线就是“高速公路机制”了。我们从enter point点进入查找,查找绿色点临近节点的时候,就可以用过红色连线“高速公路机制”快速查找到结果。高速公路是怎么形成的呢?在初始建图时,点还很少,这时连接的m个点在建图后期中间可能加入了成千上万个点,所以在建图初期连接的这些边就自然变成了“高速公路”。
对于查询来说,这里有三个点集合:candidates、visitedset、results 。其中candidates为当前要考察的点集合,visitedset为已经访问过的点集合,results为当前距离查询点最近的点集合;前两者为变长,最后为定长。
Expand Down Expand Up @@ -82,9 +80,8 @@ return best k elements from result
## 3 HNSW Graph
HNSW是对NSW的进一步优化,论文中的一张图足以解释HNSW做了什么改变:

![](/assets/images/rag/hnsw_4.webp "图3.1 示意图")
![](/assets/images/rag/hnsw_4.webp "图3.1 HNSW示意图")

HNSW
(1)第0层中包含图中所有节点;
(2)向上节点数依次减少,遵循指数衰减概率分布;
(3)建图时新加入的节点由指数衰减概率函数得出该点最高投影到第几层;
Expand Down Expand Up @@ -113,6 +110,6 @@ HNSW
edge(p1, p2) occludes edge(p1, p3) if
d(p1, p2) < d(p1, p3) and d(p2, p3) < d(p1, p3)

![](/assets/images/rag/hnsw_5.webp "图3.2 示意图")
![](/assets/images/rag/hnsw_5.webp "图3.2 裁剪边示意图")

用在这张图上,p1连接了p2、p3、p4,其中p1p3会被裁掉。

0 comments on commit 8eca6ed

Please sign in to comment.