Simhash理论
首先,hash算法是将对象映射成一个哈希值,当两个对象完全相同时,会有相同的哈希值。
类似的,在自然语言处理中,如果两个文章也可以映射成hash值,如果两个文章具有相似的内容,哈希值也应该相似,这就是simhash算法的思想。
simhash是一种能计算文档相似度的hash算法。具体做法是:
1.通过simhash将一篇文章映射成64bit。
2.比较两篇文章的64bit的海明距离,就能知道文章的相似程度。
3.若两篇文章的海明距离<=K,可认为这两篇文章很相近,可认为它们是重复的文章。
对于海量文档排重:将所有文章都表示成64bit simhash值,搜索和当前文档相差小于K位的文档,即可判定为重复文档。
海明距离
两个码字的对应比特取值不同的比特数称为这两个码字的海明距离。 一个有效编码集中, 任意两个码字的海明距离的最小值称为该编码集的海明距离。
举例如下:
1 0 1 0 1
0 0 1 1 0
海明距离的计算取相同位置的异或关系,对应位置相同则为1,不同则为0。
从第一位开始依次有第一位、第四、第五位不同,则海明距离为 3.
simhash过程
实验过程
- 清洗文本,去除停用词等。
- 选择权重最高的top10个关键词作为文本特征并按权重高低排序。
keyWord = jieba.analyse.extract_tags(
'|'.join(content), topK=10, withWeight=True, allowPOS=())
- 对其中的特征词词(feature),进行普通的哈希之后得到一个64为的二进制,得到长度为10的(hash : weight)的集合。
def string_hash(self,source):
if source == "":
return 0
else:
x = ord(source[0]) << 7
m = 1000003
mask = 2 ** 128 - 1
for c in source:
x = ((x * m) ^ ord(c)) & mask
x ^= len(source)
if x == -1:
x = -2
x = bin(x).replace('0b', '').zfill(64)[-64:]
print(source,x)
return str(x)
- 根据得到一串二进制数(hash)中相应位置是1是0,对相应位置取正值weight和负值weight。
keyList = []
for feature, weight in keyWord:
weight = int(weight * 10)
feature = self.string_hash(feature)
temp = []
for i in feature:
if(i == '1'):
temp.append(weight)
else:
temp.append(-weight)
keyList.append(temp)
例如一个词经过2得到哈希值和对应权重是:(010111:5),经过步骤3之后可以得到列表[-5,5,-5,5,5,5]。
即对一个文档,我们可以得到20个长度为64的列表[weight,-weight…weight]。
- 对4中得到的列表中每个值取和进行判断,当为负值的时候去0,正值取1。
simhash = ''
for i in list1:
if(i > 0):
simhash = simhash + '1'
else:
simhash = simhash + '0'
return simhash
例如,[-7,1,-9,9,3,9]得到010111,这样,我们就得到一个文档的simhash值了。
- 计算海明距离
def hammingDis(self, com):
t1 = self.simhash
t2 = com.simhash
#n表示运算次数
n = int(t1, 2) ^ int(t2, 2) #2进制异或运算,有1则1
i = 0
#进行与运算,如果对应位置相等则为1,否则为0
while n:
n &= (n - 1)
i += 1
return i
总结
simhash是goole针对海量网页的去重,对于网页中的文档存在大量抓取,拷贝等现象。不同门户网站之间会互相转载其他网站的文章,这些文章基本雷同。对这些文章如果用余弦相似度计算,会导致计算量大,时间复杂度高。simhash针对网页排重有比较好的计算效果。