1. 数据管线的第一性原理模型是数据分布的镜像很多人第一次训练语言模型时会把注意力全放在模型结构上——多少层、多少头、隐藏维度多大。但真正跑过一轮完整训练的人都知道模型最终表现出的能力边界几乎完全由数据决定。你喂进去什么分布的数据模型就长出什么样的能力。这不是玄学而是训练目标函数直接决定的模型在最小化训练数据上的交叉熵损失本质上就是在拟合数据的概率分布。我在早期做小规模预训练实验时踩过一个典型的坑当时为了快速验证模型结构随手用了一批未经过滤的混合语料里面混杂了大量重复的模板化文本、低质量翻译腔和格式混乱的网页抓取内容。结果训练loss降得很漂亮但模型生成的东西要么是复读机式的重复要么是语法正确但语义空洞的废话。后来把数据清洗流程补上同样的模型结构、同样的超参生成质量立刻上了一个台阶。这个经历让我彻底明白一件事数据管线的质量上限就是模型能力的上限。这一篇要聊的就是怎么把原始语料一步步变成可以喂给模型的训练数据。我会从数据来源的取舍、清洗去重的具体手段、分词与打包的策略、数据配比的逻辑一直到训练过程中动态调整数据分布的经验完整走一遍。适合已经了解Transformer基本结构、准备动手做预训练或继续预训练的读者。如果你还在纠结模型结构选型建议先把数据这块搞明白因为结构可以换数据管线一旦跑歪后面全是白费功夫。2. 原始语料的来源选择与质量分级2.1 不同来源语料的特性对比做预训练第一步是确定数据从哪来。常见来源大致分几类网页抓取文本、书籍、学术论文、代码仓库、百科类内容、论坛问答、新闻资讯。每一类的语言风格、信息密度、噪声类型都不一样不能一锅端。来源类型语言风格信息密度主要噪声适合阶段网页抓取口语化、碎片化低到中广告、导航栏、乱码通用预训练书籍正式、连贯高版权、扫描错误通用预训练学术论文严谨、术语密集极高公式、引用格式继续预训练代码仓库结构化、逻辑强高注释混杂、重复代码能力训练百科内容中性、规范高模板化、更新滞后知识注入论坛问答口语、互动性强中灌水、情绪化对话能力训练我一般会把数据分成通用底座和能力增强两层。通用底座用网页抓取加书籍加百科保证语言覆盖面和基础流畅度能力增强层根据目标场景补充代码、论文或问答数据。两层分开处理、分开配比比混在一起洗要可控得多。2.2 质量分级的实操判断标准拿到原始语料后第一件事不是急着清洗而是做质量分级。我的做法是先用规则粗筛一遍再人工抽检几百条定标准。规则粗筛主要看几个指标文本长度太短的直接丢通常低于50个字符的段落信息量不足、特殊符号占比超过30%基本是乱码或代码残留、重复行比例单篇内重复行超过一半的丢掉、语言置信度用轻量语言识别模型打分低于阈值的剔除。人工抽检这一步很多人会跳过但它决定了你后面自动化清洗的阈值怎么定。我通常会从每个来源随机抽200条按可直接用清洗后可用直接丢弃三档标注算出各档比例。如果某个来源直接丢弃超过40%说明这个来源本身质量太差要么换来源要么加大清洗力度。注意质量分级的标准不是一成不变的。做通用预训练时口语化、带情绪的网络文本可以保留但做专业领域继续预训练时这类文本反而要大幅降低权重。标准跟着目标走不要照搬别人的阈值。2.3 版权与合规的边界处理数据来源的合规性是个绕不开的问题。我的原则是优先使用明确开放授权或公共领域的数据集对来源不明的抓取数据保持谨慎。实际操作中会对每批数据记录来源、授权状态和抓取时间建立数据台账。这不是为了应付检查而是当后面发现某批数据导致模型输出异常时能快速定位并回滚。另外涉及个人信息的文本要在清洗阶段做脱敏处理邮箱、电话、身份证号这类模式用正则统一替换。这一步在数据管线里成本很低但漏掉的话后面很麻烦。3. 清洗去重把噪声挡在分词之前3.1 文本清洗的层次化流程清洗不是一步到位的事我习惯分成三层来做每层解决不同粒度的问题。第一层是字符级清洗。处理HTML标签残留、转义字符、不可见控制符、全半角混用。这一步用正则和字符映射表就能搞定重点是别误伤正常文本。比如HTML标签用正则匹配时要小心把数学表达式里的尖括号也干掉所以我会先做标签识别再做内容提取而不是无差别替换。第二层是段落级清洗。去掉导航栏文本、版权声明、页脚、广告段落。这类内容往往有固定模式比如版权所有点击查看更多相关推荐之类的短语。维护一个模式库命中就整段删除。但模式库要定期更新因为网页模板会变。第三层是文档级清洗。判断整篇文档是否值得保留。指标包括有效字符占比、句子平均长度、重复n-gram比例、语言一致性。我一般会设一个综合评分低于阈值的整篇丢弃。import re def clean_text(text): # 字符级去除控制符和多余空白 text re.sub(r[\x00-\x08\x0b-\x0c\x0e-\x1f], , text) text re.sub(r\s, , text) # 段落级去除常见模板短语所在行 lines text.split(\n) filtered [l for l in lines if not re.search(r版权所有|点击查看|相关推荐, l)] return \n.join(filtered)这段代码只是示意实际管线里每一步都会更细。关键是理解清洗的层次逻辑先处理字符再处理段落最后处理文档从细到粗避免在粗粒度操作时丢失可修复的细节。3.2 去重的粒度选择与陷阱去重是数据管线里最容易被低估的环节。重复数据会让模型过度拟合特定模式还会浪费训练算力。但去重的粒度选不好要么漏掉大量近似重复要么误删有价值的相似表达。我一般做三级去重文档级精确去重对整篇文档做哈希完全相同直接去重。这一步简单但只能抓完全一样的。段落级近似去重用MinHash或SimHash对段落做指纹相似度超过阈值的归为一组保留质量最高的那条。阈值一般设在0.8到0.9之间太低会误删太高会漏。句子级子串去重针对那些在多个文档里反复出现的句子比如免责声明、固定开场白建立高频句子黑名单直接过滤。这里有个坑近似去重的阈值不是越高越好。我试过把阈值设到0.95结果发现大量只差几个词的重复段落还是留了下来模型照样学出复读行为。后来降到0.85配合人工抽检确认没有误删效果明显改善。提示去重后一定要统计去重率。如果去重率低于10%说明阈值可能太松如果高于60%要么数据源本身重复严重要么阈值太激进误删了。正常范围我经验里在20%到40%之间比较合理。3.3 清洗效果的验证方法清洗完怎么知道做得好不好不能只看剩了多少数据。我的验证方法是从清洗后的数据里随机抽100条人工读一遍统计明显噪声边界情况干净可用的比例。如果明显噪声超过5%说明清洗规则还有漏洞回去补规则。另一个方法是看清洗前后模型的小规模对比实验。用同样的模型结构、同样的训练步数分别喂清洗前和清洗后的数据比较验证集loss和生成样本质量。这个对比虽然费算力但最能说明问题。我做过一次清洗后的数据在验证集上loss低了约0.15生成样本的重复率从30%降到8%左右。4. 分词、打包与注意力掩码的配合4.1 分词器选择对数据利用率的影响分词器决定了文本怎么切分成模型能处理的token序列。很多人直接用现成的分词器但如果你的数据分布和分词器训练时的分布差异大token利用率会很低等于变相浪费了上下文窗口。举个例子如果你的数据里有大量专业术语或代码而分词器是在通用语料上训练的这些术语会被切成很多碎token一条本来200字的内容可能占掉400个token位置。解决办法要么是用领域数据继续训练分词器要么是在数据里对高频术语做特殊处理。我一般会先统计一下用目标分词器编码一批数据看平均每个token对应多少字符。英文一般1个token对应4个字符左右中文1个token对应1.5到2个字符。如果明显偏离这个范围就要考虑调整分词器或数据。4.2 序列打包的几种策略模型训练时输入是固定长度的序列但文档长度参差不齐。怎么把文档拼成固定长度直接影响训练效率和模型行为。最粗暴的是截断超长的切掉短的单独成一条。缺点是短文档浪费大量padding长文档丢失尾部信息。好一点的是拼接打包把多个短文档首尾相接拼到固定长度文档之间插入分隔符。这样利用率高但要注意分隔符要让模型能区分不同文档的边界。更精细的是带注意力掩码的打包拼接后通过注意力掩码让每个token只能看到同一文档内的token不跨文档注意。这样既提高了利用率又避免了文档间的错误关联。实现上稍微复杂需要在数据加载时生成对应的掩码矩阵。def pack_sequences(docs, max_len, sep_id): packed [] buffer [] for doc in docs: if len(buffer) len(doc) 1 max_len: packed.append(buffer[:max_len]) buffer [] buffer.extend(doc [sep_id]) if buffer: packed.append(buffer[:max_len]) return packed这段是简化版拼接逻辑实际用的时候还要处理掩码生成和padding对齐。我的经验是如果算力充足且追求效果用带掩码的打包如果追求吞吐量普通拼接打包也够用但分隔符一定要加。4.3 掩码策略与训练目标的匹配注意力掩码不只是打包时用。在训练目标上因果语言模型用的是下三角掩码每个位置只能看到自己和之前的token。这个和打包掩码要叠加使用实现时注意维度对齐。我踩过一次坑打包掩码和因果掩码的维度没对齐导致模型实际上能看到跨文档内容训练loss异常低但生成时完全不是那么回事。排查了半天才发现是掩码广播时出了错。所以掩码这块写完一定要用小样本手动验证打印出掩码矩阵看是否符合预期。5. 数据配比不同来源的权重怎么定5.1 配比失衡的典型症状数据配比是预训练里最像玄学的部分但其实有迹可循。配比失衡最典型的症状是模型在某类任务上表现异常好在另一类上异常差。比如代码数据占比过高模型写代码很溜但日常对话磕磕巴巴百科数据过多模型回答事实性问题准确但生成故事时干巴巴。我一般会先定一个基线配比然后通过小规模消融实验调整。基线配比参考公开经验网页类60%到70%书籍10%到15%百科5%到10%代码5%到10%问答5%左右。这个不是标准答案只是起点。5.2 用验证集表现反推配比调整配比的依据不能只看训练loss要看分领域的验证集表现。我会准备多个验证集通用语言建模、事实问答、代码补全、对话连贯性。每调整一次配比跑一小轮训练看各验证集的loss变化。如果某个验证集loss明显高于其他说明对应领域的数据可能不足或质量不够。这时候有两种做法增加该领域数据占比或者提高该领域数据的质量。我倾向于先提质量再提占比因为低质量数据加多了反而拖累整体。验证集类型关注指标配比调整方向通用语言建模困惑度网页/书籍比例事实问答准确率百科/论文比例代码补全通过率代码数据比例对话连贯性人工评分问答/论坛比例5.3 课程学习式的动态配比固定配比不是唯一选择。课程学习的思路是训练初期用高质量、易学习的数据后期逐步加入难度更高、噪声更多的数据。这样模型先建立稳定的语言基础再适应复杂分布。我的做法是分三个阶段第一阶段用清洗最严格的数据配比偏书籍和百科第二阶段加入中等质量的网页数据第三阶段加入全部数据但保持高质量数据的采样权重。每个阶段的切换点根据验证集loss的下降趋势来定一般在前一阶段loss下降变缓时切换。这种动态配比实现上就是在数据加载器里按阶段调整各来源的采样概率。代码不复杂但需要提前规划好阶段划分和数据准备。6. 训练中的在线数据调整与监控6.1 实时监控数据质量指标数据管线不是离线跑完就完事了。训练过程中要持续监控喂进去的数据质量。我会在数据加载器里埋几个统计指标当前batch的平均长度、特殊token占比、重复n-gram比例、各来源的实际采样比例。这些指标实时打到监控面板上。如果发现某个来源的实际采样比例和设定值偏差超过5%说明采样逻辑有问题如果重复n-gram比例突然升高可能是某批数据没洗干净混进来了。注意监控指标要设告警阈值。我一般设重复n-gram比例超过15%就告警因为正常清洗后的数据这个值通常在5%以下。6.2 发现数据问题后的回滚策略训练中途发现数据问题最怕的是不知道问题数据从哪来、影响了多少步。所以数据台账和checkpoint管理要配套。每批数据进管线时记录来源和批次号每个checkpoint记录对应的数据步数。一旦发现问题先定位是哪个批次的数据然后评估影响范围。如果只是少量噪声可以继续训练但加大后续清洗力度如果影响严重回滚到问题数据进入前的checkpoint替换数据后重新训练。回滚虽然费时间但比让模型带着污染继续学要划算。6.3 数据管线的迭代节奏数据管线不是一次搭好就不动的。随着训练推进和对模型表现的观察清洗规则、配比、打包策略都可能要调。我的节奏是每训练完一个阶段做一次数据管线复盘看哪些规则命中率异常、哪些来源质量波动、哪些配比需要调整。复盘时重点看两个东西一是清洗规则的命中统计如果某条规则几乎不命中考虑删掉如果某条规则命中率突然飙升检查是不是数据源变了。二是分领域验证集的表现趋势如果某个领域持续没提升优先怀疑该领域数据。这套迭代节奏跑下来数据管线会越来越贴合你的目标场景。模型是数据分布的镜像你把数据管线打磨得越精细模型长出来的能力就越符合预期。我在最近一轮实验里光是调整数据配比和清洗阈值就让同等规模模型的对话连贯性评分提升了将近两成而模型结构一行没改。数据这块的投入回报是实打实的。