2026/10/3 3:15:57

大模型数据清洗实战:pandas六步流程与质量过滤

大模型数据清洗实战:pandas六步流程与质量过滤 上周一个做算法岗的朋友找我吐槽说他准备微调一个7B参数的模型辛辛苦苦从网上爬了几百万条数据结果一跑训练Loss直接飘了怎么调学习率都救不回来。我问他数据清洗做了没他说做了去重了。再追问编码统一没、HTML标签清没清、短文本过滤没、敏感信息脱敏没他一脸茫然。这个场景太典型了——很多人一上来就研究模型结构、调参技巧却忘了大模型项目里真正决定上限的恰恰是那些看起来最不起眼的数据清洗工作。这篇就聊大模型场景下的数据清洗属于「基本篇」先把最核心的思路、流程、工具和坑讲清楚。内容主要面向两类人一类是准备做大模型微调或RAG应用开发的工程师另一类是以前做传统数据处理、想转大模型方向的同学。看完你至少能搞清楚大模型数据清洗和普通BI清洗有什么区别、基本流程怎么设计、用Python的pandas怎么落地一套可复用的清洗管线以及哪些坑是我真金白银踩出来的。1. 大模型数据清洗到底洗什么1.1 清洗在整条链路里的位置先摆一张完整链路图在脑子里数据采集 - 数据清洗 - 数据标注 - 格式转换 - 训练/微调 - 评测 - 部署上线。很多教程喜欢从模型结构讲起但我个人的经验是大模型项目的瓶颈往往不在模型而在数据和算力之间那段无人区。数据清洗就是这段无人区里最耗时的环节。传统软件工程讲究Garbage in, garbage out到了大模型这里这个原则被放大了一个量级。一个小模型过拟合可能只是准确率掉几个点但大模型如果在海量低质量数据上训练学到的就不是知识而是噪声模式比如重复的措辞、残缺的句子、甚至偏见和有害内容。更麻烦的是这种污染很难在事后通过微调完全修正因为模型参数已经把那些模式记住了。数据清洗要解决的核心问题可以概括成四个字去伪存真。伪包括重复、噪声、错误、冗余、有害内容真就是能让模型学到通用规律的高质量文本。整个过程不是简单跑一个脚本就完事而是需要一套可追踪、可评估、可迭代的工程流程。1.2 大模型清洗和传统数据处理差在哪如果你以前做过数据分析、数据仓库那一套刚接触大模型数据清洗时容易犯一个错觉这活我熟pandas清洗嘛。其实差别非常大我列几个关键差异供参考目标不同。传统清洗面向统计报表追求字段完整性、一致性、准确性大模型清洗面向语料质量追求内容的多样性、连贯性、信息密度和安全性。规模不同。传统清洗处理的是几千到几百万行结构化表格大模型语料动辄几十GB甚至TB级别这决定了工具链和算法选型完全不同。质量维度不同。传统清洗主要看空值、异常值、格式错误大模型清洗还要看困惑度Perplexity、重复率、语言类型、毒性、隐私泄露等等很多指标传统数据处理里根本不存在。标注环节不同。大模型微调数据通常需要人工或模型辅助标注标注质量直接影响指令遵循能力这是传统清洗流程里没有的环节。拿一个具体例子说你爬了一批网页正文里面可能有导航菜单、广告、版权声明、页脚传统清洗可能只去一下HTML标签就结束了但大模型语料清洗要求你把广告字段导航文本这类低信息密度内容单独识别并剔除否则模型会学到免费领取点击咨询这种噪声模式。1.3 脏数据的主要来源和类型我先帮你建立一张敌人清单清洗说白了就是跟下面这些家伙搏斗重复数据精确重复、近似重复改了几个字、加了空格、换了标点会让模型产生记忆偏差出现过拟合。格式混乱全半角混用、编码不一致UTF-8和GBK乱码、多余换行和空格、Markdown和HTML混杂。低质量文本纯标点、无意义字符、过短片段、胡言乱语、乱码残片这类数据拉低模型的表达流畅度。内容污染广告、导航、版权声明、日志信息、模板化文本拉低语料的信息密度。语言混杂目标是中文模型结果混入大量英文、日文、代码片段、拼音缩写破坏模型的语言一致性。隐私与安全手机号、身份证号、邮箱、住址、账号密码等敏感信息不洗要出合规事故。有害内容暴力、色情、仇恨言论、诱导性内容这部分不光影响模型安全还涉及合规红线。一个大致的经验比例从网上爬的原始数据清洗后能留下40%-70%就不错了质量差的源可能只剩20%。这不是夸张很多公开数据集发布时的清洗后版本原始噪音之大远超想象。2. 动手之前清洗方案怎么搭2.1 先想清楚数据给谁用这是我最想强调的一点不同的下游任务清洗策略天差地别。别上来就套模板。预训练数据追求的是大规模、多样性、高信息密度清洗重点是去重尤其是近似去重和低质量过滤因为规模太大没法逐条质检。微调数据追求的是指令-回答对的质量和一致性清洗重点在格式标准化、答案验证、指令多样性而且要保留一些难例不能一个劲过滤。RAG知识库数据追求的是片段切分的合理性和检索友好性清洗重点在去噪、结构拆分、元数据补充长度太短或太长的文档都不是好事。举个我踩过的例子。之前做RAG项目我把一个PDF解析出来的文本做了激进的去重结果把它原有的章节结构打乱了每个切分的chunk都缺头少尾召回率直接崩了。后来才明白不同场景的数据形态要求不一样清洗之前必须做用户故事。建议你在清洗前写下一段话这些数据将用于____任务期望模型具备____能力当前数据的最大问题是____。写不清楚这三个空后面所有清洗决策都会变形。2.2 工具选型从pandas到Spark工具选型取决于数据规模和计算资源我见过很多过度设计的方案也见过不少一把pandas走天下翻车的案例给你一个实用的选型参考数据规模推荐工具适用场景说明百万行以内pandas 自定义Python脚本微调数据集、RAG语料灵活、上手快适合单机处理千万到亿级Dask / Modin / Polars预训练语料初筛在单机多核基础上做并行兼容pandas API十亿级以上Spark / Ray / MapReduce大规模预训练语料分布式处理需要集群环境复杂语义清洗大模型API / 本地部署模型低质量文本分类、PII识别准确率高但成本高适合精筛阶段基本篇里我会重点讲pandas路线因为大部分读者第一次做大模型项目数据量还没到必须上Spark的程度。但你要有这个概念如果数据超过单机内存pandas的read_csv直接OOM该考虑Dask或Spark了。另外一个值得提的工具是datasets库Hugging Face出的它对大模型语料处理很友好支持内存映射、流式读取可以和pandas无缝互转。基本篇的清洗流程我一般用pandas完成核心逻辑再用datasets管理清洗后的数据集这样进入训练流程时可以直接用load_dataset加载。2.3 流水线设计原则可追溯、可回滚、可复现数据清洗不是一次性的小脚本它本质上是一个pipeline工程。我见过太多人直接把原始数据覆盖了清洗到第三步发现第二步规则写错了想回滚发现数据没了只能重新爬。那感觉就像数据库没做备份一样酸爽。你需要注意三条基本原则可追溯每一步清洗都要能回答两个问题——处理了多少条删掉了多少条删掉的为什么删所以必须在每一阶段留下统计日志哪怕是简单打印几行指标。可回滚原始数据永远保留一份只读副本清洗过程的中间产物按版本命名例如data_dedup_v1.parquet、data_filtered_v2.parquet。不要原地修改永远生成新文件。可复现清洗脚本里尽量不要写魔法数字比如min_len50所有阈值用配置项管理最好用YAML或常量文件统一维护方便后续调整参数重跑。在实际项目中我一般这样组织目录data/ raw/ # 原始数据只读 interim/ # 中间产物按版本存 processed/ # 最终清洗后数据用于训练 scripts/ clean/ 01_explore.py 02_normalize.py 03_dedup.py 04_filter.py 05_desensitize.py config/ clean_config.yaml这样做的好处是每跑一轮实验配置、脚本、数据版本都是对齐的。模型效果不好需要回溯时能快速定位是清洗策略的问题还是模型结构的问题而不是两眼一抹黑。3. 基本篇实操六步清洗法附pandas代码下面进入重点我总结了一套适合入门和大多数微调场景的六步清洗法探查 - 标准化 - 去重 - 噪声过滤 - 质量过滤 - 隐私清洗。最后加一步抽样质检和划分姑且算2/3步但核心思路就是这六件事。每一步我都会给出直接能用的代码和参数建议。3.1 第零步数据探查先别急着清洗很多人洗完数据才发现字段读错了、编码选错了、或者某列全是空值返工成本极高。所以我强烈建议把数据探查当成正式一步哪怕只是花五分钟。import pandas as pd df pd.read_csv(raw_data.csv, nrows5000) # 先读前5000行探路 print(df.shape) print(df.columns.tolist()) print(df.dtypes) print(df.head(3))然后对文本列做基础统计df[text_len] df[text].astype(str).str.len() print(df[text_len].describe())这里有几个判断技巧如果min是0说明有空值如果max异常大比如几十万字可能混入了拼接错误如果median远小于mean说明存在少量超长文本拉高了均值后续要考虑截断或拆分。describe()的分位数信息能帮你确定后续长度过滤的合理阈值这一步偷懒后面必踩坑。还有一个必查项是编码。用Python读取文件时常见错误是UnicodeDecodeError或者在read_csv时用sep不对导致一列数据变成一堆列。我推荐用chardet或charset-normalizer检测编码from charset_normalizer import from_path result from_path(raw_data.csv).best() print(result.encoding) # 例如 utf-8 / gb18030探查阶段不是写论文核心目标只有一个知道你的数据长什么样、有什么明显的病。探查清楚后再动手效率会高很多。3.2 第一步格式标准化这一步的目标是让所有文本形态统一为后面的去重和过滤打好基础。我在格式标准化时一般按顺序处理四类问题编码统一全部转成UTF-8这是大模型训练和后续处理最省心的编码。全半角转换把全角字母数字、标点转成半角中文标点保留中文语境下全角标点反而是规范。换行和空格规范化连续的换行压缩成单换行行首行尾空格去掉制表符转空格。不可见字符清理去零宽空格、控制字符等这类字符肉眼看不见但很影响分词和模型生成质量。代码示例import re import unicodedata def normalize_text(text): if not isinstance(text, str): return # 去零宽字符和特殊控制字符 text re.sub(r[\u200b\u200c\u200d\u2060\ufeff], , text) # 全角转半角字母数字部分 text unicodedata.normalize(NFKC, text) # 统一换行先替换所有换行变体为 \n text re.sub(r\r\n?, \n, text) # 连续空行压缩为单个空行 text re.sub(r\n{2,}, \n, text) # 行首行尾空格 text re.sub(r[ \t], , text) # 去掉每行首尾空格 text \n.join(line.strip() for line in text.split(\n)) return text.strip() df[text] df[text].apply(normalize_text)这里有一个细节值得说明为什么全半角用NFKC而不是简单replace因为NFKC是Unicode的兼容分解与重组能把全角字母转成A、全角数字转成1、兼容字符比如带圈数字①也一并处理比手写正则优雅很多。但注意全角中文标点如。会保留因为NFKC默认不会把中文标点转成英文标点这对中文语料是合理的。如果你确实需要中文标点转英文标点比如做代码注释类语料才需要额外映射。标准化阶段不要做的太激进。比如不要把所有英文标点都转成中文标点也不要删除所有标点——这会伤害语料的自然度。我见过有人为了干净把所有符号删掉结果模型生成能力大幅下降因为标点本身就是语言的重要组成。3.3 第二步去重去重是大模型数据清洗的重头戏。重复数据的来源包括爬虫重复抓取、不同来源转载同一篇文章、同一内容在不同时间被生成多次。重复数据对训练的伤害是隐性的模型花大量算力在记忆重复内容上导致泛化能力下降还会让评测指标虚高因为测试集里可能也混着同样的重复文本。先做精确去重这个最简单df df.drop_duplicates(subset[text], keepfirst) print(f精确去重后剩余: {len(df)} 条)但实际问题里近似重复才是大头。一篇文章在不同网站上改了几个字、插了几段广告、加了不同标题精确去重完全无能为力。处理近似重复的基本思路是把文本转成n-gram集合用Jaccard相似度或MinHash进行相似度计算再对高相似度文本做删除或保留策略。pandas里可以用textdistance或rapidfuzz做小规模模糊去重示例pip install rapidfuzzfrom rapidfuzz import fuzz from itertools import combinations def fuzzy_dedup(df, text_coltext, threshold85, sample_size50000): df df.reset_index(dropTrue) texts df[text_col].tolist() drop_indices set() # 小数据集直接两两比较大数据集建议先做MinHash分桶 for i, j in combinations(range(len(texts)), 2): if i in drop_indices or j in drop_indices: continue sim fuzz.partial_ratio(texts[i], texts[j]) if sim threshold: drop_indices.add(j) return df.drop(indexlist(drop_indices))注意这个partial_ratio的阈值要根据语料来调我一般从85开始太严高阈值保留太多近似重复太松删除过多有效信息。这里也需要提醒上面的代码只适合小数据集如果你有几十万条以上两两比对的复杂度是O(n^2)跑不动。正式项目一般用MinHash LSH做分桶只对可能相似的文本两两比对。基本篇不展开MinHash原理后续进阶篇再细讲。去重的另一个维度是质量优先保留。两条文本近似重复时不要总是保留先出现的而应该保留信息更完整、表达更流畅的那条。所以去重要结合长度、困惑度等指标做优先级排序。最简单的做法先按长度降序排序再去重这样留下的往往是更长更完整的版本。3.4 第三步噪声过滤格式标准化之后需要把一批有格式但没内容的噪声剔除。最常见的几类HTML标签、脚本、样式残留爬虫数据重灾区。URL、邮箱、电话号码等无意义串。广告、导航、版权信息等模板化文本。纯标点、纯表情、无意义重复字符。可以先用正则清理一部分明显的残留标签import re def strip_html(text): # 去掉 script 和 style 内容 text re.sub(r(script|style)[^]*.*?/\1, , text, flagsre.S | re.I) # 去掉其他HTML标签 text re.sub(r[^], , text) return text def strip_urls(text): text re.sub(rhttp[s]?://\S|www\.\S, , text) return text df[text] df[text].apply(strip_html) df[text] df[text].apply(strip_urls)然后可以做规则过滤把明显不可能是正常语料的条目直接剔除# 去掉过短文本 df df[df[text_len] 30] # 去掉标点符号占比过高的文本 def punct_ratio(text): puncts len(re.findall(r[^\w\s], text)) return puncts / max(len(text), 1) df[punct_ratio] df[text].apply(punct_ratio) df df[df[punct_ratio] 0.3] # 去掉重复字符占比过高的文本例如 啊啊啊啊啊啊啊 def max_repeat_ratio(text): repeats max((len(m.group()) for m in re.finditer(r(.)\1{3,}, text)), default0) return repeats / max(len(text), 1) df[repeat_ratio] df[text].apply(max_repeat_ratio) df df[df[repeat_ratio] 0.2]噪声过滤的核心心法是宽进严出先尽量清理掉明确的格式性噪声再过滤明显的垃圾。不要一上来就用激进的正则删内容比如你不能把iPhone 15 Pro中间的空白去掉也不能把代码里的#当成噪声删掉。文本形态多样宁可少删不可错删的前提是你的任务对噪声容忍度相对高后续还有质量过滤兜底。3.5 第四步质量过滤格式清洗解决能不能看的问题质量过滤解决值不值得学的问题。这一步判断依据不是规则而是内容本身的好坏。三个常用的抓手第一是语言识别。如果你要训练中文模型需要剔除大量英文、日文、代码混杂物。可以用langdetect或fasttext的lid.176.bin模型检测语言。langdetect慢但简单几十万条数据还能接受上千万条建议用fasttext。from langdetect import detect def detect_lang(text): try: return detect(text) except Exception: return unknown df[lang] df[text].apply(detect_lang) df df[df[lang].isin([zh-cn, zh-tw, zh])]注意langdetect对短文本很不友好少于20个字经常误判。所以在做语言过滤时可以结合一个前提条件如果文本长度小于一定阈值不做语言判断或者用更宽容的规则。第二个是困惑度过滤。困惑度可以理解为模型对这段文本的惊讶程度模型预测下一个词越困难困惑度越高文本越可能不通顺。乱码、胡言乱语、机器翻译腔的文本困惑度通常很高。实践中可以用一个预训练语言模型计算困惑度比如用GPT-2或者中文的BERT模型。基本篇先提思路代码实现放进阶篇因为纯Python跑大规模困惑度计算还是比较吃算力的。第三个是信息密度与多样性判断。有人会把这类问题简化为去重和长度过滤但我更倾向于单独处理。比如同样一段新闻不同的转载源可能产生几十条近似内容单纯长度过滤去不掉需要用第3.3节的近似去重再补一刀。还有一类复读机文本整段都在重复同一句话或者同一结构可以用n-gram多样性指标TTRType-Token Ratio来量化。def ttr(text): tokens re.findall(r\w, text) if len(tokens) 2: return 0 return len(set(tokens)) / len(tokens) df[ttr] df[text].apply(ttr) df df[df[ttr] 0.3]TTR越接近1说明文本用词越丰富越接近0说明重复越严重。阈值同样需要根据语料类型调整比如技术文档的TTR天然偏高对话文本偏低。不要拿同一个阈值硬套所有类型的语料。3.6 第五步隐私与安全清洗这步在基本篇经常被忽略但恰恰是最不能省的一步。你采集的数据里很可能带着手机号、邮箱、身份证号、家庭住址甚至账号密码。这些数据一旦进了训练集模型可能会在生成时背出来轻则泄露隐私重则引发合规事故。常规做法是先用正则做PII个人身份信息检测和脱敏def desensitize(text): # 手机号简单匹配大陆11位手机号 text re.sub(r(?!\d)1[3-9]\d{9}(?!\d), [手机号], text) # 邮箱 text re.sub(r[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}, [邮箱], text) # 身份证18位 text re.sub(r(?!\d)\d{17}[\dXx](?!\d), [身份证], text) # 链接中的账号参数简单处理按需扩展 text re.sub(r(token|access_key|password)[^\s], r\1[已脱敏], text) return text df[text] df[text].apply(desensitize)只能解决初级问题。更复杂的PII比如人名、公司内部代号、特定业务ID正则匹配不到就需要用NLP模型或者规则词典来辅助识别。对于基本篇掌握正则脱敏已经能覆盖绝大多数中文语料的日常场景。脱敏比删除好因为保留文本的结构和语义模型能从中学到表达方式但学不到具体个人信息。但脱敏要小心替换后如果格式差异太大比如把邮箱替换成[邮箱]导致前后文不通反而引入噪声。这也是为什么有些团队选择直接把包含PII的整条文本删掉而不是脱敏——准确率和召回率很难兼得看你的下游需求权衡。3.7 第六步抽样质检与数据集划分清洗流程走完别急着拿去训练。先做一次人工质检这是所有步骤里最容易被跳过的也是我强烈建议你保留的。质检方法很简单从清洗后的数据里随机抽100-200条用肉眼扫一遍记录不合格率。如果不合格率超过5%说明清洗策略有问题回头调整阈值再跑一轮。sample df.sample(n200, random_state42) for i, row in sample.iterrows(): print(f--- {i} ---) print(row[text][:200])抽样时建议分层抽样比如按文本长度分为短、中、长三层每层各抽一部分。因为不同长度的文本可能各有各的问题短文本可能是碎片长文本可能有拼接错误。只抽前200条容易漏掉分布极端值。质检通过后按任务需求划分数据集。微调场景一般分成train/validation/test三份比例7:2:1或者8:1:1。这里注意一个细节划分前要先shuffle而且固定随机种子保证可复现。否则每次跑重新shuffle验证集一直在变你很难判断模型效果的提升到底来自模型改动还是数据划分变动。from sklearn.model_selection import train_test_split train_df, temp_df train_test_split(df, test_size0.2, random_state42) val_df, test_df train_test_split(temp_df, test_size0.5, random_state42) # 保存为 parquet保留中间格式 train_df.to_parquet(data/processed/train.parquet) val_df.to_parquet(data/processed/val.parquet) test_df.to_parquet(data/processed/test.parquet)如果你是做RAG知识库这一步就变成切分chunk 构建索引划分逻辑完全不同。所以我再次强调清洗流程是跟着下游任务走的别把一套流程当万能药。4. 实操中的常见坑与排查技巧4.1 编码与乱码问题字符编码是大模型数据清洗里最隐蔽的坑。最典型的是爬下来的数据混着UTF-8、GBK甚至Latin-1编码pandas一次性read_csv读进来就是一串乱码。更难受的是有些文件本身内部编码就不统一有的行是UTF-8有的行是GBK转哪边都是错。我的排查顺序是先用charset-normalizer检测整体编码再用df[text].astype(str).str.contains()找出替换字符UFFFD凡是含大量的行大概率是编码错乱先单独拎出来看。如果是混合编码文件建议用binary模式逐行检测编码再分别转码虽然麻烦但胜在准确。这个场景下不要幻想有银弹该写循环就写循环。项目里常备一个编码自检函数非常有用把常见乱码特征比如é这种UTF-8错误解码成Latin-1的特征串都列成正则在探查阶段跑一遍能提前发现很多隐藏问题。4.2 内存不足与性能问题pandas处理千万级数据时经常MemoryError这不是代码写得差是工具选错了。如果你已经发现数据量超出内存有几个降级方案按块读取pd.read_csv(..., chunksize100000)分块清洗后合并。适合清洗逻辑是逐行独立的情况。选择列只读需要的列用usecols参数别把无关列全部load进来。调整数据类型文本列用string类型数值列用int32/float32能省很多内存。换DaskDask的API和pandas几乎一样改动成本低。import dask.dataframe as dd ddf dd.read_csv(big_data.csv, blocksize64MB) # 清洗逻辑基本照搬pandas写法 ddf[text_len] ddf[text].str.len() ddf ddf[ddf[text_len] 30] df ddf.compute() # 最后转成pandas做精细处理Dask不是银弹它适合单机多核并行 超出内存才有必要用的场景。数据大到单机完全放不下还是得上Spark或者按业务做数据分片。4.3 过滤阈值设错导致误删这是每个做清洗的人都会经历的事故。我见过有人设min_len500过滤短文本结果把所有产品评论、短对话全部干掉了微调出来的模型对话能力一塌糊涂。短文本不等于低质量很多人日常交流就是短句不能因为追求看起来有内容就一刀切。阈值设置建议遵循数据驱动原则先跑一遍长度分布直方图看分布形态再定阈值。比如长度分布如果是长尾的应该把阈值设在分布的尾部拐点处而不是拍脑袋。而且阈值一定要配置化保留在YAML里方便后续调整和对比。再提醒一点过滤操作要在标准化和去重之后做。原因很简单原始文本可能带着大量HTML标签和多余空格长度虚高在清洗前做长度过滤会把正常文本误删或漏删。4.4 去重误伤正常文本去重误伤最典型的场景是代码语料。你收集了一批代码数据很多代码片段本来就是重复的——同一个开源项目的不同版本、同一段API示例在不同教程里反复出现。但你并不想把这些全部删除因为代码语料需要保留不同上下文中的用法多样性。这种情况下精确去重可以保留第一次出现但近似去重阈值要放宽或者按代码结构做AST去重而不是文本相似度去重。另一个我踩过的坑把本文由XXX整理发布这种模板文本去重时所有文章都被判为近似重复因为公共模板部分占比太高。解决办法是去重前先剔除模板部分页面footer、免责声明等再做去重。所以清洗步骤的顺序不是随意定的噪声过滤做在去重之前是有道理的。4.5 清洗后数据分布偏移清洗做的太狠会导致清洗后的数据跟真实场景分布不一致模型学到的都是标准文本遇到真实用户的随便表达就懵了。这个问题没有标准答案只能通过一个原则来平衡清洗是去除有害噪声而不是去除多样性。比如过滤语法错误如果这个数据集是用来做对话模型的用户的输入本来就有很多口语化、不完整、带错别字的表达全部纠正反而不真实。判断标准只有一个清洗后的数据是否符合你期望模型面对的真实场景。4.6 常见问题速查表现象可能原因解决方案Loss不降或震荡数据噪声大、重复严重加强去重和质量过滤检查标签质量模型生成乱码语料中混入大量乱码字符在标准化阶段清理控制字符和非UTF-8内容模型只会复读近似重复数据太多做MinHash近似去重或提高去重比例模型泄露隐私未做PII脱敏正则模型识别清洗后抽样检查模型语言混杂语言过滤不严用fasttext做语言识别严格过滤非目标语言中文模型总输出英文英文语料占比过高统计语言分布调整过滤阈值微调后指令遵循差指令数据质量低检查指令-回答对一致性保留高质量样本评测指标虚高测试集混入重复数据在划分前全局去重并将测试集单独做一次近重复检测这张表不是万能诊断手册但覆盖了新手最容易遇到的几类问题。真遇到模型效果不好先回看数据再调模型能省大量无效实验时间。5. 写到最后一点个人体会这个系列叫「基本篇(1)」所以我并没有把模糊去重、MinHash、困惑度过滤、大模型辅助清洗这些进阶内容铺开写。但我必须说一句清洗这件事本质上和模型训练一样是个迭代优化的过程。没有任何一套清洗参数在所有数据集上都能直接用到最好你需要不停地在清洗 - 训练 - 评测 - 看bad case - 再调整清洗策略这个循环里打磨。我个人的一个习惯是每个清洗版本跑完后保存当时的配置文件和数据版本号并在模型训练结束后回看——如果bad case里频繁出现某种文本特征比如超长列表、大量数字、重复句式大概率不是模型的问题而是清洗时漏掉了一类噪声。这个从bad case反推清洗策略的思路比盲目堆清洗规则有效率得多。另一个值得推荐的技巧在清洗流程里留一个旁路——把被清洗掉的数据也保存一份打成带标签的数据集。之后想做数据增强、难例挖掘、或者训练一个数据质量分类器的时候这些被淘汰的数据就是现成的负样本。这个习惯我吃了很多红利强烈建议你从一开始就养成。下一篇进阶篇我会重点讲MinHash与LSH近似去重的工程实现、困惑度过滤的完整代码、以及怎么用本地部署的大模型做低质量文本的自动分类。如果你正准备上手做第一个大模型数据清洗项目先把这篇里的六步流程跑通比学任何花哨技巧都重要。最后再分享一个小经验别迷信公开的高质量数据集。哪怕是网上口碑很好的清洗后数据集放到你的具体任务里依然可能有大量不合适的样本。自己动手洗一份数据虽然慢但你对数据分布的理解会深一个层次这种理解在后续调模型时会给你巨大的回报。数据清洗不是脏活累活它是大模型项目里最值得花时间的环节之一。