最近在做大规模文本的相似度计算。发现传统的word2vec,等方法,把句子转化成词向量,然后两两计算,会造成很大的计算量,就查找资料发现simhash算法。
simhash 被google用于去重网页。
simhash主要方案分成5步。
1.分词。我(2)爱(4)中国(10),对一句话进行分词,并得到每个词的权重。
2.hash。分别对{我,爱,中国}进行哈希函数计算。{我:1010,爱:0101,中国:1011}
3.加权。对hash出的值进行计算。我的权重是2,所以对应的我的hash值,某位为1的映射成2,0的映射成-2,
所以,我:(2,-2,2,-2),爱(-4,4,-4,4),中国(10,-10,10,10)
4.整合。分别对我,爱,中国,对应位置的数值相加(2-4+10,-2+4-10,2-4+10,-2+4+10)即为(8,-8,8,12)
5.降维。将(8,-8,8,12)中大于0的变为1,小于0的变成0,得到simhash值为(1,0,1,1)
那么怎么减少计算呢,就需要进行索引操作。
比如一个16维的simhash数据simhash1 = (1111,0000,0101,1010)要找到海明距离为3以内的值,根据抽取原理,必有4位数是相同的。
海明距离为3,就是和simhash1 中相同位置,只有3个数据不一样,比如simhash2 =(1111,0001,0111,1011)和simhash1的海明距离为3.
抽屉原理就是说,现在有3个物品,放入4个抽屉中,那么最少有一个抽屉是空着的。对应到这里就是,我们有3个数与原有的simhash值不同,那么最少有一个连续4位数是相同的。
所以,把16维的hash串,分成4串(1111,0000,0101,1010)根据每串做索引,就会减少比较的数据量。
我用python的字典实现了一个倒排索引。
#hash_list是一个simhash对象列表,列表中每个元素H,的simhash数值为H.hash hash_dict = {} for i,h in enumerate(hash_list): #将哈希数值转化成01序列,并填充到64维 hash_string = str(bin(h.hash).replace('0b', '')).zfill(64) for j in range(0,8): #将64维的数据分成8份,作为hash_dict的键 key = hash_string[j*8:(j+1)*8] #查找Key是否在字典中,如果不在,则把key添加到hash_dict中,然后把hash_list中的元素添加进去。 if key not in hash_dict.keys(): hash_dict[key] = [] hash_dict[key].append(hash_list[i]) #查找Key是否在字典中,如果在,直接把hash_list中的元素添加进去。 else: hash_dict[key].append(hash_list[i])
然后进行相似度检索时,直接查找hash_dict,就可找到最少有8维相同的数据,减小计算量。
小伙伴们理解了吗,赶紧使用起来!