NLP_文本去重_附Python实现【MinHash和MinHashLSH】算法

NLP_文本去重_附Python实现【MinHash和MinHashLSH】算法

  • 前言
  • 代码的实现【注释丰富】

前言

大规模的文本去重是目前比较热门的一个技术,由于大模型的兴起,更多的高质量数据集也是大家迫切需要的。

关于如何进行文本去重?

直观的方法首先是利用Python正则表达式进行去重。
推荐学习:1. re — 正则表达式操作 2. 正则表达式 - 教程

然后是利用文本之间的相似度进行去重。
这里主要讲第二种。
推荐学习:1. 张振虎大佬的博客 2. Github的实现源码 3. 文本内容相似度计算方法:minhash 4. Python的datasketch库中的MinHashLSH

ok!这里不多赘述关于MinHash的更多基础知识,感兴趣的朋友相信在网上也能找到。
Talk is cheap. Show me the code.
然后在看代码之前说一下算法的实现流程也是非常有帮助的 😗

我们要实现去重的话,需要实现一个query操作,即给定一个文本计算出数据中有哪些是与该文本相似的。然后在对该集合进行相应的操作进行去重。而这里MinHash只是用来估计两个文本之间的 Jaccard 相似度和基数(estimate Jaccard similarity and cardinality),所以这里需要用到MinHashLSH,其实现了一个分桶的操作,然后对桶进行哈希来计算已insert文本之间的相似度,即可以实现query操作

  1. 首先是逐行读取文档中的文本内容,然后对每行文本进行hash,并加入到lsh中(lsh.insert(doc_id, m))。【注意:lsh = MinHashLSH(threshold=0.9, num_perm=256)minhashes = {}都是全局的】
  2. 对字典进行操作,如果取该文本则标记为1,反之为0。这里有三种算法
    • 其一【随机算法】,首先标记所有文本都取,然后查询相似集合随机取集合中的一个文本,再标记其余文本不取。
    • 其二【簇算法】,将相似的所有数据标为一个簇,然后对簇使用随机算法。
    • 其三【最小标号算法】 ,每次取集合中下标最小的数据。即如果当前文本是相似集合中最小的文本则取,否则不取。该算法的优势是无需全局的字典,只需要考虑当前的数据即可。
  3. 最后,实现将去重后的文本写入文件,和写入哪些是被去重的文档进行结果对比!

下面看代码!💘💘💘

代码的实现【注释丰富】

