基于 SimHash 算法的文本相似度计算原理简介

背景

上周某一天,笔者搜遍全网,综合各种不完整的代码片段、GitHub 上几十个 SimHash 项目、几十个相关网络资源文章后,终于搞定了一个还算精确的 SimHash 算法的 Java 版本。

输出是检验掌握一个知识点的简单标准,本文就来详细介绍一下基于 SimHash 算法的相似文本检索的原理和实现过程。

文本相似度的应用

最近在搞一个漏洞库爬虫项目,需要综合分析并合并几个漏洞网站的漏洞信息,不同漏洞的发布基本上是相似的,但是不同网站可能稍有差异,怎么将不同网站发布的、本质是同一种漏洞的记录合并呢?

这就涉及到了文本相似度计算问题。基本思路是:对网页漏洞解析时,先以漏洞简介或者标题去系统的漏洞库中检索,是否有相似的漏洞,如果存在,视为同一条漏洞信息,追加漏洞来源即可。

这个问题的经典解决方案就是 Google 用来处理海量文本去重的算法 SimHash ,它是一种局部敏感 hash 【全称: Locality Sensitive Hashing】,全称和缩写的 Sim 有些出入,不知道为什么呢!

利用 SimHash 判断文本相似度的基本流程为:

  1. 分别计算两个文本的 SimHash 值,得到两个定长的二进制序列;
  2. 对两个二进制数进行异或计算,得到两个文本的海明距离;
  3. 海明距离小等于 3 ,视为相似。

SimHash 算法简介

Java 里有 HashMap ,想必大家都不陌生,它通过一个函数得到值来标识某项数据的位置,为了避免 Hash 冲突,选中的函数应该尽量让数据分散。

SimHash 与我们熟悉的 Hash 不太一样,它要求对相同或相似的文本,产生相同或者相近的哈希值,值得相似程度同时能反映输入得文本得相似度。

它的原理是:
在这里插入图片描述

基本流程是:

  1. 分词:对文本进行分词,得到 N 个 词 w o r d 1 word_1 word1 w o r d 2 word_2 word2 w o r d 3 word_3 word3 …… w o r d n word_n wordn
  2. 赋权重:对文本进行词频统计,为各个词设置合理的权重 w e i g h t 1 weight_1 weight1 w e i g h t 2 weight_2 weight2 w e i g h t 3 weight_3 weight3 …… w e i g h t n weight_n weightn
  3. 哈希:计算每个分词的哈希值 h a s h i hash_i hashi,得到一个定长的二进制序列,一般是 64 位,也可以是 128 位;
  4. 加权重:变换每个分词 w o r d i word_i wordi h a s h i hash_i hashi,将 1 变成正的权重值 w e i g h t i weight_i weighti,0 变成 − w e i g h t i -weight_i weighti,得到一个新的序列 w e i g h t H a s h i weightHash_i weightHashi
  5. 叠加权重:对每个 w e i g h t H a s h i weightHash_i weightHashi 各个位上的数值做累加,最终得到一个序列 l a s t H a s h lastHash lastHash,序列的每一位都是所有分词的权重的累加值;
  6. 降维:再将 l a s t H a s h lastHash lastHash 变换成 01 序列 simHash ,方法是:权重值大于零的位置设置为 1,小于 0 的位置设置为 0,它就是整个文本的局部哈希值,即指纹。

从文本到 SimHash 指纹的过程,有一个经典的示例:
在这里插入图片描述

扫描二维码关注公众号,回复: 11879551 查看本文章

算法实现需要解决的三个技术点:

  1. 分词,有 hanlp 分词、IKAnalyzer 分词 和 word 分词等工具可以用;
  2. 权重,为各个分词赋上合理的权重,可以自定义统计算法,也可以借助 word 的词频统计;
  3. 哈希算法,这个可以自己选择,也可以用现有的算法,如 Murmur 哈希,JDK 的 hash 。

汉明距离

看看来自百度百科的解释:

汉明距离是以理查德·卫斯里·汉明的名字命名的。在信息论中,两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。

从其定义上来看,就是二进制序列异或计算时相异为 1 ,异或的结果中 1 的总数,就是两个字符串的汉明距离,所以实现汉明距离计算的方法也很简单。

启示录

本篇先介绍基础概念,下一篇笔者将继续介绍具体的算法实现。从 SimHash 算法的流程来看,比较难理解的是,为什么可以直接对哈希值中的 0 或 1 替换为权重值,然后对所有分词累加权重呢?

虽然没有搜到直接能用的 SimHash 算法的 Java 实现,但是找到了两篇比较详细的文章,对笔者理解局部敏感哈希算法提供了很大的帮助,它们是:

参考链接一
参考链接二

猜你喜欢

转载自blog.csdn.net/wojiushiwo945you/article/details/108812835