看到这个标题的时候我正好在梳理多模态大模型的微调实验清单然后整个人就顿了一下。2025年机器学习顶会上这篇标题被截断的工作前半段信息量已经足够大了First SFT, Second RL, Third UPT: Continual Improving Multi-Modal LLM Reasoning via Unsu...。做多模态大模型Multi-Modal LLM下面简称MLLM的人看到SFT和RL这两个缩写都会会心一笑这是当下对齐技术的两板斧真正让人停下来琢磨的是UPT以及后面被截断的Unsu。如果是Unsupervised那这篇文章讲的就是一条完整的三阶段持续提升管道用有监督微调打底用强化学习对齐偏好最后用无监督信号继续涨点。我想把这个管道拆开揉碎结合我实际复现过程中的经验和踩坑记录给你一份能直接拿去用的参考。这个内容适合正在做MLLM微调、对齐、推理能力提升的工程师和研究者也适合刚入门、想搞清楚SFT、RL、偏好学习之间到底是什么关系的同学。1. 标题里的“三步走”到底说了什么1.1 为什么一步到位不行先聊一个最基础的问题为什么不能只做SFT或者只做RL非要分三步我的理解是这样的。SFTSupervised Fine-Tuning解决的问题是让模型“会说”和“会听”。你给它一批问题-答案对让它学会期望的作答格式、语气、知识范围。但问题是SFT的输出质量上限由采集的标注数据决定。标注数据里有什么模型就会到什么水平标注数据里没有的推理步骤模型很难自己长出来。RLReinforcement Learning解决的是“在答案空间里挑选更优解”。它不教模型具体的句子而是给定一组候选答案通过奖励信号告诉模型哪个更好、哪个更差然后让模型往好的方向偏移。问题在于纯RL直接跑在SFT模型上非常容易把模型带到“奖励黑客”的方向——模型学会了钻空子输出自相矛盾的长篇大论表面上符合奖励函数实际推理质量一塌糊涂。UPT结合标题前半段的语境我更倾向把它理解为Unsupervised Preference Tuning无监督偏好调优解决的是当外部标注资源和人工反馈都枯竭之后模型还能不能继续提升的问题。它通过模型自身产生多样性数据、自行构造偏好信号在没有任何人工标注的情况下完成下一轮自我优化。所以这三步不是简单的“按顺序跑一遍”而是每一层都在解决上一层的遗留问题。SFT先给模型一个稳定的行为基座RL在基座上做偏好对齐UPT在数据匮乏阶段用自生成信号做持续优化。这就是标题里Continual Improving的含义并不是跑完第三阶段就结束而是三个阶段可以在不同数据块上循环使用。1.2 三步各自的定位我习惯用一句话概括三步SFT让模型“毕业”RL让模型“拿高分”UPT让模型“在没有新题目的情况下自己刷题”。这句话虽然不那么严谨但能帮你快速想清楚什么时候该用哪一步。具体到技术层面三者的训练对象和损失函数也不同。SFT用的是标准的交叉熵损失监督信号是完整的参考答案RL用的是策略梯度或隐式奖励优化监督信号是偏好对chosen和rejectedUPT本质上也是偏好学习但它的偏好对不是人工标注的而是由模型自己采样、自己评判得到的。讽刺的是整条链路里最贵的不是RL而是UPT的数据构建逻辑——你要自己设计一套可信任的“伪偏好信号”否则第三阶段会把模型带偏。我在复现这个思路时也深刻体会到很多时候模型提升的瓶颈不再是模型结构或者训练技巧而是数据从哪里来。SFT靠人工RL靠人类偏好UPT靠算法自生成这三个阶段的演进本质上是“数据来源从外部转向内部”的演进。2. 第一步SFT先把“说人话”的地基打好2.1 数据构造才是真正的门槛如果你真的打算复现这套三步走我建议你把三分之二的精力压在SFT阶段的数据准备上。原因很简单RL和UPT的质量上限完全取决于SFT之后模型的初始状态。SFT阶段如果模型连基本的图文对应关系都不稳定后面任何强化学习都是空中楼阁。多模态SFT数据和纯文本SFT数据有一个本质区别每条训练样本里都有图像和文本两个模态而图像信息必须经过视觉编码器变成向量再和文本token拼接。这就带来两个实际问题。第一数据的图像-文本对齐质量必须严格把关。我做数据清洗的时候发现公开数据集里不少样本存在图文错配和OCR乱码问题。你看着两条样本差不多其实一张图配了错误的文本模型学着学着就把“图和文有什么关系”这个最根本的事情学歪了。我的做法是先跑一个小的视觉问答预检模型把所有训练样本过滤一遍凡是预检模型答非所问的一律剔除这个操作大概会过滤掉5%到10%的脏数据但换来的是训练稳定性的明显提升。第二CoTChain-of-Thought数据要比单步答案数据更值得投入。这里有个很关键的细节——推理能力提升的核心并不是“多算几步”而是模型能看到中间推理过程。你可以在SFT阶段给模型灌输大量带有完整思维链的答案让它在RL阶段输出的候选答案本身就具有“先推理、后结论”的结构。我实际测试过SFT数据里CoT占比在60%以上时后面RL阶段的收敛速度和稳定性都会有可感知的改善。2.2 训练细节与参数底座SFT阶段的训练参数配置取决于你用的是全参微调还是LoRA。我的个人建议是做多模态推理项目时优先尝试LoRA尤其是当你的显存资源有限的时候。我以一个7B规模的MLLM为例给出一个实测过的基础配置参考注意这只是参考不是标准答案参数项推荐值或推荐做法备注训练方式LoRA (rank64, alpha128)显存压力小后续RL阶段便于切换学习率1e-5带warmup (前3%步数)高学习率容易让视觉编码器剧烈漂移批次大小128条样本gradient accumulation凑多模态样本短序列多实际tokens看batch训练轮数2~3轮再多容易过拟合指令格式视觉编码器冻结或极低学习率(1e-7)更新视觉编码器收益不稳定最大序列长度2048~4096长图中包含多个OCR块时尤其注意损失计算范围仅计算Answer部分token不计算图像token和系统提示token实操的时候有几个细节很容易被忽略。第一个事大部分开源MLLM的模板里会有类似image是什么这样的指令分隔训练时务必确保loss mask正确否则模型会把prompt部分也背下来到了推理阶段就会在回复里复述用户输入非常呆。第二个事多模态数据里同一张图可能被多次使用分别搭配不同的问题。我在数据组织上会把同一张图的所有变体放在同一个批次里而不是打散到全局。这样可以减少同一个视觉编码在不同batch里反复正向反向传播的浪费训练速度会快一些。第三个事不要一上来就冻结视觉编码器不更新。对于复杂图表类数据视觉编码器完全不更新的话OCR位置信息和图表结构信息可能提取得不够。我建议的做法是前三分之一的训练步数冻结视觉编码器后半程解锁并用主学习率的千分之一来更新它。这个策略在多个数据集上都验证有效。3. 第二步RL用偏好信号让模型学会“挑好答案”3.1 PPO还是DPO这是个成本问题进入RL阶段第一个绕不开的决策就是用PPO还是DPO。很多刚接触这个方向的同学会在这一步纠结很久我给出一个比较务实的判断标准——训练资源和稳定性。PPO需要同时加载策略模型、参考模型、奖励模型和价值模型至少是四份模型副本显存开销非常夸张。好处是你可以用任何形式的奖励信号包括过程奖励、结果奖励、甚至手工规则奖励坏处是超参数敏感KL系数和GAE参数稍微不合适模型输出就开始崩表现为吐乱码、重复短语、情绪空洞。DPO的最大优点是把偏好学习简化为一个分类式目标直接对偏好对chosen和rejected做优化不需要单独的奖励模型。它只需要策略模型和参考模型两份权重训练机制也更接近SFT稳定性好很多。代价是你必须得有高质量的偏好对数据——这一点在多模态领域尤其稀缺。我的个人建议是如果你手头只有几万条偏好对而且数据质量没有把握选DPO如果你有明确的奖励函数定义比如数学题想用最终答案对错作为奖励那可以考虑PPO但要为调参留出时间。3.2 多模态场景下的奖励设计多模态RL和纯文本RL相比有一个额外的难点模型的输入一半是图像token一半是文本token当你只对文本输出计算奖励时模型很难通过梯度把“文本不好”这个信号传导到视觉编码层。换句话说图像理解部分很难被RL信号直接修正所以RL阶段的大部分收益来自优化模型的“语言组织策略”和“推理路径选择”而不是视觉感知能力。这个认知很重要它影响你的数据设计。我实际做下来效果最好的偏好对样本不是单纯的“答案错vs答案对”而是“推理路径混乱但答案碰巧对”和“推理路径清晰且答案对”的对比。也就是说偏好对应该重点体现推理过程的优劣而不是只看最终结果的正确性。这也是为什么我前面强调SFT阶段要灌CoT数据——有了CoTRL阶段才能对“推理链的每一步选择”构造出有价值的偏好信号。具体训练中我推荐使用DPO损失其中超参数β温度系数通常取0.1到0.3之间。β太大会让模型对偏好差异过于激进导致输出变得极端β太小则优化效果不明显。如果你实验中发现训练完成后模型输出明显变长、但逻辑密度下降多半是β偏大。此时把β从0.2降为0.1往往能缓解“话痨化”问题。另外有一个我曾经踩过的坑DPO训练的时候梯度同时作用在chosen和rejected两条输出上。如果rejected输出特别短而chosen输出特别长模型为了迎合偏好会在长度维度上钻空子——疯狂加长输出。所以我在构造偏好对的时候会做一个长度匹配chosen和rejected的token长度尽量控制在1.2倍以内。这一点对最终的稳定性影响非常大。4. 第三步UPT没有新标注怎么继续涨点4.1 UPT的两种理解方式UPT这个缩写在学术圈里其实并不像SFT和RL那样有公认定义结合这个标题的语境我倾向于认为它是Unsupervised Preference Tuning。但如果作者实际写的是Unified Preference Training逻辑上也说得通。不管是哪种核心思想都是统一的在偏好数据不依赖人工标注的情况下让模型自己生成、自己评价、自己更新。这里需要强调的是UPT并不等于“无监督学习”。它依然是一种偏好学习只是偏好对来源于模型自身的采样。这种“用自己当老师”的做法在纯文本推理领域已经有不少变体比如拒绝采样微调、自一致性蒸馏等。而放到多模态场景下难点在于自生成数据的质量怎么判断——因为图像理解错误和文本推理错误经常纠缠在一起。4.2 自生成偏好数据的流程我复现这个思路时把UPT的完整流程拆成了四个环节下面逐一展开。第一环采样。选取一批未标注的图文输入让当前模型以较高温度我常用temperature0.8或1.0采样多条输出一般每条输入采4到8条。第二环筛选。这步是核心也是最需要工程功力的。对多模态推理任务而言我采用的方法是先找个可计算的客观结果做粗筛。比如数学题以最终答案是否匹配为第一道门把答案错误的样本直接丢弃然后对答案正确的多个输出用模型自身计算自一致性分数逻辑上越一致的输出越可能是高质量推理链。第三环组对。把经过筛选的高质量输出作为chosen把那些答案错误、但长度相似且在指令格式上“看起来还行”的输出作为rejected构造出偏好对。第四环更新。使用DPO或者KTO之类的偏好优化算法在新的自生成偏好对上对模型进行一轮轻量更新。这个流程本质上是在人为制造一个“答案已知、过程未知”的训练环境让模型从自己过去的尝试中学习。这里我不推荐直接用答案正确性作为唯一筛选标准因为多模态推理中经常出现“最终答案对但推理过程有严重幻觉”的情况比如把橙色的苹果说成是香蕉最后绕回一个正确数字。所以筛选阶段我在答案匹配之外还会用一个简单的幻觉检测启发式让模型在回答之前先描述图像中的关键元素然后检查描述是否和实际标签匹配。这个操作虽然朴素但确实能过滤掉不少“碰巧答对”的样本。UPT还有一个容易被忽略的技术要点别忘了冻结参考模型。无论你是用DPO还是其他偏好优化方法参考模型都必须保持在更新前的状态否则偏好优化目标就变成了移动靶。我在早期实验中偷懒没有冻结参考模型结果训练第一个epoch后就开始出现严重的不稳定输出来回摇摆损失曲线像心电图后来老老实实冻结才恢复正常。4.3 持续改进的机制防止模型学完就忘既然标题强调的是Continual Improving那UPT阶段就必须考虑灾难性遗忘的问题。我在实际操作中发现单纯在自生成数据上重复做UPT模型会在两三轮之后出现一种症状新任务的理解越来越顺但是之前SFT阶段学会的基础指令遵循能力开始退化。比如你问一个图像里有几把椅子它可能答对了但问“请用JSON格式输出标题和描述”的时候又忘记了JSON格式要求。解决这个问题的办法不复杂叫数据重放。具体操作上我每一轮UPT都会混入20%左右的历史训练数据其中大部分来自SFT阶段的核心指令数据。这些数据不参与偏好构造只是在每个batch里作为普通监督样本出现稳住模型的全局能力。这个比例我试过10%和30%20%左右在我的设置下效果最好既能有效防遗忘又不至于把UPT优化方向带偏。还有一个细节UPT阶段更新幅度一定要小。我用的学习率只有SFT阶段的十分之一到五分之一即1e-6到2e-6之间。如果你发现UPT之后模型输出的基础能力没有变化那大概率是学习率太低如果你发现模型开始变得“油腔滑调”那大概率是学习率太高或者偏好对质量不过关。这种“微调量级”的把控可能是UPT阶段最核心的工程判断。5. 多模态推理训练中的实操避坑清单5.1 图像Token与文本Token的隐形冲突多模态模型训练里有一个很隐蔽的问题图像token的数量往往远大于文本token。一张高分辨率图片切成patch之后可能产生数百甚至上千的视觉token而一道数学题的问题文本可能只有几十个token。当这两部分拼接进同一个序列时attention的计算开销会严重偏向图像侧而反向传播时梯度也主要集中在图像token上。这在RL阶段尤其危险。因为DPO这类偏好优化的目标是文本输出如果视觉token占比过高模型会把大量容量都花在“更精细地看图像”上而对文本输出的优化不足。我建议在数据准备期就对图像分辨率做一个统一策略不是所有任务都需要超高分辨率简单的视觉问答任务可以适当降采样只有OCR密集或图表密集的任务才保留高分辨率。把高分辨率图像的占比控制在30%到40%训练效率和最终效果都会更好。5.2 训练稳定性与显存占用以7B MLLM做RL阶段训练为例我的显存经验数据是这样的使用LoRA 8卡80G显存DPO训练可以比较舒服地容纳单卡batch size 2到4如果用PPO那显存直接翻倍还多经常需要把batch size压到1模型并行和梯度累积会拖慢整个训练节奏。为了在有限显存里提升训练效率我从实践中总结出两个有效手段。第一用FlashAttention-2并且手动开启梯度检查点这样能省下不少激活值显存第二把视觉编码器彻底冻结只优化LLM部分的LoRA参数。视觉编码器的参数量很大冻结之后不仅显存压力小反向传播也快了很多。代价是模型无法通过RL阶段改善视觉感知但就像前面说的RL本来也不擅长改视觉感知这个代价可以接受。5.3 评测陷阱与数据泄漏最后说一个很多文章不会提、但实践中一定会遇到的问题评测数据泄漏。当你使用公开的多模态推理数据集来验证三步走的提升效果时一定要检查数据集是否出现在你的SFT训练数据里。我在一次实验中SFT阶段不小心混入了评测集的同源数据结果评测分数暴涨十几个点我还高兴了两天后来做消融时发现是泄漏白高兴一场。我的排查手段很原始但很有效把评测数据的文本部分和训练数据做一次模糊匹配不能只做精确匹配因为很多公开数据集存在转写噪声。匹配比例超过3%就要考虑从训练数据里剔除对应样本或者换一套不相干的评测集。另外一个评测陷阱是多模态模型对Prompt格式特别敏感。同一道题用不同的系统提示或者不同的图像占位符格式得分差异可能非常大。我的习惯是评测时固定一套统一的模板并且和SFT训练时的模板保持一致这样才能相对公平地对比不同阶段的提升幅度。6. 一个可复现的最小管道6.1 硬件与框架选择如果你看完前面的分析想自己动手复现一套我建议不要一开始就用最大规模的模型。我的经验是用7B到8B规模的MLLM做验证跑通管道后再考虑升级。硬件上8卡80G是最舒服的配置如果资源紧张4卡也可以但batch size和训练速度会明显受限。框架方面我用的是HuggingFace TRL来管理SFT和DPO训练视觉编码部分用现成的MLLM库加载预训练权重DeepSpeed做ZeRO-3并行。选这套组合是因为它迭代速度快、社区兼容性好遇到问题容易搜到解决方案。如果你团队里已经有人熟练使用其他RL框架那也没必要换核心训练逻辑都是通用的。6.2 核心训练流程伪代码下面是我这套三步走管道的最小化伪代码可以当作代码骨架来参考。# stage 1: SFT model load_multimodal_llm(base-mllm-7b) train_dataset load_sft_data(sft_mixed.jsonl) # 图文指令数据 trainer SFTTrainer( modelmodel, train_datasettrain_dataset, argstraining_args(sft), # lr 1e-5, lora_rank 64, epochs 2 formatting_funcapply_multimodal_template, ) trainer.train() model.save_pretrained(stage1_sft_model) # stage 2: RL (DPO) ref_model load_multimodal_llm(stage1_sft_model) policy_model load_multimodal_llm(stage1_sft_model) dpo_dataset load_dpo_pairs(preference_pairs.jsonl) # chosen / rejected dpo_trainer DPOTrainer( modelpolicy_model, ref_modelref_model, train_datasetdpo_dataset, beta0.15, argstraining_args(dpo), # lr 1e-6, epochs 1 ) dpo_trainer.train() policy_model.save_pretrained(stage2_rl_model) # stage 3: UPT policy_model load_multimodal_llm(stage2_rl_model) ref_model_for_upt load_multimodal_llm(stage2_rl_model) # 冻结 unlabeled_queries load_unlabeled_inference_data(unlabeled_queries.jsonl) new_pairs construct_preference_pairs( policy_model, unlabeled_queries, temperature0.8, num_samples6, answer_matchTrue, self_consistencyTrue, ) replay_data load_sft_data(sft_core_sample.jsonl) # 20%重放 upt_trainer DPOTrainer( modelpolicy_model, ref_modelref_model_for_upt, train_datasetnew_pairs, beta0.1, argstraining_args(upt), # lr 1e-6, epochs 1 ) upt_trainer.train() # 每个batch混入重放数据用普通SFT损失稳住基础能力 policy_model.save_pretrained(stage3_upt_model)这套流程看着不长但每次跑一轮完整的三步走以我的经验数据处理和清洗占的时间能到总耗时的一半以上。所以不要小看数据环节。6.3 值得记录的经验数字跑完这套流程后我记录了几个比较敏感的数字可以给你做参照。SFT阶段3万条左右的高质量图文指令样本可以支撑起7B模型的指令遵循能力RL偏好对数量不需要太多8000到1万条高质量偏好对就能看到明显效果UPT阶段自生成偏好对推荐做到1.2万条以上同时混入20%的历史SFT核心数据做重放这样能保证持续提升的同时不遗忘旧能力。如果经过一轮完整三步走多模态推理评测集上的得分提升不到2个百分点我首先会怀疑SFT阶段的数据质量多半是CoT占比太低或者脏数据太多。把SFT阶段重做一遍比继续硬调RL和UPT超参数更划算。这个判断在我多次实验中都被验证了。我个人在实际操作中最深的体会是这三个阶段的真正分界线不是算法而是数据策略。SFT阶段你为数据付费RL阶段你为偏好付费UPT阶段你开始为自己的模型判断力付费。越往后走工程里最值钱的东西越不是显卡和代码而是你设计“伪偏好信号”时的那点判断力。这也是为什么同一套三步走管道不同团队跑出来的效果差异能非常大——大家卡的坑根本不在一处。