Simhash
Simhash是一种为文档生成指纹的算法,然后可以通过计算simhash值之间的汉明距离来度量两个文档的相似度。
算法步骤:
-
用某种算法找到文档的话题词,例如TF-IDF,
e.g., [‘simhash’, ‘文档检索’, ‘以图搜图’]。
-
用某种哈希算法将这些话题词哈希化,
e.g., [‘10101’, ‘11111’, ‘10100’],
PS: 想生成多少位的simhash,就用多少位的哈希算法。
-
让字符串中的’1’不变,‘0’变成’-1’,将结果相加,
e.g., [[1, -1, 1, -1, 1], [1, 1, 1, 1, 1], [1, -1, 1, -1, -1]] -> [3, -1, 3, -1, 1]。
-
让大于0的位为1,否则为0,结果就是这篇文档的simhash值,
e.g., ‘10101’。
汉明(海明)距离
二进制数a和b之间的汉明距离 = a^b的结果中1的个数。
e.g. 10101和00000的汉明距离是3。
检索
根据汉明距离的计算方式可以看出,两个simhash值之间的汉明距离越小,两篇文档越相似。
为了举例方便,示例中的simhash仅有5位,然而实际应用时,simhash值可能有128位甚至更多。要知道检索一条汉明距离小于给定阈值的simhash时间复杂度是 O ( n 2 ) O(n^2) O(n2),在海量数据下使用该算法的代价是昂贵的。
为了解决这个问题,需要用空间去换时间。具体思路为:
- 设汉明距离<n时认为文档与给定文档相似;
- 将simhash值分为n段,则汉明距离<n时两串simhash之间至少有一段完全相同;
- 将信息保存到哈希表中,其中n段中的每一段都作为key,simhash值以及用户自定义的其他字段作为value。
这样,检索速度最快为 O ( 1 ) O(1) O(1),最慢为 O ( n ) O(n) O(n),远优于原本的 O ( n 2 ) O(n^2) O(n2),缺点是空间膨胀到原来的n倍。通常n为4,是一个可以接受的膨胀倍率。
PS: 如果将信息保存在数据库中,时间复杂度将会是 l o g n logn logn,同样远优于 O ( n 2 ) O(n^2) O(n2)。
扩展
在simhash算法的步骤1中,提取了n个话题词,话题词即为最能体现这篇文档特征的词,也就是说,在表示这篇文档的词袋向量的数组中提取了n个最能体现这篇文档特征的索引;
类似的,对于图片,可以把它转化成某种积分图,然后在表示这张图片的积分图的数组中提取n个最能体现这张图片特征的索引。
在我的实现中,我使用了HSV直方图,和mmh3算法。当然这些都是可以用户自定义的,具体请参考策略模式,以及我项目目录里的类图。
github地址
https://github.com/Qyokizzzz/simhash