🐸 青蛙编程网
首页 / AI / 微调 LLaMA-Factory / 5. 偏好对齐

5. 偏好对齐

这是什么:偏好对齐(preference alignment)= 让模型不只会「答对」,还更会「答得好」。DPO(Direct Preference Optimization,直接偏好优化)是其中最省事的做法。

为什么用:上一章的 SFT 只能教模型「标准答案长这样」,教不了它「两个都对的答案里哪个更好」。语气、礼貌、拒答边界这些没标准答案的东西,全靠偏好数据来教。

如何用:本章先横向对比几种对齐方式,再写出偏好数据的格式,最后用官方 qwen3_lora_dpo.yaml 跑一次 DPO。

5.1 为什么 SFT 之后还要再来一步

SFT 的教材是「一问一答」,答案唯一。现实里很多问题没有唯一答案:

问:帮我写封辞职邮件
A 版:语气生硬,像通知
B 版:礼貌专业,留有余地

两个都「对」,但显然 B 更好。这类偏好用 SFT 学不了,因为 SFT 只会把 A 版当成唯一正确答案学进去。

偏好对齐补的正是这一课:给它两个答案,告诉它哪个更好,让模型朝好的那个方向挪。

5.2 各种对齐方式怎么选

方式 配置写法 需要什么 特点
RM + PPO stage: rm → stage: ppo 偏好对 + 一个奖励模型 经典 RLHF,效果好但流程复杂、算力贵
DPO stage: dpo + pref_loss: sigmoid 偏好对 官方推荐的主流选择,隐式奖励模型,不用额外训 RM
ORPO stage: dpo + pref_loss: orpo 偏好对 单模型一次训完,不需要参考模型
SimPO stage: dpo + pref_loss: simpo 偏好对 DPO 简化版,去掉参考模型,长度归一化
KTO stage: kto 好/坏标签 只要标「好」或「坏」,不需要成对

几个关键区别:

  • DPO 虽然不用单独训一个奖励模型,但训练时仍然要加载一个冻结的参考模型(原始基座)做对照。代码里 use_ref_model 在 stage: dpo 且 pref_loss 不是 orpo/simpo 时才为真。
  • ORPO / SimPO 把参考模型去掉了,显存省一半,流程也更简单,代价是效果通常略逊于 DPO。
  • KTO 走的是另一条路:只要告诉模型「这条回答是好是坏」,不需要成对的 chosen/rejected,标注成本低得多。
  • pref_loss 在代码里的完整取值是 sigmoid / hinge / ipo / kto_pair / orpo / simpo,sigmoid 就是标准 DPO。

5.3 偏好数据长什么样

偏好数据要在 dataset_info.json 里加 "ranking": true,框架才知道要按偏好数据来读。

Alpaca 风格(扁平,好坏回答各是一个字符串):

[
  {
    "instruction": "帮我写封辞职邮件",
    "input": "",
    "chosen": "您好,感谢您这段时间的付出。我想申请离职,方便的话想约个时间交接工作。",
    "rejected": "我不干了,以后别找我。"
  }
]

ShareGPT 风格(官方 demo 用的就是这种):

[
  {
    "conversations": [
      {"from": "human", "value": "Hi! I'd like to create a new language game."}
    ],
    "chosen": {"from": "gpt", "value": "That sounds like a fun idea! Here are some tips..."},
    "rejected": {"from": "gpt", "value": "I don't understand. Go away."}
  }
]

对应的注册说明:

{
  "my_pref_demo": {
    "file_name": "my_pref_demo.json",
    "ranking": true,
    "formatting": "sharegpt",
    "columns": {
      "messages": "conversations",
      "chosen": "chosen",
      "rejected": "rejected"
    }
  }
}

chosen 和 rejected 要针对同一个 prompt。质量比数量重要:1000 条干净的偏好对,好过 10 万条随手编的。

5.4 DPO 配置逐项

官方示例 examples/train_lora/qwen3_lora_dpo.yaml 的关键部分:

### method
stage: dpo
do_train: true
finetuning_type: lora
lora_rank: 8
lora_target: all
pref_beta: 0.1
pref_loss: sigmoid  # choices: [sigmoid (dpo), orpo, simpo]

