MiMo V2.6开源后训练recipe全解析:从数据配方到避坑实践
最近开源圈子最热闹的话题之一,就是 MiMo V2.6 把完整后训练 recipe 放了出来。注意,我这里说的不是权重,不是 demo,而是 recipe——也就是从基座模型到最终对话模型之间,那一整套“怎么喂数据、怎么调策略、怎么踩坑”的操作方案。这个东西平时各家都捂得很严,因为模型架构可以抄,数据配比和训练策略才是真正拉开差距的地方。所以这份开源 recipe 的价值,比单纯放权重高得多。
这篇内容适合正在做模型对齐、后训练、或者想把自己手里的基座模型调成能用的对话模型的算法工程师。不管你是做开源社区的复现派,还是内部训练管线搭建派,这份 recipe 都值得逐行看。我会按我自己的理解把它拆成五个部分:recipe 到底开源了什么、数据配方怎么设计、训练链路怎么排布、如何落地复现到自己的模型上,以及我踩过的坑和排查思路。
1. 开源recipe到底开源了什么
1.1 为什么说recipe比权重更值钱
很多同学看到开源模型第一反应是下权重、跑推理。但权重只代表“结果”,recipe 代表的才是“过程”。同样一个基座模型,用不同的数据顺序、不同的优化器参数、不同的偏好优化策略去训练,最后出来的模型性格、能力边界、幻觉率完全不一样。这也是为什么很多团队能蒸馏出看着差不多的模型,但一上复杂推理和长对话就露馅。
MiMo V2.6 开源的 recipe 之所以引起我注意,是因为它直接铺开了一条完整路径:从预训练好的权重开始,到 SFT、再到偏好优化、再到最后的平衡性调整。里面包含数据样本示例、超参配置、调度策略、甚至评估脚本的组织方式。也就是说,你不光能知道它用了什么,还能知道它为什么这么用,以及每一步想解决什么问题。
用做菜打比方,权重是“别人做好的菜”,recipe 是“从洗菜到装盘的操作手册”。你拿到的 recipe 如果足够详细,就不只是能复刻味道,还能根据自己手头的食材改配方。对于开源社区来说,这类信息比静态权重更能推动大家往前走。
1.2 MiMo V2.6 recipe的组成结构
从公开资料来看,MiMo V2.6 的 recipe 大致可以分成四大块:
| 模块 | 核心内容 | 解决的问题 |
|---|---|---|
| 数据配方 | 指令数据、偏好对、通用语料的配比与筛选规则 | 决定模型学什么、往哪个方向对齐 |
| 训练策略 | SFT、偏好优化的顺序、迭代次数、学习率调度 | 决定模型怎么学、会不会遗忘或发散 |
| 稳定性措施 | 梯度裁剪、loss 监控、回滚策略、MoE 负载均衡 | 保证训练过程不崩、可复现 |
| 评估方案 | 基准测试集合、规则评测、人工抽样标准 | 判断每一步改动到底有没有用 |
这里特别值得注意的一点是,MiMo V2.6 是 MoE 架构。MoE 模型的后训练和稠密模型不完全一样:专家路由的负载均衡、共享专家的更新策略、以及后训练阶段是否冻结部分参数,都会直接影响最终效果。好的 recipe 会把这类细节也写清楚,而不是只丢个 loss 曲线出来。
所以解读这份 recipe,我建议你带着三个问题去读:每个阶段的数据分布长什么样?每个阶段的超参变化背后的动机是什么?它在哪些地方做了“保守”处理、哪些地方做了“激进”处理?把这三个问题搞明白,整条 recipe 的骨架就出来了。
2. 数据配方:后训练的“食材”怎么选、怎么配
2.1 质量过滤与去重的常见手法
后训练的第一步永远不是训练,而是整理食材。MiMo V2.6 这类开源 recipe 里通常会强调几个数据清理步骤,我把它们按优先级整理如下:
- 规则过滤:用启发式规则去掉包含乱码、过短内容、重复字符、异常标点符号的样本。这一步成本最低,但能滤掉不少低质量数据。
- 去重:文本层面做 MinHash 去重,语义层面做 embedding 相似度去重。去重不彻底,模型容易记住训练集中重复出现的内容,表现为复读机式输出。
- 质量打分筛选:有些方案会用一个小模型给候选样本打分,比如按连贯性、信息密度、是否符合问答格式来排序,只保留高分段数据。这类做法很吃计算资源,但效果直观。
- PPL 过滤:用现成语言模型计算样本困惑度,把“模型觉得太奇怪”的样本剔除。需要注意,PPL 过滤对领域数据不太友好,可能误伤专业内容。
我自己的经验是,规则过滤加去重是底线,质量打分筛选是加分项。如果你手头数据量不大,比如只有几万条指令数据,前期规则清洗的重要性反而更高——因为少量样本里每一条都很珍贵,清洗不干净影响会被放大。
2.2 指令数据与偏好数据的构造逻辑
数据配方里最核心的两类食材,一是指令数据,二是偏好对数据。
指令数据的核心在于多样性,而不是数量。只堆数学题,模型就只会做数学题;只堆闲聊,模型就变成聊天机器人。好的 recipe 会刻意让数据覆盖代码、数学、逻辑推理、日常问答、多轮对话、工具调用等不同场景,并且每个场景内再分难度梯度。这样模型在后训练之后既不会丢掉基座模型原有的通用能力,又能学会指令跟随。
难度梯度这一点很多人容易忽略。如果数据全是简单指令,模型学到的只是表面格式;如果全是高难度样本,训练初期 loss 就下不去。实际操作中,我喜欢按“简单 50%、中等 30%、困难 20%”这样的比例起步,跑完一个 epoch 看分项指标再调整。
偏好对数据的构造则更讲究。每个偏好对包含一个 prompt、一个被选中的回答(chosen)和一个被拒绝的回答(rejected)。关键在于,被拒绝的回答最好和 chosen 在长度、风格上接近,但内容质量上有明显差距。如果两者的差距一眼就能看出来,模型学不到细微的偏好;如果太接近,模型又容易把边界学歪。
构造偏好对还有一个实操技巧:不要只用模型自己的负采样,可以混入一部分人工撰写的错误答案,或者故意构造“看似流畅但有事实错误”的样本。这能让模型在偏好优化阶段就学会拒绝表面正确的内容,对减少幻觉很有帮助。
2.3 数据配比为什么不能一步到位
一个很容易犯的错误是:希望一开始就把数据配比定死,然后整个训练过程都按这个比例走。MiMo V2.6 的 recipe 给我的感觉是,数据配比是动态调整的。
举个例子:第一轮 SFT 阶段,指令数据可能占 70%,通用语料占 30%,目的是让模型学会指令跟随的同时不遗忘基座能力。到了后续的偏好优化阶段,通用语料占比会下降,偏好对数据的占比会上升,因为模型已经基本具备指令跟随能力,重点要转向“什么该说什么不该说”。
动态调整背后有一个隐藏逻辑:后训练不是一次性把模型“煮熟”,而是分阶段地塑造行为。每个阶段的数据配比,应该回答“这个阶段我最想解决什么问题”这个命题。如果你发现模型多轮对话能力差,就应该在下一阶段加大多轮对话数据的比重,而不是推翻重来。
所以我建议你记录每一次数据配比的变更,并且每次变更只动一个变量。一次只加一个维度的数据量,才能准确判断是哪部分数据在起作用。边练边调,比闭门造车一次定稿要靠谱得多。
3. 训练策略:从SFT到偏好优化的完整链路
3.1 SFT阶段:打基础,别贪快
SFT 阶段是后训练的起点。这个词全称是 Supervised Fine-Tuning,也就是用标注好的输入-输出对训练模型学习指令跟随。很多 recipe 会把 SFT 的样本量控制在几十万条这个量级,并且只用 1 到 3 个 epoch。为什么 epoch 数这么少?因为后训练阶段模型已经具备很强的语言能力,SFT 的主要目标是暴露行为模式,而不是重新学习知识。跑太多轮,模型会过拟合到指令数据的表面格式,反而丢失泛化性。
SFT 阶段有三个细节值得留意:
- 学习率要低:一般比预训练阶段低一到两个数量级,常见区间在 1e-5 到 5e-5。学习率太高,模型参数被大幅扰动,基座能力会快速退化,表现就是训练 loss 好看,但通用 benchmark 掉点。
- 序列长度要够:如果数据里有大量长文档或长代码,序列长度设短了会导致截断,模型学不到完整的上下文关联。MiMo 这类模型通常支持较长的上下文长度,训练时建议保持与推理时一致的序列长度。
- loss mask 必须做对:指令部分不应该计入 loss,只对回答部分计算损失。这一点实现上不难,但很容易遗漏。如果指令 token 也参与梯度更新,模型会学到“跟着 prompt 念”,而不是“按要求回答”。
SFT 完成后,我习惯先做一轮“烟雾测试”,也就是拿几十个典型 prompt 看输出风格是否正常、格式是否正确。这个阶段通常还看不出模型有多聪明,但能看出它有没有学会基本的对话礼仪和格式要求。
3.2 DPO/RLHF阶段:调偏好,控分寸
SFT 完成后,模型已经能“好好说话”了,但还没学会“什么不该说”。这时需要偏好优化阶段上场。MiMo V2.6 这类新模型的 recipe 通常会选择 DPO 或它的变体作为主力算法,因为 DPO 不依赖单独的奖励模型,实现简单、训练稳定,也更容易复现。
DPO 的核心思路很简单:让模型在有偏好对的情况下,增大 chosen 样本的概率,减小 rejected 样本的概率,同时用 KL 散度约束模型的更新幅度,避免和参考模型偏离太远。这里有一个降温和控制参数需要特别留意:通常表现为 beta 系数,它决定模型偏离参考模型的程度。beta 太大,模型会被偏好对强行拉偏,容易产生重复和自嗨;beta 太小,偏好优化起不到作用,模型学不到被拒绝样本中的教训。
除了 DPO,部分 recipe 也会提到从拒绝采样中构造偏好对。就是说,先用当前模型采样一批回答,再用规则或人工挑出好和坏的两个回答,组成新的偏好对,重新训练。这个小技巧的妙处在于,偏好对是“自己产的”,数据分布和真实推理状态更接近,不会出现训练数据与推理数据分布不一致的问题。
我观察到的一个经验是,偏好优化阶段不要只跑一轮。比较有效的做法是:第一轮偏好优化后,接一轮评估,再从失败样本里筛选出代表性的坏案例,构造新的偏好对,继续第二轮。这个循环可以跑两三轮,每一轮模型都会在上一轮的薄弱点上有明显改善。
3.3 关键超参数与MoE模型训练细节
如果你训练的是稠密模型,直接套用通用超参问题不大。但 MiMo V2.6 是 MoE 架构,这里有几个额外细节能决定成败。
第一个是路由负载均衡。MoE 模型在训练过程中,如果某些专家经常被选中、某些专家几乎不参与,模型整体能力会被少数专家扛住,收敛变慢且能力不均。常见的解决方法是加 load balancing loss 惩罚路由分布的不均匀度,loss 权重通常设在一个很小的值,比如 0.01 以下。后训练阶段这个值到底开多大,不同 recipe 有不同选择,我的建议是先在短轮次训练下对比“开”与“不开”的评测结果再定。
第二个是共享专家和冻结策略。有些实现里会在后训练阶段冻结大部分通用层,只训练一部分低秩适配模块或特定层。好处是训练成本大幅下降,坏处是如果冻结部位不当,模型能力提升有限。这个没有统一标准,只能根据你的计算资源和目标效果折中选择。
第三个是梯度累积与批次大小。MoE 模型显存占用高,通常需要梯度累积来模拟较大的有效 batch size。偏大 batch 的好处是训练稳定,坏处是模型更新频率低,跑同样多步数时学习率调度需要更慢地衰减。我自己习惯把有效 batch size 控制在 128 到 512 之间,具体看数据量大小。
4. 把recipe复现到自己的模型上
4.1 显存、框架与并行方案
读再多 recipe,最后都得落到自己的训练环境里。先说显存估算:一个 7B 到 8B 稠密模型做 SFT,16 张 80G 显存的卡是舒适区;如果是同规模的 MoE 模型,激活参数少,理论显存压力更小,但路由和中间状态也会占显存,实际省不了太多。MiMo V2.6 这类模型的完整复现,建议至少准备 8 卡以上的集群。
框架选择上,目前主流是 Hugging Face Transformers + DeepSpeed,或者用 Megatron-LM 做大规模并行。后训练阶段不需要预训练那么重的并行,DeepSpeed ZeRO-2 或 ZeRO-3 完全够用。如果是 MoE 模型,需要确认框架对 MoE 的专家并行和负载均衡 loss 支持到哪一步,否则要自己适配。
并行方案上,如果没有跨机条件,单机多卡优先做数据并行,简单可靠。跨机训练时,可以用张量并行降低单卡显存压力,但通信开销会增加。我的建议是:能数据并行解决的问题,不要轻易上张量并行。
4.2 一份可参考的训练管线
下面这个简化的训练编排,是我在实际复现过程中验证过比较顺手的一个结构。
先讲数据准备阶段。在启动训练之前在项目根目录创建data、output、scripts三个目录。数据清洗脚本放进scripts,清洗后的数据存到data。这个目录结构不要图省事,因为 recipe 迭代过程中的对比实验很多,目录混乱会让回归测试变得很痛苦。
下面是一个典型 SFT 训练脚本的 Python 风格伪代码,帮助你快速理解几个关键模块:
from transformers import AutoModelForCausalLM, AutoTokenizer from datasets import load_dataset from trl import SFTTrainer model = AutoModelForCausalLM.from_pretrained("MiMo-7B-Base") tokenizer = AutoTokenizer.from_pretrained("MiMo-7B-Base") # 关键:指令部分要 mask,只让模型学习回答部分 def formatting_func(example): return tokenizer.apply_chat_template( example["messages"], tokenize=False, add_generation_prompt=False, mask_instruction=True, ) trainer = SFTTrainer( model=model, tokenizer=tokenizer, train_dataset=load_dataset("your_cleaned_data.jsonl"), formatting_func=formatting_func, args=TrainingArguments( output_dir="./output/mimo_sft", learning_rate=2e-5, # 低学习率 per_device_train_batch_size=4, gradient_accumulation_steps=32, # 模拟大 batch num_train_epochs=2, # epoch 不要多 warmup_ratio=0.03, bf16=True, logging_steps=10, save_strategy="steps", save_steps=100, ), ) trainer.train()再讲训练和保存。每次跑完一个阶段,不要把最后一个 checkpoint 当成唯一结果。保存到output目录时带上 step 和 eval 指标命名,比如sft_epoch2_bs128_lr2e-5。这样后续做对比时,你可以随时回到之前某个 checkpoint 重新走偏好优化的分支路径,而不是一条路走到黑。
训练过程中日志要留全。除了 loss,还要记录学习率变化、梯度范数、吞吐量,以及每个评测节点上的分数。日志本身就是 recipe 的一部分。很多复现项目最终跑偏,回头看日志才发现某个阶段的学习率异常或数据混合出问题。
4.3 评测怎么搭:榜单分数和实际体验要兼顾
评测是复现 recipe 的关键闭环。这里要避免一个误区:只盯着 MMLU、GPQA、HumanEval 这类公开榜单。榜单分数高,不代表对话体验好。后训练的目标是“对齐”,对齐的核心是让模型按用户期望的方式完成真实任务。所以评测矩阵至少要包含三块:
| 评测类型 | 覆盖能力 | 常用例子 |
|---|---|---|
| 通用知识 | 基座能力是否保留 | MMLU、CMMLU |
| 代码与推理 | 复杂任务能力 | HumanEval、GSM8K、GPQA |
| 对话与守则 | 对齐质量、安全性 | 多轮对话抽样、规则检查、拒答率 |
我自己的实际操作是,每个 SFT 和 DPO 阶段结束后,先用一套固定的评测 prompt 跑一遍,再手动抽看 30 条左右输出。固定评测 prompt 保证横向对比公平;人工抽看能捕捉到评测集覆盖不到的细节,比如语气、废话率、重复度、对用户情绪的理解。
评测结果不一定每次都进步。有时候某个榜单涨了、另一个榜单跌了,这是正常现象。关键在于你要能判断这次涨跌是不是符合当前训练阶段的目标。如果 DPO 阶段目标是降低有害输出,那对话安全指标上升、代码能力小幅下降,就可以接受。如果安全没提升、代码反而崩了,就要回头检查数据配比和超参。
5. 实操中的常见问题与排查清单
5.1 问题速查表
复现过程中最常见的问题,往往不是模型架构的问题,而是数据、超参、环境这些看似简单的环节。下面这一组问题对照和建议,是我从多次实操中整理出来的。
| 现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| loss 不下降或震荡 | 学习率过大、数据噪声高 | 降低学习率一个量级;检查数据是否有未清洗干净的错乱样本;确认 loss mask 是否正确 |
| loss 下降但输出全是复读 | 偏好对中 chosen 与 rejected 太接近 | 重构偏好对,拉大质量差距但保持风格接近;检查重复惩罚参数 |
| 通用 benchmark 掉点严重 | SFT 学习率过高、epoch 过多 | 回退到低学习率、减少 epoch;检查通用语料配比是否被指令数据挤占 |
| 多轮对话能力差 | 指令数据中多轮样本不足 | 增加多轮对话数据,构造真实场景的长对话样本 |
| 训练中显存溢出 | batch size 过大、序列过长 | 降低 per-device batch size,增加梯度累积步数;考虑开启序列打包 |
| MoE 路由不稳定 | 负载均衡 loss 缺失或权重太小 | 增加 load balancing loss 的权重;监控路由分布,确认每个专家利用率 |
这个表格不是标准答案,更像一份排查起点。遇到问题先确认数据、再确认超参、再确认实现,这个顺序能帮你少走很多弯路。
5.2 三条独家避坑心得
第一,多换几个随机种子跑短实验。后训练本身随机性不小,只跑一次实验就下结论很危险。我通常会在小规模数据上跑三组不同 seed 的短实验,看效果是否稳定。如果三组结果差异巨大,说明当前方案太脆弱,不要贸然放大规模。
第二,不要在每个阶段结束后只评估一次。后训练过程里,模型在不同 checkpoint 上的表现会有波动,可能 500 步效果最好、1000 步效果已经下降。保存多个 checkpoint,每次验证都记录下来。最终使用时不一定取最后一个 checkpoint,而是取验证效果最好的那个。这个操作习惯救过我好几次。
第三,记录一个实验笔记文件。每次实验的关键信息:数据来源、数据量、配比、学习率、随机种子、评测分数、备注,全部写进一个表格。后训练迭代次数多、分支多,没有记录的话,一周之后你完全记不清自己试过什么。所谓“可复现的 recipe”,本质就是一条完整的实验记录。
6. 写在最后:复现recipe的三个习惯
我身边很多朋友拿到 MiMo V2.6 开源 recipe 之后,第一反应都是直接找权重、跑推理、再看看有没有现成测试脚本。我能理解这种冲动,但如果你真的想从中收获最大的价值,我建议养成三个习惯。
第一个习惯是逐行读配置,而不是跳着看。recipe 里的每一个超参都有原因,读得快等于没读。哪怕只是warmup_ratio=0.03这种小参数,背后也关系着训练早期稳定性。第二个习惯是每次都只改一个变量。这听起来像废话,但实际执行起来很难,因为总想着“顺手把另一个参数也调了吧”。严格遵守一次只改一个变量,你的实验结论才可信。第三个习惯是接受“失败实验也是收获”。很多时候跑完一轮数据,评测分数不升反降,但你会因此知道这个方向是死胡同,这本身就是信息。
后训练的 recipe 不是魔法,不会让你跑完一遍就得到一个完美模型。它更像一份地图,告诉你大方向在哪、哪里容易翻车、怎么绕开前面的坑。方向对了,剩下的事情只是时间和资源的问题。希望这篇解读能帮你在复现 MiMo V2.6 后训练 recipe 的路上少踩几个坑。