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】高维向量的相似度快速计算

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MinHash算法是一种用于计算两个集合之间相似度的快速算法。其基本思想是将集合中的元素哈希成一个个签名,再比较两个集合的签名相同的元素个数。 具体实现步骤如下: 1. 首先将集合中的元素进行哈希,得到一个个哈希值。 2. 然后将哈希值分成若干组,每组取一个最小值作为这一组的签名。 3. 对于两个集合,比较它们的签名相同的元素个数,用这个数值作为它们的相似度。 下面是一个简单的Python实现: ```python import hashlib def minhash(set1, set2, num_hashes): # 随机生成num_hashes个哈希函数 hash_funcs = [hashlib.sha1(str(i).encode()).hexdigest() for i in range(num_hashes)] # 初始化两个集合的签名 set1_sig = [float('inf')] * num_hashes set2_sig = [float('inf')] * num_hashes # 计算集合的签名 for val in set1: # 对每个元素进行哈希 hash_val = hashlib.sha1(str(val).encode()).hexdigest() # 更新集合的签名 for i in range(num_hashes): if hash_funcs[i] in hash_val: set1_sig[i] = min(set1_sig[i], val) for val in set2: # 对每个元素进行哈希 hash_val = hashlib.sha1(str(val).encode()).hexdigest() # 更新集合的签名 for i in range(num_hashes): if hash_funcs[i] in hash_val: set2_sig[i] = min(set2_sig[i], val) # 计算相似度 similarity = sum([set1_sig[i] == set2_sig[i] for i in range(num_hashes)]) / num_hashes return similarity ``` 这个实现中,我们使用了SHA1哈希函数来生成哈希值。在生成签名时,我们将哈希值分成了num_hashes组,并且每组取一个最小值作为签名。最后,我们计算两个集合的签名相同的元素个数,并用这个数值作为它们的相似度
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值