资讯动态

多源中文语料清洗与AI语义去重全流程实践

发布时间:2026/10/17 13:02:27 来源:尧图企业网站定制
做AI训练数据的人大多有个共识算法模型可以复现高质量数据集才是真正的护城河。去年我接手了一个语料清洗项目目标是构建一套覆盖新闻、技术博客、百科词条和问答社区的千万级中文语料库。爬虫采集本身不难真正让人头疼的是清洗和去重——线上数据有大量HTML标签残留、重复段落、营销水文和语义相近的改写内容如果直接拿去训练轻则拉低模型效果重则让模型学会输出广告腔和废话。这篇文章完整记录了我从多源采集到AI语义去重的全过程包括爬虫架构设计、清洗管线搭建、去重策略选型以及踩过的那些坑。整个链路分四层推进采集层负责多源数据的稳定抓取和合规控制清洗层做编码修复、标签剥离和规则过滤去重层先用MinHash粗筛再用向量模型精排去重最后是质量评估和入库管理。如果你也在做NLP训练数据、知识库构建或者舆情分析这套方案可以直接对标落地。1. 多源语料采集爬虫架构与合规边界1.1 数据源选型与采集策略多源语料的价值在于覆盖不同领域的语言风格和表达方式但数据源选择不能只看数量更要看平衡性。我最终圈定了四类来源新闻门户提供正式书面语技术社区提供专业术语和高密度信息表达百科类站点提供结构化的定义和解释问答社区则补充口语化和场景化的问句表达。每类选2到3个目标站点而不是一味追求大而全。并发控制是采集端的核心参数。我用了生产者消费者模型请求队列用Redis实现消费者按域名维度做了并发限额。具体做法是给每个域名单独初始化一个信号量新闻类站点并发数控制在5以内技术社区可以放宽到8问答类站点限制在3。这个差异化配置不是拍脑袋想出来的——新闻站反爬严格高频请求容易触发验证码问答站页面渲染重并发高了反而拖慢解析速度。爬虫框架选用了Python异步方案结合代理池做请求分发。代理池里混用了数据中心代理和住宅代理比例控制在7比3。数据中心代理便宜但容易被封住宅代理稳定但成本高混合使用能在成本和稳定性之间找到平衡。合规提示爬虫采集必须尊重目标网站的robots协议和用户条款只采集公开数据不碰个人隐私信息和版权保护的付费内容。我自己在项目里建立了一份《数据源合规清单》逐站确认采集边界这一步骤不是走过场而是项目能否健康推进的基本前提。1.2 采集管道的异常处理与断点续采爬虫写起来容易跑得稳很难。我在管道里加了三级重试机制网络请求超时重试2次解析失败重试1次入库冲突直接跳过并记录日志。重试之间用指数退避策略间隔从1秒开始翻倍最大不超过30秒。这套机制跑了一周后整体采集成功率稳定在97%以上。断点续采是我一开始就设计进去的。每个待采集URL进入Redis队列后先写入一个处理中状态采集完成更新为已完成失败则退回待重试队列。这样程序崩溃重启后直接扫描Redis队列就能恢复现场不用从头再来。实际操作中我每隔一小时会把队列长度和已完成数量输出到日志方便监控进度和定位卡点。页面解析用了规则解析加模板抽取的方式。常规页面用XPath定位正文节点遇到动态渲染的页面就切换到Playwright渲染后再解析。这里有个经验解析规则尽量写成配置化的JSON文件不要硬编码在代码里。换站点换模板时改配置比改代码高效得多也方便非开发人员参与维护。2. 清洗管线搭建从乱码到干净文本的处理流程2.1 编码修复与HTML标签剥离采集到的原始HTML页面文本质量参差不齐。最常见的问题是编码混乱有些页面声明是UTF-8实际内容却是GBK编码直接解析会出现大量乱码。我先用编码检测库做自动识别检测置信度低于90%的样本强制尝试多种编码解码再配合人工抽检确认。这一层能修掉大约30%的乱码样本。HTML标签剥离用了解析库的get_text方法但直接调用会有两个问题一是脚本和样式标签里的内容会混进来需要先移除script、style、noscript节点二是标签之间的空白字符和不可见字符会残留需要配合正则做二次清理。我写了一个文本提取函数按这个顺序处理移除无意义节点、提取正文文本、压缩连续空白、补全标点边界。处理后的文本干净度直接决定了后续去重和入库的质量基线。import re from bs4 import BeautifulSoup def extract_clean_text(html): soup BeautifulSoup(html, lxml) for tag in soup([script, style, noscript, iframe]): tag.decompose() text soup.get_text(separator\n) text re.sub(r[ \t\u3000], , text) text re.sub(r\n\s*\n, \n, text) text re.sub(r[\x00-\x08\x0b\x0c\x0e-\x1f], , text) return text.strip()编码修正和标签剥离是清洗管道的地基这一层做不好后面所有环节的效果都会打折扣。我自己测试过如果跳过编码修正直接做其他清洗样本里乱码比例会维持在5%左右这些噪声进模型后很难被自动纠正。2.2 质量规则的精细调参规则过滤不是简单地按长度一刀切。我按语料类型分别设定阈值新闻类要求正文不少于200字技术博客不少于150字百科词条不低于100字问答类不少于30字。长度阈值的设定逻辑是太短的文本缺乏语义完整性训练时容易变成噪声但问答类场景故意放宽限制因为短问句在意图识别任务中有独立价值。除了长度还有两道硬性过滤一是广告和营销话术的匹配规则比如“加微信领取”“点击链接注册”“限时优惠”这类模式用关键词库加正则组合过滤二是重复字符和乱码模式连续重复字符超过阈值比如连续同一个字超过5次直接丢弃这是识别水文的成本最低的特征。PPL困惑度过滤用来筛掉语义不连贯的文本。我用了语言模型来计算文本困惑度阈值定在100以内保留。这个阈值不是理论推导出来的而是抽样评估了3000条保留样本和1000条被丢弃样本人工判断准确率在85%以上才定下的。困惑度过滤对技术社区和问答类语料的提升特别明显这两个来源经常出现答非所问或碎片化表达常规规则很难识别困惑度能有效捕捉语义断裂。2.3 敏感信息与低质内容的兜底策略文本里偶尔会夹带手机号、邮箱、身份证号等个人信息这些必须摘除或替换。我用正则做了脱敏处理把手机号中间四位替换为星号邮箱用户名部分打码身份证号直接丢弃整条样本。脱敏不是可选项而是数据合规的底线动作。还有一个容易被忽略的环节是文本去重前置检查。我在清洗层先做了一次精确去重用SHA256哈希对整篇文本取指纹相同指纹直接去重。这一步能把多源采集时的重复文章干掉一大半尤其是新闻站点之间互相转载的情况。精确去重不能替代语义去重但能极大减轻后续AI去重的压力属于性价比极高的前置过滤。3. AI语义去重从MinHash到向量模型的组合策略3.1 MinHash粗筛快速圈定候选重复集合语义去重的难点在于同一内容可以有多种表述方式直接哈希比对抓不到改写过的文本。我的方案分两阶段先用MinHash做粗筛再用向量相似度做精排。MinHash的核心思路是把文本转换成多个哈希值的最小值集合然后通过集合间的相似度指数近似估算文本重复度。它的优势是计算快、可并行、适合海量数据场景。实现上我对每篇文本做分词后取5-gram的shingle再用不同的哈希函数为每个shingle计算哈希值每个哈希函数取集合最小值。相似度通过两个集合中相同最小值的比例来估算。为了支持大规模比对我用LSH局部敏感哈希把哈希签名分band处理将候选重复文本聚合到同一个桶里。这个设计让全量两两比对的复杂度从O(n²)降到近似线性。from datasketch import MinHashLSH, MinHash def build_minhash(text, num_perm128): tokens jieba.lcut(text) m MinHash(num_permnum_perm) for token in tokens: m.update(token.encode(utf-8)) return m lsh MinHashLSH(threshold0.75, num_perm128) for doc_id, text in corpus.items(): m build_minhash(text) lsh.insert(doc_id, m)阈值0.75是什么意思我抽样测试过0.75能召回80%左右的改写重复样本同时把误判率控制在10%以内。阈值调节的逻辑是业务上可接受的漏判和误判怎么平衡。如果后续向量精排的算力充足粗筛阈值可以略微调低以提高召回率。粗筛的目标不是找全所有重复而是错杀放过后为精排圈定有性价比的候选集。3.2 向量精排语义相似度的准确度量粗筛圈定候选集后我用预训练模型对每篇文本生成句向量再计算候选对之间的余弦相似度。模型选型上做过对比纯中文场景下通用中文预训练模型的检索效果中规中矩但换了针对文本匹配优化的模型后相似度计算与人工判断的对齐度明显提升。模型推理用GPU跑128维向量单条文本推理时间约80毫秒百万级语料的向量生成大致在一天内完成。相似度阈值的选择不能拍脑袋。我在验证集上标注了一批正负样本人工判定是否为重复内容画了P-R曲线选中精确率和召回率交叉点附近的阈值0.85。为什么不是0.9或0.80.9会漏掉大量改写程度较高的重复0.8则会误杀语义相近但内容独立的文本。阈值最终定为0.85在这个点上人工一致性评估的F1值最高。# 向量相似度精排阶段的核心计算 from sklearn.metrics.pairwise import cosine_similarity def semantic_dedup(candidate_pairs, embeddings, threshold0.85): duplicates [] for doc_a, doc_b in candidate_pairs: vec_a embeddings[doc_a].reshape(1, -1) vec_b embeddings[doc_b].reshape(1, -1) score cosine_similarity(vec_a, vec_b)[0][0] if score threshold: duplicates.append((doc_a, doc_b, float(score))) return duplicates精排阶段我用了一个技巧不是简单把重复文档删除而是保留质量更高的一篇。质量打分综合考虑文本长度、信息密度、来源权威性和语言规范性得分高者保留得分低者入回收站。这个设计对语料的最终质量影响非常大比如新闻来源的报道被百科条目复制过去保留的应该是百科条目而不是新闻稿因为百科的结构化表达更适合模型学习。3.3 去重效果与数据分布监控整轮去重跑完后精确去重去掉了约25%的完全重复样本MinHash粗筛圈出大约18%的候选重复集合向量精排最终判定其中12%为语义重复。三个阶段累计下来原始语料量从约150万篇压缩到约100万篇去重率约三分之一。去重完成后不能直接认为万事大吉。我对保留语料做了分布监控按来源、长度区间、时间维度统计数量确保去重没有系统性偏向某个数据来源。实际监控中发现问题MinHash对短文本的区分度偏弱问答类短样本误判率高于长文本所以短文本的MinHash签名我用了更细的3-gram颗粒度同时将阈值从0.75调整到0.68单独一套参数处理短文本场景。去重逻辑写完后我跑了一轮长尾检查随机抽了3000条保留样本做人工复看。结果显示误保留的重复样本比例约1.2%误删除的独立样本比例约0.5%这个水平对训练数据集来说可以接受。如果你对误删除更敏感可以调低精排阈值代价是保留更多重复内容。4. 清洗后的质量评估与入库管理4.1 质量评估指标的设计思路语料清洗完了质量到底怎么样得用数据说话。我设计了四个核心指标文本完整度正文提取后是否包含完整的开头和结尾、语言规范性通过困惑度和语法错误率综合评估、信息密度有效信息词占总词数的比例、重复率语义重复样本占比。这四个指标从不同维度覆盖了训练数据质量的核心要求。评估方法是按来源分层抽样每个来源随机抽500条由人工标注员按1到5分打分。评分一致性用相关系数衡量分数低于阈值的标注结果剔除掉。这套评估流程跑一轮大概需要两天时间但换来的是对每条数据质量基线的真实掌握。指标评估方式合格线实际值文本完整度人工抽检≥4.0分4.3语言规范性PPL困惑度≤10082信息密度有效词占比≥40%47%语义重复率自动计算≤5%2.1%4.2 清洗管道的可复现设计清洗管道我用配置文件驱动所有阈值和规则集中在YAML文件里管理而不是散落在各种代码中。数据源、采集频率、清洗规则、去重阈值都写在配置中修改参数不必改动代码逻辑方便复用和调试。# pipeline_config.yaml sources: news: base_urls: [...] concurrency: 5 tech: base_urls: [...] concurrency: 8 cleaning: min_length: news: 200 tech: 150 wiki: 100 qa: 30 ppl_threshold: 100 dedup: minhash_threshold: 0.75 short_text_threshold: 0.68 semantic_threshold: 0.85整个管道跑完一遍后输出的语料按来源和清洗批次做了分目录存放每个文件附带元数据JSON记录来源、采集时间、清洗参数版本。这套记录方式保证了语料可追溯——训练出问题时能定位到是哪个批次的哪条规则引起的而不是对着百万条数据抓瞎。元数据管理是清洗管道里成本最低但收益最高的设计强烈建议加上。5. 实践中的常见问题与排查手册5.1 高频踩坑点记录爬虫采集阶段最常见的问题是代理池污染。用了某些免费代理后请求成功率直线下降排查发现是代理列表混入了大量失效IP和高延迟节点。解决方式很简单每次请求前对代理做一次连通性测试延迟超过3秒的IP直接剔除每5分钟重新校验一次代理池。清洗阶段的大坑是误删边界案例。有个技术博客讲算法复现全文代码块占比超过70%按文本长度规则看是合格语料但语言模型的困惑度偏高差点被过滤掉。后来我给代码类语料单独建了规则分支检测到代码块占比超阈值时跳过困惑度过滤只做格式规范化。去重阶段最让人头疼的是改写程度极高的文本。有些营销号把一篇文章改几个措辞就重新发布MinHash的5-gram对这种轻度改写不够敏感。我把shingle从5-gram改成3-gram后召回率提升明显但误判率也同步上升了。最终的方案是3-gram用于候选召回向量精排的阈值从0.85微调到0.88误判率回落的同时保持了召回率。5.2 根据经验和需求不断迭代清洗管道不是一次性建设完就固定不变的。数据源的内容风格会演化新出现的站点结构需要适配模型的训练需求也会调整语料分布。我的习惯是每个月抽一次全流程样本做人工复盘重点检查清洗规则的误杀率和去重阈值的适配度。做一次全量重跑会耗时数小时但换来的是训练数据的长期稳定性。对算力资源有限的情况向量精排可以分批处理。把MinHash粗筛后的候选对按桶拆分分多个批次依次计算相似度避免一次性把所有向量装入内存。我用这个方式成功在单卡GPU上处理了百万级语料峰值显存占用控制在12GB以内。做数据工程有时候不是看有多牛的硬件而是看怎么把现有资源榨干。我个人实际跑下来最深的体会是语料清洗项目里真正耗时的不在代码实现而在阈值调优和边界Case处理。自动化的规则能解决80%的问题剩下20%需要靠人工抽检和持续迭代来补位。如果你的项目时间紧张至少保证精确去重和规则过滤做到位语义去重可以先跑一版标准参数后续再逐步精调。这个项目做完以后我又往管道里加了一个自动标注工具对清洗后的语料做粗粒度标签分类新闻、技术、口语、科普等方便后续按需采样。如果你也需要定向增强某一类语料的规模这个工具延伸会很实用。数据是AI的上游把清洗和去重这件事做扎实了模型的训练效果会给你真实的回报。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