大模型语料清洗与质量评估:从脏数据到高质量训练集
不知道你有没有这种体验辛辛苦苦爬到几十个TB的文本数据高高兴兴往里灌结果模型训练出来的效果惨不忍睹生成的内容乱七八糟甚至一句完整的话都说不利索。我最早做数据工程的时候就被这样毒打过一次后来才意识到语料清洗与质量评估这件事才是整个数据工程链路里最不该省功夫的一环。说白了大模型也好行业模型也罢喂进去的是垃圾出来的必然就是垃圾这个道理放在哪都成立。这篇文章就把我在语料清洗与质量评估上踩过的坑、总结的方法、沉淀下来的流程一次性讲清楚适合正在做数据采集、数据集构建、模型预训练或者微调数据准备的朋友参考也适合想系统了解数据工程第一道工序的初学者。1. 语料清洗与质量评估在数据工程中的定位1.1 为什么语料清洗是第一道坎很多人对数据工程的理解是从“下载数据”开始的但实际上数据工程的真正起点是理解你手里的数据到底有多脏。从公开渠道采集来的原始文本里面掺杂的远不只是“没用的句子”这么简单。我拆过一份号称“全网通用语料”的原始数据包5TB的分卷压缩文件打开之后发现里面什么都有网页导航栏、用户评论里的广告、乱码文本、重复了无数遍的模板段落、半个句子的截断文本、全是标点符号的垃圾行、以及大量已经被删掉的“404页面残留”。如果把这份数据直接丢给分词器或者模型训练脚本产生的后果包括但不限于词表被垃圾词撑爆、模型困惑度长期不降、生成内容出现莫名其妙的重复废墟。所以语料清洗干的事情本质上就是把“原始数据”转化为“可学习数据”。这一步的输出质量直接决定了后续所有环节的上限。模型结构再先进训练策略再精巧只要语料本身是脏的一切白搭。我之前跟某个做行业模型的团队聊天他们一开始觉得数据清洗“无非就是去个重、删个无用行”结果第一个版本模型训完发现模型对数字的理解完全错乱。排查到最后问题出在他们用的语料里混杂了大量从网页抽取的乱码数字串比如“0fffc13a9”这样的十六进制片段。这类脏数据不清理模型学到的“数字”根本不是人类理解的数字。1.2 质量评估让清洗从“凭感觉”变成“有依据”很多团队做清洗靠的是“感觉”——感觉这个文本怪怪的就删掉感觉那个句子挺通顺就留下来。这种做法最大的问题是不可复现、不可量化、不可规模化。质量评估存在的意义就是给清洗动作建立一套可量化的反馈机制。清洗前的数据有多脏清洗后干净了多少哪些类型的噪音没有清干净全语料的整体质量在什么水平这些都需要用指标去度量而不是用“看起来还行”去描述。打个比方清洗是“治病”质量评估就是“体检报告”。你不可能一边治病一边不看血压和心率指标那跟闭着眼开药没区别。质量评估告诉你清除率、重复率、困惑度、格式合规率这些核心指标你才知道下一步该往哪个方向使劲。2. 语料清洗的核心步骤与方法2.1 去重先干掉最明显的重复问题语料去重是整个清洗流程里性价比最高的一步。原因很简单网络数据天然自带大量重复内容同一篇新闻可能被几十个网站转载同一个段落在搜索引擎的快照里反复出现有些数据集内部甚至存在逐字节相同的文件。去重可以从两个粒度来做第一层是文档级精确去重。最直接的办法是对每篇文档计算哈希值比如MD5或SHA-1然后对哈希值建索引去重。优点是真的快缺点是一旦内容有任何微小改动比如加了页脚、改了标点哈希值就完全变了精确去重就失效了。第二层是文档级模糊去重。这里我推荐用MinHash或者SimHash的思路把文档切分成若干个n-gram片段每个片段哈希后取最小值作为特征多个特征组合成签名通过比较签名的汉明距离判断两份文档是否相似。实际操作中我用MinHash对一组十万级别的中文文章做相似度去重整套流程跑完不到半小时查出来的近似重复率通常在15%到30%之间具体数值取决于数据来源的集中程度。做模糊去重一定要设置合适的相似度阈值。阈值设太高比如0.95几乎只能去掉逐字重复的内容阈值设太低比如0.6会把同一主题不同写法的文章也误伤掉。我的习惯是先跑一轮小样本人工看一眼不同阈值下的bad case再定最终的阈值。2.2 噪声过滤网页噪音与逻辑垃圾网页采集语料最大的特点就是**“脏得很均匀”**。什么意思呢就是每一篇文档里都混着导航菜单、cookie提示、广告区块、相关推荐这些跟正文无关的内容。针对这类问题我的处理思路分三步走第一步结构性去噪。用解析器提取网页正文时把常见的导航区块、底部版权、评论列表那部分内容直接丢弃。对于用爬虫抓到的HTML源码优先用正文提取算法而不是简单地去掉标签之后全保留否则页头页尾的内容全都混进正文里了。第二步正则规则过滤。这一步处理的是“看起来像正文但根本不是正文”的垃圾。我总结过几条高频规则全行都是符号或特殊字符的行直接删重复标点超过连续10个以上的行直接删不包含任何中文字符或英文单词的行直接删长度小于20个字符的“短碎片”直接删。第三步模型辅助过滤。规则过滤只能处理“看得见的脏”对于“看起来很像样但内容完全无逻辑”的文本规则就无能为力了。这时候可以训练一个轻量级的文本质量分类器用少量人工标注样本把语料分成“高质量”“低质量”两类然后用这个分类器跑全量数据。这里要提醒一点不想一上来就训练模型的团队可以先用一个稍笨但有效的办法——困惑度过滤。用一个小型的语言模型计算每篇文档的困惑度困惑度越高说明这段文本越不符合自然语言的统计规律通常就是垃圾文本或者多语言混杂文本。这个方法在项目初期出效果极快。2.3 规范化统一字符、格式与写法清洗做完“减法”之后还要做“整形”。所谓整形就是把残留下来的可用文本统一成一致的格式方便下游使用。这个环节有几个细节非常容易被忽略但影响却非常大全角与半角转换。中文语料里经常混着全角英文字母和半角标点不统一的话同一个词会被词表切成两个不同的token白白浪费词表容量。Unicode规范化。在文本处理里不同的字符序列可能在视觉上完全一致但编码不同。要做好标准化就要做Unicode NFKC规范化把兼容字符统一转换成标准形式。做数据清洗的朋友一定遇到过“看起来一样的字但搜不到”这种诡异情况多半就是编码没规范化。繁简体统一。如果你做的是通用中文语料建议明确指定语料的语言版本。混着繁体中文和简体中文不是不能训练但会让模型的输出风格不稳定。可以用OpenCC这类工具做批量转换保持全语料一致。连续空白符压缩。制表符、多个连续空格、行尾多余空格这些看起来不痛不痒但一旦进入tokenizer就会产生大量无意义token。统一用一个空格替换连续的空白字符是我每次清洗pipeline里固定的一步。2.4 隐私与合规清洗不可跳过的一环很多人做数据清洗只顾着“去脏”却忘了还要“去敏”。对于可能包含个人信息的语料例如电话号、身份证号、邮箱、详细地址一定在清洗环节做脱敏处理。我用的方案是正则匹配加实体识别双管齐下。先用正则把明显的号码、邮箱匹配出来并替换成占位符再用命名实体识别模型把剩余的地址、人名、机构名标出来人工复核。占位符的选取也有讲究不要直接用“***”建议用“[PHONE]”“[EMAIL]”这类语义明确的标记这样模型在训练时能知道这是一个被隐藏的字段而不是一段乱码。脱敏的工作量不小但绝不能省。尤其是做开源数据或者商业数据的时候一旦出了隐私问题后果非常麻烦。3. 质量评估的指标体系与量化方法3.1 文档级质量评估指标质量评估不是一个“好”或“不好”的判断题而是一组可以对比的数值指标。我在实际项目中稳定使用以下几类文档级指标文本长度分布。清洗后的语料里如果还有大量超短文本比如少于50字或超长文本都要留意。超短文本往往信息量不足超长文本可能是多篇文章拼接错误。重复率指标。这里的重复不只是文档间的重复还有文档内部的重复。有些采集器会把某段内容重试后拼接进同一篇文档就会导致文档内部出现大段一模一样的内容。计算文档内最长公共子串占全文的比例是一个很有效的内部重复检测方法。字符类别分布。统计一篇文档里中文、英文、数字、标点、符号的占比。一篇“中文语料”如果英文占比超过10%要么是双语内容要么是清洗没干净需要单独抽出来看。困惑度PPL。用语言模型给文档打分PPL越低说明文档越符合语言习惯。PPL非常高还不一定是垃圾也可能是特定领域的专有文本比如代码、数学公式但这类文本需要单独分流处理不能混在通用语料里。3.2 数据集整体统计评估单篇文档的指标看完了还要看整个数据集的“群体画像”。这一步能帮你发现很多文档级指标察觉不到的问题。信息熵与压缩率。对数据集做一次gzip压缩比较压缩前后的大小。如果压缩率特别高说明数据集内部的冗余度很高也就是说大量文本都是重复或者相似的。我之前处理过一份数据压缩前18GB压缩后只有3GB这说明里面至少有80%是重复内容清洗之后果然发现大量站点互抄。n-gram频率分析。跑一遍2-gram和3-gram的统计看高频片段是否符合预期。如果高频3-gram全是“更多内容请点击”“欢迎访问本站”这类话说明导航类噪音没有清理干净。转折点检测。按时间顺序或按来源网站维度分组统计每个组的质量指标平均值。如果某个来源的PPL异常高或者重复率异常高就需要针对性做一次补充清洗。3.3 语义层面的质量评估统计指标只能告诉你数据“长什么样”语义评估才能告诉你数据“说了什么”。对大规模语料做全量语义评估不现实一般用抽样的方式来做。抽样语义评估的核心是Embedding空间分布检查。把抽样文档编码成向量然后做聚类和可视化。高质量语料在Embedding空间里应该是分散但有一定内部结构的如果大量数据聚成一个巨大的团说明语料主题非常单一多样性不足如果数据分布成几个明显孤立的簇说明语料来源构成单一需要补充其他领域的数据。另一个常用的语义指标是相似度去重后的唯一率。对抽样文档计算两两相似度统计相似度高于阈值的文档对占比。这个值如果过高即使文档级模糊去重已经做过了也要回头检查是不是阈值太宽松。4. 实操从原始语料到高质量语料的完整流程4.1 环境准备与工具选型语料清洗工程不大但数据量绝对不会小。一台单机处理几十GB数据运气好能跑处理几个TB就非常吃力了。我的建议是起步阶段直接用单机多进程处理当数据量超过单机处理能力再迁移到分布式计算框架。工具选型方面我只说我自己验证过的方案文本处理用Python跑正则引擎快生态齐全。数据量大时首选PySpark用DataFrame API处理半结构化文本非常顺手。中文分词用jieba或者更快的pkuseg不过在清洗阶段我一般尽量少分词因为清洗阶段要保留原始文本形态分词是后面的事。编码检测用charset-normalizer处理非UTF-8编码的旧数据必备。HTML正文提取用读网页源码库我用的是json里的纯文本解析方案。4.2 清洗pipeline的实现步骤我把整个清洗流程拆成5个阶段每个阶段单独跑阶段之间落盘方便中途查看进度和排查问题。阶段一原始数据摸底。不急着清洗先对数据做一次全面扫描统计总文档数、总字节数、编码分布、来源分布、抽样看100篇文档长什么样。这一步花1小时左右能让你对接下来的清洗策略做到心里有数。阶段二格式转换与去噪。把各种格式的文件统一转成纯文本做编码统一、字符规范化、全半角转换同时把明显的网页导航文本、无效行、纯符号行删掉。阶段三去重。先跑精确去重再跑MinHash模糊去重。模糊去重这里我建议做一层“代表性文档保留”的操作对一组相似文档保留质量分数最高的一篇其他删掉而不是随机保留。阶段四过滤与清理。跑规则过滤和分类器过滤去掉低质量文本。同时做隐私脱敏。这一阶段还有个隐藏任务把明显不属于目标领域的文本挑出来单独分流。比如做通用语料的时候就把大量代码、纯英文内容、数学公式单独存放在另一个目录不直接删除。阶段五质量评估与报告生成。对清洗后的数据跑全套评估指标生成一份包含清洗前后对比的报告。这份报告要留档后面每次版本迭代都用同一套指标体系跑才能看到变化。4.3 关键步骤的参数与计算过程参数不落地说再多都是空谈。我把自己常用的参数写出来给大家参考注意这不是标准答案具体数值一定要根据你自己的数据来调。MinHash实现上我习惯用datasketch库。签名数量num_perm设为128threshold设为0.8。意思是两篇文档的MinHash签名相似度超过0.8就判为重复。这个阈值对新闻类数据比较合适如果是社交评论类数据文本本身短相似度普遍偏高阈值可以上调到0.85以上否则误删率很高。困惑度过滤的阈值设定我是结合分布图来定的。先跑1万篇抽样文档的PPL画出分布直方图一般会是“主体集中 右侧长尾”的形状。PPL超过主体集中区间的3倍左右就可以定义为低质量文本。我这个数据集里的PPL主体在80到200之间所以300以上的基本一删一个准。n-gram重复过滤我用的是2-gram和3-gram组合。对每篇文档计算重复的n-gram占全部n-gram的比例超过10%的文档就标记为内部重复过高。设置这个阈值的时候建议先人工看20篇被标记的文档如果大多数看起来确实重复严重说明阈值合理如果看着都挺正常说明阈值太严了。4.4 一次完整实操的量化结果拿我之前处理过的一个项目数据来说原始语料约800GB预处理后得到纯文本语料620GB最后清洗完毕剩下370GB整体保留率大约60%。这个保留率看起来有点低但拆开看每一项都有原因页码导航和模板噪音清掉约80GB跨站点重复内容去掉约140GB低质量短碎片和无语义文本去掉约30GB。剩下的370GB里我抽样了2000篇文档做人工评测可读性达标率约97%主题分布覆盖了新闻、百科、论坛、书籍、评论这几个大类均匀度比清洗前好了很多。这个结果并不惊艳但很真实。数据清洗不是要把数据量“洗得越大越好”而是要把“有效数据”保留下来的比例尽量提高。5. 常见问题与排查技巧实录5.1 清洗后台词表爆炸怎么办这个词表爆炸不是模型训练阶段的问题而是清洗阶段就出现了你发现清洗后的数据里token种类数量比预期多了好几万。排查思路先看是不是繁体、简体混存导致同一个词被拆成多个形态再看是不是全角半角没有统一最后看是不是有大量非目标语言的字符混入。我遇到过最离谱的一次词表膨胀的源头是大量被爬虫收录的“乱码标题”——网页编码识别错误导致正文变成了类似“é”的拉丁扩展字符串中文完全丢失。后来在清洗流程里加了一条规则检测到非预期字符占比超过5%的文档直接丢弃这类问题再没出现过。5.2 为什么要保留一些“低质量”文本清洗不是把质量层次全部抹平。早期我做清洗过于激进把所有PPL偏高的文本全部删掉结果模型训练出来之后虽然语言很流畅但常识性知识严重匮乏因为很多包含冷门知识和特殊表述的长尾文本恰恰PPL偏高。后来我调整了策略按质量分层而不是一刀切。高PPL文本不是直接删除而是降权处理。预训练阶段高PPL数据按20%的比例混合进去微调阶段只用高置信度数据。这样既保证了语料的整体质量又没有丢失长尾信息。5.3 质量评估报告要包含什么质量评估报告我一般固定包含四个部分第一是清洗前后数据量对比和保留率第二是各项质量指标的分布图包括PPL、重复率、文档长度、字符类别占比第三是抽样文档的人工评测结果至少200篇第四是遗留问题清单比如哪些类型的数据依然质量不高、后续打算怎么处理。这份报告不是写给领导看的是写给下一个阶段的自己看的。没有这份报告隔两周再回来做数据迭代你根本想不起来上次清洗到什么程度、遗留了什么问题。5.4 清洗流程设计的三个避坑心得第一个坑清洗阶段太早分词。分词会把文本切碎切碎之后就很难做精确的文档级去重了。清洗阶段保持原始文本形态所有分词留到下游做。第二个坑正则规则写得太贪。宁可规则少一点、宽松一点也不要让规则把一篇正常的文章拦腰截断。我的习惯是每加一条过滤规则就抽样看50篇被这条规则删掉的文本确认没有误杀再上线。第三个坑没有版本控制。清洗流程不是跑一次就完了数据更新后要重跑。我建议把清洗代码、参数、输入输出路径全部纳入版本管理每次清洗跑完生成完整报告这样每一版数据集的来源都可追溯。6. 写在最后的个人体会如果只能从这篇文章带走一件事我希望是语料清洗与质量评估不是一份可以一劳永逸的体力活而是一个需要持续迭代、不断复盘的技术系统。我自己做数据工程这几年的真实体会是最耗时的地方从来不是“把代码跑通”而是“把质量看明白”。当你看到同样的清洗流程在不同批次的数据上跑出完全不同的保留率、完全不同的PPL分布时你就会明白每个数据集都有自己的脾气拿一套固定流程到处套早晚要翻车。最后再分享一个我一直在用的小技巧每做完一轮清洗就从数据里随机抽200到500条文本存成一个“质检样本集”隔几天再人工读一遍分别记录第一次读和第二次读的感受。很多时候质量问题第一次读看不出来过几天再回看那些“哪里不对劲但说不上来”的感觉会变得非常清晰。这些直觉配合量化指标一起用才是语料清洗里最可靠的判断依据。