LSH
locality sensitive hashing(LSH) 是哈希算法中,比较得要的方法。LSH方法是将相似的数据以较高的数据哈希到同一个桶里面,从而达到近似邻检索的目的,另外,待测数据维度非常大时,lsh也可用于降维。
LSH族[1]
LSH族 \(\mathcal H = \{h: S\rightarrow U\}\) 称为\((r1,r2,p1,p2)\)对于测度\(D\)是局部敏感的,如果 \(\forall x,q\in S\)有
如果\(v\in B(q,r_1)\), 那么有 \(Pr_{\mathcal H}[h(q)=h)(v)]\ge p_1\),
如果\(v\notin B(q,r_2)\),那么有\(Pr_{\mathcal H}[h(q)=h)(v)]\le p_2\).
上面的不等式还需要满足两个条件,即\(p_1>p_2\)以及\(r_1<r_2\). 在近似最近邻中,也即\((R,c)\)-NN问题中,可设\(r_1 = R\)以及\(r_2 = c\cdot R\).
已知一啥希族满足上述定义,可以通过使用多种不同的哈希函数来扩大p1与p2之间的距离,这样检索的效果也会更好。比如, 我们选了k个哈希函数,可以定义一个函数族\(\mathcal G = \{g: S\rightarrow U^k\}\),使得\(\mathcal g(v) = \{h_1(v),h_2(v2),...,h_k(v)\}\),其中\(h_i(v)\in\mathcal H\). 然后我们创建L个\(g_i(v), i \in \{1,2,...,L\}\).\(g_i(v)\)之间是相互独立的。
LSH是一种思想,或者说是一种框架,只要定好哈希函数以及距离测度D即可。
有几个比如有代表性的工作:minhash,simhash,p-stable hash等。这些基本区别是使用不同的hash以及不同的距离(或相似)方法。
MinHash特别适用于数据比较稀疏的情形, 比如新闻去重等等。在讲minhash之前需要讲一个滑动窗口切词法(shingling).
比如:'today is a sunny day',使用宽度为3的滑窗,会生成如字符片断:
'tod', 'oda','day','ay ', 'y a',...
这样将每个片段one-hot编码,这样此句话就会变成类似如下这种只包括0,1两个值的长向量:
[0,0,0,1,0,0,0,0,1,....]
此过程类似分词后构建词表过程。只不过,使用shingle的操作一定程度上保留了文章的词序性(特别是滑窗较宽时,比如10或20时,比一般的词长一些,类似n-grams). 此类操作在对网页去重等,有比较不错的效果,操作也简单,只需要计算两网页one hot编码向量的jaccard距离即可。但当两个向量很大时,计算jaccard距离会很耗资源,耗时. 如果可以找一种简单快速地计算Jaccard距离的方法,那么对于高维数据的相似性评估就会很方便。经观察,如果将集合A与B合并成一个集合C, 并从集合C中随机抽取一个元素X,那么此元素X既属于A也属于B的概率为|\(A\cap B\)|/\(|A\cup B|\),即Jaccard相似度 . 基于此逻辑,我们可以将所有元素找到构成一个词典列表。然后我们有放回地每次从中随机抽取一个元素,判断其是否在某两个集合中(比如仍是A,B两集合),经过多次重复,然后统计取出元素同时在集合A,B中的频率,即可近似等于两者的(Jaccard)相似度。
将集合\(\mathbb U\)中的元素用onehot的方式表示,那么对于集合:
其可以其表示成一个二元(共现)矩阵,类似如下(仅为一示例)。
Index | \(S_1\) | \(S_2\) | ... | \(S_n\) |
---|---|---|---|---|
0 | 0 | 1 | ... | 0 |
1 | 0 | 0 | ... | 0 |
2 | 1 | 0 | ... | 1 |
... | ... | ... | ... | ... |
m | 1 | 0 | ... | 0 |
将这二元矩阵按行重新排列,那么某一集合\(S_k\)的minhash值为即为排列后的第一个“1”所在行号。那么
如何理解呢?这里有一个关键点要记在心里,即按行进行重新排列的,那么每行的元素是一起同时变动的。那么从第一行开始往下找,在第\(S_{k_1}\)列与第\(S_{k_2}\)列同时出现一的概率就是其jaccard相似度。这样,可以让\(n = k\times L\),即可以执LSH操作。
simhash
首先将数据如网页文本转化成一组特征(如基本的nlp操作,如分词,去停用词,tdidf等等),然后,将特征哈希成一个f维的二元特征向量(比如64),然后将其转化成示性向量(具体做法是将其中的0变成-1,而1还是1), 然后乘以此特征权重(比如词频),然后,再将所有物征相加,最后再进行转化成二元特征向量(元素为正变为1, 其他变为0), 这样的再拼接成一个哈希值,即是此文本的哈希特征值。
比如: 在一网页中只有处理后只有三个词" simhash","simlarity", " algorithm",对应的词频分别为3,1,5。使用哈希函数{‘simhash': '101101','simlarity': '110010,,'algorithm':'100001'}, 然后转化成特征向量化{'simhash':[1,0,1,1,0,1],'simlarity':[1,1,0,0,1,0],'algorithm':[1,0,0,0,0,1]},再转化成示性向量{'simhash':[1,-1,1,1,-1,1],'simlarity':[1,1,-1,-1,1,-1],'algorithm':[1,-1,-1,-1,-1,1]}。接着,将特征的特征向量与其权重(即词频)相乘:{'simhash':[3,-3,3,3,-3,3],'simlarity':[1,1,-1,-1,1,-1],'alogrithm':[5,-5,-5,-5,-5,5]},再将向量相加:[9, -7,-3,-3,-7,7], 再转化成二元向量[1,0,0,0,0,1],最后生成文本哈希特征值:'100001'.
根据经验,如果两个网页的汉明距离不大于3的话,那么它们是重复的,基于此,可以将64位的哈希值平均分成四份,根据抽屉原理,至少有一个片断是相等的。
Datar M, Immorlica N, Indyk P, et al. Locality-sensitive hashing scheme based on p-stable distributions[C]//Proceedings of the twentieth annual symposium on Computational geometry. 2004: 253-262. ↩︎