数据工程
第 8 课:数据工程
上一课我们算清了"要多少数据"。但数据不是天上掉下来的——从 Common Crawl 的原始网页,到能喂给模型的干净 token,中间要经过采集、去重、过滤、配比、合成、评估六道工序。这一课讲清每一道工序在做什么、代价是什么。
② 判断"去重"和"质量过滤"各自解决什么问题、代价是什么
③ 解释合成数据为什么会有"模型崩塌"风险,以及什么条件下可以安全使用
上一课我们算清了"要多少 token"。现在问下一个问题:这些 token 从哪来?
因为最直接的答案是"从网上抓",所以必须先看清:抓下来的原始网页,到底有多少能直接用。可以把这类比成从河里打水——水是免费的,但要喝还得过滤、煮沸、去掉泥沙,真正能入口的比例远小于打上来的量。
| 格式 | 内容 | 体积量级 | 能否直接训练 |
|---|---|---|---|
| WARC | 原始 HTTP 响应(含 HTML 标签) | 最原始、最大 | 不能 |
| WET | 抽取后的纯文本 | 比 WARC 小一个量级以上 | 仍不能(含模板文本) |
| WAT | 元数据(URL、时间、链接) | 小 | 不用于训练 |
| 工序 | 典型判据 | 作用 |
|---|---|---|
| 长度过滤 | 少于 50 个词 或 多于 10 万个词 → 丢弃 | 去掉碎片与超长噪声 |
| 字母词占比 | 非字母词占比 > 20%(即至少 80% 的词须含字母)→ 丢弃 | 去掉二进制、日志、代码混淆串 |
| 重复率过滤 | n-gram 重复 > 30% → 丢弃 | 去掉刷屏、模板化文本 |
| 语种过滤 | 目标语种之外 → 丢弃 | 用 fastText 分类器判断语种 |
| 工序级联 | 相对剩余量(量级示意) | 说明 |
|---|---|---|
| 原始抓取(HTML) | 100% | 含全部标签与元数据 |
| 正文抽取后 | 约 10%~20% | 只剩正文文本 |
| 去重后 | 再降至约 50%~70% | 公开数据集的去重削减率多在 30%~50%(口径差异大) |
| 叠加质量过滤后 | 进一步下降 | 最终可用比例通常远低于一半 |
数据工程的第一性认识是:原始语料的价值密度极低。Common Crawl 单轮未压缩体积达数百 TiB 量级,经过抽取与多轮过滤后剩下的可用 token 只是零头。所以"数据不够用"往往不是抓得少,而是没洗干净——这也是为什么 DCLM 的实验里,数据筛选的差异能让 7B 模型在少用 40% 算力的前提下 MMLU 还高 6.6 个百分点。
上一张卡片我们看到去重会砍掉约 1/3 的数据。直觉上"数据变少了应该更差",但事实相反——为什么?
因为重复数据会让模型把"记住原文"当成"学会规律",所以去掉重复反而提升泛化。可以把这类比成备考:同一道题抄 100 遍,你记住的是这道题的答案;做 100 道不同的题,你学会的是解题方法。
更自然的第一反应是用精确哈希去重(如 MD5 / SHA256),但它只认「一模一样」的字节:转载页往往只改一个标点或加一行页脚,就会被判成两篇不同的文档。而大规模网页抓取里,精确或近似重复的页面可占 30%~50%(统计口径因抓取范围而异),精确哈希基本抓不到后面这一大类——所以必须上近似去重。
| 方法 | 能发现什么 | 发现不了什么 |
|---|---|---|
| 精确哈希(MD5/SHA256) | 完全相同的文档 | 改了一个标点的同一篇文章 |
| 近似去重(MinHash+LSH) | 相似度 > 阈值的文档 | 语义相同但用词完全不同的改写 |
| 方法 | 两两比较次数(n 篇文档) | 复杂度 |
|---|---|---|
| 暴力两两比较 | n(n−1)/2 | O(n²):n = 100 亿时 ≈ 5 × 10¹⁹ 次 |
| MinHash + LSH 分桶 | 只比同桶内的文档 | 近似 O(n):规模随 n 线性增长,不再爆炸 |
| 场景 | 重复率 | 后果 |
|---|---|---|
| 不去重 | 约 30%~50% | 算力有 1/3 花在重复内容上;模型倾向记忆原文 |
| 去重后 | 接近 0 | 同样算力覆盖更多不同内容;下游表现提升 |
去重是"用数据量换数据多样性"的交易:砍掉 1/3 的 token,但换来的是同样算力下更广的覆盖。DCLM 的实验显示,数据筛选策略的差异可以让 7B 模型在少用 40% 算力的情况下还高出 6.6 个百分点(对比基线为 MAP-Neo,arXiv:2406.11794)——数据质量不是"锦上添花",而是与算力同等量级的杠杆。
上一张卡片讲完去重。但去过重之后,语料里还有大量"语法没问题、就是没价值"的文本——比如产品页的参数罗列。怎么把它们筛掉?
因为规则只能识别"形式特征"(太短、符号太多),所以必须再加一层"内容判断"。可以把这类比成招聘:简历初筛看硬性条件(学历、年限)是规则过滤;面试官判断"这人能力如何"是模型过滤——两者缺一不可。
更省成本的做法是只用规则过滤(不跑模型推理),但它只能筛「形式」、筛不出「价值」——产品页规格、SEO 拼凑文都语法通顺,规则一条都不触发;DCLM 的对照实验显示,补上模型过滤后,7B 模型在少用 40% 算力的情况下 MMLU 反而高 6.6 个百分点。
| 规则 | 阈值(示例) | 筛掉的典型内容 |
|---|---|---|
| 长度 | 少于 50 个词 或 多于 10 万个词 | 碎片标题、网页日志 |
| 字母词占比 | 至少 80% 词含字母(非字母词占比 > 20%) | 数值表、代码混淆串 |
| n-gram 重复率 | > 30% | 模板化文本、刷屏内容 |
| 语种识别 | 非目标语种 | 混入的外语文档 |
| 过滤器 | 训练目标 | 能识别 | 识别不了 |
|---|---|---|---|
| fastText 语种分类 | 176 语种分类 | 语种 | 内容质量 |
| 质量分类器 | 高分文本 vs 低分文本 | "像教科书"的段落 | 领域外的专业内容 |
| 有害内容分类 | 毒性识别 | 攻击性内容 | —— |
| 数据集 | 模型规模 | 训练 token | MMLU 5-shot | 训练算力 |
|---|---|---|---|---|
| MAP-Neo(此前开源最佳) | 7B | —— | 基线 | 基线 |
| DCLM-BASELINE | 7B | 2.6T | +6.6 个百分点 | 少 40% |
| 对照:Mistral-7B-v0.3 | 7B | —— | 63% | —— |
| 对照:Llama 3 8B | 8B | —— | 66% | 比 DCLM 多 6.6 倍 |
规则过滤管"形式",模型过滤管"价值"。前者便宜但不能判断内容好坏,后者能判断但要为全量语料付一次推理成本。两者是串联关系而不是二选一:先用规则快速砍掉大部分垃圾(省下模型推理的成本),再用模型过滤在剩余部分里精挑——这也是几乎所有主流数据集的共同做法。
上一张卡片把垃圾筛掉了。但剩下的"好数据"来自不同领域——网页、代码、论文、百科。它们该按什么比例混合?
因为配比直接决定模型的能力分布,所以它不能拍脑袋。可以把这类比成配营养餐:蛋白质、碳水、脂肪各占多少,决定了运动员练出什么体质——配方错了,练得再多也补不到该补的地方。
更自然的做法是按语料里各领域现成的 token 量来分配(The Pile 早期就这么做)——但这等于假设「量多就该多用」,并不保证对下游能力最优;DoReMi 只把采样权重换一换,在同一份语料上平均 few-shot 就高了 6.5 个百分点,且用 1/2.6 的步数就追平基线。
| 路线 | 怎么做 | 优点 | 代价 |
|---|---|---|---|
| 启发式 | 按各领域现有 token 量分配(The Pile 做法) | 零额外成本 | 可能次优 |
| 面向下游任务调 | 在多个下游任务上搜最优权重 | 目标明确 | 可能要训上千个模型;过拟合任务集 |
| 代理模型搜索(DoReMi) | 用小模型自动搜权重,再用到大模型 | 成本低、不依赖下游任务 | 需一次代理模型训练 |
| 项目 | 数值 | 说明 |
|---|---|---|
| 代理模型规模 | 280M 参数 | 用于搜索权重 |
| 目标模型规模 | 8B 参数 | 约 30 倍大(280M → 8B) |
| 参数量比 | 约 1/30 | 代理模型参数量仅为目标的约 1/30,实际搜索成本还取决于 token 预算与搜索轮数 |
| 指标 | 基线(The Pile 默认配比) | DoReMi 配比 | 变化 |
|---|---|---|---|
| 平均 few-shot 准确率 | 基线 | —— | +6.5 个百分点 |
| 达到基线精度所需步数 | 1.0× | —— | 1/2.6 |
| 是否用到下游任务 | —— | 不使用 | 可跨任务复用 |
数据配比是被低估的算力杠杆:不改模型、不改算力,只调各领域的采样权重,就能让模型少跑 60% 的步数达到同样精度(DoReMi)。而它之所以长期靠直觉,是因为"试错成本高"——代理模型把试错成本压到约 1/30(参数量比),才让自动搜索变得可行。
上一张卡片讲完配比。但真实数据快用完了(第 7 课的数据墙),于是自然想到:用模型生成数据来补,行不行?
因为模型生成的内容只覆盖它学到的分布,所以一代代只用它训练会让"罕见情况"被逐步抹掉——这就是模型崩塌。可以把这类比成复印件的复印件:每复印一次就丢一点细节,几十代后只剩一团模糊;只有把原件一直留在纸堆里,才不会再模糊。
| 方式 | 训练集构成 | 结局(理论结论) |
|---|---|---|
| 纯合成 | 第 N 代全部用第 N−1 代生成 | 必然崩塌(无法避免) |
| 替换式混合 | 每代用"新合成"替换掉部分真实数据 | 逐步崩塌(真实数据被侵蚀) |
| 累积式混合 | 真实数据不删,合成数据不断追加 | 可避免崩塌 |
| 环节 | 发生了什么 | 后果 |
|---|---|---|
| 第 1 代采样 | 从真实分布里采样,稀有事件本来就少 | 采样样本里尾部已欠代表 |
| 第 2 代模型 | 学的是"已欠代表的样本" | 生成时尾部更少 |
| 第 3~4 代 | 偏差逐代累积 | 模型忘记稀有事件存在 |
| 合成占比 | 是否有崩塌风险 | 建议 |
|---|---|---|
| 100%(纯合成) | 必然崩塌 | 不可用 |
| 较高比例 + 真实数据被替换 | 有 | 不可取 |
| 合成占少数 + 真实数据累积不删 | 可避免 | 推荐 |
合成数据不是"能用/不能用"的二元问题,而是用法问题:替换真实数据 → 崩塌;累积而不删真实数据 → 安全。所以工程上的铁律是——永远保留一份人类撰写的真实数据作为锚点,合成数据只做增量。这也再次印证第 7 课的数据墙:数据不够不能靠纯合成绕过去。
上一张卡片讲完合成数据的边界。现在把整条管线收口:怎么知道一份语料真的变好了?
因为"洗得干净"本身不是目标(干净但没提升能力的语料毫无价值),所以必须用模型层的对照实验来判断。可以把这类比成换食材:菜谱不变、厨师不变,只换食材,然后请同一批人盲测——这样才知道变好是不是食材的功劳。
| 指标 | 怎么算 | 健康信号 |
|---|---|---|
| 去重率 | 去重前后 token 差 ÷ 去重前 | 约 30%~50%(过低可能没去干净) |
| 领域分布熵 | 各领域占比的香农熵(衡量分布均匀程度) | 不宜过低(过低=过度集中) |
| 平均文档长度 | token 总数 ÷ 文档数 | 不宜过短(过短=碎片多) |
| 做法 | 控制变量 | 读数 |
|---|---|---|
| 固定小模型 + 固定算力,只换数据集 | 架构、超参、token 预算全不变 | 验证集 perplexity、下游基准分 |
| DCLM 的实际做法 | 7B 模型、2.6T token | MMLU 5-shot 提升 6.6 个百分点 |
| 方案 | 对照成本 | 结论可信度 |
|---|---|---|
| 用 7B 模型测每组语料 | 每次完整预训练(极高) | 高 |
| 用 400M~1B 小模型测 | 约 1/50~1/100(估算) | 中(趋势可信,绝对值不可外推) |
数据工程的闭环判据是"同一算力下模型是否更强",而不是"语料是否更干净"。这条判据决定了数据工程必须与训练实验绑定——没有对照实验,就只是手工艺,不是工程。这也是为什么第二部分把数据工程放在预训练之前:先学会怎么判断数据好坏,才知道预训练该喂什么。