java hmacsha算法(Simhash算法原理和网页查重应用)
传统的hash算法只负责将原始内容尽量均匀随机地映射为一个签名值 ,原理上相当于伪随机数产生算法 。产生的两个签名 ,如果相等,说明原始内容在一定概率下是相等的;如果不相等 ,除了说明原始内容不相等外 ,不再提供任何信息 ,因为即使原始内容只相差一个字节 ,所产生的签名也很可能差别极大 。从这个意义上来说 ,要设计一个hash算法 ,对相似的内容产生的签名也相近 ,是更为艰难的任务 ,因为它的签名值除了提供原始内容是否相等的信息外 ,还能额外提供不相等的原始内容的差异程度的信息 。
而Google的simhash算法产生的签名,可以用来比较原始内容的相似度时 ,便很想了解这种神奇的算法的原理 。出人意料 ,这个算法并不深奥,其思想是非常清澈美妙的 。
Simhash算法
simhash算法的输入是一个向量 ,输出是一个f位的签名值 。为了陈述方便 ,假设输入的是一个文档的特征集合,每个特征有一定的权重 。比如特征可以是文档中的词 ,其权重可以是这个词出现的次数 。simhash算法如下:
1 ,将一个f维的向量V初始化为0;f位的二进制数S初始化为0;
2 ,对每一个特征:用传统的hash算法对该特征产生一个f位的签名b 。对i=1到f:
如果b的第i位为1 ,则V的第i个元素加上该特征的权重;
否则 ,V的第i个元素减去该特征的权重。
3 ,如果V的第i个元素大于0 ,则S的第i位为1 ,否则为0;
4 ,输出S作为签名 。
算法几何意义和原理
这个算法的几何意义非常明了 。它首先将每一个特征映射为f维空间的一个向量,这个映射规则具体是怎样并不重要 ,只要对很多不同的特征来说 ,它们对所对应的向量是均匀随机分布的,并且对相同的特征来说对应的向量是唯一的就行。比如一个特征的4位hash签名的二进制表示为1010 ,那么这个特征对应的
4维向量就是(1, -1, 1,
-1)T ,即hash签名的某一位为1,映射到的向量的对应位就为1 ,否则为-1 。然后 ,将一个文档中所包含的各个特征对应的向量加权求和 ,加权的系数等于该特征的权重 。得到的和向量即表征了这个文档 ,我们可以用向量之间的夹角来衡量对应文档之间的相似度。最后 ,为了得到一个f位的签名 ,需要进一步将其压缩 ,如果和向量的某一维大于0 ,则最终签名的对应位为1 ,否则为0 。这样的压缩相当于只留下了和向量所在的象限这个信息,而64位的签名可以表示多达264个象限 ,因此只保存所在象限的信息也足够表征一个文档了 。明确了算法了几何意义 ,使这个算法直观上看来是合理的 。但是,为何最终得到的签名相近的程度 ,可以衡量原始文档的相似程度呢?这需要一个清晰的思路和证明 。在simhash的发明人Charikar的论文中[2]并没有给出具体的simhash算法和证明 ,以下列出我自己得出的证明思路 。
Simhash是由随机超平面hash算法演变而来的,随机超平面hash算法非常简单 ,对于一个n维向量v ,要得到一个f位的签名(f<<n) ,算法如下:
1 ,随机产生f个n维的向量r1,…rf;
2 ,对每一个向量ri ,如果v与ri的点积大于0 ,则最终签名的第i位为1 ,否则为0.
这个算法相当于随机产生了f个n维超平面 ,每个超平面将向量v所在的空间一分为二,v在这个超平面上方则得到一个1 ,否则得到一个0 ,然后将得到的
f个0或1组合起来成为一个f维的签名 。如果两个向量u, v的夹角为θ,则一个随机超平面将它们分开的概率为θ/π ,因此u,
v的签名的对应位不同的概率等于θ/π 。所以 ,我们可以用两个向量的签名的不同的对应位的数量,即汉明距离 ,来衡量这两个向量的差异程度 。Simhash算法与随机超平面hash是怎么联系起来的呢?在simhash算法中 ,并没有直接产生用于分割空间的随机向量 ,而是间接产生的:第
k个特征的hash签名的第i位拿出来 ,如果为0 ,则改为-1 ,如果为1则不变 ,作为第i个随机向量的第k维 。由于hash签名是f位的 ,因此这样能产生
f个随机向量 ,对应f个随机超平面。下面举个例子:假设用5个特征w1,…,w5来表示所有文档,现要得到任意文档的一个3维签名 。假设这5个特征对应的3维向量分别为:
h(w1) = (1, -1, 1)T
h(w2) = (-1, 1, 1)T
h(w3) = (1, -1, -1)T
h(w4) = (-1, -1, 1)T
h(w5) = (1, 1, -1)T
按simhash算法 ,要得到一个文档向量d=(w1=1, w2=2, w3=0, w4=3, w5=0)
T的签名 ,先要计算向量m = 1*h(w1) + 2*h(w2) + 0*h(w3) + 3*h(w4) + 0*h(w5) = (-4,
-2, 6) T,然后根据simhash算法的步骤3 ,得到最终的签名s=001 。
上面的计算步骤其实相当于 ,先得到3个5维的向量,第1个向量由h(w1),…,h(w5)的第1维组成:
r1=(1,-1,1,-1,1) T;
第2个5维向量由h(w1),…,h(w5)的第2维组成:
r2=(-1,1,-1,-1,1) T;
同理 ,第3个5维向量为:
r3=(1,1,-1,1,-1) T.
按随机超平面算法的步骤2 ,分别求向量d与r1,r2,r3的点积:
d T r1=-4 < 0 ,所以s1=0;
d T r2=-2 < 0 ,所以s2=0;
d T r3=6 > 0 ,所以s3=1.
故最终的签名s=001 ,与simhash算法产生的结果是一致的。
从上面的计算过程可以看出 ,simhash算法其实与随机超平面hash算法是相同的 ,simhash算法得到的两个签名的汉明距离 ,可以用来衡量原始向量的夹角 。这其实是一种降维技术,将高维的向量用较低维度的签名来表征 。衡量两个内容相似度 ,需要计算汉明距离 ,这对给定签名查找相似内容的应用来说带来了一些计算上的困难;我想,是否存在更为理想的simhash算法 ,原始内容的差异度 ,可以直接由签名值的代数差来表示呢?
大规模网页的近似查重
详细内容可以看WWW07的 Detecting Near-Duplicates
for Web Crawling。例如,文本的特征可以选取分词结果 ,而权重可以用df来近似 。
Simhash具有两个“冲突的性质 ”:
1. 它是一个hash方法
2. 相似的文本具有相似的hash值 ,如果两个文本的simhash越接近 ,也就是汉明距离越小 ,文本就越相似 。
因此海量文本中查重的任务转换位如何在海量simhash中快速确定是否存在汉明距离小的指纹 。也就是:在n个f-bit的指纹中 ,查询汉明距离小于k的指纹 。
在文章的实验中 ,simhash采用64位的哈希函数 。在80亿网页规模下汉明距离=3刚好合适 。
因此任务的f-bit=64 , k=3 , n= 8*10^11
任务清晰 ,首先看一下两种很直观的方法:
1.
枚举出所有汉明距离小于3的simhash指纹 ,对每个指纹在80亿排序指纹中查询 。(这种方法需要进行C(64 ,3)=41664词的simhash指纹,再为每个进行一次查询)2.
所有接近的指纹排序到一起 ,这至多有41664排序可能 ,需要庞大的空间 。提出的方法介于两者之间,合理的空间和时间的折中 。•
假设我们有一个已经排序的容量为2d ,f-bit指纹集。看每个指纹的高d位 。该高低位具有以下性质:尽管有很多的2d位组合存在 ,但高d位中有只有少量重复的 。•
现在找一个接近于d的数字d’,由于整个表是排好序的 ,所以一趟搜索就能找出高d’位与目标指纹F相同的指纹集合f’。因为d’和d很接近 ,所以找出的集合f’也不会很大 。• 最后在集合f’中查找 和F之间海明距离为k的指纹也就很快了 。
• 总的思想:先要把检索的集合缩小 ,然后在小集合中检索f-d’位的海明距离
按照例子 ,80亿网页 有2^34
个 ,那么理论上34位就能表示完80亿不重复的指纹。我们假设最前的34位的表示完了80亿指纹 ,假设指纹在前30位是一样的 ,那么后面4位还可以表示24个 ,
只需要逐一比较这16个指纹是否于待测指纹汉明距离小于3 。假设:对任意34位中的30位都可以这么做 。
因此在一次完整的查找中 ,限定前q位精确匹配(假设这些指纹已经是q位有序的,可以采用二分查找 ,如果指纹量非常大 ,且分布均匀,甚至可以采用内插搜索) ,之后的2d-q个指纹剩下64-q位需要比较汉明距离小于3 。
于是问题就转变为如何切割64位的q 。
将64位平分成若干份 ,例如4份ABCD,每份16位 。
假设这些指纹已经按A部分排序好了 ,我们先按A的16位精确匹配到一个区间 ,这个区间的后BCD位检查汉明距离是否小于3 。
同样的假设 ,其次我们按B的16位精确匹配到另一个区间 ,这个区间的所有指纹需要在ACD位上比较汉明距离是否小于3 。
同理还有C和D
所以这里我们需要将全部的指纹T复制4份 , T1 T2 T3 T4 , T1按A排序 ,T2按B排序…
4份可以并行进行查询 ,最后把结果合并 。这样即使最坏的情况:3个位分别落在其中3个区域ABC,ACD,BCD,ABD…都不会被漏掉 。只精确匹配16位 ,还需要逐一比较的指纹量依然庞大,可能达到2d-16个 ,我们也可以精确匹配更多的。
例如:将64位平分成4份ABCD ,每份16位,在BCD的48位上 ,我们再分成4份 ,WXZY,每份12位 ,
汉明距离的3位可以散落在任意三块 ,那么A与WXZY任意一份合起来做精确的28位…剩下3份用来检查汉明距离 。
同理B ,C ,D也可以这样 ,那么T需要复制16次 ,ABCD与WXYZ的组合做精确匹配 ,每次精确匹配后还需要逐一比较的个数降低到2d-28个 。不同的组合方式也就是时间和空间上的权衡。最坏情况是其中3份可能有1位汉明距离差异为1 。
算法的描述如下:
1)先复制原表T为Tt份:T1,T2,….Tt
2)每个Ti都关联一个pi和一个πi ,其中pi是一个整数, πi是一个置换函数 ,负责把pi个bit位换到高位上 。
3)应用置换函数πi到相应的Ti表上,然后对Ti进行排序
4)然后对每一个Ti和要匹配的指纹F 、海明距离k做如下运算:
a) 然后使用F’的高pi位检索 ,找出Ti中高pi位相同的集合
b) 在检索出的集合中比较f-pi位 ,找出海明距离小于等于k的指纹
5)最后合并所有Ti中检索出的结果
由于文本已经压缩成8个字节了,因此其实Simhash近似查重精度并不高:
附参考文献:
[1] Detecting near-duplicates for web crawling.
[2] Similarity estimation techniques from rounding
algorithms.[3] http://en.wikipedia.org/wiki/Locality_sensitive_hashing
[4] http://www.coolsnap.net/kevin/?p=23
[5] http://www.cnblogs.com/linecong/archive/2010/08/28/simhash.html
[6] http://blog.csdn.net/lgnlgn/article/details/6008498
博主注:本文主要就是拼接了参考文献[5][6]而成。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!