文本去重的技术方案讨论(一)

article/2025/5/6 23:06:59

转发请注明出处:https://blog.csdn.net/HHTNAN

对于文本去重来说,我个人处理上会从数据量、文本特征、文本长度(短文本、长文本)几个方向考虑。
常见的去重任务,如网页去重,帖子去重,评论去重等等。
好的去重任务是不仅比对文本的相似性,还要比对语义上的相似性。

下面我们来介绍下文本去重的方案。

1.传统签名算法与文本完整性判断

一、传统签名算法与文本完整性判断
问题抛出:
(1)运维上线一个bin文件,将文件分发到4台线上机器上,如何判断bin文件全部是一致的?
(2)用户A将消息msg发送给用户B,用户B如何判断收到的msg_t就是用户A发送的msg?

思路:

一个字节一个字节的比对两个大文件或者大网页效率低,我们可以用一个签名值(例如md5值)代表一个大文件,签名值相同则认为大文件相同(先不考虑冲突率)

回答:

(1)将bin文件取md5,将4台线上机器上的bin文件也取md5,如果5个md5值相同,说明一致

(2)用户A将msg以及消息的md5同时发送给用户B,用户B收到msg_t后也取md5,得到的值与用户A发送过来的md5值如果相同,则说明msg_t与msg相同

结论: md5是一种签名算法,常用来判断数据的完整性与一致性

md5设计原则: 两个文本哪怕只有1个bit不同,其md5签名值差别也会非常大,故它只适用于“完整性”check,不适用于“相似性”check。

新问题抛出:

有没有一种签名算法,如果文本非常相似,签名值也非常相似呢?
此方法来源于网络,我认为很好,故直接引用了,作为开篇,如有侵权,可随时与我联系。

simhash

在这里插入图片描述
simhash是google用来处理海量文本去重的算法。 google出品,你懂的。 simhash最牛逼的一点就是将一个文档,最后转换成一个64位的字节,暂且称之为特征字,然后判断重复只需要判断他们的特征字的距离是不是<n(根据经验这个n一般取值为3),就可以判断两个文档是否相似。

原理

simhash值的生成图解如下
在这里插入图片描述
概花三分钟看懂这个图就差不多怎么实现这个simhash算法了。特别简单。谷歌出品嘛,简单实用。

  • 1、分词,把需要判断文本分词形成这个文章的特征单词。最后形成去掉噪音词的单词序列并为每个词加上权重,我们假设权重分为5个级别(1~5)。比如:“
    美国“51区”雇员称内部有9架飞碟,曾看见灰色外星人 ” ==> 分词后为 “ 美国(4) 51区(5) 雇员(3) 称(1) 内部(2)
    有(1) 9架(3) 飞碟(5) 曾(1) 看见(3) 灰色(4) 外星人(5)”,括号里是代表单词在整个句子里重要程度,数字越大越重要。

  • 2、hash,通过hash算法把每个词变成hash值,比如“美国”通过hash算法计算为 100101,“51区”通过hash算法计算为
    101011。这样我们的字符串就变成了一串串数字,还记得文章开头说过的吗,要把文章变为数字计算才能提高相似度计算性能,现在是降维过程进行时。

  • 3、加权,通过 2步骤的hash生成结果,需要按照单词的权重形成加权数字串,比如“美国”的hash值为“100101”,通过加权计算为“4
    -4 -4 4 -4 4”;“51区”的hash值为“101011”,通过加权计算为 “ 5 -5 5 -5 5 5”。

  • 4、合并,把上面各个单词算出来的序列值累加,变成只有一个序列串。比如 “美国”的 “4 -4 -4 4 -4 4”,“51区”的 “ 5
    -5 5 -5 5 5”, 把每一位进行累加, “4+5 -4±5 -4+5 4±5 -4+5 4+5” ==》 “9 -9 1 -1 1 9”。这里作为示例只算了两个单词的,真实计算需要把所有单词的序列串累加。

  • 5、降维,把4步算出来的 “9 -9 1 -1 1 9” 变成 0 1 串,形成我们最终的simhash签名。 如果每一位大于0 记为1,小于0 记为 0。最后算出结果为:“1 0 1 0 1 1”。
    整个过程图为:

在这里插入图片描述
到此,如何从一个doc到一个simhash值的过程已经讲明白了。

