前言:
这篇博客主要从python(3)代码的角度来讲本人是如何实现simhash的,不足之处还望大家不吝赐教。simhash的算法原理大家可以参考简单易懂讲解simhash算法。值得一提的是文中提到simhash对长文本更加有效,个人从算法原理角度思考过后感觉也是如此。
初始化
首先我们要明确我们需要的是什么,给定了一个大语料,我们希望得到的是每个词的编码以及对应的权重,那么我们可以将目标拆分为以下几个:
1.对文本进行分词,并根据需要进行停词处理。
2.对每个词进行hash编码(一般用32位的就够了)。
3.对每个词赋予权重,这里正好就能用idf的值作为其权重值,idf具体介绍可见tf-idf进行关键词提取
说了这么多,还是用代码来看看具体是怎么做到的吧~
分词
一上来就是最让人头疼的问题,如何进行分词,分词的相关理论也是种类繁多,能够成一个专题,这里大家有兴趣的可以自行去查阅相关资料,我在这里使用的是jieba分词,jieba是一个功能强大的分词工具,大家可以参考博客好玩的分词–jieba模块的的基本用法,分词完毕后我将其保存为新的txt(test_ws.txt),之后我们载入的文本文件便是分词过后的文件了。
hash编码和idf值计算
# -*- coding:utf-8 -*-
import numpy as np
import codecs
import math
BITS = 31
path = './data/FAQ_toB_par'
class simHash(object):
# 初始化,遍历文档(已分词),得到词汇表,并进行32位(能表示2^32种情况,完全足够)\
#hash编码和对应的idf值,将idf值作为其权重进行运算,分别存入两个字典(dict)
def __init__(self, path):
f = codecs.open(path, 'r', encoding='utf-8')
dictHash = dict()
dictWeight = dict()
i = 0#hash编码
lines = 0#记录文本数量,以计算idf值
#遍历文本,进行hash编码和统计df词频(在多少篇文章出现过,而不是总词频,\
#比如某个词在一个文本中出现三次也只算一次)
for line in f:
lines += 1
temp = set(str(line).strip().split())#避免重复统计词频
for item in temp:
if item not in dictWeight:
dictWeight[item] = 1
dictHash[item] = i
i += 1
else:
dictWeight[item] += 1
f.close()
del i
#hash编码转为array形式的二进制,方便计算
for item in dictHash:
L = list(bin(dictHash[item]))[2:]
intL = [int(x) for x in L]
for i in range(len(intL)):
if intL[i] == 0:
intL[i] = -1
intL = (BITS - len(intL))*[-1]+intL
dictHash[item] = np.array(intL)
#根据词频计算idf值
for item in dictWeight:
dictWeight[item] = math.log(lines/dictWeight[item])
self.dictHash = dictHash
self.dictWeight = dictWeight
至此便完成了由大语料得到词语的hash编码和idf值,我们通过词典可以查看某个词:
print(self.dictHash['什么'])
print(self.dictWeight['什么'])
然后我们还需要根据两个词典对句子进行hash编码(注意输入的句子是分词过后的),
同时还定义了一个计算两个句子Hamming距离的函数,(Hamming距离越小说明句子相似度越高)。
#根据词的hash对句子进行hash编码
def senHash(self, sen):
senHashCode = np.zeros(BITS)
temp = sen.strip().split()
for item in temp:
senHashCode += self.dictHash[item]*self.dictWeight[item]
for i in range(BITS):
if senHashCode[i] > 0:
senHashCode[i] = 1
else:
senHashCode[i] = 0
return senHashCode
#获取两个句子的Hamming distance,dis越小说明相似度越高
def sen2senDis(self, sen1, sen2):
temp1 = self.senHash(sen1)
temp2 = self.senHash(sen2)
Hamming = 0
for i in range(BITS):
if temp1[i] != temp2[i]:
Hamming += 1
return Hamming
到这里基本上就结束了,这里我是定义的一个类,大家可以用这个类来进行相似文本去重等操作。
来源:CSDN
作者:IGV丶明非
链接:https://blog.csdn.net/gzt940726/article/details/80460419