导航

simhash

Posted on 2021-02-02 20:49  寒宵飞飞  阅读(382)  评论(0编辑  收藏  举报

一、Simhash简介

  传统的Hash算法只负责将原始内容尽量均匀随机地映射为一个签名值,原理上仅相当于伪随机数产生算法。传统的hash算法产生的两个签名,如果原始内容在一定概率下是相等的;如果不相等,除了说明原始内容不相等外,不再提供任何信息,因为即使原始内容只相差一个字节,所产生的签名也很可能差别很大。所以传统的Hash是无法在签名的维度上来衡量原内容的相似度,而SimHash本身属于一种局部敏感哈希算法,它产生的hash签名在一定程度上可以表征原内容的相似度。

  SimHash算法是Google公司进行海量网页去重的高效算法,它通过将原始的文本映射为64位的二进制数字串,然后通过比较二进制数字串的差异进而来表示原始文本内容的差异。简单描述:simhash是一种快速判断两个文本是否相同(相似)的方法

二、SimHash流程描述

  simhash算法分为5个步骤:分词、hash、加权、合并、降维,具体过程如下所述:

  1. 分词。把需要判断文本分词形成这个文章的特征单词。最后形成去掉噪音词的单词序列并为每个词加上权重,我们假设权重分为5个级别(1~5)。比如:“ 美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人 ” ==> 分词后为 “ 美国(4) 51区(5) 雇员(3) 称(1) 内部(2) 有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)”,括号里是代表单词在整个句子里重要程度,数字越大越重要。
  2. hash。通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为 101011。这样我们的字符串就变成了一串串数字,还记得文章开头说过的吗,要把文章变为数字计算才能提高相似度计算性能,现在是降维过程进行时。
  3. 加权。通过 2步骤的hash生成结果,需要按照单词的权重形成加权数字串,比如“美国”的hash值为“100101”,通过加权计算为“4 -4 -4 4 -4 4”;“51区”的hash值为“101011”,通过加权计算为 “ 5 -5 5 -5 5 5”。
  4. 合并。把上面各个单词算出来的序列值累加,变成只有一个序列串。比如 “美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5 -5 5 -5 5 5”, 把每一位进行累加, “4+5 -4+-5 -4+5 4+-5 -4+5 4+5” ==》 “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的,真实计算需要把所有单词的序列串累加。
  5. 降维。把4步算出来的 “9 -9 1 -1 1 9” 变成 0 1 串,形成我们最终的simhash签名。 如果每一位大于0 记为 1,小于0 记为 0。最后算出结果为:“1 0 1 0 1 1”。

三、SimHash文本相似比较

  海明距离: 两个码字的对应比特取值不同的比特数称为这两个码字的海明距离。一个有效编码集中, 任意两个码字的海明距离的最小值称为该编码集的海明距离。举例如下: 10101 和 00110 从第一位开始依次有第一位、第四、第五位不同,则海明距离为 3.

异或: 只有在两个比较的位不同时其结果是1 ,否则结果为 0 

对每篇文档根据SimHash 算出签名后,再计算两个签名的海明距离(两个二进制异或后 1 的个数)即可。根据经验值,对 64 位的 SimHash ,海明距离在 3 以内的可以认为相似度比较高。

四. SimHash查询、存储和索引

  如何在海量的样本库中查询与其海明距离在3以内的记录呢?

eg:数据库有2^34(差不多10亿)的simhash签名文档,现在给你一个新的文档,判断这个文档在不在现有数据库中,怎么判断?

  最简单办法就是比较10亿次,算法复杂度o(n)这显然是不能接受的

解决方案:

将simhash串(64位)拆成4个相等长度的子串A1 A2 A3 A4,每个串长16位

A文档:A1 A2 A3 A4

B文档:B1 B2 B3 B3

根据抽屉原理

如果A文档和B文档的海明距离在3以内,那么A1-B1,A2-B2,A3-B3,A4-B4之间必然有一组完全相同

这个可以用反证法,如果每组都不同,那么A文档和B文档的海明距离一定>=4(每个分组的海明距离都>=1,4个分组加一起的海明距离>=4)

所以我们可以将数据库10亿+的simhash签名,按照每个simhash拆分成4个子组(eg:1111111100000000,0000000011111111,1010101000000000,0101010100000000)

A:seg 0-15:(1111111100000000)

B:seg 16-31:(0000000011111111)

C:seg 32-47:(1010101000000000)

D:seg 48-63:(0101010100000000)

这样我们就能得到40亿+的simhash seg组,每个seg组的记录数为2^16

如果数据是分布均匀的 那么每个seg组的每个seg(例如00000000 00000000)就会有2^32 / 2^16 = 262144条记录

对于新目标文档 查询流程为:

首先进行初筛:对于新目标文档 拆分成4个seg组后 和数据库做hash匹配 理论上就能找到 4×262144 = 100W+条单个seg相同的记录

但是只是单个seg相同 并不能保证这两个文档的海明距离就是3了 (可能一个seg相同 其他3个seg加一起的海明距离>>3)

然后进行准确筛选,将这100W个文档分别和目标文档比较海明距离,就可以准确判断该文档是否在数据库中存在了

这样10亿+的判断被优化成了100W+的判断

 

 

五. SimHash原理解析

从上面的流程我们可以看到,影响simHash值的主要因素就是文档的关键字段(权重高),如果两个文档的关键字段大致相同,那么这两个文档的simhash值大致也会相同

如果每个字段的权重都一样 会发生什么结果?

eg1:白娘子传奇传奇白娘子 这两个文本的simhash就会一模一样,因为拆分出的词语 白娘子传奇 计算后加一起的simhash都一样 跟顺序无关了 早加晚加最终的值都一致   

eg2:simhash只能做相似判断 不能做包含判断,例如白娘子白娘子传奇白娘子传奇之法海你不懂爱白娘子白娘子传奇之法海你不懂爱的汉明距离就很长 

白娘子-分词:[白娘子]

白娘子传奇-分词:[白娘子, 传奇]]

白娘子传奇之法海你不懂爱-分词:[白娘子, 传奇, 之, 法海, 你, 不, 懂, 爱]

白娘子-simhash:0110011000001110111110001111111111100001110000000000000000000000

白娘子传奇-simhash:1110011000011110111111101111111111111011110000000000000000000000

白娘子传奇之法海你不懂爱-simhash:0110011111111000110101101110111111111010010000000000000000000000

白娘子 和 白娘子传奇:hanming distance is 7

白娘子传奇 和 白娘子传奇之法海你不懂爱:hanming distance is 12

白娘子 和 白娘子传奇之法海你不懂爱:hanming distance is 17

 

 其他算法:

  • 百度的去重算法(直接找出此文章的最长的n句话,做一遍hash签名。n一般取3。 工程实现巨简单,据说准确率和召回率都能到达80%以上。)
  • shingle算法
  • Minhash and LSH

 

 

 

本文参考

https://www.cnblogs.com/sddai/p/10088007.html

https://cloud.tencent.com/developer/article/1189493

https://cloud.tencent.com/developer/article/1043655

https://www.biaodianfu.com/simhash.html

 

 

 

 

 

 

 

 

 

 

 

 

simhash