大家可能会有疑问,经过这么多步骤搞这么麻烦,不就是为了得到个 0 1 字符串吗?我直接把这个文本作为字符串输入,用hash函数生成 0 1 值更简单。其实不是这样的,传统hash函数解决的是生成唯一值,比如 md5、hashmap等。md5是用于生成唯一签名串,只要稍微多加一个字符md5的两个数字看起来相差甚远;hashmap也是用于键值对查找,便于快速插入和查找的数据结构。不过我们主要解决的是文本相似度计算,要比较的是两个文章是否相识,当然我们降维生成了hashcode也是用于这个目的。看到这里估计大家就明白了,我们使用的simhash就算把文章中的字符串变成 01 串也还是可以用于计算相似度的,而传统的hashcode却不行。我们可以来做个测试,两个相差只有一个字符的文本串,“你妈妈喊你回家吃饭哦,回家罗回家罗” 和 “你妈妈叫你回家吃饭啦,回家罗回家罗”。

通过simhash计算结果为:

10000100101011011111111000001010110100010011111000010010110010111000010010101101011111100000101011010001001111100001101010001011

通过 hashcode计算为:

11111111111111111111111111111111100010000011001101001110110111101010010001111111110010110011101

大家可以看得出来,相似的文本只有部分 01 串变化了,而普通的hashcode却不能做到,这个就是局部敏感哈希的魅力。目前Broder提出的shingling算法和Charikar的simhash算法应该算是业界公认比较好的算法。在simhash的发明人Charikar的论文中并没有给出具体的simhash算法和证明,量子图灵”得出的证明simhash是由随机超平面hash算法演变而来的。

下面是关于【海明距离】
二进制串A 和 二进制串B 的海明距离 就是 A xor B 后二进制中1的个数。
举例如下:

>     A = 100111;
>     B = 101010;
>     hamming_distance(A, B) = count_1(A xor B) = count_1(001101) = 3;

当我们算出所有doc的simhash值之后,需要计算doc A和doc B之间是否相似的条件是:

A和B的海明距离是否小于等于n,这个n值根据经验一般取值为3,

simhash本质上是局部敏感性的hash,和md5之类的不一样。 正因为它的局部敏感性,所以我们可以使用海明距离来衡量simhash值的相似度。

simhash是由 Charikar 在2002年提出来的,参考 《Similarity estimation techniques from rounding algorithms》 。

通过这样的转换,我们把库里的文本都转换为simhash 代码,并转换为long类型存储,空间大大减少。现在我们虽然解决了空间,但是如何计算两个simhash的相似度呢?难道是比较两个simhash的01有多少个不同吗?对的,其实也就是这样,我们通过海明距离(Hamming distance)就可以计算出两个simhash到底相似不相似。两个simhash对应二进制(01串)取值不同的数量称为这两个simhash的海明距离。举例如下: 10101 和 00110 从第一位开始依次有第一位、第四、第五位不同,则海明距离为3。对于二进制字符串的a和b,海明距离为等于在a XOR b运算结果中1的个数(普遍算法)。

为了高效比较,我们预先加载了库里存在文本并转换为simhash code 存储在内存空间。来一条文本先转换为 simhash code,然后和内存里的simhash code 进行比较,测试100w次计算在100ms。速度大大提升。

小节:

  • 1、目前速度提升了但是数据是不断增量的,如果未来数据发展到一个小时100w,按现在一次100ms,一个线程处理一秒钟 10次,一分钟 60 10 次,一个小时 6010 60 次 = 36000次,一天 601060*24 = 864000次。 我们目标是一天100w次,通过增加两个线程就可以完成。但是如果要一个小时100w次呢?则需要增加30个线程和相应的硬件资源保证速度能够达到,这样成本也上去了。能否有更好的办法,提高我们比较的效率?
  • 2、通过大量测试,simhash用于比较大文本,比如500字以上效果都还蛮好,距离小于3的基本都是相似,误判率也比较低。但是如果我们处理的是微博信息,最多也就140个字,使用simhash的效果并不那么理想。看如下图,在距离为3时是一个比较折中的点,在距离为10时效果已经很差了,不过我们测试短文本很多看起来相似的距离确实为10。如果使用距离为3,短文本大量重复信息不会被过滤,如果使用距离为10,长文本的错误率也非常高,如何解决?

代码实现

