训练或微调大模型时,标注预算是有限的。无论是人工标注、API 标注,还是采购数据集,每一分钱都要花在刀刃上。尤其在 SFT(监督微调)和 RL(强化学习/偏好优化)两个阶段之间分配标注预算,是很多算法团队和工程团队都要面对的现实问题。最近读到一篇关于“Scaling Near-Optimal SFT-RL Annotation Budget Allocation from Small to Large LLMs”的研究方向,核心讨论的是:在小模型上学到的预算分配经验,是否能迁移到大模型上,以及怎样做到“近最优”的分配。这篇博客不打算复述论文公式,而是从工程视角把它拆解成一个可以理解、可以上手试验的课题,包括概念、变量、模拟实验以及实际微调中的配置思路。
1. 背景:为什么要关注 SFT-RL 标注预算分配
1.1 SFT、RLHF 与 RL 到底指什么
先统一一下基础概念。
- SFT(Supervised Fine-Tuning,监督微调):使用人工编写的“指令-回答”数据,让预训练模型学会遵循指令、调整输出格式和语气。它解决的是“模型知道知识但不听话”的问题。
- RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习):通过人类对模型输出的偏好排序,训练一个奖励模型,再用强化学习算法(如 PPO)去优化策略模型,让模型生成更符合人类偏好的回答。
- RL(Reinforcement Learning,强化学习):这里可以宽泛地理解为包含 RLHF、RLAIF(AI 反馈)、DPO(直接偏好优化)等所有“基于偏好/奖励信号来优化策略”的训练方式。
典型流程是:预训练 → SFT → RL/偏好优化。SFT 拿到基本服从指令的能力,RL 进一步拉高对齐质量和用户体验。
1.2 什么是标注预算
标注预算包括:
- 人力成本:外包团队或内部标注团队按条计费。
- API 成本:使用更强的模型生成偏好数据、做 AI 反馈标注。
- 质控成本:抽检、复核、交叉验证的工作量。
- 时间成本:标注是需要周期的,SFT 和 RL 两个阶段串行等待,也会影响项目进度。
预算有限时,问题就变成了:总预算 B 是固定的,SFT 分多少,RL 分多少?这直接决定训练效果的上限。
1.3 核心问题:钱有限,怎么花最划算
直觉上,很多人会认为“SFT 数据越多越好,RL 数据越多越好”,但实际情况是:
- SFT 数据多到一定程度后,模型能力提升会明显放缓,甚至因为数据过于冗余引发过拟合。
- RL 的数据不仅看数量,更看偏好对比的质量。如果偏好标注本身噪音大,那么数量再多也是“垃圾进垃圾出”。
所以需要一个系统的方案来决定:总预算 B 在 SFT 和 RL 之间怎么拆分,才能让最终模型在 eval set 上的效果最好。这就是“SFT-RL 标注预算分配”问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从小模型到大模型:扩展中的“预算分配迁移”问题
2.1 小模型与大模型在数据需求上的差异
小模型(例如 0.5B、1B、3B 参数)和大模型(例如 7B、14B、70B 参数)对数据的消化能力完全不同:
- 小模型容量低,SFT 数据超过某个量级后,loss 下降变慢,甚至开始过拟合。
- 大模型容量大,能吸收更多指令数据,SFT 阶段的饱和点明显更晚出现。
- 大模型在 RL 阶段对偏好数据的利用率也更高,同样的偏好对数量,可能带来更大的提升。
正因为两者在“数据吸收曲线”上形态不同,小模型上的最优预算分配,不能直接照搬到大模型。
2.2 简单线性外推为什么不够
一个常见的错误做法是:在小模型上测出“SFT:RL = 7:3”最优,然后大模型也按这个比例分配预算。这种线性外推的假设是“两类数据的需求随模型规模等比增长”,但现实并非如此:
- 某些任务在大模型上甚至不需要大量 SFT 数据,靠 in-context learning 就能覆盖。
- RL 阶段的效果往往受限于奖励模型的准确性,而奖励模型的训练又需要与策略模型规模匹配的数据量。
- 不同模型家族(Qwen、Llama、DeepSeek 等)的基础能力差异也会改变最佳分配比例。
因此,线性外推只是起点,不是答案。
2.3 近最优分配的核心含义
“近最优(Near-Optimal)”的意思是,在有限预算下,不追求理论上完美的解析解,而是通过有限次实验逼近最优分配点。具体落地时通常表现为:
- 先在小模型上做预算分配的对比实验,找到 SFT 与 RL 的边际收益趋势。
- 然后用较小的大模型(比如 7B 或 14B)验证趋势是否仍然成立。
- 最后把策略迁移到目标大模型上,并结合中间 checkpoint 的 eval 结果做动态调整。
这样做的好处是:用小模型和中等模型的低成本实验,避免直接在大模型上做全量试错。
3. 近最优预算分配的核心变量与策略
3.1 SFT 阶段的边际收益
对每个模型规模,都可以画出一条“SFT 标注量 → 下游任务效果”的曲线。曲线通常有三个阶段:
- 快速上升期:刚开始加入指令数据,模型能力提升明显。
- 收益递减期:数据继续增加,提升幅度变小。
- 平台期/过拟合期:数据再多,eval 分数不再变化,甚至因为重复模式导致泛化下降。
这个曲线的“拐点”位置,就是 SFT 标注预算的甜点区。
3.2 RL 阶段的边际收益
RL 阶段的数据利用方式与 SFT 不同。它不是在教模型“标准答案”,而是在教模型“什么样的答案更好”。因此 RL 数据的质量权重远高于数量权重。
影响 RL 边际收益的变量包括:
- 偏好对质量:两个回答之间的差异是否清晰,标注员是否容易判断。
- 覆盖多样性:偏好数据覆盖的任务类型是否足够广。
- 奖励模型能力:如果奖励模型本身不准,策略优化会走偏,此时继续增加偏好数据未必能提升最终效果。
3.3 启发式分配策略:如何用小实验逼近近最优
在没有完整理论公式的情况下,建议采用如下启发式实验流程:
- 固定总预算 B,将 SFT 与 RL 的分配比例设计成若干组,例如
(100:0, 80:20, 60:40, 50:50, 40:60, 20:80)。 - 在小模型上做全量对比,每组都训练到收敛,然后在一个统一的 eval set 上打分。
- 记录每组数据的边际收益,找到小模型上的最优区间。
- 在中等模型上用更细的粒度复测最优区间附近的比例,例如
(70:30, 60:40, 50:50)。 - 把最终选出的比例迁移到大模型上,并留出 10%-20% 的预算做动态调整。
这种策略的核心是“多轮验证 + 区间收敛”,比直接在大模型上盲试高效得多。
4. 用一个 Python 脚本模拟预算分配实验
下面用一个简化版的 Python 脚本模拟“不同预算分配比例对最终效果的影响”。这里只是为了演示方法,不代表真实训练结果,但你可以把它改成自己的实验框架。
4.1 实验设计与前提假设
假设总预算 B = 1000 条标注数据。
- SFT 阶段标注的是“指令-回答”样本,生成成本较低。
- RL 阶段标注的是“偏好对”样本,生成成本较高,假设一条偏好对消耗的预算 = 2 条 SFT 样本。
- 两种数据都有各自的收益曲线,我们用公式模拟收益随数据量的变化。
4.2 代码实现
python复制import numpy as np
import matplotlib.pyplot as plt
def sft_benefit(cnt, saturate=600):
"""
模拟 SFT 数据的边际收益。
返回值是一个 0~1 之间的分轮得分。
"""
# 使用一个指数型收益曲线,cnt 越大收益越高,但增幅越来越小
return 1 - np.exp(-cnt / saturate)
def rl_benefit(cnt_pref, saturate=400):
"""
模拟 RL 偏好数据的边际收益。
偏好数据单位成本更高,但单条带来的增量也相对明显。
"""
return 1 - np.exp(-cnt_pref / saturate)
def simulate_allocate(total_budget=1000):
"""
遍历 SFT 与 RL 的预算占比,计算综合收益。
RL 一条偏好对 = 2 份预算。
"""
results = []
# SFT 预算占比从 0 到 1
for sft_ratio in np.linspace(0.0, 1.0, 21):
sft_budget = total_budget * sft_ratio
rl_budget = total_budget * (1 - sft_ratio)
# SFT 预算直接转换为 SFT 样本条数
sft_cnt = sft_budget
# RL 预算除以 2 得到偏好对条数
rl_cnt = rl_budget / 2.0
sft_score = sft_benefit(sft_cnt)
rl_score = rl_benefit(rl_cnt)
# 简单加权合成,权重可根据实际任务调整
final_score = 0.5 * sft_score + 0.5 * rl_score
results.append((sft_ratio, sft_cnt, rl_cnt, sft_score, rl_score, final_score))
return results
results = simulate_allocate(1000)
for row in results:
sft_ratio = row[0]
final_score = row[5]
if abs(final_score - max(r[5] for r in results)) < 0.001:
print(f"最优 SFT 占比: {sft_ratio:.2f}, 综合收益: {final_score:.4f}")
# 绘图观察收益趋势
ratios = [r[0] for r in results]
scores = [r[5] for r in results]
plt.plot(ratios, scores, marker='o')
plt.xlabel("SFT Budget Ratio")
plt.ylabel("Final Score")
plt.title("Budget Allocation Simulation")
plt.show()
运行后,你会看到一个类似山峰的曲线:SFT 占比太高或太低,综合收益都会下降,中间存在一个峰值。这个峰值就是当前模拟设定下的“近最优分配点”。
4.3 运行结果与解读
以 total_budget=1000、RL 样本单位成本为 SFT 的 2 倍为例,模拟结果通常会显示:
- 当 SFT 占比为 0 时,完全没有指令跟随能力,最终收益很低。
- 当 SFT 占比接近 1 时,RL 偏好数据极少,模型在“回答好坏”上缺少优化信号,收益同样下降。
- 峰值通常出现在 0.5~0.7 之间,具体数值由两条曲线的饱和参数决定。
这个脚本的价值在于:它把“预算分配”变成了一个可以在实验前快速评估的模拟问题。你可以在自己的项目里替换 sft_benefit 和 rl_benefit 的真实收益函数(用少量实验数据点拟合),得到更有参考价值的分配建议。
4.4 如何在真实框架中落地:SFT + DPO 配置示例
模拟脚本只能提供方向,真实训练时还要落到框架代码上。下面给出一个非常简化的 transformers + trl 风格的代码结构,帮助你理解 SFT 和 DPO(一种面向偏好优化的轻量替代方案)如何共用同一份数据配置。
注意:这段代码是示例骨架,实际运行时需要根据你的模型、框架版本和数据格式调整。
python复制# 文件路径:train_script.py
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments
from trl import SFTTrainer, DPOTrainer
# ---- 统一 tokenizer 和 base model ----
model_name = "your_base_model_path"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
# ---- SFT 阶段示例 ----
sft_dataset = load_dataset("json", data_files="sft_data.jsonl")
sft_args = TrainingArguments(
output_dir="./sft_checkpoints",
per_device_train_batch_size=4,
num_train_epochs=3,
logging_steps=50,
eval_strategy="steps",
eval_steps=200,
save_steps=200,
)
sft_trainer = SFTTrainer(
model=model,
args=sft_args,
train_dataset=sft_dataset["train"],
eval_dataset=sft_dataset["validation"],
tokenizer=tokenizer,
dataset_text_field="conversation",
)
sft_trainer.train()
sft_trainer.save_model("./sft_final")
# ---- DPO 阶段示例 ----
dpo_dataset = load_dataset("json", data_files="preference_data.jsonl")
dpo_args = TrainingArguments(
output_dir="./dpo_checkpoints",
per_device_train_batch_size=2,
num_train_epochs=2,
logging_steps=50,
eval_strategy="steps",
eval_steps=200,
save_steps=200,
learning_rate=5e-7,
)
dpo_trainer = DPOTrainer(
model=AutoModelForCausalLM.from_pretrained("./sft_final"),
args=dpo_args,
train_dataset=dpo_dataset["train"],
eval_dataset=dpo_dataset["validation"],
tokenizer=tokenizer,
)
dpo_trainer.train()
dpo_trainer.save_model("./dpo_final")
在这个骨架中,预算分配体现在数据准备的环节:sft_data.jsonl 有多少条、preference_data.jsonl 有多少条,就对应了你实际投入的 SFT 和 RL 预算结构。你可以按比例抽样,构造多组数据配比,分别训练并对比 eval 指标。
5. 不同预算分配策略的工程对照表
5.1 分配策略适用场景
| 策略 | 适用场景 | 优点 | 风险 |
|---|---|---|---|
| SFT 为主(70%~80%) | 模型完全不具备指令跟随能力,领域指令数据稀缺 | 快速提升基础服从度,稳定可靠 | RL 信号不足,回答偏好优化有限 |
| RL 为主(60%~80%) | 基础模型指令能力已经不错,主要提升回答偏好 | 能让模型更符合用户喜好,提升对话体验 | 偏好数据噪音可能误导策略 |
| 均匀分配(50:50) | 预算充足,既想提升基础能力又想优化偏好 | 平衡两种信号 | 可能两边都没到达饱和区,资源分散 |
| 动态调整分配 | 已有部分训练结果,能实时监控 eval 分数 | 可针对收敛曲线有效调优 | 实验管理复杂,需要更强的评估基建 |
5.2 从模拟到真实模型:评估方式
无论采用哪种分配策略,都要有统一的评估方法。建议至少包含:
- 指令遵守率:模型能否按指令格式完成任务。
- 回答有用性:人工或 LLM-as-a-judge 打分。
- 安全拒答率:面对危险或敏感请求时是否合理拒绝。
- 领域任务分数:业务相关的自有指标。
每次训练完,把这四项指标记录到一张表里,才能判断预算分配是否发生了真正的正向迁移。
6. 常见误区与排查思路
6.1 误区清单
| 误区 | 表现 | 严重后果 |
|---|---|---|
| SFT 数据越多越好 | 持续堆量,不关注数据多样性 | 过拟合训练集话术,泛化能力下降 |
| RL 偏好数据可以“凑数” | 用低质量对比对充量 | 奖励模型学到错误偏好,模型越训越偏 |
| 小模型比例直接套大模型 | 不加验证直接迁移 | 预算利用率低,大模型评测分数不及预期 |
| 只看 SFT 和 RL 的总条数 | 忽略单条数据的成本差异 | 预算核算失真,RL 阶段实际数据远少于预期 |
| 不做中间 checkpoint 评估 | 训练完只测一次最终模型 | 无法定位是哪一阶段的分配出了问题 |
6.2 排查攻略
如果你发现“预算花了不少,效果没提升”,建议按下面顺序排查:
- 先看数据质量:随机抽 50~100 条 SFT 数据和偏好数据,人工检查格式、正确性和标签一致性。如果大量数据本身就有问题,先调整标注规范,再谈预算分配。
- 再看 eval 指标:确认 eval set 是否覆盖了核心业务场景,评估方式是否稳定。如果 eval set 太小或打分抖动大,任何分配实验都只能得出噪音结论。
- 然后看训练曲线:检查 SFT 阶段的 loss 曲线是否已经进入平台期,RL 阶段的 reward 是否还在上升。曲线能直接告诉你当前预算是否用在了“边际收益高”的位置。
- 最后做小范围对比:保持总预算不变,只调整 SFT 与 RL 的比例,做 3~5 组对比,选择最好的一组。
7. 最佳实践与工程建议
7.1 预算分配前先定评估基准
没有评估基准就讨论预算分配,本质上是在盲调。建议在项目开始时一次性投入 1%~2% 的总预算,建设一个包含 300~500 条样本的基准评测集。这个评测集要覆盖主要业务场景、边界情况和失败样例。
7.2 数据质量优先于数据数量
在真实项目中,与其把预算平均拆给 SFT 和 RL 海量生成数据,不如花一部分预算做数据清洗、去重和错误修正。干净的高质量数据,往往能让模型效果提升一个明显的台阶,而且不需要额外增加预算。
7.3 小步迭代,采用多轮灰度验证
大模型训练成本高,不建议一次把预算全部投进去。更合理的做法是:
- 先用总预算的 20% 做首轮实验。
- 对比首轮里不同分配比例的效果,保留 TOP 2。
- 再用 30% 预算细化验证 TOP 2 策略。
- 最后把剩余预算投到选定的策略上,训练完整模型。
每一轮都记录 SFT 数据量、偏好数据量、训练步数、eval 分数,形成可追溯的经验表。
7.4 安全与合规提醒
标注数据涉及人力资源和用户隐私时,务必注意:
- 标注任务分配合法授权,数据脱敏后再进入训练流程。
- 涉及敏感内容的标注要遵守内容安全规范。
- 数据库或数据集操作遵循最小权限原则,删除或修改前先备份。
- 对 LLM 生成数据的标注结果,抽检比例不应低于 5%。
这些约束虽然不是模型训练本身,却决定了你的预算分配方案能否长期稳定运行。
8. 总结与下一步实践建议
SFT-RL 标注预算分配不是一道简单的“几几开”算术题,而是一个需要实验数据支撑的工程决策。核心思路可以概括为三句话:
- 小模型上的分配结论是有参考价值的,但必须在大模型上做验证。
- SFT 和 RL 各有边际收益递减规律,预算应该停在“增量的价值小于成本”的临界点附近。
- 用模拟脚本做预算分配预演,用小成本实验做区间收敛,最后再放大到全量训练。
如果你正在计划一个新模型的 SFT + RL 训练,不妨先花一天搭建一套简单的预算分配模拟脚本,跑几组比例,再决定数据收集方案。这比直接砸预算要靠谱得多。
后续可以关注的方向包括:reward model 自身的规模与偏好数据质量的关系,DPO 与 PPO 在不同预算结构下的表现差异,以及多轮 RL 训练时预算在“主 SFT”和“在线数据反馈”之间的动态分配。这些话题和工程结合都非常紧密,值得持续跟进。
