vlambda博客
学习文章列表

【文本去重】SimHash算法

SimHash算法是Google进行海量网页去重的算法。

它将原始的文本映射为64位的二进制数串,然后通过比较二进制数字串的差异来表示原始文本内容的差异。这种相似度比较的算法的好处在于:可索引化、效率高。

SimHash的原理:

  1. 分词,将文档的文本分词,去掉停用词,并为每个词赋予一个权重(比如该词出现的次数、该词的TF-IDF值等),进而得到这个文档的词特征向量。比如『美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人』分词后得到『美国(4) 51区(5) 雇员(3) 称(1) 内部(2) 有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)』,括号里是词的权重,数字越大表示该词越重要。当文档较长时,也可以取文档的TF-IDF值权重最高的n个词和权重组成向量。
  2. hash,通过hash算法把每个词映射成hash值(二进制串),比如『美国』映射成hash值『100101』,『51区』映射成hash值『101011』.这样每个词都映射成了01的数字串。
  3. 加权,按照词的权重计算词的数字串的加权,1的位置乘以正权重,0的位置乘以负权重。比如『美国』的权重是4,hash值加权得到『4 -4 -4 4 -4 4』;『51区』的权重是5,hash值加权得到『5 -5 5 -5 5 5』。
  4. 合并,把各个词算出来的加权序列值累加,变成一个序列串。比如『美国』的『4 -4 -4 4 -4 4』和『51区』的『5 -5 5 -5 5 5』的 每一位累加后得到『9 -9 1 -1 1 9』。
  5. 降维,把合并得到的序列串转换成01串,形成最终的simhash前面。转换方式是:如果该位大于0,转成1,小于0转成0,所以4中得到的序列串转换后得到『1 0 1 0 1 1』。

整个流程可以概括成下面的图:

img

经过SimHash算法处理后,一个复杂的文本就转换成了只有0和1的等长的数字串,可以通过海明距离来判断两个文本的相似度。海明距离是指两个01串所有对应位取值不同的数量。一般认为海明距离小于等于3的两个文本是相似的。

在实际应用中,要引入倒排索引存储SimHash指纹。步骤如下:

  1. 入库,针对每一个文档,计算其64位的SimHash指纹,将其切分成4份,建立倒排索引,每一份指纹存储到对应的数据库中,即分片1存储在库1,分片2存储到库2...
  2. 相似度判断,假定海明距离为3以内的文本是重复的,根据鸽巢原理(也叫抽屉原理),如果两个指纹的海明距离在3以内,那么一定会有一个指纹分片完全相同。所以针对拟入库的文档,将其SimHash指纹切分为4片,每一片到对应的库中进行等值查询,查询到的即是疑似重复的文档。接下来只需要将待入库的文档和疑似重复的文档进行海明距离比较,即可判断是否重复,是否需要入库。
  3. 再入库,针对相似的文档,重复步骤1建立倒排索引,以供后续文档的比对。

整个流程可以表示成下面的图:

img

假设样本库,有 条数据(171亿数据),假设数据均匀分布,则每个16位(16个01数字随机组成的组合为 个)倒排返回的最大数量为 个候选结果,4个16位截断索引,总的结果为:4*262144=1048576,约为100多万,通过上面的降维处理方式,原来需要一一比较171亿次,现在只需要比较100万次即可得到结果,大大提升了计算效率。

但是在短文本场景下,这种度量方法的效果将会变得很差,因为相似的短文本之间的汉明距离通常是大于3的。并且该算法中,基于汉明距离的相似性阈值选取的越高,该算法的时间复杂度也会越高。

Simhash的Python实现可参考文献[4]。

抽屉原理:桌上有十个苹果,要把这十个苹果放到九个抽屉里,无论怎样放,我们会发现至少会有一个抽屉里面放不少于两个苹果。这一现象就是我们所说的“抽屉原理”。 ——来自百度百科

参考资料:

[1] simhash算法原理及实现

[2]

[3]

[4] 浅谈simhash及其python实现