使用SimHash算法实现千万级文本数据去重插入(python版代码)_simhashindex-程序员宅基地

技术标签: SimHash  python  文本去重  

前言,最近在搞大量数据插入MySQL的时候悲催的发现速度越来越慢,因为我的数据来多个源,使用流式更新,而且产品要求在这个表里面不能有数据重复,划重点!衡量数据是否重复的字段是文本内容,字段类型是text,…那么问题来了,如何在千万级数据量实现去重插入呢?而且要快!

自杀式做法

1.管它重复不重复,先插入了再说
2.使用group by 先对不能重复的字段进行分组,在用一个having count(<field_name>)>1把重复的句子给拿出来
3.使用for循环查询每条存在重复记录的句子,保留最小id的句子记录,其余删除。

这样的做法导致我每次对全表进行一次去重,大概需要花费7个小时的时间…
在这里插入图片描述
后面,经过老大的点拨,发现可以用simhash算法进行去重,二话不说,撸起袖子就是干呗!

SimHash式优雅做法

simhash是由 Charikar 在2002年提出来的,参考 《Similarity estimation techniques from rounding algorithms》,这个文章的基本思想就是相似的文档具有相似的hash指纹,那么可以通过其hash指纹值来的对比来实现文档的去重。这个算法被google应用于处理海量文本去重,其效果肯定是大大的有,特别是对于长文本的去重。在github上,找到了大牛写好的simhash包,先结合源码,来谈谈simhash的一个去重原理。

step1 安装simhash包

pip install simhash
(or: easy_install simhash)

step2 特征提取
拿到文档之后,先对文档做特征抽取,抽取出文档中的关键词以及对应的权重,常见的做法有TF-IDF,然后处理的文档如果是中文话,需要进行分词,常用的开源分词包有jieba,snowNLP,甚至可以用sentencePiece自己训练一个分词模型。在demo中,使用的是作者在外部写的一个获得特征的方法,获得一个List:

"""
设置一个长度为3的滑动窗口,并只匹配数字英文加下划线,如输入'你好啊,今天真高兴':
返回['你好啊', '好啊今', '啊今天', '今天真', '天真高', '真高兴']
"""
def get_features(s):
    width = 3
    s = s.lower()
    s = re.sub(r'[^\w]+', '', s)
    return [s[i:i + width] for i in range(max(len(s) - width + 1, 1))]

在源码中,对传入的value做了不同的处理,如果传入的值是simhash或者数字,那就不做处理,如果传入了一个list,那么就进入build_by_features函数中,如果是原始的字符串文本,那么就进入build_by_text函数:

if isinstance(value, Simhash):
    self.value = value.value
elif isinstance(value, basestring):
    self.build_by_text(unicode(value))
elif isinstance(value, collections.Iterable):
    self.build_by_features(value)
elif isinstance(value, numbers.Integral):
    self.value = value
else:
    raise Exception('Bad parameter with type {}'.format(type(value)))

其中的build_by_text函数最终也是调用了build_by_features,在这之前,build_by_text调用了 _tokenize函数,这个函数和之前的get_features函数差不多,这里默认的滑动窗口宽度是4.

 def _tokenize(self, content):
     content = content.lower()
     content = ''.join(re.findall(self.reg, content))
     ans = self._slide(content)
     return ans

之后将得到的词块list进行统计,输出一个字典,key是词块,value是频数,最后将这个字典输入build_by_features函数。build_by_features函数也接受单层list输入,如[‘我’,‘爱’,‘python’],之前会自动给这些词块赋1的权重。

step3 将特征词转换为SimHash值
在这个阶段需要使用到hash,想必大家对hash这个词都是耳熟能详了,但是hash值是怎么算出来的,在这里简单说一下,无论你多长的输入A,都会输出一段固定的值B,B这个值就是A数据的指纹,也就是散列表,我们知道指纹基本上是独一无二的,那么对于hash值来说,要找到一个hash值的两个不同的输入,在计算上是不可能的,因此这也是为什么hash值能够用于去重的原理。在这个simhash中,默认的hash算法是MD5,当然你可以传入sha1, sha256其它的算法,python的hashlib包已经封装好了。其中默认的指纹长度为64。

