邻近搜索(Annoy HNSW LSH KD tree)
大纲Annoy:Approximate Nearest Neighbors Oh YeahHNSW:Hierarchcal Navigable Small World graphsKD Tree:K dimentional TreeLSH:Locality Sensitive HashingAnnoyAnnoy 是 Spotify 开源的高维空间求近似最近邻的库,在 Spotify使用它进行音乐推
大纲
Annoy:Approximate Nearest Neighbors Oh Yeah
HNSW:Hierarchcal Navigable Small World graphs
KD Tree:K dimentional Tree
LSH:Locality Sensitive Hashing
Annoy
Annoy 是 Spotify 开源的高维空间求近似最近邻的库,在 Spotify
使用它进行音乐推荐。
Annoy通过将海量数据建立成一个二叉树来使得每个数据查找时
间复杂度是O(log n)。
HNSW
近邻图(Proximity Graph):最朴素的图算法
思路:构建一张图,每一个顶点连接着最近的 N 个顶点。Target (红点)是待查询的向量。
在搜索时,选择任意一个顶点出发。首先遍历它的友节节,找到距离与 Target 最近的某一节点,将其
设置为起始节点,再从它的友节点出发进行遍历,反复迭代,不断逼近,最后找到与 Target 距离最近
的节点时搜索结束。
近邻图存在问题解决方法:
某些点无法被查询到 -> 规定构图时所有节点必须有友节点。
相似点不相邻的问题 -> 规定构图时所有距离相近到一定程度的节点必
须互为友节点。
关于某些点有过多友节点的总是 -> 规定限制每个节点的友节点数量。
初始点选择地很远 -> 增加高速公路机制(跳表)。
建图过程(规定最多四个友节点):
节点的友节点在新的节点插
入的过程中会不断地被更新。
如何增加高速公路机制 ?
HNSW = NSW + 跳表
飞机
地铁
步行
** 去目的地 先坐飞机,在做地铁 最后步行
code:
https://github.com/nmslib/hnswlib
KD Tree
KD Tree
K:K邻近查询中的k;D:空间是D维空间(Demension)tree:二叉树
K-D tree的建立就是分裂空间(分成两堆)的过程。
计算每个点的坐标的每一维度上的方差,取方差最大的那一维对应的中间
值,作为分裂点。
以此类推,直到每个空间中最多有一个点。
以此类推,直到每个空间中最多有一个点。
如何查找某个点的临近点?
首先通过二叉树搜索(比较待查询节点和分裂节点的分裂维的值,小于等于就进入左子树分支,等于就进入右子树分支直到叶子结点);
顺着搜索路径找到最近邻的近似点,也就是与待查询点处于同一个子空间的叶子结点;
回溯搜索路径,并判断搜索路径上的结点的其他子结点空间中是否可能有距离查询点更近的数据点,如果有可能,则需要跳到其他子结点空间中去搜索(也就是将其他子结点加入到搜索路径)。
重复这个过程直到搜索路径为空。
如何查找某个点的临近点?
- 现有一个k-d tree:样本集{(2,3), (5,4), (9,6), (4,7),(8,1), (7,2)}
切分轴:粗实线,细实线,虚线 查找点(2.1,3.1)的邻近点? search_path:<(7,2), (5,4), (2,3)>
(2,3):作为当前最佳结点nearest, dist为0.141
search_path:<(7,2), (5,4), (2,3)>
- (2,3):作为当前最佳结点nearest,
dist为0.141回溯至(5,4),以(2.1,3.1)为圆心,以dist=0.141为半径画一个圆,并不和超平面y=4相交,所以不必跳到结点(5,4)的右子空间去搜索,因为右子空间中不可能有更近样本点了。
回溯至( 7 , 2 ) , 同理, 以( 2 . 1 , 3 . 1 ) 为圆心,
以dist=0.141为半径画一个圆,并不和超平面x=7相交,所以也不用跳到结点(7,2)的右子空间去搜索。
至此,search_path为空,结束整个搜索,返回nearest(2,3)作为(2.1,3.1)的最近邻点,最近距离为0.141。
查找点(2,4.5)?
search_path:<(7,2), (5,4), (4,7)>
从search_path中取出(4,7)作为当前最佳结点nearest,dist为3.202;
回溯至(5,4),以(2,4.5)为圆心,以dist=3.202为半径画一个圆与超平面y=4相交,所以需要跳到(5,4)的左子空间去搜索。
将(2,3)加入到search_path中,
search_path:<(7,2), (2, 3)>;
另,(5,4)与(2,4.5)的距离为3.04 < dist = 3.202,所
以将(5,4)赋给nearest,并且dist=3.04。
search_path:<(7,2), (2, 3)>
dist=3.04
回溯至(2,3),(2,3)是叶子节点,直接平判断(2,3)是否离(2,4.5)更近,计算得到距离为1.5,所以nearest更新为(2,3),
dist更新为(1.5)。
回溯至(7,2),同理,以(2,4.5)为圆心,以dist=1.5为半径画一个圆,不和超平面x=7相交, 所以不用跳到结点(7,2)的右子空间去搜索。
至此,search_path为空,结束整个搜索,返回nearest(2,3)
作为(2,4.5)的最近邻点,最近距离为1.5。
LSH
min-hashing
特征矩阵按行进行一个随机的排列后,第一个列值为1的行的行号。
两列的min-hashing相等的概率就是这两列的Jaccard相似度。
P(h(Si)=h(Sj)) = sim(Si,Sj)
可以把一篇文档看作关于词的集合。
集合S,T的Jaccard相似度:
S = “我 喜欢 苹果”,T = “我 喜欢 土豆”
集合S,T的Jaccard相似度:2/4
选择n个随机排列作用于特征矩阵,得到n个min hash值,h1,h2,…,hn。
这n个最小hash值组成一个n维向量,称为最小签名,两列的最小签名的相似度即为两列的Jaccard相似度的估计
局部敏感hash
对签名矩阵M按行进行分组,将矩阵分成b组,每组由r行组成。
n = b * r
- 只要两列有一组的最小签名相同,那么这两列就会放到同一个桶而成为候选相似项。
局部敏感hash
• 令 p = sim(si,sj)
• 已知两个min-hash相同的概率 =两列的相似度= p
• b组,r行
(1)在某个组中所有行的两个签名值都相等概率是 pr
(2)在某个组中至少有一对签名不相等的概率是1−pr
(3)在每一组中至少有一对签名不相等的概率是(1−pr) b
(4)至少有一个组的所有对的签名相等的概率是1−(1−pr) b
Annoy: https://github.com/spotify/annoy
HNSW: https://github.com/nmslib/hnswlib
LSH: https://github.com/mattilyra/LSH
KD tree: sklearn
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)