传统的 hash 算法只负责将原始内容尽量均匀随机地映射为一个签名值,原理上相当于伪随机数产生算法。产生的两个签名,如果相等,说明原始内容在一定概 率 下是相等的;如果不相等,除了说明原始内容不相等外,不再提供任何信息,因为即使原始内容只相差一个字节,所产生的签名也很可能差别极大。从这个意义 上来 说,要设计一个 hash 算法,对相似的内容产生的签名也相近,是更为艰难的任务,因为它的签名值除了提供原始内容是否相等的信息外,还能额外提供不相等的 原始内容的差异程度的信息。
而 Google 的 simhash 算法产生的签名,可以满足上述要求。出人意料,这个算法并不深奥,其思想是非常清澈美妙的。
算法简介
1,将一个 f 维的向量 V 初始化为 0 ; f 位的二进制数 S 初始化为 0 ;
2,对每一个特征:用传统的 hash 算法对该特征产生一个 f 位的签名 b 。对 i=1 到 f :
如果b 的第 i 位为 1 ,则 V 的第 i 个元素加上该特征的权重;
否则,V 的第 i 个元素减去该特征的权重。
3,如果 V 的第 i 个元素大于 0 ,则 S 的第 i 位为 1 ,否则为 0 ;
4,输出 S 作为签名。
算法几何意义
相似度比较
异或: 只有在两个比较的位不同时其结果是1 ,否则结果为 0
对每篇文档根据SimHash 算出签名后,再计算两个签名的海明距离(两个二进制异或后 1 的个数)即可。根据经验值,对 64 位的 SimHash ,海明距离在 3 以内的可以认为相似度比较高。假设对64 位的 SimHash ,我们要找海明距离在 3 以内的所有签名。我们可以把 64 位的二进制签名均分成 4块,每块 16 位。根据鸽巢原理(也成抽屉原理,见组合数学),如果两个签名的海明距离在 3 以内,它们必有一块完全相同。
我们把上面分成的4 块中的每一个块分别作为前 16 位来进行查找。 建立倒排索引。
simhash-py
要更准确的对文章进行排重,需要找到好的simhash算法。目前我知道的有python-hashes,simhash-py。两个库通过简单的修改,再加上中文分词库,可以比较好的对中文文章计算hash。simhash-py可使用我fork的版本以支持中文文章的simhash (通过里面的hash_token或hash_tokenpy两个函数实现对切词之后的中文文章进行simhash计算)。
simhash算法
simhash算法最简单实现的库应该是python-hashes库了。使用过程当中发现,对于排重的使用目的来说,这个库的simhash算法有缺点是:只考虑到文章存在哪些词,没有考虑到词的顺序。不过相应的优点是,可以实现海量文章相似度计算。文章相似度计算忽略词的顺序之后效果更好。
simhash-py内部实现了simhash算法。它里面的simhash使用了cyclic hash算法,这个算法考虑到N(可以在3~5)个词之间的顺序。考虑到词的顺序的hash算法在排重过程当中会更准确,不过这个我也没有特别好的测试:)
simhash加快搜索
若看过本文推荐的simhash的原理讲解那篇文章,发现可以通过“shard”方式加快simhash值的搜索,从而能快速的知道是否存在重复的文章。而simhash-py库通过C++的Judy库实现了这一点。
simhash集群处理
既然可以通过“shard”方式,那么很容易把这个思路拓展到集群上。所以相应的,simhashpy的作者实现了simhash-cluster。
总结:
simhash算法可以进行海量文档的排重,同时也可用于文档检索