### dataset
dataset: dpo_en_demo
template: qwen3_nothink
cutoff_len: 2048
max_samples: 1000

### output
output_dir: saves/qwen3-4b/lora/dpo

### train
per_device_train_batch_size: 1
gradient_accumulation_steps: 8
learning_rate: 5.0e-6
num_train_epochs: 3.0
bf16: true

跟 SFT 配置最大的不同有三点:

项 SFT DPO 为什么
learning_rate 1.0e-4 5.0e-6 DPO 要在原模型附近小幅挪动,学习率必须小一个数量级
stage sft dpo 换成偏好阶段
pref_beta — 0.1 KL 约束强度,越大越保守、越不容易跑偏

跑起来:

llamafactory-cli train examples/train_lora/qwen3_lora_dpo.yaml

你会看到(示意):

[INFO] Fine-tuning method: LoRA
[INFO] Number of trainable parameters: 16,515,072
{'rewards/chosen': 0.83, 'rewards/rejected': 0.31, 'rewards/accuracies': 0.91, 'loss': 0.42}

rewards/accuracies 是最关键的指标:它表示「模型在多少比例的样本上确实认为 chosen 比 rejected 好」。健康值是 0.5~0.7 起步并往 0.8 以上走;如果一直卡在 0.5,说明模型压根没学到,可能是偏好数据太弱或者学习率太小。

5.5 换成 ORPO、SimPO、KTO

ORPO / SimPO 只改一个字段:

stage: dpo
pref_loss: orpo   # 或 simpo

注意 stage 仍然是 dpo——代码里 stage 没有 orpo 这个取值,ORPO 和 SimPO 是靠 pref_loss 区分的。

KTO 换 stage 和数据集:

### method
stage: kto
do_train: true
finetuning_type: lora
lora_rank: 8
lora_target: all
pref_beta: 0.1

### dataset
dataset: kto_en_demo

KTO 的数据要多一个 kto_tag 字段(true 表示好,false 表示坏):

[
  {
    "conversations": [
      {"from": "human", "value": "帮我写封辞职邮件"},
      {"from": "gpt", "value": "您好,感谢您这段时间的付出……"}
    ],
    "kto_tag": true
  }
]

注册说明要写成:

{
  "my_kto_demo": {
    "file_name": "my_kto_demo.json",
    "formatting": "sharegpt",
    "columns": {
      "messages": "conversations",
      "kto_tag": "kto_tag"
    }
  }
}

奖励模型(RM) 则用 stage: rm,喂的也是偏好对,产出的是一个「打分器」:

stage: rm
dataset: dpo_en_demo
output_dir: saves/qwen3-4b/lora/reward

训完的 RM 再喂给 stage: ppo 做强化学习,就是完整的经典 RLHF 流程——但这需要同时在线跑四个模型(策略、参考、奖励、价值),个人机器很难扛,除非你有明确的理由,否则优先选 DPO。

动手改

  1. 把 pref_beta 从 0.1 改成 0.5 再训一次,对比 rewards/accuracies 有什么变化
  2. 把 pref_loss 改成 orpo 跑一遍,感受一下「不需要参考模型」在显存和速度上的差别
  3. 找一份公开的 UltraFeedback 数据(dataset: ultrafeedback),注册好之后训一次,和 demo 数据比一比 accuracies

小结

  • SFT 教「标准答案」,偏好对齐教「哪个更好」,两者互补
  • DPO 是主流选择:stage: dpo + pref_loss: sigmoid,不需要单独训奖励模型
  • ORPO / SimPO 是 pref_loss 的取值,不是独立的 stage;KTO 才用 stage: kto
  • 偏好数据要 "ranking": true,ShareGPT 风格用 chosen / rejected 两个对象,KTO 多一个 kto_tag
  • DPO 的学习率要比 SFT 小一个数量级(5.0e-6 vs 1.0e-4),pref_beta 控制保守程度
  • 看 rewards/accuracies 判断有没有学到;一直停在 0.5 就是没学动

下一章:导出与部署 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。