def _hashfunc(x):
    return int(hashlib.md5(x).hexdigest(), 16)   #后面的16是指16进制,得到转换为10进制的数

计算完hash之后,便按照单词的权重形成加权数字串,再使用位与运算和位或运算得到最终的表示句子指纹的simhash,其中value是就是这里算出来的ans,是一个int型的数值串。

  v = [0] * self.f
        masks = [1 << i for i in range(self.f)]
        if isinstance(features, dict):
            features = features.items()
        for f in features:
            if isinstance(f, basestring):
                h = self.hashfunc(f.encode('utf-8'))
                w = 1
            else:
                assert isinstance(f, collections.Iterable)
                h = self.hashfunc(f[0].encode('utf-8'))
                w = f[1]
            for i in range(self.f):
                v[i] += w if h & masks[i] else -w   #在这一步实现了加权
        ans = 0
        for i in range(self.f):
            if v[i] > 0:
                ans |= masks[i]       #位或运算
        self.value = ans

step4 比较不同文本之间的距离
算好了不同文本的simhash值,然后就可以使用计算之间的距离了,利用位运算得到距离,这里的距离叫做海明距离,比如, 10101 和 00110 从第一位开始依次有第一位、第四、第五位不同,则海明距离为3:

 def distance(self, another):
     assert self.f == another.f
     x = (self.value ^ another.value) & ((1 << self.f) - 1)
     ans = 0
     while x:
         ans += 1
         x &= x - 1
     return ans

小结
以上就是simhash计算的一个基本流程。作者还封装了一个好用的SimhashIndex便于进行大规模数据去重,示例代码如下:

import re
from simhash import Simhash, SimhashIndex
def get_features(s):
    width = 3
    s = s.lower()
    s = re.sub(r'[^\w]+', '', s)
    return [s[i:i + width] for i in range(max(len(s) - width + 1, 1))]

data = {
    1: u'How are you? I Am fine. blar blar blar blar blar Thanks.',
    2: u'How are you i am fine. blar blar blar blar blar than',
    3: u'This is simhash test.',
}
objs = [(str(k), Simhash(get_features(v))) for k, v in data.items()]
index = SimhashIndex(objs, k=3)

print index.bucket_size()

s1 = Simhash(get_features(u'How are you i am fine. blar blar blar blar blar thank'))
print index.get_near_dups(s1)

index.add('4', s1)			
print index.get_near_dups(s1)    

大致流程就是用SimhashIndex初始化数据,其中bucket存储了对应句子的simhash值,get_near_dups()函数返回的是和输入句子相似的句子id的列表,这里默认的海明距离是3(可以修改的,不过论文里提到选择3是一个比较折中的方案),如果拿来去重的话,那就是判断get_near_dups()返回结果是否为空的list,如不为空,那么该句子重复。

在这里插入图片描述
用了simhash后,流式大数据去重不再烦恼!
在这里插入图片描述

参考资料:

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sinat_33455447/article/details/88956583

智能推荐

Docker 快速上手学习入门教程_docker菜鸟教程-程序员宅基地

文章浏览阅读2.5w次,点赞6次,收藏50次。官方解释是,docker 容器是机器上的沙盒进程,它与主机上的所有其他进程隔离。所以容器只是操作系统中被隔离开来的一个进程,所谓的容器化,其实也只是对操作系统进行欺骗的一种语法糖。_docker菜鸟教程

电脑技巧:Windows系统原版纯净软件必备的两个网站_msdn我告诉你-程序员宅基地

文章浏览阅读5.7k次,点赞3次,收藏14次。该如何避免的,今天小编给大家推荐两个下载Windows系统官方软件的资源网站,可以杜绝软件捆绑等行为。该站提供了丰富的Windows官方技术资源,比较重要的有MSDN技术资源文档库、官方工具和资源、应用程序、开发人员工具(Visual Studio 、SQLServer等等)、系统镜像、设计人员工具等。总的来说,这两个都是非常优秀的Windows系统镜像资源站,提供了丰富的Windows系统镜像资源,并且保证了资源的纯净和安全性,有需要的朋友可以去了解一下。这个非常实用的资源网站的创建者是国内的一个网友。_msdn我告诉你

