本文环境:python
python3.5git
ubuntu 16.04github
第三方库:算法
文件寄于github: https://github.com/w392807287/angelo_tools.gitsql
没多久就要写毕业论文了,听说须要查重,对文档重复断定还挺好奇的因此看了下相关的东西。发现simhash比较好用,实现简单。ubuntu
顾名思义 simhash是一种hash算法,之前在我印象中hash算法是将一个对象映射成一个hash值,通常只要求当两个对象彻底相同时才有相同的hash值,而两个类似的对象的hash值并不须要有任何关系。只相差一个字符hash出来的值也可能相差十万八千里。可是若是hash函数设计的足够巧妙,也可让类似的对象拥有相同或者类似的hash值,使用hash来进行类似性搜索更方便快捷。
simhash就是这么一个神奇的算法。它知足:bash
simhash能够将文档hash到一个64位二进制数,使得类似的文档具备类似的二进制数。对于一个文档,咱们能够把文中的每一个词或者词组做为一个特征,统计各个特征出现的频率(固然也能够加入词性的权重,怎么去设置、统计特征能够视状况而定)。下面的例子中咱们使用 jieba 作分词。微信
目标文档 “葫芦娃葫芦娃,一根藤上七朵花”,获得的特征与相应的频率:(葫芦娃,0.33),(一根,0.17,(藤上,0.17),(七朵,0.17),(花,0.17)。而后对特征值进行hash,方便演示这里映射到6位:函数
在文本中,出现频率高的特征,其对应的向量份量的绝对值更大,对最终向量相加的结果影响也更大。所以,若是两个文档类似,那么它们出现频率高的特征也应该比较接近,最终获得的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是11111101011001101110111100101101
00101101001010110001100000101110
海明距离为16。
知乎上有个提问,小时候缺爱的女孩子,长大后该怎么办?或许在我这里,只是但愿一直有人陪。喜宝说,我想要不少不少的爱,要不就是不少不少的钱,实在不行,我有个坏毛病,常常会半夜饿到不行,爬起来找吃的。是真的饿到胃疼,有时候直接饿醒了,每次看到电影里的台词,睡着了就不饿了,我是压根不相信。究其缘由,是大学的时候没人陪我吃饭,每次都是一直等到有人陪个人时候,我才会去吃饭,最后把本身饿到胃疼,长此以往我不喜欢一我的吃饭,也不喜欢一我的逛街,更不喜欢一我的呆着,但是成长啊,每每是越不喜欢的便越要学会接受它。我和他认识的时候,是由于贴吧聚餐,他主动找我要的微信,附带一个如沐春风般的笑容。我一直觉得他是被个人美色打动,后来问他缘由。他说,他第一次看见那么能吃的女孩子,他惊呆了,但是有以为看我吃饭很意思,仿佛食物都有了灵魂,让人的心情莫名的好了起来。咱们初相识,是由于他看见了我饿死鬼投胎的吃相。咱们在一块儿,是由于他厨艺很好,好到什么程度呢?就是那种你吃过一顿,就能惦记一生的感受。即使是如今回忆起他来,个人味蕾都会有反应。他老是给我作不少不少好吃的,午后阳光从窗子洒进来,窗帘是淡绿色的小碎花,空气里弥漫着饭香味,咱们两我的坐在桌前,一边吃饭,一边聊天。我喜欢和他一块儿手挽着手去菜市场买菜,西红柿土豆黄瓜小白菜,手里拎着的这些果蔬食物,有一次,咱们从菜市场回去的路上,明明是艳阳高照的天气,却忽然间下起了冰雹,那是他第一次看见冰雹,被砸了一下以后,便立马丢了手里的菜,双手护住我,我傻了吧唧的去捡菜,被砸了一身。他立马臭骂了我一顿,说我是他见过,最好吃的女孩子了。
这段是第二段稍加修改,simhash为:00100101001010110000100000101110
与第二段的海明距离为2
能够看出效果仍是很明显的。
能序列化的东西都能hash,也就都能比较类似度。simhash属于局部敏感哈希(Local-Sensitive Hashing, LSH),下次讲讲如何比较图片的类似度,使用感知哈希(Perceptual Hashing)。