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计算过程

首先列一下整个计算过程,主要分为以下几个步骤:

  1. 将比较的内容转换为相应的特征行

  2. 对文档对应特征的行进行重排列

  3. 取排列后每列的第一个非零值即为MinHash

  4. 重复多次1-2步骤,得到原特征向量的新表示,称signature,即降维后的新向量

  5. 计算两个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(元素)ABC
1a011
2b110
3c010
4d100
5e101
6f001

3. 矩阵重排

下面我们进行3次重新组合矩阵,

(1)第一次排序:依次按照行号123456的顺序排列,也就是上表,集合A、B、C对应第一个值为1对应的行号分别为2,1,1,所以得到各集合的数字签名为(A,B,C) 为(2,1,1)

(2)第二次排序:同理,按照行号243561的顺序进行排列,结果如下表:

LineNo(行号)Element(元素)ABC
1a110
2b100
3c010
4d101
5e001
6f011

得到各集合的数字签名为(A,B,C)为(1,1,4)

(3)第三次排序:按照行号312645的顺序进行排列,结果为下表,

LineNo(行号)Element(元素)ABC
1a010
2b011
3c110
4d001
5e100
6f101

得到各集合的数字签名为(A,B,C)为 (3,1,2)

4. 合并结果

通过三次排序后,我们将三次的Hash值结果合并,如下表:

ABC
第一次Hash结果211
第二次Hash结果114
第三次Hash结果312

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>
  1. 计算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算法介绍

【MinHash, LSH】高维向量的相似度快速计算

以上是关于MinHash算法原理与应用(Java版)的主要内容,如果未能解决你的问题,请参考以下文章

比较图片相似度算法介绍与应用(Java版)

比较图片相似度算法介绍与应用(Java版)

比较图片相似度算法介绍与应用(Java版)

文本局部敏感哈希-MinHash算法原理

minhash算法用于文本查重

SimHash算法原理与应用(Java版)