from datasketch import MinHash, MinHashLSH
import pickle
import tarfile
import os
import re
from simhash import Simhash
import json
from datetime import datetime
import numpy as np
from collections import defaultdict
import nltk
from nltk.util import ngrams
from unidecode import unidecode
import jieba
import randomnltk.download('punkt')  # Download NLTK data
width = 5
hash_k = 5
max_hash_len = 0# lsh = MinHashLSH(threshold=0.5, num_perm=9000, params=(450, 20))
lsh = MinHashLSH(threshold=0.9, num_perm=256)
minhashes = {}# 中文数据预处理,滑动窗口为width
def preprocess(s):s = s.lower()   # 转为小写s = re.sub(r'[^\w]+', '', s)    # 去除非字母数字下划线return [s[i:i + width] for i in range(max(len(s) - width + 1, 1))]# 先预处理,然后jieba分词,算minhash加入lsh
def add_to_lsh(doc_id, doc_text):# tokens = nltk.word_tokenize(preprocess(doc_text)) # 用于英文的tokens = jieba.lcut("".join(preprocess(doc_text))) # 使用jieba进行中文分词# while len(tokens) < 5:#     tokens.append('<PAD>')# tokens = preprocess(doc_text)# ngram_set = set(ngrams(tokens, 5))  # 5-grams# m = MinHash(num_perm=9000)m = MinHash(num_perm=256)for ngram in tokens:m.update("".join(ngram).encode('utf8'))minhashes[doc_id] = mlsh.insert(doc_id, m)# 读取文档的每一行,加入members,并记录minhashes
def index_minhash(num):# hashes = []members = []print("Starting part_02%0.3d"%(num), len(members))with open("/home/zikang/project/datastory/redpajama-data/data_prep/book/data/part-02%0.3d"%(num), "r") as f:lines = f.readlines()for idx, i in enumerate(lines):if idx % 5000 == 0:print("This is part_02%0.3d"%(num), idx)member = json.loads(i)members.append(member)try:if max_hash_len == 0:add_to_lsh(idx, member['content'])else:add_to_lsh(idx, member['content'][:max_hash_len])except:continue          # print("Finishing part_02%0.3d"%(num), len(hashes), len(members))print("Finishing part_02%0.3d"%(num), len(members))# return (hashes, members)return members# 【随机算法】首先标记所有数据都取,然后随机选择一个取的集合,标记其余不取。
def get_match(n):value_dict = {}for index in range(n):value_dict[index] = 1for index in range(n):try:if value_dict[index]:results = lsh.query(minhashes[index])random_element = random.choice(results)value_dict[random_element] = 1else:continuefor i in results:if i != random_element:value_dict[i] = 0lsh.remove(i)except:value_dict[index] = 0return value_dict# 【簇算法】 将相似的所有数据标为一个簇,然后对簇使用随机算法
# def get_match(n):# value_dict = {}# for index in range(n):#     value_dict[index] = 1# for index in range(n):#     try:#         if value_dict[index]:#             list1  = lsh.query(minhashes[index])#             # 将列表转换为集合,并求并集#             set1 = set(list1 )#             for l in list1:#                 list2 = lsh.query(minhashes[l])#                 set2 = set(list2)#                 merged_set = set1.union(set2)#             # 将集合转换回列表#             merged_list = list(merged_set)#             random_element = random.choice(merged_list)#             value_dict[random_element] = 1#         else:#             continue#         for i in merged_list:#             if i != random_element:#                 value_dict[i] = 0#                 lsh.remove(i)#     except:#         value_dict[index] = 0# return value_dict# 【最小标号算法】 每次取集合中下标最小的数据
# def get_match(n):
#     value_dict = {}
#     for index in range(n):
#         flag = 1
#         try:
#             results = lsh.query(minhashes[index])
#             for x in results: # 如果当前不是序号最小的则flag=0
#                 if x >= index:
#                     continue
#                 flag = 0
#                 break
#             value_dict[index] = flag
#         except:
#             value_dict[index] = flag
#     return value_dictif __name__ == "__main__":import argparseparser = argparse.ArgumentParser()parser.add_argument('-w', type=int, default=5, help='the window size')parser.add_argument('-num_perm', type=int, default=128, help='the number of permutation functions used in MinHash.')parser.add_argument('-k', type=int, default=1, help='find K nearest region')parser.add_argument('-l', type=int, default=90, help='the max length of the text for hashing, 0 means no limit')parser.add_argument('-n', type=int, default=2, help='the number of processes to run')args = parser.parse_args()num_perm = args.num_permwidth = args.whash_k = args.kmax_hash_len = args.ln_jobs = args.noutfile = "去重后文本的存放路径/yyyy.jsonl"reject_file = "去重后被丢弃文本的存放路径/nnnn.jsonl"get_members = index_minhash(471)# hashes_members.extend(get_book(n_jobs))# print("Finish getting hashes and members!")print("Finish getting lsh and members!")# import itertools# hashes = list(itertools.chain(*[item[0] for item in hashes_members]))# print("hashes长度: ",len(hashes))# import itertools# members = list(itertools.chain(*[item for item in get_members]))print("members长度: ",len(get_members))# 打印一些相似数据# results = lsh.query(minhashes[0])# with open(reject_file, 'w') as f: # 写过滤后的数据#     for i in results:#         nnct = {"content": get_members[i]["content"]}#         f.write(json.dumps(nnct, ensure_ascii=False) + '\n')n = len(get_members)temp_dict = get_match(n)# print(temp_dict)with open(outfile, 'w') as f: # 写过滤后的数据count = 0for i in range(n):          if temp_dict[i] == 1:# meta = {}# for feature in get_members[i]:#     if feature != "content":#         meta[feature] = get_members[i][feature]# new = {"meta": meta, "content": mem["content"]}try:new = {"content": get_members[i]["content"]}count += 1f.write(json.dumps(new, ensure_ascii=False) + '\n')except:continueprint("count:",count)print("去重的数量", n-count)with open(reject_file, 'w') as f: # 写过滤后的数据for i in range(n):if temp_dict[i] == 0:try:nnct = {"content": get_members[i]["content"]}f.write(json.dumps(nnct, ensure_ascii=False) + '\n')except:continue

