基于hash的文档判重——simhash
本文环境:
python3.5
ubuntu 16.04
第三方库:
- jieba
文件寄于github: https://github.com/w392807287/angelo_tools.git
simhash介绍
没多久就要写毕业论文了,据说需要查重,对文档重复判定还挺好奇的所以看了下相关的东西。发现simhash比较好用,实现简单。
顾名思义 simhash是一种hash算法,以前在我印象中hash算法是将一个对象映射成一个hash值,一般只要求当两个对象完全相同时才有相同的hash值,而两个相似的对象的hash值并不需要有任何关系。只相差一个字符hash出来的值也可能相差十万八千里。但是如果hash函数设计的足够巧妙,也可以让相似的对象拥有相同或者相似的hash值,使用hash来进行相似性搜索更方便快捷。
simhash就是这么一个神奇的算法。它满足:
- 当两个对象的距离不大于d1时,它们的hash值相同的概率不小于p1,即如d(x, y) ≤ d1,则P(hash(x) = hash(y)) ≥ p1.
- 当两个对象的距离不小于d2时,它们的hash值相同的概率不大于p2,即如d(x, y) ≥ d2,则P(hash(x) = hash(y)) ≥ p2.
simhash可以将文档hash到一个64位二进制数,使得相似的文档具有相似的二进制数。对于一个文档,我们可以把文中的每个词或者词组作为一个特征,统计各个特征出现的频率(当然也可以加入词性的权重,怎么去设置、统计特征可以视情况而定)。下面的例子中我们使用 jieba 做分词。
目标文档 “葫芦娃葫芦娃,一根藤上七朵花”,得到的特征与相应的频率:(葫芦娃,0.33),(一根,0.17,(藤上,0.17),(七朵,0.17),(花,0.17)。然后对特征值进行hash,方便演示这里映射到6位:
- 葫芦娃:100100
- 一根:010101
- 藤上:101010
- 七朵:111010
- 花:001010
然后根据二进制数的各个二进制位,我们队每个特征构造一个向量。如果一个特征映射到的二进制数的某一位是1,则其向量对应位置上的分量为该特征的频率,否则为频率的相反数。如:
葫芦娃:(0.33,-0.33,-0.33,0.33,-0.33,-0.33)
……
将向量相加,得到(0.33,-0.33,0,0,0,-0.66)
对于每个分量,如果大于0就取1,否则取0,这样就能得到二进制数的simhash,即100000。
在文本中,出现频率高的特征,其对应的向量分量的绝对值更大,对最终向量相加的结果影响也更大。因此,如果两个文档相似,那么它们出现频率高的特征也应该比较接近,最终得到的hash值也就越接近。在google网页的检索中,64位hash中至多有3个二进制位不同可判定为相似文档。
算法实现
def simhash(cls, s, RE=None, cut_func=None):
if RE:
REX = RE
else:
REX = re.compile(u'[\u4e00-\u9fa5]+')
if not cut_func:
cut_func = cls.cut_func #jieba.cut
cut = [x for x in cut_func(s) if re.match(REX, x)]
ver = [[v * (int(x) if int(x) > 0 else -1) for x in k] for k, v in cls.hist(cut).items()]
ver = np.array(ver)
ver_sum = ver.sum(axis=0)
sim = ''.join(['1' if x > 0 else '0' for x in ver_sum])
return sim
首先我们用正则定义了感兴趣的区域,这里我们只取我们感兴趣的中文。然后我们定义了分词所用的函数,这里使用的是jieba分词。
然后我们得到分词的结果:cut = [x for x in cut_func(s) if re.match(REX, x)]
得到向量矩阵:ver = [[v * (int(x) if int(x) > 0 else -1) for x in k] for k, v in cls.hist(cut).items()]
为了方便计算我们引入numpy帮我们做矩阵计算:
ver = np.array(ver)
ver_sum = ver.sum(axis=0)
最后将计算结果转换为二级制hash。因为我们这里使用的32位md5给分词结果做的hash所以最后得到的hash值也是32位的:
11111101011001101110111100101101
其中我们用到了几个工具函数:
@classmethoddef
hist(cls, cut):
_cut = {x: 0 for x in set(cut)}
for i in cut:
_cut[i] += 1
return {cls.hash_bin(k): v/len(cut) for k, v in _cut.items()}
hist函数是将分词列表转换为特征频率向量的。
@classmethoddef
hash2bin(cls, hash):
d = ''
for i in hash:
try:
if int(i) > 7:
d = d + '1'
else:
d = d + '0'
except ValueError:
d = d + '1'
return d
@classmethoddef
hash_bin(cls, s):
h = hashlib.md5(s.encode()).hexdigest()
return cls.hash2bin(h)
其中hash_bin函数用来将字符Hash成二级制hash值,基础hash算法为32位md5。
hash2bin函数是将16进制hash值映射成二进制hash。
为了方便比较我们使用海明距离来判定两个hash值的相似度:
@staticmethoddef
haiming(s1, s2):
x = 0
for i in zip(s1, s2):
if i[0] != i[1]:
x += 1
return x
效果
1993年,南京大学有这样一个男生寝室,四个男生都没有女朋友,于是搞了个组合叫“名草无主四大天王”。这四大天王坚持每晚举行“卧谈会”,从各种学术上讨论如何摆脱光棍状态。这一年的11月,校园的梧桐树落叶凋零,令他们分外伤情。他们在11日这一天晚上卧谈时,符号学的灵感突然登门造访。11月11日,四个1字排开,不正是好像四根光秃秃的棍子吗?这四根光棍不正是在巧妙地诉说着“名草无名四大天王”的凄凉吗?
*
知乎上有个提问,小时候缺爱的女孩子,长大后该怎么办?或许在我这里,只是希望一直有人陪。喜宝说,我想要很多很多的爱,要不就是很多很多的钱,实在不行,有健康也是好的。我有个坏毛病,经常会半夜饿到不行,爬起来找吃的。是真的饿到胃疼,有时候直接饿醒了,每次看到电影里的台词,睡着了就不饿了,我是压根不相信。为什么会半夜饿?究其原因,是大学的时候没人陪我吃饭,每次都是一直等到有人陪我的时候,我才会去吃饭,最后把自己饿到胃疼,久而久之,就渐渐习惯了熬到很晚才吃饭。我不喜欢一个人吃饭,也不喜欢一个人逛街,更不喜欢一个人呆着,可是成长啊,往往是越不喜欢的便越要学会接受它。(二)讲讲上一段恋爱吧。我和他认识的时候,是因为贴吧聚餐,他主动找我要的微信,附带一个如沐春风般的笑容。我一直以为他是被我的美色打动,后来问他原因。他说,他第一次看见那么能吃的女孩子,他惊呆了,可是有觉得看我吃饭很意思,仿佛食物都有了灵魂,让人的心情莫名的好了起来。我们初相识,是因为他看见了我饿死鬼投胎的吃相。我们在一起,是因为他厨艺很好,好到什么程度呢?就是那种你吃过一顿,就能惦记一辈子的感觉。即便是现在回忆起他来,我的味蕾都会有反应。他总是给我做很多很多好吃的,午后阳光从窗子洒进来,窗帘是淡绿色的小碎花,空气里弥漫着饭香味,我们两个人坐在桌前,一边吃饭,一边聊天。我喜欢和他一起手挽着手去菜市场买菜,西红柿土豆黄瓜小白菜,手里拎着的这些果蔬食物,就好像我拥有的全世界。有一次,我们从菜市场回去的路上,明明是艳阳高照的天气,却突然间下起了冰雹,那是他第一次看见冰雹,被砸了一下之后,便立马丢了手里的菜,双手护住我,我傻了吧唧的去捡菜,被砸了一身。他立马臭骂了我一顿,说我是他见过,最好吃的女孩子了。
以上是简书一片文章中的节选。
两个的simhash是1111110101100110111011110010110100101101001010110001100000101110
海明距离为16。
知乎上有个提问,小时候缺爱的女孩子,长大后该怎么办?或许在我这里,只是希望一直有人陪。喜宝说,我想要很多很多的爱,要不就是很多很多的钱,实在不行,我有个坏毛病,经常会半夜饿到不行,爬起来找吃的。是真的饿到胃疼,有时候直接饿醒了,每次看到电影里的台词,睡着了就不饿了,我是压根不相信。究其原因,是大学的时候没人陪我吃饭,每次都是一直等到有人陪我的时候,我才会去吃饭,最后把自己饿到胃疼,久而久之我不喜欢一个人吃饭,也不喜欢一个人逛街,更不喜欢一个人呆着,可是成长啊,往往是越不喜欢的便越要学会接受它。我和他认识的时候,是因为贴吧聚餐,他主动找我要的微信,附带一个如沐春风般的笑容。我一直以为他是被我的美色打动,后来问他原因。他说,他第一次看见那么能吃的女孩子,他惊呆了,可是有觉得看我吃饭很意思,仿佛食物都有了灵魂,让人的心情莫名的好了起来。我们初相识,是因为他看见了我饿死鬼投胎的吃相。我们在一起,是因为他厨艺很好,好到什么程度呢?就是那种你吃过一顿,就能惦记一辈子的感觉。即便是现在回忆起他来,我的味蕾都会有反应。他总是给我做很多很多好吃的,午后阳光从窗子洒进来,窗帘是淡绿色的小碎花,空气里弥漫着饭香味,我们两个人坐在桌前,一边吃饭,一边聊天。我喜欢和他一起手挽着手去菜市场买菜,西红柿土豆黄瓜小白菜,手里拎着的这些果蔬食物,有一次,我们从菜市场回去的路上,明明是艳阳高照的天气,却突然间下起了冰雹,那是他第一次看见冰雹,被砸了一下之后,便立马丢了手里的菜,双手护住我,我傻了吧唧的去捡菜,被砸了一身。他立马臭骂了我一顿,说我是他见过,最好吃的女孩子了。
这段是第二段稍加修改,simhash为:00100101001010110000100000101110
与第二段的海明距离为2
可以看出效果还是很明显的。
能序列化的东西都能hash,也就都能比较相似度。simhash属于局部敏感哈希(Local-Sensitive Hashing, LSH),下次讲讲如何比较图片的相似度,使用感知哈希(Perceptual Hashing)。
基于hash的文档判重——simhash的更多相关文章
- 基于word2vec的文档向量模型的应用
基于word2vec的文档向量模型的应用 word2vec的原理以及训练过程具体细节就不介绍了,推荐两篇文档:<word2vec parameter learning explained> ...
- 基于slate构建文档编辑器
基于slate构建文档编辑器 slate.js是一个完全可定制的框架,用于构建富文本编辑器,在这里我们使用slate.js构建专注于文档编辑的富文本编辑器. 描述 Github | Editor DE ...
- 基于Zabbix API文档二次开发与java接口封装
(继续贴一篇之前工作期间写的经验案例) 一. 案例背景 我负责开发过一个平台的监控报警模块,基于zabbix实现,需要对zabbix进行二次开发. Zabbix官方提供了Rest ...
- 多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)
老有人觉得MSComm通讯控件很土,更有人大声疾呼:忘了它吧.确实当我们对串口编程有了一定的了解后,应该用API函数写一个属于自己的串口程序,由于编程者对程序了解,对程序修改自如.但我一直没有停止过用 ...
- MFC中 创建基于CFormView的文档视图程序
在MFC中可以创建多种类型的窗口程序,如对话框程序.单文档结构程序(非文档/视图结构).单文档(文档/视图结构)以及多文档视图结构程序等. 在编写一般的小工具时,我们的首选显然是对话框程序,不过基于对 ...
- 如何优雅的写UI——(2)MFC下基于CFormView的文档视图程序
在MFC中可以创建多种类型的窗口程序,如对话框程序.单文档结构程序(非文档/视图结构).单文档(文档/视图结构)以及多文档视图结构程序等. 在编写一般的小工具时,我们的首选显然是对话框程序,不过基于对 ...
- 基于FlashPaper的文档播放器
本文主要讨论.描述了使用Adobe公司的Flex与FlashPaper产品完成对发布到网上的文档资料进行只读控制,也就是说只允许浏览操作.对下载.打印进行控制. FlashPaper FlashPap ...
- rabbitmq 3.7.8基于centos7部署文档
rabbitmq 3.7.8部署文档 安装erlang 安装依赖环境 yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel ope ...
- 基于DOMContentLoaded实现文档加载完成后执行的方法
我们有时可能需要一些在页面加载完成之后执行的方法,其实js原生就提供了onload方法,所以我们最简单的办法就是直接给onload赋值一个函数,在页面加载完成之后就会自动执行 widnow.onloa ...
随机推荐
- (Problem 39)Integer right triangles
If p is the perimeter of a right angle triangle with integral length sides, {a,b,c}, there are exact ...
- 大数据技术 vs 数据库一体机[转]
http://blog.sina.com.cn/s/blog_7ca5799101013dtb.html 目前,虽然大数据与数据库一体机都很火热,但相当一部分人却无法对深入了解这两者的本质区别.这里便 ...
- 投资新兴市场和细分市场 good
新兴市场对程序员来说,就是一种新的语言.一个新的平台.一套新的框架.新兴市场因为刚刚兴起,所以几乎所有人都在同一个起跑线,特别适合后进者.我认识从一个2011年开始学习iOS开发的同学,他能能力中等, ...
- android 关于多任务下载问题
关于多任务下载问题 近期项目中,遇到一个问题,列表数据中的图片地址是一个需要下载JS再解析的字段,之前的图片下载是一个异步的过程,由一个队列处理. Android系统以不同寻常的方式处理多个应用 ...
- POJ 3294 Life Forms(后缀数组+二分答案)
[题目链接] http://poj.org/problem?id=3294 [题目大意] 求出在至少在一半字符串中出现的最长子串. 如果有多个符合的答案,请按照字典序输出. [题解] 将所有的字符串通 ...
- Nitrous.IO融资665万美元 帮助开发者省去配置调试等工作-CSDN.NET
Intro to Computer Science Class Online (CS101) - Udacity Building a Search Engine Nitrous.IO融资665万美元 ...
- 我的MYSQL学习心得 mysql的权限管理
这一篇<我的MYSQL学习心得(十三)>将会讲解MYSQL的用户管理 在mysql数据库中,有mysql_install_db脚本初始化权限表,存储权限的表有: 1.user表 2.db表 ...
- move.js
function startMove(obj,json,fn){ var flag=true;//标志所有运动是否到达目标值 clearInterval(obj.timer); obj.timer=s ...
- java之Set源代码浅析
Set的接口和实现类是最简单的,说它简单原因是由于它的实现都是基于实际的map实现的. 如 hashSet 基于hashMap,TreeSet 基于TreeMap,CopyOnWriteArraySe ...
- sysctl: command not found
在安装RedHat5.9时没有在安装时定制软件包,在后面使用sysctl命令时提示: -bash: sysctl: command not found 找了半天原来是还需要安装: rpm -ivh p ...