文本相似性处理(好比论文查重)

首先介绍simHash

simhash算法分为5个步骤:分词、hash、加权、合并、降维

直接给例子,理解的更加生动些吧:https://blog.csdn.net/chinawangfei/article/details/82385842

  • 1:分词。首先,判断文本分词,形成这个文章的特征单词。然后,形成去掉噪音词的单词序列。最后,为每个分词加上权重。我们假设权重分为5个级别(1~5),比如:“ 美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人 ” ==> 分词后为 “ 美国(4) 51区(5) 雇员(3) 称(1) 内部(2) 有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)”,括号里是代表单词在整个句子里重要程度,数字越大越重要。

  • 2:hash。通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为 101011。这样,我们的字符串就变成了一串串数字,还记得文章开头说过的吗?要把文章变为数字计算,才能提高相似度计算性能,现在是降维过程进行时。

  • 3:加权。在第2步骤hash生成结果后,需要按照单词的权重形成加权数字串,比如“美国”的hash值为“100101”,通过加权计算为“4 -4 -4 4 -4 4”;“51区”的hash值为“101011”,通过加权计算为 “ 5 -5 5 -5 5 5”。

  • 4:合并。把上面各个单词算出来的序列值累加,变成只有一个序列串。比如 “美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5 -5 5 -5 5 5”, 把每一位进行累加, “4+5 -4+-5 -4+5 4+-5 -4+5 4+5” ==》 “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的,真实的计算需要把所有单词的序列串累加。

  • 5:降维。把第4步算出来的 “9 -9 1 -1 1 9” 变成 0 1 串,形成我们最终的simhash签名。 如果每一位大于0 记为 1,小于或等于0 则记为 0。最后算出结果为:“1 0 1 0 1 1”。

 

这个例子完了之后其中对于hash部分还是很懵逼,我很是想知道这个hash算法咋给计算的,为啥子要这么计算,因为在代码里就长这样了:

private BigInteger hash(String source) {
    if (source == null || source.length() == 0) {
        return new BigInteger("0");
    } else {
        char[] sourceArray = source.toCharArray();
        BigInteger x = BigInteger.valueOf(((long) sourceArray[0]) << 7);
        BigInteger m = new BigInteger("1000003");
        BigInteger mask = new BigInteger("2").pow(this.hashBits).subtract(
                new BigInteger("1"));
        for (char item : sourceArray) {
            BigInteger temp = BigInteger.valueOf((long) item);
            x = x.multiply(m).xor(temp).and(mask);
        }
        x = x.xor(new BigInteger(String.valueOf(source.length())));
        if (x.equals(new BigInteger("-1"))) {
            x = new BigInteger("-2");
        }
        return x;
    }
}
 
 
public BigInteger simHash() {
    int[] v = new int[this.hashBits];
    List<String> words = cutSentenceToWords(sentence);
    for (String word : words) {
        BigInteger t = this.hash(word);
        for (int i = 0; i < this.hashBits; i++) {
            BigInteger bitmask = new BigInteger("1").shiftLeft(i);
            if (t.and(bitmask).signum() != 0) {
                v[i] += 1;
            } else {
                v[i] -= 1;
            }
        }
    }
    BigInteger fingerprint = new BigInteger("0");
    for (int i = 0; i < this.hashBits; i++) {
        if (v[i] >= 0) {
            fingerprint = fingerprint.add(new BigInteger("1").shiftLeft(i));
        }
    }
    return fingerprint;
}

 

接下来介绍大数据量下存储和查询

到这里相似度问题基本解决,但是按这个思路,在海量数据几百亿的数量下,效率问题还是没有解决的,因为数据是不断添加进来的,不可能每来一条数据,都要和全库的数据做一次比较,按照这种思路,处理速度会越来越慢,线性增长。

可以参考这个:http://blog.itpub.net/69901774/viewspace-2675029/

抽屉原理,也称鸽巢原理。下面我们简单举例说一下:

桌子上有四个苹果,但只有三个抽屉,如果要将四个苹果放入三个抽屉里,那么必然有一个抽屉中放入了两个苹果。如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有n+1个元素放到n个集合中去,其中必定有一个集合里至少有两个元素。

抽屉原理就是这么简单,那如果用它来解决我们海量数据的遍历问题呢?

针对海量数据的去重效率,我们可以将64位指纹,切分为4份16位的数据块,根据抽屉原理在海明距离为3的情况,如果两个文档相似,那么它必有一个块的数据是相等的。

那也就是说,我们可以以某文本的 SimHash 的每个16位截断指纹为 Key,Value 为 Key 相等时文本的 SimHash 集合存入 K-V 数据库即可,查询时候,精确匹配这个指纹的4个16位截断指纹所对应的4个 SimHash 集合即可。

如此,假设样本库,有2^37 条数据(1375亿数据),假设数据均匀分布,则每个16位(16个01数字随机组成的组合为2^16 个)倒排返回的最大数量为
(2^37)*4/(2^16)=8388608个候选结果,4个16位截断索引,总的结果为:4*8388608=33554432,约为3356万,通过
这样一来的降维处理,原来需要比较1375亿次,现在只需要比较3356万次即可得到结果,这样以来大大提升了计算效率。

数据压缩

如果再加上 Snappy 压缩呢?
如果再加上 Fast-Diff 编码呢?
如果再开启 Mob 对象存储呢? 每个 Set 是不是可以存10万个键值对?每行只需90个 Set 集合。

这些只是存储空间上面的,但是还有更加合适的方式,就是要结合业务

例如:

1.我球所处的业务是股票的社区讨论,股票的实时性很重要的,这个也是有人在公众号上问过的,为什么抖音上面没有关于股票介绍的(原因就是根据抖音的实时热点分发,当她把内容投递到用户的时候,没准这个观点已经不再适应当时的语义环境)

2.那么实时性结合严重的话,我们就只存储近7天的文章进行相似性的比对

3.文章的属性和simhash的key-value相互的反向存储根本也就占用不了多少资源

 

然后来介绍下工程实现

代码:https://github.com/singgel/simple-simhash

 

参考资料

1.https://blog.csdn.net/u010454030/article/details/49102565
2.http://www.lanceyan.com/tech/arch/simhash_hamming_distance_similarity2-html.html
3.https://cloud.tencent.com/developer/news/218062
4.https://blog.csdn.net/qq_36142114/article/details/80540303
5.
https://blog.csdn.net/u011467621/article/details/49685107
6.http://blog.itpub.net/69901774/viewspace-2675029/
7.https://blog.csdn.net/u011467621/article/details/49685107 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值