参考链接:
[1]: https://github.com/ekzhu/datasketch

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7221.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一百三十五、Azkaban——AzkabanWebServer服务开启后秒退

一、问题 Azkaban的AzkabanWebServer服务开启后秒退&#xff0c;造成Azkaban的页面登录不上 AzkabanWebServer服务开启后&#xff0c;第一个jps里面有AzkabanWebServer&#xff0c;随后第二个jps里面没有AzkabanWebServer 二、问题原因 MySQL中azkaban数据库的表executors的…

【ARMv8/v9 异常模型入门及渐进 13 -- ARM Linux 系统调用流程分析】

文章目录 1.1 SYSCALL 过程调用规范1.1.1 系统调用流程概括1.1.2 ARMv7 系统调用中断处理 上篇文章&#xff1a;ARMv8/v9 异常模型入门及渐进 12 – ARM Linux 用户栈和系统栈及 CURRENT宏 介绍 1.1 SYSCALL 过程调用规范 当进程因为中断或者系统调用陷入到内核态时&#xff0…

Spring,SpringBoot,Spring MVC的区别是什么

1.Spring是什么 我们通常所说的 Spring 指的是 Spring Framework&#xff08;Spring 框架&#xff09;&#xff0c;它是⼀个开源框架&#xff0c;有着活跃⽽庞⼤的社区&#xff0c;这就是它之所以能⻓久不衰的原因。Spring ⽀持⼴泛的应⽤场景&#xff0c;它可以让 Java 企业级…

遇到了一个存在XSS(存储型)漏洞的网站

第一个漏洞self xss&#xff08;存储型&#xff09; 存在漏洞的网站是https://www.kuangstudy.com/ 然后点击个人设置 在编辑主页中&#xff0c;我们可以用最简单的script语句进行注入&#xff0c;提交&#xff1b; 出现弹窗&#xff0c;说明它已经把代码进行解析&#x…

java8里如何使用流?《Java8 实战》读书笔记 第 5 章 使用流

目录 第 5 章 使用流5.1 筛选和切片5.1.1 用谓词筛选&#xff08;filter&#xff09;5.1.2 筛选各异的元素&#xff08;distinct&#xff09;5.1.3 截短流&#xff08;limit&#xff09;5.1.4 跳过元素&#xff08;skip&#xff09; 5.2 映射&#xff08;map&#xff0c;fl…

JavaScript常见高级知识点

目录 防抖节流高阶函数函数柯里化数组去重set去重filter去重includes去重 数组扁平化深拷贝getBoundingCilentRectIntersectionObserver自定义事件 防抖 防抖是一种常用的技术手段&#xff0c;在JavaScript中特别常见。它的作用是控制某些高频率触发的事件&#xff0c;在一定时…

RocketMQ消息过滤Tag标签

生产者在封装Message消息时可以传入tag参数&#xff0c;消费者在进行消费时可以进行订阅主题时可以进行tag过滤,代码示例如下. //生产者 public class Producer {public static void main(String[] args) throws Exception{DefaultMQProducer producer new DefaultMQProducer(…

LLM - Chinese-Llama-2-7b 初体验

目录 一.引言 二.模型下载 三.快速测试 四.训练数据 五.总结 一.引言 自打 LLama-2 发布后就一直在等大佬们发布 LLama-2 的适配中文版&#xff0c;也是这几天蹲到了一版由 LinkSoul 发布的 Chinese-Llama-2-7b&#xff0c;其共发布了一个常规版本和一个 4-bit 的量化版本…

Linux命令行宝典:随时查询、轻松应对

&#x1f57a;作者&#xff1a; 迷茫的启明星 学习路线C语言从0到1C初阶数据结构从0到1 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

Ama no Jaku

