Skip to main content
Version: Next

微调与对齐:从"会续写"到"会对话"

基座模型只会补全文本。要变成 ChatGPT 那样的助手,需要两步:SFT 教会它"指令 → 回答"的格式,偏好对齐(RLHF/DPO)教会它"什么样的回答更好"。

一、SFT:监督微调​

数据格式​

用对话模板(chat template)把样本组织成模型要学的样子。以 ChatML 为例:

<|im_start|>system
你是一个有帮助的助手。<|im_end|>
<|im_start|>user
解释一下梯度消失<|im_end|>
<|im_start|>assistant
梯度消失是指……<|im_end|>

关键技巧:只计算回答部分的 loss​

对 prompt(system+user)部分的 token 把 label 置为 -100(PyTorch 交叉熵自动忽略),只让模型学怎么回答,不学怎么提问:

def build_labels(input_ids, prompt_len):
labels = input_ids.clone()
labels[:prompt_len] = -100
return labels

SFT 数据来源与配比​

来源例子注意
人工标注OpenAI 早期 1.3 万条贵但质量天花板
强模型蒸馏Alpaca、ShareGPT便宜量大,注意许可
自我改进Self-Instruct、Evol-Instruct由弱到强自动扩增
人类反馈改写对模型输出人工精修对齐效果最好

经验:1 万条高质量 > 100 万条噪声(LIMA 论文的"表面对齐假说":模型能力在预训练已获得,SFT 只是教格式)。配比上覆盖:问答、写作、代码、数学、拒答(安全样本)、多轮对话。

超参​

lr: 1e-5 ~ 2e-5(比预训练小 1-2 个数量级)
epoch: 2-3(多于 3 容易过拟合、灾难性遗忘)
batch: 32-128 条样本

全参数 vs LoRA:LoRA 冻结基座,只训练注入的低秩矩阵 W' = W + BA(B: d×r, A: r×d, r=8~64),显存省 2/3,适合单卡微调 7B;但追极致效果或继续预训练仍用全参数。

from peft import LoraConfig, get_peft_model

cfg = LoraConfig(r=16, lora_alpha=32, target_modules=["q_proj","k_proj","v_proj","o_proj"],
lora_dropout=0.05, task_type="CAUSAL_LM")
model = get_peft_model(base_model, cfg)

二、RLHF:用人类偏好训练​

SFT 后模型会答了,但不知道"哪个答案更好"。RLHF 三步走:

1. 收集偏好数据​

对同一 prompt 采样多个回答,标注员排序 → 得到 (prompt, chosen, rejected) 三元组。

2. 训练奖励模型(RM)​

奖励模型 = 基座 + 一个标量输出头,用 Bradley-Terry 损失:

L_RM = -log σ(r(x, y_chosen) - r(x, y_rejected))

直觉:好回答的奖励分要比坏回答高,sigmoid 把分差变成概率。

def rm_loss(r_chosen, r_rejected):
return -F.logsigmoid(r_chosen - r_rejected).mean()

3. PPO 强化学习​

用 RM 的分数作为奖励,微调策略模型,同时加 KL 惩罚防止跑偏:

目标 = E[ r(x,y) - β · KL(π_θ ‖ π_ref) ]
  • π_ref 是冻结的 SFT 模型,KL 惩罚防止模型为骗高分而输出乱码(reward hacking);
  • PPO 需要同时在线跑 4 个模型(策略、参考、奖励、价值),工程复杂、调参敏感——这是它被 DPO 大量替代的原因。

三、DPO:不用强化学习的偏好对齐​

DPO 的天才之处:把 RLHF 的目标解析地解出来,直接用偏好对训练,不需要奖励模型和在线采样。损失函数:

L_DPO = -E log σ( β·( log[π_θ(y_c|x)/π_ref(y_c|x)] - log[π_θ(y_r|x)/π_ref(y_r|x)] ) )

翻译成人话:让 chosen 回答相对参考模型的对数概率增幅,大于 rejected 的增幅。

def dpo_loss(policy_c, policy_r, ref_c, ref_r, beta=0.1):
# policy_c/r: 策略模型对 chosen/rejected 的序列对数概率
logits = beta * ((policy_c - ref_c) - (policy_r - ref_r))
return -F.logsigmoid(logits).mean()

def seq_logprob(model, input_ids, labels):
logits = model(input_ids).logits[:, :-1]
tgt = labels[:, 1:]
mask = tgt != -100
lp = torch.log_softmax(logits, -1).gather(-1, tgt.clamp(min=0).unsqueeze(-1)).squeeze(-1)
return (lp * mask).sum(-1)

DPO 稳定、简单、效果好,已成为中小团队对齐的首选;前沿实验室在 DPO 之上再叠在线 RL(RLHF/GRPO)冲极限(如 DeepSeek-R1 的推理强化学习路线)。

四、评估对齐效果​

维度工具
指令遵循IFEval、AlpacaEval(胜率)
综合能力MT-Bench、Arena 人类偏好评分
安全越狱攻击集、有害请求拒答率
遗忘检查通用基准(MMLU)对齐前后对比,防"对齐税"

五、实战路线建议​

  1. 先用开源 SFT 数据(如 Tulu-3 混合集)跑通 SFT;
  2. 用强模型对自己的 badcase 生成 corrected 回答,构造偏好对;
  3. 上 DPO(beta=0.1,lr=5e-7,1 epoch);
  4. 评估对齐税,必要时回退混数据比例。

最后一步:让模型高效地跑起来服务用户 → 推理与部署。