vue2封装对话框el-dialog组件_<el-dialog 封装成组件 vue2-程序员宅基地

文章浏览阅读1.2k次。vue2封装对话框el-dialog组件_

MFC 文本框换行_c++ mfc同一框内输入二行怎么换行-程序员宅基地

文章浏览阅读4.7k次,点赞5次,收藏6次。MFC 文本框换行 标签: it mfc 文本框1.将Multiline属性设置为True2.换行是使用"\r\n" (宽字符串为L"\r\n")3.如果需要编辑并且按Enter键换行,还要将 Want Return 设置为 True4.如果需要垂直滚动条的话将Vertical Scroll属性设置为True,需要水平滚动条的话将Horizontal Scroll属性设_c++ mfc同一框内输入二行怎么换行

redis-desktop-manager无法连接redis-server的解决方法_redis-server doesn't support auth command or ismis-程序员宅基地

文章浏览阅读832次。检查Linux是否是否开启所需端口,默认为6379,若未打开,将其开启:以root用户执行iptables -I INPUT -p tcp --dport 6379 -j ACCEPT如果还是未能解决,修改redis.conf,修改主机地址:bind 192.168.85.**;然后使用该配置文件,重新启动Redis服务./redis-server redis.conf..._redis-server doesn't support auth command or ismisconfigured. try

实验四 数据选择器及其应用-程序员宅基地

文章浏览阅读4.9k次。济大数电实验报告_数据选择器及其应用

随便推点

灰色预测模型matlab_MATLAB实战|基于灰色预测河南省社会消费品零售总额预测-程序员宅基地

文章浏览阅读236次。1研究内容消费在生产中占据十分重要的地位,是生产的最终目的和动力,是保持省内经济稳定快速发展的核心要素。预测河南省社会消费品零售总额,是进行宏观经济调控和消费体制改变创新的基础,是河南省内人民对美好的全面和谐社会的追求的要求,保持河南省经济稳定和可持续发展具有重要意义。本文建立灰色预测模型,利用MATLAB软件,预测出2019年~2023年河南省社会消费品零售总额预测值分别为21881...._灰色预测模型用什么软件

log4qt-程序员宅基地

文章浏览阅读1.2k次。12.4-在Qt中使用Log4Qt输出Log文件,看这一篇就足够了一、为啥要使用第三方Log库,而不用平台自带的Log库二、Log4j系列库的功能介绍与基本概念三、Log4Qt库的基本介绍四、将Log4qt组装成为一个单独模块五、使用配置文件的方式配置Log4Qt六、使用代码的方式配置Log4Qt七、在Qt工程中引入Log4Qt库模块的方法八、获取示例中的源代码一、为啥要使用第三方Log库,而不用平台自带的Log库首先要说明的是,在平时开发和调试中开发平台自带的“打印输出”已经足够了。但_log4qt

100种思维模型之全局观思维模型-67_计算机中对于全局观的-程序员宅基地

文章浏览阅读786次。全局观思维模型,一个教我们由点到线,由线到面,再由面到体,不断的放大格局去思考问题的思维模型。_计算机中对于全局观的

线程间控制之CountDownLatch和CyclicBarrier使用介绍_countdownluach于cyclicbarrier的用法-程序员宅基地

文章浏览阅读330次。一、CountDownLatch介绍CountDownLatch采用减法计算;是一个同步辅助工具类和CyclicBarrier类功能类似,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。二、CountDownLatch俩种应用场景: 场景一:所有线程在等待开始信号(startSignal.await()),主流程发出开始信号通知,既执行startSignal.countDown()方法后;所有线程才开始执行;每个线程执行完发出做完信号,既执行do..._countdownluach于cyclicbarrier的用法

自动化监控系统Prometheus&Grafana_-自动化监控系统prometheus&grafana实战-程序员宅基地

文章浏览阅读508次。Prometheus 算是一个全能型选手,原生支持容器监控,当然监控传统应用也不是吃干饭的,所以就是容器和非容器他都支持,所有的监控系统都具备这个流程,_-自动化监控系统prometheus&grafana实战

React 组件封装之 Search 搜索_react search-程序员宅基地

文章浏览阅读4.7k次。输入关键字,可以通过键盘的搜索按钮完成搜索功能。_react search