ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型数据清洗全攻略:质量过滤、去重与Pandas实操

大模型数据清洗全攻略:质量过滤、去重与Pandas实操 做模型的人常说一句话模型能力七分靠数据三分靠算法。这个说法在大模型时代尤其贴切因为LLM的训练本质就是“用海量文本压缩人类知识”如果喂进去的文本本身是垃圾、重复、带毒的那模型学出来的东西要么是废话要么是噪音甚至会在推理时输出有害内容。过去我做传统机器学习项目时数据清洗是EDA的一部分按列做缺失值填充、异常值剔除就够了但到了大模型场景清洗的对象变成了“文档”而不是“字段”数据量也从几千行变成几亿条文本思路和方法都得换一轮。这篇文章是“大模型数据清洗”系列的基本篇第一期面向两类读者一类是正在准备指令微调或继续预训练数据集的同学想搞清楚该用什么标准筛数据另一类是已经跑过一版baseline但发现模型效果不对怀疑是数据有问题的工程师。我会从清洗目标、核心维度、pandas落地实操、常见坑位四个角度把地基打牢后续几期再分别深入去重算法、质量打分、多语言数据配比等进阶话题。1. 先想清楚再动手——大模型数据清洗的整体设计思路1.1 清洗目标先于清洗手段我见过不少人在拿到数据后第一反应就是写正则、调pandas折腾了半天也不知道自己在清洗什么。大模型数据清洗和传统数据清洗最大的区别在于传统项目以“可用性”为目标把脏数据变成能进模型的表格LLM项目以“学习质量”为目标清洗标准直接决定模型的知识密度和生成质量。所以第一步不是写代码而是确认你的数据将被用于哪个环节预训练、指令微调、还是偏好对齐。三种场景对数据的要求差异很明显。预训练语料追求“大规模多样性”以RedPajama、C4这类公开数据集为参考清洗重点是去重、去噪、控制语种分布指令微调数据追求“指令-回答”配对质量和格式一致性清洗重点是去格式混乱、去答案噪声、统一提示词模板偏好数据则更看重对比对的合理性清洗时往往需要做内容质量的人工抽检和边界过滤。目标不同管道设计就不同投入也不同。换句话说不要一开始就想着“我要清洗这100G语料”先回答三个问题这些数据要学什么能力当前数据里哪些部分是违背这个目标的清洗后的验收标准是什么把这三个问题写在文档开头整个清洗过程才有评价依据。1.2 管道设计一次性脚本思路要丢掉数据清洗在传统业务里常常是“跑一次脚本出结果”的临时任务但在大模型场景里必须改成管道化、可追溯、可复跑的设计。原因很简单数据量大、清洗规则多你不可能检查每一条结果只能靠每一阶段的统计指标来确认规则是否生效而且规则需要迭代今天发现误删太严重改了规则就得重新跑全链路如果没有管道设计返工成本高到你怀疑人生。我建议把一个完整的清洗流程拆成如下几个阶段探查与样本标注抽样看数据长什么样标出明显问题类型乱码、脏HTML、无意义重复、敏感词、语言混杂。规则清洗统一编码、去HTML标签、去URL/邮箱等噪声、过滤超短超长文本。质量过滤语种识别、困惑度过滤、字符重复率过滤、符号占比过滤。去重精确去重配合MinHash近似去重去除训练集内重复和与下游测试集重叠的样本。隐私与合规过滤识别手机号、身份证、地址等个人信息做脱敏或剔除。统计报告输出每阶段记录输入输出量、去掉比例、抽样保留样本形成清洗日志。每个阶段都尽量输出独立的中间结果比如清洗后的parquet分片、过滤统计JSON、抽样校验文本。这样当你发现最后一批数据有问题时能快速定位是哪个阶段造成的。这套思路比任何具体代码都值钱因为LLM项目的清洗不是一个“做完就结束”的事而是一个会随业务演进不断迭代的过程。2. 大模型数据清洗绕不开的四个核心维度2.1 质量过滤规则启发式与模型打分并用质量过滤是清洗管道里最耗时间的一环也是影响模型效果的关键。业界常用的思路分成两层第一层是规则启发式第二层是用小型模型打分排序。规则启发式的核心是设置合理的阈值组合把明显低质量的文本丢掉。常用的特征包括文本长度、特殊字符占比、重复n-gram比例、句子长度的方差、HTML标签残留数量等。比如C4数据集的过滤规则里有一条去重率去掉连续重复行太低或者标点缺失率过高就连续丢弃对应的文档。实际操作中你可以用pandas计算一列“有效文本长度”、一列“URL链接数量”、一列“乱码字符占比”然后按各自阈值做布尔过滤。模型打分思路更适合中等以上规模的团队。比如用fastText的语言识别模型判断文本语种再用KenLM语言模型算困惑度perplexity困惑度偏高的文本大概率是乱拼词、机器翻译痕迹或无关广告内容可以直接丢弃。如果你手头有分类模型也可以把“文本是否像网页正文”“文本是否包含完整语义”这类的评分器并进管道。规则和模型结合起来效果远比单独用规则好。需要特别注意的是质量过滤最容易犯的错是“规则过强导致数据量骤降”。我看到有人用“文本必须大于512字符”过滤微调数据结果把大量高质量的短问答全丢了。正确做法是先看长度分布再定阈值时刻想到“我筛掉的数据里有多少是误杀”。2.2 去重精确去重只是开始MinHash让你处理百万级文本去重是大模型预训练数据清洗里最硬核的环节。重复数据会让模型产生记忆偏差导致生成内容陷入重复循环还会白白浪费算力。去重分两个层次精确去重和近似去重。精确去重最简单pandas一行drop_duplicates(subset[text])就能做也可以用哈希值快速去重。但现实里的重复往往不是完全相同的字符串而是“几乎相同”的文本——可能改了标点、加了空格、删了几个字。这种重复精确去重根本发现不了必须做模糊去重。近似去重的主流方案是MinHash配合LSH局部敏感哈希。核心思路是把一条文本的字符n-gram集合转成MinHash签名再用LSH把签名相近的文本分进同一个桶桶内再精确比较。实际操作可以用datasketch这个库MinHashLSH处理几百万条文本没什么压力。签名长度和n-gram大小会影响召回率我一般对中文语料取n5英文取n7签名维数128或256相似度阈值设到0.7~0.8。中文比英文更难做近似去重因为中文没有天然分词边界5-gram已经是比较保守的选择。去重这件事别只盯着训练集内部测试集和训练集之间的去重同样重要。很多同学在训练时觉得很正常一到评测时发现效果虚高一查原因才发现评测数据早就出现在训练语料里了。这种泄漏是所有“刷分”现象里最隐蔽也最伤人的一种必须提前在管道里加一层交叉去重逻辑。2.3 隐私与个人信息过滤不可省略的底线操作个人信息过滤在LLM项目里具有合规意义处理目标比传统项目更宽不仅包含结构化表格里的“手机号列”还包括模型文本中出现的人名、机构、地址、电话、邮箱等。这类内容一旦进了预训练语料模型可能记住并在生成时输出带来隐私风险。基本做法先用正则把显式的手机号、邮箱、身份证号、银行卡号等做模式匹配。移动号码段现在变化快正则别写太死1[3-9]\d{9}这种模式对国内手机号基本够用。邮箱的正则简单但容易误伤“连续字符域名”的组合在中英文混合语料里误报率不低建议多保留一条“脱敏为邮箱占位符”的策略而不是直接删除整条文档。地址和人名的识别就要上实体识别模型了中文可以用LAC或者HanLP这类工具英文可以直接用spaCy的Ner组件。性能上这种模型跑百万级文本会偏慢建议并行化处理或者只对预处理后的高价值样本做实体识别。识别出的实体可以做替换脱敏也可以直接把包含实体的文档剔除具体看数据用途。不管怎么做我都建议在隐私过滤后保存一份脱敏日志既能排查误报也能作为合规审计依据。2.4 数据配比与格式规整别让清洗完的数据还处在“原始森林”里格式规整是清洗管道中最不起眼却最容易出问题的一项。网页爬下来的语料里标题、正文、列表、导航信息混在一起如果不统一结构模型学到的格式规律就是“网页噪声”。业界通用做法是先把原始抓取数据转换成统一的JSONL格式每行一条文档字段至少包含text、source、url、timestamp方便后续按来源和时间做下钻分析。配比调控是结构化数据之外的另一层问题。中文社区里大家手头最常见的是中英文混合的网页文本如果直接混合训练模型会严重偏向英文甚至其他语言因为英文语料量级远超中文。清洗时最好把语种分布统计出来按目标比例过采样或下采样。比如预训练阶段中文数据目标占比30%你手里英文语料占90%就得做亚采样或者用额外的中文语料来补偿。这类配比决策虽然不属于“清洗”本身但在清洗报告里必须留一个章节专门记录否则后续改动分布时将无从下手。3. 用pandas快速落地一套基础清洗流程3.1 环境准备与数据探查基础篇先不用上Sparkpandas配合Python标准库足够处理千万级以下的中小语料。环境准备很简单Python 3.10以上安装pandas、numpy、datasketch、pyarrow中文处理再加jieba语言识别用fasttext或者更轻量的langdetect。拿到数据源后先抽样探查我习惯分三步走用pd.read_json(path, linesTrue)读入前5000条样本直接print出来看内容形态。用df.describe()和自定义函数统计文本长度分布、空值比例、重复比例。手动抽20~50条记录你看到的明显问题乱码、广告、不完整句子、重复段落等。探查这一步别省。清洗规则的80%来自样本观察只有20%来自公开数据集的处理经验。我见过不少团队直接在公开清洗规则上复制粘贴结果自己的行业语料被规则误删三分之一最后还得慢慢调阈值。3.2 基础清洗实操编码统一、字段规整、噪声移除先处理编码问题。现实中拿到的很多语料是GBK或GB18030编码统一转成UTF-8后再进pandas。如果读取时就报编码错误用errorsignore不是好选择它会静默删除非法字节导致数据残缺。建议分文件读先二进制检查编码再转换或者用第三方库charset-normalizer批量判定。字段规整的重点是清理不可见字符和多余空白。可以用如下代码对text列做标准化import pandas as pd import re def clean_text(s: str) - str: if not isinstance(s, str): return # 统一换行和制表符 s s.replace(\r, \n).replace(\t, ) # 去掉零宽字符和非法控制字符 s re.sub(r[\u0000-\u0008\u000b\u000c\u000e-\u001f\ufeff], , s) # 合并连续空白 s re.sub(r[ \t], , s) # 去除多余空行 s re.sub(r\n{3,}, \n\n, s) return s.strip() df[text_clean] df[text].apply(clean_text)这段代码看起来简单但很多坑就藏在“看起来简单”里。比如\ufeff是BOM头Excel导出数据经常带零宽字符在文本里肉眼看不见却会让模型训练时产生无效token连续空行看似无关紧要但会干扰模型对段落结构的理解。跑完基础清洗后重新统计text_clean的空值比例和长度分布如果长度缩水严重说明原始文本有大量纯空格或控制字符你其实是在清理一些“根本不算文本”的样本。3.3 质量过滤长度、语言、符号占比三板斧基础清洗完成后进入质量过滤。我把最简单的过滤规则按优先级列出来空值过滤df df[df[text_clean].str.len() 0]。最短长度过滤预训练语料一般过滤掉小于100字符的短文微调数据则依任务而定不是越短越差。最长长度过滤防止单条文本过长导致训练时截断产生过多碎片一般保留20000字符以内的文档。符号占比过滤计算非汉字、非字母、非数字的字符比例超过30%的文本大概率是乱码或代码噪声。重复段落过滤把文本按空行切成段落统计重复段落的占比高比例说明是“内容农场”类站点。实际操作时我会把这些规则统一写进一个函数返回布尔掩码def quality_filter(s: str) - bool: if len(s) 100 or len(s) 20000: return False # 计算有效字符比例 total len(s) useful len(re.findall(r[\u4e00-\u9fa5a-zA-Z0-9], s)) if useful / total 0.5: return False # 段落重复率检查 paras [p for p in s.split(\n) if p.strip()] if len(paras) 3: dup max(paras.count(p) for p in paras) if dup / len(paras) 0.5: return False return True df df[df[text_clean].apply(quality_filter)]这里有一个容易被轻视的点中文汉字占文本比例的计算。用[\u4e00-\u9fa5a-zA-Z0-9]这个正则对中英文混合文本很好用但如果你有代码、数学公式类语料它会误杀大量样本。所以质量过滤规则必须按数据来源分组设定比如网页文章类使用中文比例代码类使用括号和缩进特征而不是全网共用一组阈值。3.4 精确去重与MinHash近似去重实战基础篇里去重分两步走。第一步精确去重df df.drop_duplicates(subset[text_clean], keepfirst)第二步对精确去重后的数据做MinHash近似去重。用datasketch实现非常简单from datasketch import MinHash, MinHashLSH import re def ngrams(text, n5): text re.sub(r\s, , text) return {text[i:in] for i in range(len(text)-n1)} def get_minhash(text, num_perm128): m MinHash(num_permnum_perm) for g in ngrams(text): m.update(g.encode(utf-8)) return m lsh MinHashLSH(threshold0.7, num_perm128) for idx, row in df.iterrows(): m get_minhash(row[text_clean]) lsh.insert(str(idx), m)插入完后LSH会把近似文本分到同一桶中再在桶内做两两相似度确认。这里要提醒几个实际工程问题第一插入百万条文本时内存会涨得很快建议分批插入第二Python的for循环在百万级别上非常慢建议把itertuples或parallel用起来第三threshold不要设太低不然误判率很惊人我实测0.7阈值在中文语料上的误判率还可以接受0.5以下基本就全是误判了。另外精确去重前记得先给文本做一次“指纹归一化”比如去掉所有标点、统一大小写、压缩空白后再算哈希这样很多“伪差异”文本会被识别为重复。这一步通常在基础清洗阶段完成不然后面做近似去重时计算量大几十倍。3.5 清洗报告与验收标准清洗报告是整条管道里最容易偷懒却最有价值的部分。每经过一个清洗阶段我要求管道自动输出一份JSON或Markdown记录当前阶段的样本量、过滤量和过滤原因占比。最终清洗完成后按来源域名统计各来源的保留率。这个报告的妙处在于它会暴露数据采集阶段的失衡。比如说某个网站来源的保留率只有5%而这个网站却贡献了原始数据的60%那你就要反思是不是爬虫策略出了问题而不是清洗规则有问题。验收标准方面我一般做三个检查一是抽样20条清洗后的文本人工确认没有残留的HTML标签、导航菜单、广告文本二是跑一个快速的语言识别确认目标语言占比符合配比预期三是做一个“测试集重叠”检查把测试集和训练集做一次MinHash交叉去重确认没有泄漏。这三个检查通过后数据才能称为“可训练状态”。4. 踩坑记录与常见问题速查4.1 编码与读取问题有些坑不会报错但会让结果悄悄变错最隐蔽的编码问题是“读取成功但内容全乱”。有些文件明明是GBK但恰好每个字节都能被UTF-8解码结果吐出来一串乱码还不报错。碰到这种情况建议在探查阶段直接算一下乱码字符比例例如检测是否包含大量Unicode替换符\ufffd或者汉字比例是否异常低。别迷信读取器的编码检测它面对短的标题可能猜对面对长文档一样会翻车。另一个低级错误是把CSV直接当JSON读或者把JSONL当作普通文本处理。大模型语料最常见格式是JSONL按行读取没有问题但JSONL文件往往会很长pandas直接read_json默认会尝试加载整个文件内存不够就崩。建议加chunksize参数分批读取或者用read_json(path, linesTrue, chunksize10000)迭代处理。我踩过最大的坑是把一个12G的JSONL一次性加载服务器直接OOM后来改成流式处理后稳如老狗。4.2 正则规则的误杀与漏杀正则看似简单实际是清洗管道里最需要反复调的地方。一个经典误杀用[\u4e00-\u9fa5]判定中文结果把“C”“MacBook”“iPhone”里的英文字母全当成“有效中文”之外的字符进一步被质量过滤误杀。另一个经典漏杀用“手机号正则”去匹配隐私信息时漏了区号或分机号导致部分样本带着手机号混入训练集。正则规则我当时的原则是记录每条规则在样本集上的命中率和误杀率。用2000条人工标注样本跑一个小评估比凭感觉调规则可靠得多。比如隐私过滤规则你可以手工构造200条带手机号、邮箱的样本和200条“看起来很像但不是隐私”的文本跑一遍正则算精确率和召回率。大模型训练语料对“漏杀”零容忍所以这类规则宁可误杀也不要漏杀尤其是涉及个人信息的字段误删一条文本的代价远低于漏掉一条。4.3 测试集泄漏问题交叉去重是很多人前期忽略的环节。常见现象明明模型没有记忆能力但评测分数奇高或者把训练数据切分成train/val时直接按行随机切分导致同一文档的不同副本出现在两个集合中。这个问题在“网页采集语料”里尤其容易出现——同一篇文章被不同网站转载内容相似但URL不同。解决办法是对训练集、验证集、测试集都做全局MinHash签名然后做两两集合的相似度比对。阈值设在0.7附近一旦发现相似样本跨集合出现就把较新的样本从训练集移除。这步做得好你后面调参时得到的每一个分数都是可信的否则你会在模型效果上浪费大量时间而浑然不知。4.4 清洗速度和计算资源平衡大模型数据清洗最容易被低估的是计算资源。单机pandas处理几万条数据毫无压力一旦到千万条级别很多操作会慢到你怀疑人生。我当时碰到过一个真实案例用pandas跑了一个5-gram正则替换在100万条样本上跑了快三小时还没完原因就是正则引擎在长文本上的回溯爆炸。解决方案很简单——先把文本裁剪到固定长度再做正则或者改用re.compile预编译模式并用regex库替代标准库的re性能提升非常明显。如果数据量到了亿级pandas就扛不住了这时候就得换Spark或者Dask。但我不建议一开始就上Spark因为分布式调度的调试成本远高于单机。更合理的路径是先在小样本上把规则打磨好再平滑迁移到大数据引擎。规则逻辑不变变的只是执行引擎。4.5 常见问题速查表以下是我在实操中经常遇到的问题及处理办法整理成速查表方便大家直接对照排查常见问题可能原因排查方法推荐处理读取文件后大量乱码编码判定错误或混合编码用工具检测实际编码抽样打印前200字符统一转UTF-8跳过无法识别的文件文本长度过滤后数据量骤减阈值设得过于激进画长度分布直方图观察峰值按分布分位数设定阈值去重后数据量下降超预期同源转载文本过多或归一化过度统计各域名保留率按来源分组保留不同篇目MinHash误判率高threshold过低或n-gram太小抽样人工查看被去重样本调整threshold到0.7以上模型生成重复内容训练语料重复文本残留检查精确去重后的重复比例补充近似去重加强指纹归一化文档出现网页残留标签HTML标签清除不彻底搜索div、a href等模式引入HTML解析器如BeautifulSoup剥离忘记处理测试集泄漏只做了训练集内部去重检查评测分数是否异常虚高对全部数据集做交叉MinHash比对这个速查表我贴在工位上很久了基本每次清洗管道报问题都能在这张表里找到对应的影子。建议大家也建立自己的“清洗异常案例库”每个坑记一条时间久了会发现很多问题其实是可以提前预判的。做了这么久的大模型数据清洗我最大的体会是数据管道的价值不是某一条规则多精妙而是它能把“数据质量”变成一个可度量、可追踪、可回归的对象。每清洗一版数据报告的指标都应该能回答三个问题这版数据和上一版比发生了什么变化哪些规则带来了预期收益哪些误伤了数据下一版数据采集应该重点补什么如果你的管道能稳定回答这些问题那数据质量问题就已经解决了一大半。最后分享一个小技巧在清洗管道里务必把“人工抽样校验”固化成一个自动检查单元每清洗1万条自动抽20条存到一个review/目录定期快速翻一遍。这个动作看起来不起眼却是对抗“管道结果看着很合理、实际不可用”的最有效手段。数据清洗从来不是一次性的流水线操作它更像做菜时的“试吃环节”——流程跑得再顺最终还是要靠舌头确认味道对了才敢端上桌。
返回列表