#coding:utf8
import math
import jieba
import jieba.analyseclass SimHash(object):def __init__(self):passdef getBinStr(self, source):if source == "":return 0else:x = ord(source[0]) << 7m = 1000003mask = 2 ** 128 - 1for c in source:x = ((x * m) ^ ord(c)) & maskx ^= len(source)if x == -1:x = -2x = bin(x).replace('0b', '').zfill(64)[-64:]print(source, x)return str(x)def getWeight(self, source):# fake weight with keywordreturn ord(source)def unwrap_weight(self, arr):ret = ""for item in arr:tmp = 0if int(item) > 0:tmp = 1ret += str(tmp)return retdef simHash(self, rawstr):seg = jieba.cut(rawstr, cut_all=True)keywords = jieba.analyse.extract_tags("|".join(seg), topK=100, withWeight=True)print(keywords)ret = []for keyword, weight in keywords:binstr = self.getBinStr(keyword)keylist = []for c in binstr:weight = math.ceil(weight)if c == "1":keylist.append(int(weight))else:keylist.append(-int(weight))ret.append(keylist)# 对列表进行"降维"rows = len(ret)cols = len(ret[0])result = []for i in range(cols):tmp = 0for j in range(rows):tmp += int(ret[j][i])if tmp > 0:tmp = "1"elif tmp <= 0:tmp = "0"result.append(tmp)return "".join(result)def getDistince(self, hashstr1, hashstr2):length = 0for index, char in enumerate(hashstr1):if char == hashstr2[index]:continueelse:length += 1return lengthif __name__ == "__main__":simhash = SimHash()s1="感冒了怎么办"  #效果不好,语义s2="感冒了怎么治"# s1 = "100元=38万星币,加微信"  #效果不好# s2 = "38万星币100元,加VX"# with open("a.txt", "r") as file:#     s1 = "".join(file.readlines())#     file.close()# with open("b.txt", "r") as file:#     s2 = "".join(file.readlines())#     file.close()# s1 = "this is just test for simhash, here is the difference"# s2 = "this is a test for simhash, here is the difference"# print(simhash.getBinStr(s1))# print(simhash.getBinStr(s2))hash1 = simhash.simHash(s1)hash2 = simhash.simHash(s2)distince = simhash.getDistince(hash1, hash2)# value = math.sqrt(len(s1)**2 + len(s2)**2)value = 5print("海明距离:", distince, "判定距离:", value, "是否相似:", distince<=value)

在这里插入图片描述
参考资料:直通车

微信号

http://chatgpt.dhexx.cn/article/PqhE5mEW.shtml

相关文章

git 生成公钥 及使用笔记

1.输入 ssh-keygen -t rsa -C 2101419675qq.com 2.一路回车&#xff0c; 提示输入文件名和密码都直接回车 3.公钥就生成了&#xff0c;路径为 4.在GitHub或gittree中直接将id_rsa.pub中的内的内容加在sskey中就行 下面是gittree 5.其它 mkdir test cd test git init touch …

git生成公钥私钥(windows)

配置用户名和邮箱 git config --global user.name "v_sunhaojie" $ git config --global user.email "v_sunhaojiebaidu.com" 会在当前用户的目录下(C:\Users\v_sunhaojie)生成 .gitconfig文件 [user] name v_sunhaojie email v_sunhaoji…

git生成公钥的步骤

git生成公钥的步骤 1. 设置Git账户2.生成命令 1. 设置Git账户 命令如下 git config user.name &#xff08;查看git账户&#xff09; git config user.email &#xff08;查看git邮箱&#xff09; git config --global user.name “账户名” &#xff08;设置全局账户名和邮箱…

【Git】ssh公钥如何生成

1. 在C盘用户目录文件下找到.ssh文件&#xff08;若之前未生成.ssh则进行第2步&#xff09; 里面保存的是之前生成的文件&#xff0c;将.ssh文件夹删除。 2. 右键&#xff0c;点击git bash here&#xff0c;进入git界面 3. 输入ssh-keygen -t rsa -C *.com &#xff0c;连点三…

git配置公钥

一、 生成.ssh文件 在桌面打开Git Bash&#xff0c;输入以下命令&#xff1a; ssh-keygen -t rsa -C "你的邮箱xxx.com"一直按回车&#xff0c;出现以下界面表示生成ssh文件成功 二、 找到id_rsa.pub文件 到C盘下找到.ssh文件夹&#xff1a;C:\Users\86187.ssh&am…

使用Git工具生成公钥与私钥

生成密钥对 keytool -genkeypair -alias shopping -keyalg RSA -keypass shopping -keystore shopping.jks -validity 365 -storepass shopping 解析私钥 keytool -list -rfc --keystore shopping.jks | openssl x509 -inform pem -pubkey 输入口令即可

超简单git生成ssh公钥(ssh-keygen)

首先在桌面右键&#xff0c;点击Git bash Here 在命令窗口输入 ssh-keygen -t rsa -C "你的邮箱地址" 回车 这时让你输入密码&#xff0c;这个密码会在你提交项目时使用 然后直接三个回车 到达最后 你会发现桌面上会有一个.pub的文件&#xff0c;右键用记事本打开…

【技术分享】Mac环境下git生成SSH公钥

文章目录 1.查看本机的ssh公钥2.生成ssh公钥 1.查看本机的ssh公钥 ①终端进入~/.ssh目录 cd ~/.ssh②使用ls命令查看&#xff0c;如果有id_rsa.pub文件说明已经生成了公钥。 ls③使用cat命令查看公钥具体内容&#xff0c;如下图所示 cat id_rsa.pub2.生成ssh公钥 注意XXXX…

