基于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是
11111101011001101110111100101101
00101101001010110001100000101110
海明距离为16。

知乎上有个提问,小时候缺爱的女孩子,长大后该怎么办?或许在我这里,只是希望一直有人陪。喜宝说,我想要很多很多的爱,要不就是很多很多的钱,实在不行,我有个坏毛病,经常会半夜饿到不行,爬起来找吃的。是真的饿到胃疼,有时候直接饿醒了,每次看到电影里的台词,睡着了就不饿了,我是压根不相信。究其原因,是大学的时候没人陪我吃饭,每次都是一直等到有人陪我的时候,我才会去吃饭,最后把自己饿到胃疼,久而久之我不喜欢一个人吃饭,也不喜欢一个人逛街,更不喜欢一个人呆着,可是成长啊,往往是越不喜欢的便越要学会接受它。我和他认识的时候,是因为贴吧聚餐,他主动找我要的微信,附带一个如沐春风般的笑容。我一直以为他是被我的美色打动,后来问他原因。他说,他第一次看见那么能吃的女孩子,他惊呆了,可是有觉得看我吃饭很意思,仿佛食物都有了灵魂,让人的心情莫名的好了起来。我们初相识,是因为他看见了我饿死鬼投胎的吃相。我们在一起,是因为他厨艺很好,好到什么程度呢?就是那种你吃过一顿,就能惦记一辈子的感觉。即便是现在回忆起他来,我的味蕾都会有反应。他总是给我做很多很多好吃的,午后阳光从窗子洒进来,窗帘是淡绿色的小碎花,空气里弥漫着饭香味,我们两个人坐在桌前,一边吃饭,一边聊天。我喜欢和他一起手挽着手去菜市场买菜,西红柿土豆黄瓜小白菜,手里拎着的这些果蔬食物,有一次,我们从菜市场回去的路上,明明是艳阳高照的天气,却突然间下起了冰雹,那是他第一次看见冰雹,被砸了一下之后,便立马丢了手里的菜,双手护住我,我傻了吧唧的去捡菜,被砸了一身。他立马臭骂了我一顿,说我是他见过,最好吃的女孩子了。

这段是第二段稍加修改,simhash为:
00100101001010110000100000101110

与第二段的海明距离为2
可以看出效果还是很明显的。



能序列化的东西都能hash,也就都能比较相似度。simhash属于局部敏感哈希(Local-Sensitive Hashing, LSH),下次讲讲如何比较图片的相似度,使用感知哈希(Perceptual Hashing)。

 
时间: 2025-01-16 08:33:07

基于hash的文档判重——simhash的相关文章

基于浏览器的文档处理控件TX Text Control .NET Server for WP

TX Text Control .NET Server for WPF控件为用于ASP.NET服务器环境提供一个完全可编程的文字处理引擎,并且包含一个WPF客户端版本 具体功能: 合并Microsoft Word模板,生成打印就绪的PDF文件 在浏览器中以所见即所得模式编辑处理文档 从同一个文档的每一页中生成图片或meta文件 使用来自不同源的数据从零开始通过编程生成文档 在所有支持的格式之间转换文档 完全独立于 Microsoft Word, Adobe Acrobat,及其他第三方软件 一台

html文档页面重绘和重新布局

