个人博客:www.mzwang.top
Efficient K-Nearest Neighbor Graph Construction for Generic Similarity Measures
Wei Dong([email protected]);
Moses Charikar([email protected]);
Kai Li([email protected]).
Department of Computer Science, Princeton University
In Proceedings of the 20th international conference on World wide web; 2011
K近邻图的构建在很多基于Web的应用上是一个重要的操作,比如协同过滤(基于用户的邻居作推荐)、相似性搜索等。一个有效地构建方法将使K近邻图的应用更加广泛。
暴力构建K近邻图的时间复杂度为 O ( n 2 ) O(n^2) O(n2),为了能更高效的构建K近邻图,现存的工作扩展性都不太好,而且一般都特定于具体的相似性度量。
有效的K近邻图构建仍然是一个开放的问题,解决该问题的已知方案中没有一个是通用、有效和可扩展的。因此,本文提出了NN-Descent方法,该方法具有以下优点:
通用。适用于任意的相似性度量准则。
可扩展。随着数据集尺寸的增加,Recall仅有很小的下降。由于对每一个数据点的局部信息进行操作,因此适用于分布式计算环境(MapReduce).
节省空间。整个构建过程仅涉及到一种数据结构——近邻图。
快速、精确。百分之几的相似性比较便可实现90%以上的召回率。
容易实施。主要代码不超过200行(C++)。
如何有效地构建一个K近邻图,具体如下:
V V V表示数据集,数据集尺寸为 N = ∣ V ∣ N=|V| N=∣V∣,相似性度量 σ \sigma σ: V × V → R V \times V \rightarrow R V×V→R。 ∀ v ∈ V \forall v \in V ∀v∈V, B K ( v ) B_K(v) BK(v)表示 v v v的 K K K个最近邻, R K ( v ) = { u ∈ V ∣ v ∈ B K ( u ) } R_K(v)= \lbrace u \in V | v \in B_K(u) \rbrace RK(v)={u∈V∣v∈BK(u)}表示 v v v的反向K个最近邻。 B [ v ] B[v] B[v]和 R [ v ] R[v] R[v]分别表示 B K ( v ) B_K(v) BK(v)和 R K ( v ) R_K(v) RK(v)的近似。 B ‾ [ v ] = B [ v ] ∪ R [ v ] \overline{B}[v]=B[v] \cup R[v] B[v]=B[v]∪R[v]表示 v v v的一般邻居。
当在 V V V上的度量方式为距离度量时,即 d d d: V × V → [ 0 , + ∞ ] V \times V \rightarrow [0,\ +\infty] V×V→[0, +∞]。 ∀ r ∈ [ 0 , + ∞ ] \forall r \in [0,\ +\infty] ∀r∈[0, +∞],以 v v v为球心的r-球定义为: B r ( v ) = { u ∈ V ∣ d ( u , v ) ≤ r } B_r(v)=\lbrace u \in V | d(u, \ v) \leq r\rbrace Br(v)={u∈V∣d(u, v)≤r}。
如果 ∃ c \exists c ∃c满足:
∣ B 2 r ( v ) ∣ ≤ c ∣ B r ( v ) ∣ , ∀ v ∈ V (1) |B_{2r}(v)| \leq c|B_{r}(v)|, \ \forall v \in V \tag{1} ∣B2r(v)∣≤c∣Br(v)∣, ∀v∈V(1)
则称度量空间V增长受限, c c c是增长常量。
基本思想:邻居的邻居更可能是邻居。
我们可以从 V V V中每一个点的现有的近似K近邻出发,通过探索该点邻居的邻居(在当前近似K近邻中)而不断完善该点的K近邻。换句话说,可从粗略的K近邻图出发通过改进而不断完善它。对这一观点的量化表达如下:
让 K = c 3 K=c^3 K=c3(后面公式推导要用到, K K K取此值是方便推导),假定已有的近似K近邻图(可以随机给每个点选邻居构建,也可通过其它数据结构辅助构建,如哈希,树等)为 B B B。 ∀ v ∈ V \forall v \in V ∀v∈V, B ′ [ v ] = ⋃ v ′ ∈ B [ v ] B [ v ′ ] B^\prime[v]=\bigcup _{v^\prime \in B[v]} B[v^\prime] B′[v]=⋃v′∈B[v]B[v′]表示 v v v所有邻居的邻居集合,它也是在完善 v v v的K近邻时的候选点集。当B的精度比较高时(迭代完善了一定次数或通过某种更好的方式初始化B),高到什么程度呢?就是给定一个固定的半径 r r r,对 ∀ v ∈ V \forall v \in V ∀v∈V, B [ v ] B[v] B[v]包含的K个邻居均匀地分布在 B r ( v ) B_r(v) Br(v)中。这样的话,当各事件相互独立且 K < < ∣ B r / 2 ( v ) ∣ K<< |B_{r/2}(v)| K<<∣Br/2(v)∣时, B ′ [ v ] B^\prime [v] B′[v]很可能包含在 B r / 2 ( v ) B_{r/2}(v) Br/2(v)中的K个邻居。换句话说,对 ∀ v ∈ V \forall v \in V ∀v∈V,通过探索 B ′ [ v ] B^\prime [v] B′[v]来使 v v v到它的近似K近邻的距离减半。
对 B r / 2 ( v ) B_{r/2}(v) Br/2(v)中的一点 u u u,要从 B ′ [ v ] B^\prime[v] B′[v]里面找到,则至少存在一点 v ′ v^\prime v′,使得 v ′ ∈ B [ v ] v^\prime \in B[v] v′∈B[v],且 u ∈ B [ v ′ ] u \in B[v^\prime] u∈B[v′]。接下来,我们只需要找满足上述条件的 v ′ v^\prime v′即可。而若 v ′ ∈ B r / 2 ( v ) v^\prime \in B_{r/2}(v) v′∈Br/2(v),则有以下几个不等式成立:
由以上3个不等式和假定的各事件的独立性可得:
P { v ′ ∈ B [ v ] ∧ u ∈ B [ v ′ ] } ≥ K / ∣ B r / 2 ( v ) ∣ 2 (2) P\lbrace v^\prime \in B[v] \land u \in B[v^\prime]\rbrace \geq K/|B_{r/2}(v)|^2 \tag{2} P{v′∈B[v]∧u∈B[v′]}≥K/∣Br/2(v)∣2(2)
注解: 上式其实就是1.与2.两个事件同时发生的概率再由3.式化简的结果。它的意义是,对于 B r / 2 [ v ] B_{r/2}[v] Br/2[v]中的确定的点 v ′ v^\prime v′,它既是 v v v的邻居又是 u u u的反向邻居的概率大于等于 K / ∣ B r / 2 ( v ) ∣ 2 K/|B_{r/2}(v)|^2 K/∣Br/2(v)∣2。
因此,当 v v v的邻居从 B r / 2 ( v ) B_{r/2}(v) Br/2(v)中取时,在 B r / 2 ( v ) B_{r/2}(v) Br/2(v)中的一点 u u u属于 v v v的邻居的邻居的概率为:
P { u ∈ B ′ [ v ] } ≥ 1 − ( 1 − K / ∣ B r / 2 ( v ) ∣ 2 ) ∣ B r / 2 ( v ) ∣ ≈ K / ∣ B r / 2 ( v ) ∣ (3) P\lbrace u \in B^\prime[v]\rbrace \geq 1-(1-K/|B_{r/2}(v)|^2)^{|B_{r/2(v)}|} \approx K/|B_{r/2(v)}| \tag{3} P{u∈B′[v]}≥1−(1−K/∣Br/2(v)∣2)∣Br/2(v)∣≈K/∣Br/2(v)∣(3)
注解: 先考虑 u u u不是 v v v的邻居的邻居的概率。此时,从 B r / 2 ( v ) B_{r/2}(v) Br/2(v)中取出的一点设为 x x x, x x x不是 v v v的邻居或者 u u u不是 x x x的邻居,发生这种情况的概率由式(2)可得应为 1 − K / ∣ B r / 2 ( v ) ∣ 2 1-K/|B_{r/2}(v)|^2 1−K/∣Br/2(v)∣2, B r / 2 ( v ) B_{r/2}(v) Br/2(v)中一共有 ∣ B r / 2 ( v ) ∣ |B_{r/2}(v)| ∣Br/2(v)∣个点,它们都不满足上述情况( x x x不是 v v v的邻居或者 u u u不是 x x x的邻居)的概率为: ( 1 − K / ∣ B r / 2 ( v ) ∣ 2 ) ∣ B r / 2 ( v ) ∣ (1-K/|B_{r/2}(v)|^2)^{|B_{r/2(v)}|} (1−K/∣Br/2(v)∣2)∣Br/2(v)∣,这便是 u u u不是 v v v的邻居的邻居的概率,从而 u u u是 v v v的邻居的邻居的概率为: 1 − ( 1 − K / ∣ B r / 2 ( v ) ∣ 2 ) ∣ B r / 2 ( v ) ∣ 1-(1-K/|B_{r/2}(v)|^2)^{|B_{r/2(v)}|} 1−(1−K/∣Br/2(v)∣2)∣Br/2(v)∣。下面对该式进行化简,由于 K < < ∣ B r / 2 ( v ) ∣ K<< |B_{r/2}(v)| K<<∣Br/2(v)∣,因此 K / ∣ B r / 2 ( v ) ∣ 2 K/|B_{r/2}(v)|^2 K/∣Br/2(v)∣2是无穷小,化简过程用到一个重要极限:
lim x → ∞ ( 1 + 1 x ) x = e (4) \lim_{x \rightarrow \infty}(1+\frac{1}{x})^x=e \tag{4} x→∞lim(1+x1)x=e(4)
一个等价无穷小公式:
e x − 1 ∼ x e^x -1 \sim x ex−1∼x
整个数据集的直径设为 Δ \Delta Δ,式(3)表明,只要我们取一个足够大的 K K K(取决于增长因子 c c c),即使我们从一个随机的K近邻图开始,通过探索每一个对象邻居的邻居,便可找到该对象的处于半径为 Δ / 2 \Delta/2 Δ/2的范围内的K个近邻。不断的迭代这一过程,每个对象的邻居距离该对象的距离会不断收缩,最终,构建一个高质量近似K近邻图。
注解:(1)处为更新统计,如果某一个对象的K近邻列表更新了, c c c就会加1。算法1的终止条件为自然终止,即没有更新时( c = 0 c=0 c=0)终止。
让每一个对象探索它邻居的邻居的操作也可通过局部连接等价实现。局部连接可这样理解:给定一点 v v v,它的邻居集为 B ‾ [ v ] \overline{B}[v] B[v],在 B ‾ [ v ] \overline{B}[v] B[v]上的局部连接是计算每一对不同的 p p p和 q q q之间的相似性( p , q ∈ B ‾ [ v ] p,q \in \overline{B}[v] p,q∈B[v]),并且根据此相似性更新 B [ p ] B[p] B[p]与 B [ q ] B[q] B[q]。通俗的将,局部连接就是每一个点介绍它的邻居去了解彼此。
局部连接能代替一个对象探索它邻居的邻居的操作吗?看下面的示例:
如图2所示, b ∈ B K ( a ) b \in B_K(a) b∈BK(a), c ∈ B K ( b ) c \in B_K(b) c∈BK(b)。在算法1中,当探索到 a a a时,我们需要比较 a a a与 c c c,当探索到 c c c时,我们也需要比较 a a a与 c c c,这是冗余计算的一种情况,可通过索引编号的顺序来解决。同样地, a a a与 c c c之间的比较可通过对 B ‾ [ b ] \overline{B}[b] B[b]进行局部连接来实现。
局部连接实现起来很简单,那么它有什么好处呢?
随着算法的执行,每一个对象的K近邻更新的幅度逐渐减小。而且,在某次迭代中参与比较的两个点,就更可能在之前的迭代中已经比较过了。这就造成冗余计算,而增量搜索就是要解决这个问题的。
采样是为了解决以下两个问题:
使用采样来缓解这两个问题的具体方案如下:
因此,我们就可以通过取样率 ρ \rho ρ来进行精度和速度的trade-off。
一个很自然的终止标准是:某次迭代中,K近邻图不再被改善。实际上,开始迭代时,K近邻图能充分的更新,而随着迭代的进行,K近邻图更新的次数快速收缩,此时的迭代就显得意义不大了,考虑到迭代的计算成本,这些迭代其实没必要执行。为了解决这个问题,本文采取的方案是:在每次迭代中,统计所有对象K近邻列表更新的次数 c o u n t count count,当 c o u n t < δ K N count < \delta KN count<δKN时终止发生,其中 δ \delta δ是精度参数,它粗略反应了由于提前终止允许错过的真正的K近邻的比例。
注解: 算法2是在算法1的基础上结合了四个改进(局部连接;增量搜索;采样;提前终止),注意算法2其实也不能完全避免冗余计算,先理解一下这个算法,然后我会给出示例。
(1)、(2)属于增量搜索和采样部分,对于当前对象 v v v,在它的邻居列表中取 ρ K \rho K ρK个标记为true的邻居到 n e w [ v ] new[v] new[v],并将这些邻居标记为false(对于伪代码中的(3)),在它的邻居列表中取出所有标记为false的邻居到 o l d [ v ] old[v] old[v]。
(4)是取 v v v的反向邻居,正如取 v v v的 o l d [ v ] old[v] old[v]一样,其它所有点也会取各自的 o l d old old,以所有点的 o l d old old集合中包含的点作为探索范围,检查它们的邻居列表中含 v v v的点,含 v v v则加入到 o l d ′ [ v ] old^\prime [v] old′[v], o l d ′ [ v ] old^\prime [v] old′[v]的意义是:点 v v v的反向邻居,且在该反向邻居的邻居表中, v v v被标记为false。 n e w ′ new^\prime new′同理。
(5)是说最后参与局部连接的 o l d [ v ] old[v] old[v]是由两部分组成:一部分是从 v v v的邻居列表中取出的标记为false的邻居集,另一部分是从 o l d ′ [ v ] old^\prime [v] old′[v]中取样的 ρ K \rho K ρK个点。最后参与局部连接的 n e w [ v ] new[v] new[v]同理((6))。
(7)表示局部连接。 n e w [ v ] new[v] new[v]里面的点相互之间进行局部连接,为防止重复比较,设定比较顺序。 n e w [ v ] new[v] new[v]中的点与 o l d [ v ] old[v] old[v]中的点进行局部连接。
(8)统计更新,某一对象的邻居列表更新时,新插入的对象标记为true(满足:增量搜索)。
(9)为终止条件。当更新量小于某一阈值时终止。
冗余计算示例
如图3所示,第一次迭代时 v 3 v_3 v3和 v 4 v_4 v4都取样了 v 1 v_1 v1,都没有取样 v 2 v_2 v2,因此,它们的邻居列表中 v 1 v_1 v1都标记为false, v 2 v_2 v2都标记为true。此时, n e w ′ [ v 1 ] new^\prime[v_1] new′[v1]含 v 3 v_3 v3、 v 4 v_4 v4,若 v 3 v_3 v3、 v 4 v_4 v4都被取样加入到参与局部连接的 n e w [ v 1 ] new[v_1] new[v1],则 v 3 v_3 v3和 v 4 v_4 v4会进行一次相似性计算。第二次迭代时, v 3 v_3 v3和 v 4 v_4 v4都取样了 v 2 v_2 v2,然后 v 2 v_2 v2在它们的列表中被标记为false。此时, n e w ′ [ v 2 ] new^\prime[v_2] new′[v2]含 v 3 v_3 v3、 v 4 v_4 v4,若 v 3 v_3 v3、 v 4 v_4 v4都被取样加入到参与局部连接的 n e w [ v 2 ] new[v_2] new[v2],则 v 3 v_3 v3和 v 4 v_4 v4又会进行一次相似性计算。
当然,上述分两次迭代的说明也可在一次迭代中发生。不过,上述冗余计算的情况在取样过程的参与下发生的概率是很小的。
一种新的构建K近邻图的方法,具体创新包括:
具体实验分析可以看作者的原文。本文提出的NN-Descent方法可使用任意度量方式构建的K近邻图。经验复杂度为 O ( n 1.14 ) O(n^{1.14}) O(n1.14),很容易实现并行化。
本文一开始是随机构建一个K近邻图,这样做的优点是简单快速。但是,迭代的过程过多地依赖随机初始化的K近邻图,这样可能不够稳定,某些情况下只需几次迭代,而另一些情况则可能需要很多。因此,一个简单地改进可从初始化K近邻图这个角度入手。
最近提出的基于近邻图的近似最近邻搜索算法——NSG和NSSG,他们在构建索引时,第一步构建K近邻图与第二部MRNG或SSG选边策略是分开进行的,有没有可能在K近邻图构建的同时执行某一选边策略。
选边的时候将三角不等式考虑进去,从而避免一些不必要的计算。