使用SimHash算法实现千万级文本数据去重插入(python版代码)

2023-10-27

前言,最近在搞大量数据插入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后,流式大数据去重不再烦恼!
在这里插入图片描述

参考资料:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用SimHash算法实现千万级文本数据去重插入(python版代码) 的相关文章

随机推荐

  • 使用扩展卡尔曼滤波(EKF)融合激光雷达和雷达数据(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 大多数自动驾驶汽车都配备了激光雷达和雷达
  • 内存屏障(cpu内存屏障 与java内存屏障)

    文章目录 CPU 内存屏障 定义 读写屏障指令 为什么会出现内存屏障 java内存屏障 java内存屏障存在意义 java中内存屏障的主要类型 LoadLoad 屏障 StoreStore 屏障 LoadStore 屏障 StoreLoad
  • Linux:工具(vim,gcc/g++,make/Makefile,yum,git,gdb)

    目录 工具功能 1 vim 1 1 vim的模式 1 2 vim常见指令 2 gcc g 2 1 预备知识 2 2 gcc的使用 3 make Makefile make Makefile的使用 4 yum yum三板斧 5 git git
  • 使用指针封装一个实现strcat功能的函数

    strcat函数的原理 将两个字符串内的数据进行拼接 将拼接好的数据放在目标字符串中 编程思想 使用char类型的两个指针 dest src 指向目标字符串和只读字符串首地址 通过while循环使指针 dest 指向目标字符串尾地址 再通过
  • Python 算法交易实验61 ADBS:QuantData到MyQuantBase-续3(故障处理)

    说明 故障重现并找到了 我觉得可以把这个问题当成一种设计模式予以强化 内容 1 故障重现 我发现在CNT Worker运行后 WorkOut队列会有小概率出现没有衍生特征的情况 进而无法输出 产生了阻塞 当启动CNT Worker时发生 观
  • linux 文件转换ascii,linux 小技巧(查找替换文件中的ascii编码字符)

    这里纪录一些linux下用到的小技巧 以免遗忘 在linux中经常碰见各种文件处理 最常用的就是替换文件中的某些字符 常见字符替换还是很容易完成 但是有些不可见字符以及ascii编码字符等等都无法直接使用常见方法替换 这里可以用下面的几种方
  • 一个在线学习正则表达式的网站

    今天发现了一个不错的网站regexr com 可以在线学习正则表达式 如图 网站左边包含了常用的正则表达式 我们可以随时参考 右边是一些示例文字 英文段落 电话号码 网址 电子邮箱地址等都有 网站上面可以输入正则表达式 当我们把鼠标移动到正
  • 微信小程序自定义主题颜色【状态栏tab样式同步更改】

    此功能使用js控制变量 调整颜色值 赋值给css颜色达到切换自定义颜色效果 1 创建公共样式userStyle js文件 通过定义style1和style2来控制全局颜色改变 注意 颜色值务必为十六进制 避免API不兼容颜色 userSty
  • 【Python】Python错误类型03

    Python程序设计错误可以分为三类 语法错误 运行时错误 逻辑错误 1 语法错误 print Hello World 2 运行时错误 运行时错误是导致程序意外终止的错误 如果Python解释器检测到一个不可能执行的操作 就会出现运行时错误
  • 2020新版siteground主机空间服务器购买选择图文教程-跨境电商外贸网站最佳主机空间

    Siteground主机空间怎么样 很多国内的小伙伴可能对siteground主机空间比较陌生 感觉不如bluehost或者Godaddy名气大 实际上siteground在国外是一家非常有名气和实力的美国主机服务商 也是wordpress
  • 以太坊生成合约地址以及存在的账户碰撞

    Eip1014 1 create 通过CREATE关键字创建合约 Create creates a new contract using code as deployment code func evm EVM Create caller
  • Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields论文笔记

    这篇论文是2017年的CVPR 使用的是自底向上的结构 本文的重点在于提出PAFs Part Affinity Fields Realtime Multi Person 2D Pose Estimation Using Part Affin
  • 网络协议系列1—TC/PIP网络模型

    文章目录 一 TCP IP网络模型 二 UDP 1 面向无连接 2 有单播 多播 广播功能 3 UDP是面向报文的 4 不可靠性 5 头部开销小 传输数据报文时很高效 三 TCP 1 连接过程 第一次握手 第二次握手 第三次握手 2 TCP
  • Tip__Unity 3D模型上的材质球为灰色 改不动的问题

    正解 unity导入的模型无法编辑材质球属性 取巧 新建一个材质球 拖到模型原材质球位置 就可以把原材质球替换掉 然后修改新材质球的主图和Shader
  • 我把这一年学的 CSS 知识点精炼总结成了一篇文档

    文章目录 一 CSS简介 1 什么是CSS 二 CSS语法 1 语法规则 2 注释 三 CSS选择器 1 CSS的id选择器 2 CSS的class选择器 四 CSS创建 1 外部样式表 2 内部样式表 3 内联样式 4 多重样式 5 多重
  • 连接器信号完整性仿真教程 七

    本将介绍微带线及差分微带线仿真 做连接器信号完整性仿真时 有时后没法将激励端口直接设置到连接器端子上 这就需画出连接器PCB PAD 将激励端口设置在PAD的端面上 或者用引线连接PAD 将引线引出到适当的位置 再在引线端设置激励端口 通常
  • mac M1配置selenium的chromedriver

    1 确认浏览器版本 2 下载对应的chromedriver M1版是mac arm64版 3 将驱动放在 H O M E b i n
  • js的变量数据类型

    1 什么是变量 1 变量 变化的量 在JS程序中 用于储存数据的容器 2 如何在JS程序中使用变量 1 声明变量 告诉浏览器 我要使用这个变量 var变量名称 声明变量的语法 2 初始变量 给变量赋值 变量名称 值 赋值 将值储存到变量中
  • 编程实现时钟表盘刻度

    首先看个时钟刻度显示效果 一个表盘有60个刻度 每5个刻度就有一个刻度尺寸偏长 画该表盘步骤如下 画外围圈 这个就是画一个圆 假设其圆心坐标为 x 0 y
  • 使用SimHash算法实现千万级文本数据去重插入(python版代码)

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