• 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    以上结果得到是字节数组,可根据实际需要转换成相关的二进制数或十进制数。

    总结

    与SimHash不同,如果比较的文本中有一个字符或者个别文字不同,那么得到的Hash值结果也是不同的,但与传统Hash算法不同,相差不大的文本得到的Hash值相差不大。因此该算法适用于精准比较文本差异的场景,只要有略微差异也认为是不一样的。

    参考资料

    minHash算法介绍

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

  • 相关阅读:
    天池AI练习生计划 - 第一期Pyhton入门与实践 正式上线!通关赢取双重礼品!
    Letbook Cookbook题单——数组二分与双指针
    从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(九)文件服务篇(2):集成minio文件服务
    CrownCAD 2023发布的背后,国产软件如何破局突围?
    智能优化之遗传算法
    字符串的简单介绍和字符串的大小比较
    【Android笔记51】Android之两种常见的数据交互方式(Activity和Activity、Activity和数据适配器)
    【设计模式】十、组合模式
    力扣刷题记录103.1-----518. 零钱兑换 II
    程序、进程与线程
  • 原文地址:https://blog.csdn.net/u013034223/article/details/127793973