章节导航 第 17 章 / 共 20 章
数据混合与配方
三轴配方(任务/领域/难度)、SFT阶段配方(Tulu-3: 通用30%+数学20%+代码15%+安全10%+指令10%+多语5%+知识5%+工具5%)、DPO阶段配方(Helpfulness 35%/Honesty 15%/Harmlessness 15%/推理20%/长度去偏10%)、RL/RLVR阶段配方(pass@8分桶: 0.8-1.0 10%/0.5-0.8 30%/0.2-0.5 40%/0-0.2 20%)、三阶段串联(Tulu-3: SFT 939K→DPO 271K→RLVR 30K)、动态配比(DoReMi for SFT)、课程化(简单→中等→难)、目标模型感知数据选择(Data-DPO one-step probing/激活差异转偏好/三重组合)、后训练动态配比(DataFlex统一三范式/DomainPilot token级域loss+Scaling Law粗优化+Mixing Law细优化/loss信号混淆噪声难度新颖性)、可复现基准(NebulaExp-8B 3.84M SFT+200K RL/五步处理栈/55.01→60.99→61.85)
数据是食材,配方是菜谱。
同样 100 万条数据,配比不同,模型能力差 30%。
17.1 三轴配方
后训练数据混合不是”一锅烩”,而是按”三轴”组织:
17.1.1 三轴定义
难度轴
↑
│
简单 │
│
中等 │ ★ 甜点区
│
困难 │
│
└──────────→ 领域轴
数学 代码 通用 安全 ...
↙ 任务轴
生成 抽取 推理 对话 ...
- 任务轴:生成、抽取、推理、对话、翻译、摘要…
- 领域轴:数学、代码、通用、安全、多语、知识、工具…
- 难度轴:简单、中等、困难(用 pass@K 或 LLM 评分)
17.1.2 三轴的优先级
工业实践中,三轴的优先级是:领域 > 任务 > 难度。
原因:领域失衡最致命(缺数学就是不会数学),难度可以通过课程化动态调整(见 17.6 节)。
17.1.3 三轴矩阵
每条数据在三轴上都有定位:
{
"prompt": "证明 √2 是无理数",
"axis": {
"domain": "math",
"task": "reasoning",
"difficulty": "hard",
"difficulty_score": 0.85 // 0-1,pass@8 = 0.15
}
}
配方是三轴矩阵的”权重表”:
领域\难度 简单 中等 困难
数学 5% 10% 5%
代码 5% 8% 2%
通用 10% 15% 5%
...
17.2 SFT 阶段配方
17.2.1 Tulu-3 SFT 配方(标杆)
Tulu-3 的 SFT 阶段配方是工业级参考标杆:
| 数据源 | 数量 | 占比 | 领域 | 任务 |
|---|---|---|---|---|
| WizardLM/Evol-Instruct | 280K | 30% | 通用 | 对话 |
| NuminaMath/GSM8K 增强 | 190K | 20% | 数学 | 推理 |
| Magicoder/Evol-Code | 140K | 15% | 代码 | 生成 |
| WildGuardMix | 95K | 10% | 安全 | 拒答 |
| IFEval-like | 95K | 10% | 指令遵循 | 格式 |
| 多语言(20+) | 45K | 5% | 多语 | 翻译 |
| OpenOrca 子集 | 45K | 5% | 知识 | 问答 |
| xLAM/Glaive | 45K | 5% | 工具 | 调用 |
| 总计 | 939K | 100% |
17.2.2 配方设计原则
Tulu-3 配方体现了几条原则:
1. 通用打底(30%) 通用对话数据是”底盘”,确保模型基本能用。低于 20% 模型会”偏科”。
2. 推理加强(35%) 数学 20% + 代码 15% = 35%,这是”思考能力”的核心。低于 25% 推理能力显著下降。
3. 安全底线(10%) 安全数据不能少,否则上线即翻车。但超过 20% 会过度拒答。
4. 指令遵循(10%) 指令遵循数据教模型”听懂指令”,对 IFEval 等评测至关重要。
5. 长尾覆盖(15%) 多语 + 知识 + 工具 = 15%,避免能力短板。
17.2.3 不同能力的配方敏感度
| 能力 | 配方敏感度 | 备注 |
|---|---|---|
| 数学 | 极高 | 数学数据从 10% → 20%,GSM8K 涨 8 分 |
| 代码 | 高 | 代码数据低于 10%,HumanEval 显著下降 |
| 安全 | 高 | 安全数据低于 5%,XSTest 拒答率不足 |
| 通用对话 | 中 | 通用数据 20-40% 区间影响小 |
| 指令遵循 | 中 | 需要专门数据,但 10% 足够 |
| 多语言 | 低 | 主要看绝对量,占比不是关键 |
17.2.4 数据选择:把”价值”从静态属性改为关系属性
17.2.1–17.2.3 讨论的是”选多少”(配比),但在此之前还有一步”选哪些”(数据选择)。主流选择方法——包括第 16 章的 IFD、Deita——都隐含一个假设:数据价值是数据自身的属性。
Data-DPO(Peng Sun 等 10 人)质疑的正是这一点。它的诊断是:现有方法”通常把数据价值当作相对静态的属性,很少关注数据与目标模型能力分布的兼容性”。
也就是说,同一批数据对一个 3B 模型是高价值的,对一个 70B 模型可能全是已掌握的内容。价值不是一个数,而是数据与模型之间的关系。
方法三步:
- one-step probing:用目标模型对候选样本做一步探测,观察它在不同样本上的局部训练反馈
- 转化为偏好:把样本之间的激活差异(activation differences)转成 pairwise data preferences
- 训练轻量 reward model:学习目标模型感知的数据偏好
最终选择阶段不是只看这个 reward,而是三者结合:目标模型偏好 + 外部质量分数 + 边际多样性。
为什么第三步的组合很重要:纯目标模型偏好选择会有一个系统性风险——模型会偏好”自己已经会的”或”最容易产生激活变化的”样本,前者导致无效训练,后者可能放大噪声。叠加外部质量分数(第 16 章那套)与边际多样性后,才构成可用的选择信号。这个”三重组合”本身就是一个可以直接复用的工程模板。
⚠️ 命名易混淆:Data-DPO 的名字里虽然有 DPO,但它不是用来选择 DPO 训练数据的方法,而是借用 DPO 的偏好建模思想来做 SFT 数据选择。论文摘要第一句即为 “Data selection in supervised fine-tuning…”。如果你在找”DPO 阶段的数据选择”,本章 17.3 才是对应位置,而该方向目前缺少同类的目标模型感知工作。
⚠️ 成本提示:one-step probing 需要对候选样本跑目标模型,成本随候选池规模线性增长。论文未给出在百万级候选池上的完整成本评估。落地时建议先用小规模候选池(如 100K)验证收益,再决定是否全量展开。
17.3 DPO 阶段配方
17.3.1 DPO 配方的特殊性
DPO 配方不是按”领域”分,而是按”偏好维度”分:
| 维度 | 占比 | 数据来源 |
|---|---|---|
| Helpfulness(有帮助) | 35% | UltraFeedback |
| Honesty(诚实) | 15% | TruthfulQA 增强 |
| Harmlessness(无害) | 15% | WildGuardMix |
| 推理(Reasoning) | 20% | 数学/代码偏好对 |
| 长度去偏 | 15% | 人工构造短/长对 |
| 总计 | 100% |
17.3.2 长度去偏数据的构造
def construct_length_debias_data(prompts):
"""构造"短而精"胜过"长而水"的偏好对"""
data = []
for p in prompts:
# 让 GPT-4 生成简洁版与冗长版
concise = gpt4.generate(p, style="concise")
verbose = gpt4.generate(p, style="verbose")
# 让 judge 评分
score_concise = judge(p, concise)
score_verbose = judge(p, verbose)
# 如果简洁版更好,作为 (chosen, rejected)
if score_concise > score_verbose:
data.append({
'prompt': p,
'chosen': concise,
'rejected': verbose
})
return data
17.3.3 DPO 配方的常见错误
错误 1:Helpfulness 占比过高(>50%) 导致模型变得”阿谀奉承”,Sycophancy 评测掉分严重。
错误 2:缺推理维度 推理类偏好对缺失,模型推理能力会回退。
错误 3:长度去偏缺位 不加长度去偏数据,DPO 训练后输出长度漂移 +30%。
17.4 RL/RLVR 阶段配方
17.4.1 pass@8 难度分桶配方
RL/RLVR 阶段的配方核心是难度分桶:
| pass@8 | 难度 | 推荐占比 | 训练价值 |
|---|---|---|---|
| 0.8-1.0 | 太简单 | 10% | 低(模型已会) |
| 0.5-0.8 | 简单 | 30% | 中(巩固) |
| 0.2-0.5 | 中等 | 40% | 高(学习边界) |
| 0-0.2 | 困难 | 20% | 中(突破) |
核心洞察:pass@8 在 0.2-0.5 的 prompt 训练价值最高——模型有时能解、有时不能解,处于”最近发展区”。
17.4.2 任务配比
RLVR 阶段按可验证任务类型配比:
| 任务类型 | 占比 | Verifier | 备注 |
|---|---|---|---|
| 数学 | 50% | sympy | 推理核心 |
| 代码 | 30% | 单测 | 工程能力 |
| 指令遵循 | 20% | 规则 | 格式能力 |
17.4.3 RLVR 规模
工业级 RLVR 的 prompt 池规模:
- 最小可用:1 万
- 典型:5 万-10 万
- 上限:50 万
每个 prompt 训练时采样 N=8-64 次,实际算力消耗巨大。
17.5 三阶段串联:Tulu-3 完整配方
17.5.1 Tulu-3 三阶段串联
SFT (939K) → DPO (271K) → RLVR (30K)
每阶段的数据量递减,但单条价值递增:
| 阶段 | 数据量 | 单条价值 | 训练目标 |
|---|---|---|---|
| SFT | 939K | 低 | 教格式 |
| DPO | 271K | 中 | 教品味 |
| RLVR | 30K | 高 | 教思考 |
17.5.2 为什么是递减
- SFT 教基础能力,需要大量数据覆盖各场景
- DPO 教偏好,数据量过大会过拟合
- RLVR 训练时每条要采样 N 次,30K × 16 = 480K 次生成,算力消耗大
17.5.3 阶段间的衔接
SFT → DPO:
- DPO 的 reference model = SFT 训练完的模型
- DPO 数据中的 prompt 应该与 SFT 数据分布一致
DPO → RLVR:
- RLVR 的初始 policy = DPO 训练完的模型
- RLVR 的 prompt 池应该是 DPO 模型 pass@8 在 0-0.8 区间的
def prepare_rlvr_pool(dpo_model, candidate_prompts, verifier):
"""用 DPO 模型评估候选 prompt 的 pass@8"""
pool = []
for p in candidate_prompts:
pass_at_8 = compute_pass_at_k(dpo_model, p, verifier, k=8)
if 0.0 < pass_at_8 < 0.8: # 有学习空间
p['pass_at_8'] = pass_at_8
pool.append(p)
return pool
17.5.4 串联的失败模式
失败 1:跳过 SFT 直接 DPO 模型没学会对话格式,DPO 训练时输出乱码。
失败 2:跳过 DPO 直接 RLVR 模型没有”偏好”基础,RLVR 训练时探索方向失控。
失败 3:三阶段都用同一批 prompt 会导致模型在某类 prompt 上过拟合,多样性下降。
17.6 课程化
17.6.1 课程化的核心
课程化(Curriculum Learning)的核心:从简单到困难逐步训练。
Epoch 1-10: pass@8 0.5-0.8 的 prompt(简单)
Epoch 10-30: pass@8 0.2-0.5 的 prompt(中等)
Epoch 30-50: pass@8 0-0.2 的 prompt(困难)
17.6.2 RLVR 的课程化重采
RLVR 训练中,模型的 pass@8 会变化——原本困难的 prompt 变简单。需要动态调整:
def curriculum_rlvr(model, prompt_pool, verifier, total_epochs=50):
for epoch in range(total_epochs):
# 1. 每 5 epoch 重新评估 pass@8
if epoch % 5 == 0:
for p in prompt_pool:
p['pass_at_8'] = compute_pass_at_k(model, p, verifier, k=8)
# 2. 根据当前 epoch 选难度区间
if epoch < total_epochs * 0.2:
target_range = (0.5, 0.8) # 简单
elif epoch < total_epochs * 0.6:
target_range = (0.2, 0.5) # 中等
else:
target_range = (0.0, 0.2) # 困难
# 3. 采样训练
batch = [p for p in prompt_pool if target_range[0] <= p['pass_at_8'] < target_range[1]]
train_step(model, batch, verifier)
17.6.3 SFT 的课程化
SFT 阶段也可以课程化,但效果不如 RLVR 明显:
def sft_curriculum(data, epochs=3):
# 1. 按难度排序
sorted_data = sorted(data, key=lambda x: x['difficulty_score'])
# 2. 分 3 档
n = len(sorted_data)
easy = sorted_data[:n//3]
medium = sorted_data[n//3:2*n//3]
hard = sorted_data[2*n//3:]
# 3. 逐档训练
for epoch in range(epochs):
if epoch == 0:
train(easy)
elif epoch == 1:
train(easy + medium)
else:
train(easy + medium + hard)
踩坑:SFT 课程化容易导致”灾难性遗忘”——后学的内容覆盖前学。需要在每 epoch 混入 20% 旧数据。
17.7 动态配比:DoReMi
17.7.1 DoReMi 算法
DoReMi(Xie et al., 2023)是动态配比的代表方法。核心思想:用小模型预测各领域的”最优配比”,再用到大模型上。
1. 训练一个小参考模型(1B),用初始配比
2. 用 DoReMi 算法调整配比,最大化参考模型的 loss 下降
3. 用调整后的配比训练大模型(7B/70B)
17.7.2 DoReMi 配比调整
def doremi_adjust(domain_weights, reference_model, train_data_by_domain):
# 1. 当前配比下计算各域 loss
domain_losses = {}
for domain, weight in domain_weights.items():
data = sample(train_data_by_domain[domain], int(weight * batch_size))
loss = compute_loss(reference_model, data)
domain_losses[domain] = loss
# 2. 用 DoReMi 更新规则调整权重
total_loss = sum(domain_losses.values())
for domain in domain_weights:
# loss 高的域加权重(说明该域学得差,需要更多数据)
domain_weights[domain] *= (domain_losses[domain] / total_loss) * len(domain_weights)
# 3. 归一化
total = sum(domain_weights.values())
for domain in domain_weights:
domain_weights[domain] /= total
return domain_weights
17.7.3 DoReMi 的效果
在 Pile 数据集上,DoReMi 优化后的配比训练 Llama-7B:
- 平均 loss 下降 4%
- 在下游任务上提升 3-6%
适用场景:预训练 + SFT 都可用,但 RLVR 阶段不太适用(RLVR 配比主要看 pass@8)。
17.7.4 后训练配比动态化:从”静态超参”到”训练中的第一公民”
DoReMi 解决的是预训练阶段的配比。后训练阶段的配方在工业实践中仍以静态三轴为主(17.1),原因不是不想动态化,而是三个现实阻碍。2026 年的两个工作分别从工程整合与信号质量两个方向推进。
DataFlex:先把接口统一了(arXiv:2603.26164, 2026-03-27)
DataFlex(Hao Liang 等 25 人)的诊断很朴素但切中要害:数据选择、配比优化、样本重加权这三类方法,各自长在互相独立的代码库里,接口不一致。结果是三件事都做不成——可复现性差、公平对比做不了、实际集成成本高。
它的做法是把三者统一进一个框架(基于 LLaMA-Factory 构建),支持 sample selection、domain mixture adjustment、sample reweighting 三种范式,同时保持与原有训练流程完全兼容,可作为 drop-in 替换。
DomainPilot:直指 loss 信号的根本缺陷(arXiv:2607.22769, 2026-07-24)
DomainPilot(He Zhang)列出了工业级落地的三条硬阻碍,其中第三条是对 DoReMi 路线的直接质疑:
| 阻碍 | 具体问题 | 后果 |
|---|---|---|
| 选择成本 | 数据选择在 TB 级语料上是 O(N) 成本 | 全量选择不可行 |
| 配比优化 | 现有方案引入严重 I/O 瓶颈,或需要训练辅助参考模型 | 训练被拖慢 / 额外开销 |
| 信号混淆 | 样本级重加权依赖的 loss 信号混淆了噪声、难度与新颖性 | 配比调整方向本身是错的 |
第三条值得单独展开,因为它直接动摇了 17.7.2 那套更新规则的前提。DoReMi 的规则是”loss 高的域加权重”,隐含假设是高 loss = 学得差 = 需要更多数据。但在真实数据里,一个高 loss 的样本至少有三种可能:
- 噪声:标注错误或格式损坏的脏数据 → 应该降权甚至删除
- 难度:真正有价值但模型还没学会 → 应该加权重
- 新颖性:模型没见过的新分布 → 应该谨慎加权,并观察是否破坏已有能力
样本级重加权把这三种成分当作同一个东西处理,这正是很多”loss 引导动态配比”在干净 benchmark 上有效、在真实数据上抖动的原因。
DomainPilot 的两阶段应对:引入 token 级 domain loss 监控,在训练过程中捕获各域学习动态,不中断数据管线(针对阻碍 2 的 I/O 问题);在此基础上做两级优化:
| 阶段 | 引导信号 | 做什么 | 针对的问题 |
|---|---|---|---|
| 粗优化(coarse) | Scaling Law 引导 | 拟合各域的收敛曲线,导出配比调整的先验 | 阻碍 3——拟合的是趋势,天然平滑掉单点噪声 |
| 细优化(fine) | Mixing Law 引导 | 通过受控 sweep 实验建模跨域交互效应,精调配比 | 单域 Scaling Law 看不到的域间耦合 |
整套机制通过 patch-based 架构实现——把 domain-aware loss 计算注入既有训练框架,不必替换训练栈。这一点决定了它的落地成本:不是”换一套训练系统”,而是”给你的训练系统打一个补丁”。
工程判据:如果你的 loss 引导配比在真实数据上不稳定,先不要调学习率或权重,先做一个廉价诊断——把高 loss 样本抽样出来人工看 50 条。如果其中噪声占比超过 20%,说明你遇到的是信号混淆问题,需要的是噪声过滤或域级聚合(DomainPilot 的思路),而不是继续在样本级调权重。
⚠️ 注意适用范围:DomainPilot 的论文标题与实验设定是 Language Model Fine-Tuning(微调),并非预训练。把它当作”后训练配比的动态化方案”使用是合理的,但不应用其结论替换预训练阶段的 DoReMi。
17.8 配方验证
17.8.1 消融实验
任何配方调整都要做消融:
ablation_groups = [
("baseline", default_recipe),
("+5% math", modified_recipe_math_plus),
("-5% general", modified_recipe_general_minus),
("+5% code", modified_recipe_code_plus),
]
for name, recipe in ablation_groups:
model = train_sft(recipe)
scores = evaluate(model, benchmarks=['MMLU', 'GSM8K', 'HumanEval', ...])
print(f"{name}: {scores}")
17.8.2 帕累托前沿
不同配方在不同评测上表现不同,要找”帕累托最优”——没有其他配方在所有维度都不差。
配方 A: MMLU=70, GSM8K=85, HumanEval=60
配方 B: MMLU=72, GSM8K=80, HumanEval=65
配方 C: MMLU=68, GSM8K=88, HumanEval=58
A 不是 Pareto(被 B 在 MMLU/HumanEval 上超过)
B 不是 Pareto(被 A 在 GSM8K 上超过)
C 不是 Pareto(被 A 在 MMLU/HumanEval 上超过)
→ 三者都是 Pareto 最优
最终选择根据业务优先级——如果数学最重要,选 C。
17.8.3 可复现基准:NebulaExp-8B
消融实验与帕累托前沿解决的是”怎么比”,但还有一个更前置的问题:跟谁比。
本章引用的配方数字(Tulu-3 等)来自不同论文,每篇只披露自己关心的那部分。这带来一个实践困境——你想复现别人的配方,但缺少数据构造细节、过滤规则与训练超参,消融就变成了”从零猜”。NebulaExp 的作者在论文里直接把这一点列为动机:
现有工作大多不公开详细的数据构造、过滤规则与训练配方,这阻碍了社区的可复现性与轻量级模型优化。
NebulaExp 的回应是一个全透明、消融驱动的后训练管线(基于 Qwen3-8B-base),其披露的颗粒度可以作为一个基准线:
| 维度 | 披露内容 |
|---|---|
| 基座 | Qwen3-8B-base |
| 分支设计 | 两个正交分支:通用 instruct 模型 / 复杂推理专精模型 |
| 数据规模 | 3.84M 多源 SFT 样本 + 200K 可验证 RL 候选池 |
| 处理栈 | response distillation → multi-dimensional cross-verification filtering → fine-grained difficulty grading → task classification → diversity-aware sampling |
| SFT 方案 | Instruct 分支的三阶段优化 SFT(NebulaExp-Ins-SFT) |
| 实测轨迹 | Qwen3-8B-nothink 基线 55.01 → Ins-SFT 60.99 → GRPO 61.85(平均分) |
怎么用这个基准:
- 对齐你的处理栈:上表第三行的五步处理栈可以直接对照自查——你的管线缺了哪一步,就是与公开基准的差距所在。特别是 “multi-dimensional cross-verification filtering”(多维度交叉验证过滤),这是很多团队只做单维度质量分时的常见缺口。
- 校准量级预期:3.84M SFT + 200K RL 候选池是一个可参照的量级锚点。如果你的 SFT 数据在 100K 量级却期望同等效果,差距在数据规模而非配方法。
- 作为消融的对照组:做 17.8.1 的消融时,用 NebulaExp 的完整配置作为 baseline,比用自研配方更有说服力——评审者可以复现你的 baseline。
⚠️ 引用边界:本节的规模数字来自论文披露,但论文未给出各阶段配比的具体百分比——“全透明”指的是管线结构与处理步骤透明,不等于配方数值全部公开。若需要具体配比数字,仍需查阅论文正文表格或开源仓库。此外该工作基于单一基座(Qwen3-8B-base),跨基座的迁移性未经验证。
17.9 配方工业 SOP
1. 三轴标注:每条数据标 domain/task/difficulty
2. 初始配方:参考 Tulu-3 标杆
3. 小模型消融:1B 模型快速验证
4. DoReMi 优化:用 1B 模型找最优配比
5. 大模型验证:7B 模型做最终验证
6. 课程化设计:SFT 3 epoch / RLVR 50 epoch
7. 三阶段串联:SFT → DPO → RLVR
8. 帕累托分析:多配方对比
9. 业务选择:按优先级选 Pareto 配方
10. 上线 + A/B 测试 + 反馈
第 18 章将深入多轮对话与 Agent 数据——这是后训练最前沿的难点。