E2LSH
E2LSH一个是用来解决高维空间近邻搜索问题的工具包。E2LSH实现了R-NN问题的随机化解决方案,即(R, 1 −δ )-near neighbor:每个满足||q-p||
2 ≤ R的点p被报告的概率至少为1 −δ。E2LSH的实现基于基本的LSH模式。
Hash函数族
E2LSH使用基于p-stable分布的哈希函数族:
ha,b=[a⃗ ⋅v⃗ +bw] 其中a是一个d维向量,a的每一项是从p-stable分布中随机独立选取的;b是从[0, w]中均匀选取的一个实数。有了一组确定的a和b,也就产生了一个相应的
ha,b(v):Rd⇒Z ,将一个d维的点向量v映射到一个整数。
LSH基本过程
定义g函数:
gi(v)=(h1(v),h2(v),...,hk(v)),1≤i≤L ,对应L个哈希表,每个g函数随机独立产生。每个g函数由在哈希函数族
ha,b 中随机独立选取的k个h函数组成。g函数的值对应具体的哈希桶。
使用LSH进行R-NN或k-NN搜索的过程主要分为建立索引和查询两步。建立索引时,对数据集中的每个点v∈P,计算其L个g函数值,并将其存入L个表中对应的哈希桶内。查询时,同样计算查询点q的L个g函数值,则找到q所在的L个哈希桶,计算这些哈希桶中的点与q的距离,找到规定距离R之内的点(R-NN),或k个距离最近的点(k-NN)。
参数的确定
L的确定
参数L的确定需要根据前面提到的(R, 1 −δ )-near neighbor的定义来解决,即一个近邻点被报告的概率至少为1 −δ。给定一个查询点q,近邻点v ∈ B(q,R),设p
1= p(R),两点在某个桶中发生碰撞,即在一个g函数中两点的值相等,应满足:
Prg∈G[g(q)=g(v)]≥pk1 那么q和v在L个g函数中的值都不相等概率至多为
(1−pk1)L 则q在至少一个g函数中与v的值相等的概率满足:
1−(1−pk1)L≥1−δ 则L满足:
L≥logδlog(1−pk1) 。 因为建立哈希表的时间与L的值正相关,因此为了减少建立索引的时间,L取下界。
k的确定
为了确定参数k的取值,首先需要知道使用E2LSH进行R-NN查询的查询时间分为两部分: -
Tg=O(dkL) :为查询点q计算L个
gi,1≤i≤L ,函数并在各表中检索gi(p)桶的时间 -
Tc=O(d・#collisions) :计算发生碰撞的桶中的点与查询点q之间的距离的时间 其中#collisions是碰撞的桶的数目,\#collisions的期望为:
E(#collisions)=L×∑v∈Ppk(||q−v||2)
根据Tg与Tc的计算方式可知,Tg随k的增大而增大,Tc随k的增大而减小,因此存在一个k的最优值使Tg+Tc最小。对不同的查询点q来说Tc是不同的,因此需要计算Tc的平均值,根据其平均值来估计k的最优值。 E2LSH的实现是从查询集中随机选取一组样本点组成集合S,构造一个样本数据结构,并在这个数据结构上执行多次查询,测得一组真实的Tg与Tc的值,计算出Tc的平均值 T′c , Tc=∑q∈ST′c(q)|S| ,则k取 Tg+T′c 最小时对应的值。
gi 的快速计算
在基本的LSH方法中,g函数的确定过程是在哈希函数族中均匀随机独立选取L个函数gi = (h(i)1 , … , h(i)k ),则需要O(d)时间计算h
j
(i)(q),O(dkL)时间计算g1(q), … , gL(q)。为了减少计算函数gi函数的时间,需重用一部分h
j
(i)函数,过程如下:
- 设k为偶数,m是一个常数,定义函数
ui=(h(i)1,...,h(i)k/2)
- 重新定义
gi=(ua,ub),1≤a<b≤m ,即先产生k/2个h函数,再产生k/2个h函数(不再相互独立地选取,而是可重用,且已计算过的不需要重复计算),然后进行组合产生g函数
- 总共的哈希表个数为
L=C1m+C2m−1+...+C11=m(m−1)2
因为每个g函数需要两个u函数,因此需要在至少两个u函数内q和v的值相等时,才会产生一个相应的g函数使q和v的相应值相等,即在对应桶中发生碰撞。根据u函数的定义,有:
m个u函数中q和v的值都不相等的概率为 : (1−pk/21)m
只有一个u函数中q和v的值相等的概率为: m×pk/21×(1−pk/21)m−1
根据前面内容,则新的gi下q在至少一个g函数中的值与v相等的概率满足:
1−(1−pk/21)m−m×pk/21×(1−pk/21)m−1≥1−δ
该式计算出来的L会比gi相互独立时计算的L值略大一点,但依然是 O(log1/δpk1) 的,新的时间复杂度降低为O(dkm)=O(dk√L)
实现细节
桶哈希
对数据集中的点使用哈希函数哈希之后得到g(v)=(h1(v),…,hk(v)),但将(h1(v),…,hk(v))直接存入哈希表,即占用内存,又不便于查找,为解决此问题,E2LSH使用了另外两个哈希函数:
-
h1:Zk⇒{0,...,tableSize−1}
-
h2:Zk⇒{0,...,C} ,C是一个大素数
h1的值作为哈希表的索引;h2作为链表中桶的索引。h1、h2的具体形式如下:
-
h1(a1,a2,...,ak)=((∑ki=1r'i×ai)mod prime)mod tableSize
-
h2(a1,a2,...,ak)=((∑ki=1r′'i×ai)mod prime)
其中r’和r”是两个随机整数,C取值为2
32-5
由于每一个哈希桶(Hash Buckets)gi被映射成Zk,函数h1是普通哈希策略的哈希函数,函数h2用来确定链表中的哈希桶。 (1)要在一个链表中存储一个哈希桶gi(v)=(x1,…,xk)时,使用指纹h2(x1,…,xk)构造的指纹代替向量(x1,…,xk),因此一个哈希桶gi(v)=(x1,…,xk)在链表中的存储的相关信息仅包括标识(identifier)指纹h2(x1,…,xk)和相应的原始数据点。 (2)存储指纹值h2,而不是存储gi(v)=(x1,…,xk)的值有两个原因:首先,用h2(x1,…,xk)构造的指纹将单个哈希桶的存储空间从O(k)降到了O(1);其次,使用指纹值可以更快的检索哈希表中哈希桶。通过选取一个足够大的h2的值域来保证任意两个不同的哈希桶在链表中有不同的h2指纹值。 具体过程如下图所示