登录—专业IT笔试面试备考平台_牛客网 题目大意&#xff1a;有一个n*n且仅由0和1构成的矩阵&#xff0c;每次操作可以将一整行或一整列的所有数取反&#xff0c;问能否使所有行中构成的最小数>所有列中构成的最大数 1<n<2000 思路&#xff1a;首先&#xff0c;如果…

H2TEST自动化测试

ref&#xff1a; GitHub - kunyi0605/H2testwpywinauto实战-操作h2testw.exe自动化测试脚本_肤白貌美的博客-CSDN博客 https://www.cnblogs.com/qican/p/15038067.html

Flink CEP (一)原理及概念

目录 1.Flink CEP 原理 2.Flink API开发 2.1 模式 pattern 2.2 模式 pattern属性 2.3 模式间的关系 1.Flink CEP 原理 Flink CEP内部是用NFA&#xff08;非确定有限自动机&#xff09;来实现的&#xff0c;由点和边组成的一个状态图&#xff0c;以一个初始状态作为起点&am…

文件共享服务器(五)sicis

目录 前言 一、概述 1.iscsi概念 2.iscsi介绍 3.相关名词 二、实验 1.构建iscsi服务 2.实现步骤 服务器端 客户端 3.注意事项 总结 前言 iSCSI是由IBM发明的基于以太网的存储协议&#xff0c;该协议与SUN的NFS协议都是为了解决存储资源共享问题的解决方案。两者意图…

git rebase -i

git rebase -i 是一种交互式的 rebase 方式&#xff0c;其中 -i 是 --interactive 的简写。这种方式允许你修改一系列的 commit 信息&#xff0c;在 rebase 过程中有选择地选择、编辑或者合并 commit。 在执行 git rebase -i 命令时&#xff0c;你需要提供一个参数&#xff0c…

音视频——封装格式原理

视频解码基础 一、封裝格式 ​ 我们播放的视频文件一般都是用一种封装格式封装起来的&#xff0c;封装格式的作用是什么呢&#xff1f;一般视频文件里不光有视频&#xff0c;还有音频&#xff0c;封装格式的作用就是把视频和音频打包起来。 所以我们先要解封装格式&#xff0…

集成学习Boosting - AdaBoost

目录 1. Boosting方法的基本思想 1.1 Bagging VS Boosting 1.2 Boosting算法的基本元素与基本流程 1.3 sklearn中的Boosting算法 2. AdaBoost 3 AdaBoost的基本参数与损失函数 3.1 参数 base_estimator&#xff0c;属性base_estimator_与estimators_ 3.1. 参数 learnin…

vue之vue-pboc组件

功能描述 PBOC业务页面基类组件,提供多个PBOC操作方法,,它的父级组件为vue-base #方法 查询电子现金余额及电子现金上限 queryElecBalance(); 查询电子现金明细 queryElecDetail(); pboc初始化过程接口 initDeviceInfo: function initDeviceInfo( appid, acctype, tranty…

用QFramework来重构 祖玛游戏

资料 Unity - 祖玛游戏 GitHub 说明 用QF一个场景就够了&#xff0c;在UIRoot下切换预制体达到面板切换。 但测试中当然要有一个直接跳到测试面板的 测试脚本&#xff0c;保留测试Scene&#xff08;不然初学者也不知道怎么恢复测试Scene&#xff09;&#xff0c;所以全文按S…

宋浩线性代数笔记(二)矩阵及其性质

更新线性代数第二章——矩阵&#xff0c;本章为线代学科最核心的一章&#xff0c;知识点多而杂碎&#xff0c;务必仔细学习。 重难点在于&#xff1a; 1.矩阵的乘法运算 2.逆矩阵、伴随矩阵的求解 3.矩阵的初等变换 4.矩阵的秩 &#xff08;去年写的字&#xff0c;属实有点ugl…

多态及其原理

文章目录 构成多态的条件虚函数作用&#xff1a;完成重写 重写重载 重写 隐藏为什么析构函数要搞成符合多态?原理预热对于基类指针或引用指向父类或者子类的成员函数是如何调用不同的函数呢&#xff1f; 一个类如果是基类&#xff0c;它的析构函数最好加上virtual 构成多态的条…