git ssh key的配置,git生成ssh公钥

git clone支持https和git&#xff08;即ssh&#xff09;两种方式下载源码。 使用git方式下载时&#xff0c;如果没有配置过ssh key&#xff0c;则会有如下错误提示&#xff1a; 1.首先配置用户名&#xff0c;邮箱。 git config --global user.name "这里换上你的用户名…

【Git】Gitee生成/添加SSH公钥

Gitee 提供了基于SSH协议的Git服务&#xff0c;在使用SSH协议访问仓库之前&#xff0c;需要先配置好账户/仓库的SSH公钥。 按如下命令来生成 sshkey: ssh-keygen -t ed25519 -C "xxxxxxxxxx.com" 注意&#xff1a;这里的 xxxxxxxxxx.com 只是生成的 sshkey 的名称…

git生成ssh公钥

学习一下&#xff0c;感谢&#xff01; gitee 步骤&#xff1a; 1.打开终端&#xff08;git&#xff09;进入.ssh目录 cd ~/.ssh 如果.ssh文件夹不存在&#xff0c;执行指令自动创建 mkdir ~/.ssh 2.生成RSA密钥对并进行命名 ssh-keygen -t rsa -C "你的邮箱xxx…

如何生成git公钥?

1、 打开git bash。 2、 执行ssh-keygen.exe命令。 此时到电脑的C–>users–>用户名文件夹–>.ssh文件夹下可以找到公有密钥的文件&#xff1a; 3、 执行cat ~/.ssh/id_rsa.pub&#xff0c;输出结果即为公钥。 注意&#xff1a;输出的公钥结果可能需要拷贝出…

GIT生成SSH公钥图文教程

GIT介绍 GIT是一种分布式版本控制系统&#xff0c;用于追踪文件的变化和协作开发。本文将详细介绍GIT的基本架构、工作流程和常用命令&#xff0c;并对其优势和应用场景进行分析。 1. GIT的基本架构 GIT的基本架构由三个主要组件组成&#xff1a;工作区&#xff08;Working …

Git生成/添加SSH公钥

Git生成SSH密钥 git config --global user.name "kenny" 配置用户名 git config --global user.email "602118471qq.com" 配置邮箱 此时&#xff0c; 会在C:\Users\Administrator目录下生成.gitconfig配置文件&#xff1a; 请勿删除&#xff01; ssh-key…

Git生成公钥

&#xff08;1&#xff09;第一次登录需要设置账号和密码。 &#xff08;2&#xff09;因为你的仓库属于私有的&#xff0c;组员也无法访问你远程仓库的代码。 我们可以通过公钥来允许其他成员以及自己来访问该仓库。 使用: ssh-keygen -t rsa 来帮你生成公钥。输入此命令再…

git生成公钥和私钥

1.打开Git Bash&#xff0c;输入ssh-keygen -t rsa -C “your_emailexample.com”&#xff0c;回车 提示要求输入将要生成的秘钥文件的路径&#xff0c;可以不输入&#xff0c;直接按enter保存在默认路径。这里&#xff0c;我直接按下enter 3.&#xff08;可以不输入密码&…

Git生成生成公钥和私钥

Git配置 Git安装完之后&#xff0c;需做最后一步配置。打开git bash&#xff0c;分别执行以下两句命令 git config --global user.name “用户名” git config --global user.email “邮箱” 这二步必须执行 SSH配置 1、打开git bash 2、执行生成公钥和私钥的命令&#x…

Git生成公钥、私钥以及ssh key配置

一、公钥以及私钥的生成 首先设置Git 用户签名 1.打开安装好的Git Bash&#xff1a; 2.设置Git用户签名&#xff0c;执行以下命令&#xff1a; git config --global user.name 用户名git config --global user.email 邮箱执行之后打开C盘User目录&#xff0c;会看到Git 的配置…

Git生成公钥 bash:ssh-keygen command not found

公钥是CODE识别您的用户身份的一种认证方式&#xff0c;通过公钥&#xff0c;您可以将本地git项目与CODE建立联系&#xff0c;然后您就可以很方便的将本地代码上传到CODE&#xff0c;或者将CODE代码下载到本地了。 以下介绍生成公钥和管理公钥的方法。如果你是在windows系统下使…

Git简单生成公钥和私钥的方法及git ssh key配置

Git简单生成公钥和私钥的方法 Git安装完之后&#xff0c;需做最后一步配置。打开git bash&#xff0c;分别执行以下两句命令 git config --global user.name “用户名”git config --global user.email “邮箱” 用户名看自己喜欢起&#xff0c;一般都是起些容易记的&#x…