simhash,相似文档检索、以图搜图、图片去重

611 阅读2分钟

Simhash

Simhash是一种为文档生成指纹的算法,通过计算两个simhash值之间的汉明距离来量化两篇文档的相似度。

算法步骤:

  1. 用某种算法找到文档的话题词,例如TF-IDF,

    e.g. ['simhash', '文档检索', '以图搜图']。

  2. 用某种哈希算法将这些话题词哈希化,

    e.g. ['10101', '11111', '10100'],

    ps: 想生成多少位的simhash,就用多少位的哈希算法。

  3. 让字符串中的'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]。

  4. 让大于0的位为1,否则为0,结果就是这篇文档的simhash值,

    e.g. '10101'。

汉明(海明)距离

二进制数a和b之间的汉明距离 = a^b的结果中1的个数。

e.g. 10101和00000的汉明距离是3。

检索

根据汉明距离的计算方式可以看出,两个simhash值之间的汉明距离越小,两篇文档越相似。

为了举例方便,示例中的simhash仅有5位,然而实际应用时,simhash值可能有128位甚至更多。要知道检索一条汉明距离小于给定阈值的simhash时间复杂度是O(n2)O(n^2),在海量数据下使用该算法的代价是昂贵的。

为了解决这个问题,需要用空间去换时间。具体思路为:

  1. 设汉明距离<n时认为文档与给定文档相似;
  2. 将simhash值分为n段,则汉明距离<n时两串simhash之间至少有一段完全相同;
  3. 将信息保存到哈希表中,其中n段中的每一段都作为key,simhash值以及用户自定义的其他字段作为value。

这样,检索速度最快为O(1)O(1),最慢为O(n)O(n),远优于原本的O(n2)O(n^2),缺点是空间膨胀到原来的n倍。通常n为4,是一个可以接受的膨胀倍率。

ps: 如果将信息保存在数据库中,时间复杂度将会是O(logn)O(logn),同样远优于O(n2)O(n^2)

扩展

在simhash算法的步骤1中,提取了n个话题词,话题词即为最能体现这篇文档特征的词,也就是说,在表示这篇文档的词袋向量的数组中提取了n个最能体现这篇文档特征的索引

类似的,对于图片,可以把它转化成某种积分图,然后在表示这张图片的积分图的数组中提取n个最能体现这张图片特征的索引

在我的实现中,我使用了HSV直方图,和mmh3算法,这些当然是应该能够自定义的,因此我在设计上采用了策略模式,使扩展成为了可能。

我的实现

simhash by strategy pattern.png