MinHash算法原理与应用(Java版)
Posted 好好生活_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MinHash算法原理与应用(Java版)相关的知识,希望对你有一定的参考价值。
引言
在上篇博客中,介绍了一种局部敏感哈希算法SimHash算法。本篇博客将继续介绍另一个算法,MinHash算法。与SimHash算法相同,其也属于局部敏感哈希算法;与SimHash算法不同的是,MinHash是通过Jaccard相似度来比较文本之间的相似度。
Jaccard介绍
Jaccard相似度计算的公式为:
公式比较简单,比如两个集合A和B,则Jaccard相似度=集合A与集合B的交集/集合A与集合B的并集。
下面先梳理一下MinHash计算Jaccard相似度的过程。
MinHash计算过程
首先列一下整个计算过程,主要分为以下几个步骤:
-
将比较的内容转换为相应的特征行
-
对文档对应特征的行进行重排列
-
取排列后每列的第一个非零值即为MinHash
-
重复多次1-2步骤,得到原特征向量的新表示,称signature,即降维后的新向量
-
计算两个signature之间相等值的比例,即得到近似Jaccard相似度
下面就用一个简单的示例来详细看一下整个计算过程:
1. 集合转换为位数组
依次给每个集合建立一个位数组,数组的大小由所有集合的并集结果决定。比如,我们有3个集合,分别为A、B、C,每个集合的元素如下所示:
- A = b,d,e
- B = a,b,c
- C = a,e,f
- (A U B U C) = a,b,c,d,e,f
那么每个集合转换的数组大小为6。将并集结果的元素Hash值与并集集合的大小进行%计算,各集合元素依次与并集结果比较,如果结果在集合中存在,则值记为1,否则记为0。现假定各集合的Hash值是自身,可得到集合A、B、C转换后结果为:
- Bit array A = [0,1,0,1,1,0]
- Bit array B = [1,1,1,0,0,0]
- Bit array C = [1,0,0,0,1,1]
2. 构建矩阵
数组转换为minHash的矩阵,矩阵的列为集合的元素,行是数组的结果,单独增加一列行号,用于计算最后的相似度,矩阵如下表所示:
LineNo(行号) | Element(元素) | A | B | C |
---|---|---|---|---|
1 | a | 0 | 1 | 1 |
2 | b | 1 | 1 | 0 |
3 | c | 0 | 1 | 0 |
4 | d | 1 | 0 | 0 |
5 | e | 1 | 0 | 1 |
6 | f | 0 | 0 | 1 |
3. 矩阵重排
下面我们进行3次重新组合矩阵,
(1)第一次排序:依次按照行号123456的顺序排列,也就是上表,集合A、B、C对应第一个值为1对应的行号分别为2,1,1,所以得到各集合的数字签名为(A,B,C) 为(2,1,1)
(2)第二次排序:同理,按照行号243561的顺序进行排列,结果如下表:
LineNo(行号) | Element(元素) | A | B | C |
---|---|---|---|---|
1 | a | 1 | 1 | 0 |
2 | b | 1 | 0 | 0 |
3 | c | 0 | 1 | 0 |
4 | d | 1 | 0 | 1 |
5 | e | 0 | 0 | 1 |
6 | f | 0 | 1 | 1 |
得到各集合的数字签名为(A,B,C)为(1,1,4)
(3)第三次排序:按照行号312645的顺序进行排列,结果为下表,
LineNo(行号) | Element(元素) | A | B | C |
---|---|---|---|---|
1 | a | 0 | 1 | 0 |
2 | b | 0 | 1 | 1 |
3 | c | 1 | 1 | 0 |
4 | d | 0 | 0 | 1 |
5 | e | 1 | 0 | 0 |
6 | f | 1 | 0 | 1 |
得到各集合的数字签名为(A,B,C)为 (3,1,2)
4. 合并结果
通过三次排序后,我们将三次的Hash值结果合并,如下表:
A | B | C | |
---|---|---|---|
第一次Hash结果 | 2 | 1 | 1 |
第二次Hash结果 | 1 | 1 | 4 |
第三次Hash结果 | 3 | 1 | 2 |
5. 相似度计算
相似度的计算方法是对应位置相同的元素个数比上哈希签名长度。所以,A、B、C集合间相似度结果为:
Sim(A,B) = 1/3
Sim(A,C) = 0
Sim(B,C) = 1/3
至此,以上就是MinHash计算相似度的过程,至于结果的准确性及上述过程的论证有兴趣的可以点击参考资料详细了解。
核心代码
上面的过程比较复杂,代码其实很简单,可以直接引用相关包调用方法。
1. 添加依赖包
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>8.4.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codelibs</groupId>
<artifactId>minhash</artifactId>
<version>0.3.0</version>
<exclusions>
<exclusion>
<artifactId>lucene-core</artifactId>
<groupId>org.apache.lucene</groupId>
</exclusion>
</exclusions>
</dependency>
- 计算MinHash值
@SneakyThrows
public static byte[] calculateMinHash(String text)
// Lucene's tokenizer parses a text.
Tokenizer tokenizer = new WhitespaceTokenizer();
// The number of bits for each hash value.
int hashBit = 1;
// A base seed for hash functions.
int seed = 0;
// The number of hash functions.
int num = 128;
// Analyzer for 1-bit 32 hash with custom Tokenizer.
Analyzer analyzer = MinHash.createAnalyzer(tokenizer, hashBit, seed, num);
// Calculate a minhash value. The size is hashBit*num.
return MinHash.calculate(analyzer, text);
以上结果得到是字节数组,可根据实际需要转换成相关的二进制数或十进制数。
总结
与SimHash不同,如果比较的文本中有一个字符或者个别文字不同,那么得到的Hash值结果也是不同的,但与传统Hash算法不同,相差不大的文本得到的Hash值相差不大。因此该算法适用于精准比较文本差异的场景,只要有略微差异也认为是不一样的。
参考资料
以上是关于MinHash算法原理与应用(Java版)的主要内容,如果未能解决你的问题,请参考以下文章