当浏览器下载完所有页面HTML 标记,JavaScript,CSS,图片之后,它解析文件并创建两个内部数据结构:一棵DOM树表示页面结构,一棵渲染树表示DOM节点如何显示. 在对html页面的操作过程中会改变页面的结构或者页面的外观或者同时改变页面的结构和外观.在过程中,文档可见外观改变很小(如:改变了某些元素的颜色,或者改变了某些元素的可见性,但这种改变不会影响到页面的布局),这种行为称为“重绘”,也就是浏览器对文档进行了重新绘制.如果页面外观发生了显著变化并且影响到文档的实际布局(如一个<d

使用LiveDocx和Zend Framework生成基于模板的文档

使用PHP生成打印良好的格式良好的PDF文档并非易事.传统上,使用PHP生成PDF有两种主要方法.如果有足够的时间和耐心,两者都可以完成工作,但仍然有很多不足之处: HTML-to-PDF:这种方法广泛用于主流应用程序.这里使用众多开源库之一以编程方式创建HTML文档并将其转换为PDF.但是,由于HTML不是面向页面的格式(如PDF),因此无法在HTML和PDF之间执行1对1映射.典型的文字处理文件格式功能,例如页眉和页脚,孤儿和寡妇甚至页码都不能用HTML表示. 程序化:此方法可以完全控制生成

基于word2vec的文档向量模型的应用

基于word2vec的文档向量模型的应用 word2vec的原理以及训练过程具体细节就不介绍了,推荐两篇文档:<word2vec parameter learning explained>.和<word2vec中的数学>. 在<word2vec中的数学>中谈到了训练语言模型的一些方法:比如n-gram和神经网络.在使用神经网络训练语言模型时得到的"副产物",就是word2vec词向量.基于神经网络训练语言模型有2种方案:cbow和skip-gram,

基于FlashPaper的文档播放器

本文主要讨论.描述了使用Adobe公司的Flex与FlashPaper产品完成对发布到网上的文档资料进行只读控制,也就是说只允许浏览操作.对下载.打印进行控制. FlashPaper FlashPaper是Macromedia的一款用于将操作系统所识别的文档的内容通过虚拟打印机制将内容转换为swf文件的工具, Flex Flex是Adobe公司的一款推动RIA应用的技术,它可以使我们通过编码MXML方式产生swf,它的基础是ActionScript,ActionScript可以理解为别一门编程语

VTK基于MFC单文档的开发

目录 项目的搭建 相关头文件的引用 添加成员变量vtkRenderer, vtkMFCWindow CXxxView()中实例化变量vtkRenderer CXxxView::OnInitialUpdate()中初始化变量vtkMFCWindow CXxxView::OnDraw(), 实现vtkMFCWindow的绘制 CXxxView::OnSize(), 窗口大小重绘 CXxxView::OnDestroy()中释放变量vtkRenderer CXxxView::~CXxxView()中释

基于Noootes/Dooomino的文档工作流系统(转)

(流程样例应用程序终于整理上传好可供下载了http://download.csdn.net/download/starrow/8422299) 上文分析了我们的流程配置使用三类对象,分别对应三类文档:工作流配置文档.节点配置文档和操作配置文档.本文介绍三类配置文档的内容,也可以看作本流程系统的设置教程. 这些配置文档的共同点包括: 处于编辑状态时,必填字段的右边会显示红色星号(这实际也是整个系统的共同设计),保存文档时会对这些字段进行校验. 许多字段既能输入静态值,也可输入公式以计算动态值.输入

94. 基于Notes/Domino的文档工作流系统(六)

(流程样例应用程序终于整理上传好可供下载了http://download.csdn.net/download/starrow/8422299) 上文分析了我们的流程配置使用三类对象,分别对应三类文档:工作流配置文档.节点配置文档和操作配置文档.本文介绍三类配置文档的内容,也可以看作本流程系统的设置教程. 这些配置文档的共同点包括: 处于编辑状态时,必填字段的右边会显示红色星号(这实际也是整个系统的共同设计),保存文档时会对这些字段进行校验. 许多字段既能输入静态值,也可输入公式以计算动态值.输入

在CentOS7中安装MySQL, 基于MySQL官网文档

目录 在CentOS7中安装MySQL, 基于官网文档 前言 开始 准备工作 下载安装 配置 在CentOS7中安装MySQL, 基于官网文档 前言 版本 CentOS7, 安装于windows的VMWare MySQL5.7.13 个人小创举 这次安装经历很多困难, 一开始, 惯性思维+懒惰, 不愿意多费力费脑, 在中文网站找别人咀嚼过的解决方案, 但都被各种来回抄的答案坑惨. 后来没办法硬着头皮看MySQL官网的文档, 一开始还是用浏览器的全页翻译, 也很惨. 反而, 后来换成英文原文越看越