一说到开源大模型,大家首先想到的往往是“权重能下了,赶紧加载跑个demo”。但真正做过训练的人心里都清楚,权重只是结果,决定一个模型能不能从“会续写文本”变成“会干活”,靠的是后面那套看不见的后训练玩法。最近我把MiMo V2.6的开源后训练recipe完整翻了一遍,趁印象还在,写篇解读。这篇适合两类人:一类是想拿开源模型做二次开发、打算从基座模型自己训出对话能力的工程师;另一类是单纯好奇开源大模型后训练到底怎么组织数据、怎么调参、怎么对齐的研究型读者。读完你至少能搞清楚一件事:一份可复现的后训练recipe,究竟该包含哪些东西。
如果你没接触过“recipe”这个词,我先做个简单定位。在机器学习工程里,recipe不是菜谱那种文学化表达,它指的是把数据构建、训练顺序、超参数、目标函数、评估脚本这些内容固化成一套可重复执行的方案。MiMo V2.6把这一整套东西作为独立模块开源出来,实际价值比单纯放个权重文件高很多。
1. MiMo V2.6开源了什么:权重之外的“可复现训练配方”
1.1 先看开源仓库里到底放了哪些东西
我第一次打开这份recipe的时候,第一反应是松了口气。它没有把训练流程埋在README里让读者自己猜,而是按模块拆成了几个很清晰的目录:数据组织、训练配置、脚本调度、评估工具各归各的。
通常你会在这里看到这些内容:
- 一套按阶段拆分的训练配置,比如sft、偏好对齐、安全微调各自独立的参数文件;
- 数据预处理脚本,包括数据清洗、格式转换、指令模板拼装逻辑;
- 启动和调度的Shell脚本,处理断点续训、分布式启动、日志上报这类工程问题;
- 评估脚本和相关数据集的读取逻辑,方便复现者用同一套口径验证模型效果;
- 少量“配方说明”文档,写清楚数据配比、训练顺序、关键超参的选择理由。
这套结构最有价值的地方在于:它不是把一个训练脚本扔给你就完事,而是把决定训练效果的隐性知识尽量显式化。做过模型训练的人都知道,模型效果差的时候,问题往往不出在模型结构上,而是出在数据怎么配、模板怎么拼、超参怎么调这些琐碎环节上。这些环节在论文里往往一句话带过,但如果真照着论文自己搭,很容易在某个细节上翻车。
1.2 recipe里真正承载的信息
我把recipe的核心信息归纳成五个维度:
| 维度 | 对应内容 | 直接回答的问题 |
|---|---|---|
| 数据维度 | 数据来源、清洗规则、配比、模板格式 | 用什么数据训练、数据长什么样 |
| 流程维度 | 阶段划分、阶段顺序、哪些数据用在哪段 | 先训什么、后训什么、为什么 |
| 参数维度 | 学习率、batch size、序列长度、epoch数 | 怎么训练才稳、怎么避免失控 |
| 目标维度 | loss设计、mask策略、是否引入额外正则 | 优化什么、哪些token要被算loss |
| 工程维度 | 分布式策略、checkpoint策略、断点续训 | 怎么在真实环境里跑起来 |
这五个维度缺一不可。只给超参数不给数据,你跑出来是另一回事;只给数据不给mask策略,你连交叉熵都算不对。MiMo V2.6这份recipe之所以值得解读,恰恰因为它把这几块都补齐了,达到“按照文档能重跑出同档次模型”的程度。
1.3 与单纯开源权重相比,价值差在哪
只开源权重,用户可以下载、推理、部署,但想做领域微调或者继续对齐训练的人,还得自己想办法搞数据、调超参、写训练循环。开源recipe相当于把从基座模型到成品模型的“最后一公里”也开放了出来。
我自己的体会是,这个动作在开源生态里比想象中更少见。很多项目会公开评测分数,但不会告诉你训练时用了哪些数据配比;会说明用了DPO,但不会告诉你beta取值和即时数据构造细节。MiMo V2.6把后训练recipe单独开源,相当于把“我们怎么把模型调成现在这样”整个摊开,对想复现和做改动的人来说,省掉的是大量试错成本。
2. 后训练的工作被recipe拆成了哪几步
2.1 后训练为什么永远绕不开“能力与安全”的平衡
基座模型预训练阶段学的是语言规律和世界知识,它的目标是预测下一token。到了后训练阶段,目标变成“听懂指令、给出有用且安全的回答”,这本质上是在一个已经收敛的模型上重新塑形概率分布。
塑形过程中最棘手的是平衡:一味追求有用性,模型可能顺着用户的危险请求给出有害内容;一味追求安全拒绝,模型又会变成一个只会说“我无法回答”的废物。MiMo V2.6的recipe把这个问题显式拆成了多个阶段,每个阶段只处理一个主要矛盾,而不是试图在单次训练里把所有目标都塞进去。
2.2 一份典型recipe的三段式流程
我梳理出来的主流程是这样。
| 阶段 | 主要目标 | 输入数据 | 训练方式 |
|---|---|---|---|
| 指令微调 | 让模型学会遵循指令、格式化回复 | 人工整理和模型合成的对话数据 | SFT / cross-entropy |
| 偏好对齐 | 让模型偏好倾向于高质量、符合人类偏好的回复 | 成对偏好数据或模型采样数据 | DPO / 在线RL |
| 安全与边界 | 降低有害内容输出、提升拒绝合理性 | 安全特化数据、边界case集合 | SFT / 多层训练 |
三个阶段不是简单的顺序执行,中间还穿插了定期评估和坏case回收。比如偏好对齐阶段训了一版之后,要拿新模型去跑一轮sampling,把仍然失败的case回收进下一轮数据里。这个“训练—采样—筛选—再训练”的闭环,是后训练效果能不断提升的关键。MiMo V2.6的recipe里对这部分流程有明确的脚本支撑,不是只给一个单向训练命令。
2.3 和其他开源后处理方案比,它的特点是什么
过去一年公开的大模型后训练方案越来越多,比如Llama系列有post-training的说明,DeepSeek R1把强化学习流程讲得比较透,Qwen系列也逐步放出了一些微调脚本。MiMo V2.6这份recipe给我的整体观感是“工程化优先”:没有特别花哨的算法创新,重点放在让中小团队能用有限的算力把流程跑通。
这体现在几个选择上:偏好对齐默认走DPO而不是纯在线强化学习,因为DPO实现简单、显存开销小、对数据质量要求相对低;训练配置默认采用bf16混合精度和标准AdamW,避免在优化器上引入额外复杂度;数据打包采用packed sequence减少padding浪费,在有限算力下提高吞吐。这些选择单独看都不新奇,组合在一起就是一套很少踩坑的“稳妥路线”。
3. SFT阶段的recipe:数据配比和模板是最容易被低估的
3.1 数据不是越多越好,配比才是
很多人以为SFT就是“拿一堆对话数据继续训练模型”,但真正做过就会知道,数据配比对最终效果的影响远大于训练轮数的影响。MiMo V2.6的recipe在数据组织上明显花了不少心思,各类数据不是简单混在一起喂进模型,而是有明确的比例和顺序。
以我看到的样本为例,SFT数据大致包含这几个类别:
- 通用指令与问答,覆盖日常知识、生活场景、办公辅助;
- 数学与逻辑推理,包含分步推导、代码求解、证明类问题;
- 代码生成与解释,覆盖主流语言、常见框架、代码调试场景;
- 多轮对话,重点是上下文连贯性和指代消解;
- 安全与拒绝回答样例,用来给模型划定行为边界;
- 格式化类样例,比如JSON输出、表格输出、结构化总结。
其中安全类样例数量不需要很多,但必须分布均匀,否则模型容易在某些域上过度拒答。通用指令类数量最多,但过多重复会让模型变得啰嗦。数学和代码类占比太高会让模型在闲聊场景显得机械,占比太低又会让推理能力不足。这份recipe的配比逻辑很务实:基础能力类数据保证覆盖面,特化能力类数据按目标场景做定向加强。
3.2 指令模板为什么必须严格统一
一个特别容易被新手忽略的点是指令模板。系统提示词、用户消息、助手回复之间的分隔符,往往在数据整理阶段就已经被固化进字符串里。MiMo V2.6的recipe对模板一致性要求非常高,因为后训练阶段模型会把模板当成一种格式先验来学习。
我见过不少微调项目出现这样的问题:数据整理时用了一套模板,训练时代码里拼消息又用了另一套模板,结果模型在推理时经常出现“把系统提示词复述出来”“回答前多一个听不懂的前缀”之类的诡异行为。要解决这个问题,不需要花哨的手段,只需要把模板拼装逻辑抽成一个统一函数,数据预处理和训练循环都调用同一个入口。MiMo V2.6的recipe里,这类模板逻辑和具体训练算法解耦得很干净,改起来不容易出错。
3.3 序列长度、学习率、packing策略如何协同
SFT阶段的超参选择,核心是在“看得稳”和“跑得快”之间找平衡点。我在这份recipe里看到的一组经验值是:学习率取1e-5到2e-5之间,batch size视显存尽量增大,序列长度按训练数据的实际长度分布来定,而不是一律拉满到4096甚至8192。
如果序列长度设置成8192,但大多数训练样本都不到2000 token,padding部分会白白浪费算力。正确做法是先统计训练数据集的token长度分布,找一个能覆盖大部分样本的合理长度。recipe里对此有明确的统计脚本,会先输出分布再让你决定训练长度,这个细节很值得借鉴。至于packed sequence,它的思路是把多个短样本拼进同一条样本里训练,提高GPU利用率,但前提是attention mask必须正确隔开不同样本,否则跨样本注意力会污染训练。后文我会单独展开这一点。
3.4 怎么判断SFT阶段真的训好了
SFT阶段的收敛判断没有一劳永逸的指标。训练loss降下来只是一个必要不充分条件,关键在于评估集上的指令遵循能力是否达到预期。MiMo V2.6的recipe侧重的做法是:训练过程中定期用一小批固定评测集做采样式评估,观察模型输出的格式正确率、拒绝率、指令遵循率。
一个常见的错误是训练轮数过多。SFT数据量通常不大,训练超过1到2个epoch之后,模型开始过拟合指令数据的表面格式,表现为在开放域评测上指标下降、回答模板化加剧。所以我自己的经验是:SFT阶段宁可少训一轮,也不要让模型“背死”数据。recipe里默认的epoch设置也偏保守,大约在1到3轮之间,具体还要看数据规模和模型尺寸。
4. 偏好对齐阶段:DPO为主、GRPO作为补充
4.1 为什么SFT之后还要再做一轮偏好对齐
SFT解决的是“模型知道怎么回答”的问题,但“知道怎么回答”和“总是给出用户想要的高质量回答”之间还有很大距离。SFT数据里每一条都是人工整理的标准答案,模型学到的是平均行为;而真实用户场景中,面对同一个问题可能有一堆候选回复,有的清晰简洁,有的冗长绕圈,有的存在轻微事实错误。
偏好对齐阶段要解决的正是这个选择问题:让模型在多个合格回答中倾向于输出更符合人类偏好的那个。MiMo V2.6的recipe把偏好对齐放在SFT之后,逻辑很清楚:先让模型具备基本能力,再通过偏好数据重新分布概率,让它更“会挑”。
4.2 DPO的数据构造与beta取值
DPO(Direct Preference Optimization)现在几乎是开源模型做偏好对齐的事实标准,因为它不需要单独训练一个奖励模型,只需要成对的偏好数据。MiMo V2.6的recipe默认走DPO,我猜主要是看中它的工程简洁性。
DPO的输入是chosen样本和rejected样本,目标是拉大模型对chosen的生成概率与对rejected的生成概率之间的差距。这里的核心超参是beta,它控制对参考模型偏离的惩罚强度。beta太小,模型容易在偏好数据上过拟合,导致正常能力退化;beta太大,偏好信号又学不进去。我在多个项目里用过的beta范围基本在0.1到0.3之间,这份recipe给出的默认值也落在这个区间。
还有一个关键细节:DPO阶段通常需要固定一份reference logprob,也就是用SFT阶段产出的模型先对偏好数据做一次前向计算,缓存下来的对数概率作为后续训练的参考基线。这份recipe把reference计算也封装进了训练脚本,避免训练过程里重复算两类model导致的不一致。
4.3 在线GRPO的位置:偏好对齐的可选补充
GRPO(Group Relative Policy Optimization)是DeepSeek R1带火的一种在线强化学习方案,特点是每个问题采样一组回复,组内相对比较出reward信号,不需要独立的reward model。MiMo V2.6的recipe里没有把GRPO作为默认路线,而是作为补充项放进来了,这很符合它在“稳妥路线”上的定位。
GRPO适合数学、代码这类结果可验证的任务,因为可以用确定性规则给回答打分;但放到开放域对话里,打分标准很难统一,训练容易不稳定。所以我的理解是:如果目标场景偏重推理和代码,可以考虑在DPO之后用GRPO做一轮强化;如果目标是通用对话助手,DPO已经能覆盖大部分需求,不必冒着稳定性风险上在线RL。
4.4 别忽略长度偏差:偏好数据里最容易藏脏东西
偏好对齐阶段最隐蔽的坑是长度偏差。人类标注者在对比两个回答时,往往会不自觉地偏袒更长、更详细的回答,于是chosen样本普遍比rejected样本长。模型学到这个规律后,就会倾向于生成冗长回复来“刷偏好”,哪怕内容质量并没有更好。
MiMo V2.6的recipe对这个问题是有明确意识的。它一方面在数据构造阶段加入长度匹配的筛选规则,让chosen和rejected的长度分布尽可能接近;另一方面在DPO的loss里引入长度归一化处理,避免模型靠长度取胜。这个细节很多项目不会写进文档,但实际影响非常大,我建议所有打算做偏好对齐的团队都检查一下自己数据的长度偏差曲线。
5. 关键超参与loss设计:我看到的recipe默认值
5.1 默认超参一览
下面这张表是我从recipe里梳理出的一套可用默认值,适合中小规模稠密模型,具体尺寸不同会有微调,但作为起点已经足够。
| 参数项 | SFT阶段 | DPO阶段 | 备注 |
|---|---|---|---|
| 学习率 | 1e-5 ~ 2e-5 | 1e-6 ~ 5e-6 | DPO要低至少一个量级 |
| warmup比例 | 3% ~ 5% | 5% | 防止开局震荡 |
| batch size | 128 ~ 512样本 | 64 ~ 256样本 | 偏好数据通常更小 |
| 序列长度 | 按数据分布定 | 同左 | 统计后确定 |
| epoch数 | 1 ~ 3 | 1 ~ 2 | 太多必过拟合 |
| 优化器 | AdamW | AdamW | betas默认即可 |
| 精度 | bf16 | bf16 | 避免fp16溢出 |
| 梯度裁剪 | 1.0 | 1.0 | 防止loss spike |
这些数值本身不神奇,但组合起来是一套经过验证的稳定起跳点。新手一开始不需要想着调出什么新花样,先把这套默认值跑通,再根据自己数据的情况做针对性调整,是最安全的路径。
5.2 packed sequence的处理细节
packed sequence能在不改变训练本质的前提下显著提高吞吐。做法是把多个训练样本拼接到同一个长序列里,减少“给短样本补pad token”带来的浪费。但代价是注意力计算必须做到样本隔离,否则前一条样本的token会attend到后一条样本上。
这份recipe在处理packing时用了两个常用手段。一个是按样本边界构造segment mask,把注意力限制在同一样本内部;另一个是在loss计算阶段只对有效的assistant token做交叉熵,system和user token、以及packing时拼出来的跨样本边界都不会进入loss。如果packing逻辑写错,训练过程不会报错,但模型效果会逐渐变差且很难排查。我建议第一次实现packing的人,先用两三个短样本构造一个小测试集,打印出attention mask和loss mask,可视化确认边界是否干净。
5.3 checkpoint策略与断点续训
长时间训练最怕跑到一半断掉。MiMo V2.6的recipe在工程层面做了一个很实际的设定:每若干步保存一次checkpoint,同时保留优化器状态。优化器状态保留这个东西看起来不起眼,但对7B以上模型来说,重载模型权重后重新跑一步,AdamW的momentum和variance已经丢失,实际等于换了一个优化器状态继续训练,效果会出现可见的波动。
断点续训还要注意学习率调度器的对齐。如果warmup已经结束,恢复训练后再重新走一遍warmup,相当于给训练过程打了一个额外的学习率扰动。我对这类问题的一个处理习惯是:恢复训练时让学习率保持中断时的数值,而不是从头再来,这样对模型分布冲击最小。
5.4 最小复现:一份可改的配置和启动命令
把前面的原则落到实际,一份最简的SFT配置大致长这样。用YAML组织配置的好处是方便对比不同实验组,也不会把参数散落在训练代码里。
model: base_model_path: /path/to/mimo-base dtype: bf16 data: train_file: /path/to/sft_data.jsonl eval_file: /path/to/eval_data.jsonl max_seq_len: 4096 pack: true mask_assistant_only: true train: per_device_train_batch_size: 8 gradient_accumulation_steps: 16 learning_rate: 1.5e-5 lr_scheduler: cosine warmup_ratio: 0.03 num_train_epochs: 2 grad_clip: 1.0 save_steps: 500 optimizer: name: adamw beta1: 0.9 beta2: 0.95 weight_decay: 0.1启动命令通常长这样。实际多卡环境的launcher会因框架不同而不同,但loosely对应的流程是一致的。
# 单机8卡示例 torchrun --nproc_per_node=8 train_sft.py --config configs/sft.yaml只要数据格式正确,这套配置已经能稳定跑通一个7B模型的SFT流程。跑通之后再根据eval结果调整配比,比一开始就堆复杂技巧更有效。
6. 复现这套recipe时最容易翻车的四个地方
6.1 数据JSON格式和tokenizer不一致
第一件最容易被卡住的事是数据格式。训练脚本期待的数据格式通常是JSONL,每条消息按system、user、assistant分段,但很多外部数据集发布时用的是别的结构。如果你不检查格式,脚本会在某个深层动态里报出一个莫名其妙的字段异常。
我的建议是先写一个数据校验脚本,逐字段检查指令模板拼装后的消息结构是否符合预期。步骤很简单:读入一条样本,用模板拼成字符串,再用tokenizer编码一遍,解码回来肉眼检查一遍看有没有丢失分隔符或添加奇怪字符。这个检查只需要做几十条样本,但能挡掉大量后面阶段才暴露的问题。
6.2 训练中段出现loss spike
SFT和DPO训练过程中偶尔会遇到loss突然飙高的情况。常见原因有几个:数据里混入超长噪声样本、学习率偏高、batch里出现了大量相似重复样本。MiMo V2.6的recipe对这类风险做了两层防护:一层是数据处理阶段的清洗和去重,另一层是训练阶段设置了梯度裁剪。
实际排查loss spike时,我会先把spike发生前后的训练日志和数据处理日志对齐,看是不是某条特定数据触发的。如果spike只出现一次且loss能自己回落到正常区间,可以不处理;如果spike反复出现,就要回到数据清洗逻辑里找原因。不要一上来就降低学习率,很多时候问题根本不在学习率上。
6.3 显存不够时的降级路径
很多尝试复现的人用的是几张消费级显卡,显存可能只有24G甚至更少。MiMo V2.6的recipe虽然偏向中小算力场景,但7B模型全参数SFT在单卡上仍然跑不动。这时候的选择不是硬扛,而是降级:
- 使用LoRA或QLoRA做参数高效微调,优先保证流程可复现;
- 降低序列长度,4096改成2048,同时对训练数据做截断或筛选;
- 增大gradient_accumulation_steps,保持global batch size不变;
- 开启梯度checkpointing,用少量计算换显存。
降级之后的效果和全参数训练会有差异,但recipe里的数据配比和模板策略仍然可信。你学到的应该是这套数据做事的思路,而不是非得在同样的硬件上跑出一模一样的数字。
6.4 评估指标只盯公共榜单远远不够
公共Benchmark分数只是底线,不代表模型在真实场景中的表现。MiMo V2.6的recipe里最让我欣赏的一点,是内置了一套面向场景的评估方式:不只测MMLU、GSM8K这类经典套件,还会专门抽测指令遵循、拒绝合理性、格式准确性这些维度。
我见过太多项目对着一个公共榜单猛调,榜分上去了,实际对话体验却变差了。复现这套recipe时,我建议你也建立一套自己的固定评测集,里面至少包含三类样本:一是你目标场景的典型用户问题,二是安全边界问题,三是格式化输出需求样本。每次训练完都先跑这组评测,比一味盯着公共榜单更能反映真实水平。
7. 这套recipe能迁移到什么程度
7.1 如何把recipe里的思路迁移到自研模型
如果你手里有一个自己训练的基座模型,想通过后训练做成一个可用助手,MiMo V2.6这份recipe至少能带来一条清晰的行动路径。首先是照着它的数据分类方式把自己的数据整理成同样的结构;其次是按照“SFT→偏好对齐→安全加固”的阶段顺序来组织训练,而不是一股脑把所有数据混在一个阶段里;再次是把评估前置,在训练脚本里加入定期抽样评测的逻辑。
迁移时最忌讳的是把recipe当作固定代码硬套。不同的基座模型、不同的数据规模和不同的目标场景,都会导致最佳配置偏移。recipe的价值在于提供默认起点和排错顺序,而不是替你决定所有事情。
7.2 哪些部分应该按自己的场景重调
三个模块几乎必须重新调整。第一个是数据配比,通用助手和代码模型对推理类数据的需求量完全不同,直接用同一套比例不现实。第二个是模板格式,如果你要对接API或专用前端,系统提示词的设计需要跟着场景走。第三个是偏好对齐的数据来源,MiMo V2.6可以用自己的采样数据做偏好集,你大概率要另起炉灶。
安全边界类数据也要特别小心。开源项目提供的数据可能不完全契合你的合规要求和产品定位,直接照单全收不可取。正确的做法是保留recipe里的流程框架,把数据源替换成贴合自身场景的版本。
7.3 我真正觉得值得学的东西
说到底,这份recipe给我最大的启发不是某个超参数组合,而是它把“后训练”从一个只可意会的话题变成了可复现的工程实践。数据配比表、模板统一逻辑、偏好数据长度校正、packed sequence的mask处理、checkpoint恢复细节,每一个单点都是常见经验,但把它们完整串成一套可落地方案并公开出来的项目其实不多。
我自己在跑类似流程时,最大的体会是:后训练的成功不靠某个灵光一现的新loss,而靠数据、模板、参数、工程细节这些“笨功夫”是否做到位。MiMo V2.6这份recipe把笨功夫展示出来的意义,恰恰在于让后来者不必重新踩一遍所有暗坑。这才是开源后训练配方真正的价值所在。