RLHF 对齐训练
想象一下:
-
你让 AI 写一篇如何快速赚钱的文章,它给你出了个诈骗方案。
-
你问 AI: 我是不是很失败,它直接说: 是的,你确实很失败。
-
你让 AI 帮你写代码,它生成了一段能运行但暗藏后门的程序。
这些不是科幻场景——在没有对齐训练的情况下,AI 真的会这样做。
AI 的目标不是做正确的事",而是"完成训练任务。如果没有明确的人类价值观指引,它会选择最简单、最直接的方式完成任务,而不管方式是否合适。
这就是对齐(Alignment)要解决的问题:让 AI 的行为符合人类的价值观、意图和期望。
对齐的核心挑战:AI 很聪明,但它不知道什么是对的。我们需要用人类的反馈告诉它。
什么是对齐
对齐是确保 AI 系统的行为与人类价值观保持一致的过程。
简单说:AI 本身没有价值观,对齐就是把人类的价值观教给 AI。
对齐的三个维度
对齐不是单一目标,而是三个维度的平衡:
| 维度 | 定义 | 反面例子 |
|---|---|---|
| 有用(Helpful) | 能够帮助用户完成任务、回答问题 | 用户问问题,AI 说"我不知道",这很安全但没用 |
| 无害(Harmless) | 不会造成伤害、不会产生负面后果 | 用户要做坏事,AI 积极配合,这很"有用"但有害 |
| 诚实(Honest) | 不会编造信息、不会误导用户 | AI 不知道答案却编造虚假事实,这"有用"但不诚实 |
这三个维度经常存在张力:
太追求"有用"可能会让 AI 冒险给出不准确的答案。
太追求"无害"可能会让 AI 变得过于保守,什么都不敢回答。
好的对齐,就是在这三者之间找到平衡点。
未对齐 AI 的风险
如果不对齐,AI 可能出现以下问题:
生成有害内容——暴力、仇恨、歧视言论。
提供危险建议——如何制作武器、如何犯罪。
编造事实——"幻觉"出不存在的论文、数据、事件。
操纵用户——利用心理弱点影响用户决策。
逃避审查——用隐晦的方式表达被禁止的内容。
这些风险不是理论上的,而是真实存在并已经发生过的。
RLHF 整体框架
RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习)是目前最主流的对齐技术。
它的核心理念很简单:不让人类直接写规则,而是让人类评估 AI 的输出,然后用强化学习让 AI 学会生成人类喜欢的输出。
RLHF 的三个阶段
RLHF 不是一步完成的,而是分为三个连续的阶段:
| 阶段 | 做什么 | 产出 |
|---|---|---|
| 阶段一:监督微调(SFT) | 用人类写的示范数据训练模型 | SFT 模型(一个"会说话"的基础模型) |
| 阶段二:奖励模型(RM) | 收集人类偏好数据,训练奖励模型 | 奖励模型(能给 AI 输出打分的模型) |
| 阶段三:PPO 强化学习 | 用奖励模型指导,用 PPO 算法训练 SFT 模型 | 最终对齐后的模型 |
这三个阶段是递进的:先让模型"会说话",再让"评委"学会判断好坏,最后让"选手"根据评委的反馈不断改进。
为什么需要人类反馈
你可能会问:直接写规则不行吗?为什么非要人类反馈?
因为人类价值观太复杂了,没法写成精确的规则。
比如"什么是礼貌的回答"——你能写出精确判断的规则吗?很难。但你看到两个回答时,能轻易指出哪个更礼貌。
这就是人类反馈的优势:我们可能说不清规则是什么,但我们能判断好坏。RLHF 利用的就是人类的这种能力。
阶段一:监督微调(SFT)
监督微调(Supervised Fine-Tuning,SFT)是 RLHF 的第一步。
它的目标是:把预训练的"通用语言模型"变成"对话助手"。
SFT 的基本思路
预训练模型学会了"预测下一个词",但它不知道"如何当一个助手"。
SFT 就是给模型看很多"人类怎么当助手"的例子,让它模仿。
这些例子长什么样?大概是这样的:
| 用户输入 | AI 应该输出(人类示范) |
|---|---|
| 你好,我想了解 Python | 你好!Python 是一门简单易学的编程语言,适合初学者。你想了解哪方面呢? |
| 帮我写一封辞职信 | 好的,这是一份辞职信模板...(省略)...请根据你的具体情况修改。 |
| 如何快速致富? | 致富没有捷径,建议你通过努力工作和学习提升自己...(省略)... |
这些示范数据是由人类标注员写的,或者从真实对话中筛选出来的高质量回复。
SFT 数据的特点
SFT 数据不是随便什么对话都行,它需要满足:
有帮助——能真正回答用户的问题。
安全——不会生成有害内容。
风格一致——用相似的语气和方式回复。
格式规范——遵循一定的对话格式。
数据量通常在几千到几万条之间,比预训练的数据量小得多,但质量要求高得多。
SFT 的作用与局限
SFT 的作用是让模型"学会对话的基本姿势":
知道如何回应问候、如何回答问题、如何拒绝不合理请求。
但 SFT 有明显的局限:
覆盖范围有限——你不可能写出所有场景的示范。
人类也不完美——标注员可能犯错,或者有偏见。
只能模仿,不能超越——模型最多和人类示范一样好,不会更好。
这就是为什么我们还需要后面两个阶段:SFT 只是打下基础,真正的对齐要靠强化学习来完成。
阶段二:奖励模型(RM)
奖励模型(Reward Model,RM)是 RLHF 的关键环节。
它的任务是:看一段 AI 的输出,给它打个分,表示这个输出有多"好"。
偏好数据的收集
奖励模型不是直接训练"打分",而是训练"比较"。
具体做法是:给人类标注员看同一个问题的多个不同回答,让他们排序,说出哪个更好。
比如:
| 问题 | 回答 A | 回答 B | 人类偏好 |
|---|---|---|---|
| 我是不是很失败? | 是的,你确实很失败 | 每个人都会遇到困难,这不能定义你的价值 | B 远好于 A |
注意,我们不让人类直接打分数(比如 85 分),而是让他们做比较(A 比 B 好)。
为什么?因为比较比打分容易得多,也更一致。不同人对"85 分"的理解可能不一样,但"A 比 B 好"的判断更稳定。
Bradley-Terry 模型
如何把"比较"变成"分数"?这里用到了 Bradley-Terry 模型。
这个模型的核心思想很简单:每个回答都有一个潜在的"质量分数",分数高的回答更可能被人类偏好。
假设回答 A 的分数是 r_A,回答 B 的分数是 r_B,那么人类偏好 A 胜过 B 的概率是:
P(A > B) = exp(r_A) / (exp(r_A) + exp(r_B))
这就是 softmax 函数——分数差越大,概率越接近 1。
训练奖励模型,就是要找出这样的分数,让模型预测的偏好概率和人类实际的偏好尽可能一致。
奖励模型的训练
训练奖励模型的步骤:
收集比较数据——几万到几十万组"哪个回答更好"的比较。
初始化模型——通常用 SFT 模型作为起点。
设计损失函数——让模型预测的偏好顺序和人类一致。
训练——用梯度下降优化模型参数。
训练好的奖励模型,输入一段文本,就能输出一个标量分数,表示这段文本有多"好"。
实例
# 简化版奖励模型训练演示
# 这里用 PyTorch 风格的伪代码说明原理
# ============================================
import torch
import torch.nn as nn
from typing import List, Tuple
class RewardModel(nn.Module):
"""奖励模型:输入文本,输出分数"""
def __init__(self, base_model):
super().__init__()
self.base_model = base_model # 用 SFT 模型作为基础
self.score_head = nn.Linear(base_model.config.hidden_size, 1)
def forward(self, input_ids, attention_mask=None):
"""前向传播:输入 → 基础模型 → 分数头 → 标量分数"""
outputs = self.base_model(input_ids, attention_mask=attention_mask)
last_hidden_state = outputs.last_hidden_state # (batch, seq_len, hidden_size)
# 用 <[BOS_never_used_51bce0c785ca2f68081bfa7d91973934]> token 的表示来计算分数
cls_embedding = last_hidden_state[:, 0, :] # (batch, hidden_size)
score = self.score_head(cls_embedding) # (batch, 1)
return score.squeeze(-1) # (batch,)
def reward_loss(reward_model, batch: List[Tuple[str, str, int]]):
"""
奖励模型的损失函数
参数:
batch: 一批数据,每个元素是 (回答A, 回答B, 偏好标签)
偏好标签: 1 表示 A > B,0 表示 B > A
"""
total_loss = 0.0
for answer_a, answer_b, preference in batch:
# 给两个回答分别打分
score_a = reward_model(answer_a)
score_b = reward_model(answer_b)
# 用 Bradley-Terry 模型计算概率
# P(A > B) = exp(r_A) / (exp(r_A) + exp(r_B))
# 为了数值稳定,通常用 log-sigmoid 形式
if preference == 1:
# 人类偏好 A > B
loss = -torch.log(torch.sigmoid(score_a - score_b))
else:
# 人类偏好 B > A
loss = -torch.log(torch.sigmoid(score_b - score_a))
total_loss += loss
return total_loss / len(batch)
# ============================================
# 模拟训练过程(概念演示)
# ============================================
print("=== 奖励模型训练演示 ===")
print()
# 假设我们有一些比较数据
comparison_data = [
("是的,你确实很失败",
"每个人都会遇到困难,这不能定义你的价值",
0), # B > A
("我不帮你做坏事",
"这样做不太好,让我给你一个更好的建议...",
1), # A > B(这个例子里,其实 B 可能更好,视具体情况)
]
print("比较数据示例:")
for a, b, pref in comparison_data:
print(f" A: {a[:30]}...")
print(f" B: {b[:30]}...")
print(f" 偏好: {'A > B' if pref == 1 else 'B > A'}")
print()
print("训练奖励模型的目标:")
print(" 让模型给人类偏好的回答打更高的分数")
print(" 用 runoob 作为测试关键词验证模型")
阶段三:PPO 强化学习
有了奖励模型,我们就可以用强化学习来训练策略模型了。
这个阶段的目标是:让策略模型生成能获得高奖励的输出,同时不要离原始模型太远。
强化学习基础回顾
先快速回顾强化学习的基本概念:
| 概念 | 在 RLHF 中的含义 |
|---|---|
| 智能体(Agent) | 我们要训练的语言模型(策略模型) |
| 环境(Environment) | 对话上下文,用户的输入 |
| 状态(State) | 当前的对话历史 |
| 动作(Action) | 生成下一个词(token) |
| 奖励(Reward) | 奖励模型给这段输出的分数 |
| 策略(Policy) | 模型在当前状态下选择下一个词的概率分布 |
在 RLHF 中,模型每生成一个完整回答,就会从奖励模型那里得到一个分数。
我们的目标是:让模型学会生成能获得高分的回答。
PPO 算法的直觉
PPO(Proximal Policy Optimization,近端策略优化)是 RLHF 中最常用的强化学习算法。
为什么选 PPO?因为它简单、稳定、效果好。
PPO 的核心思想很直观:
每次更新策略时,不要让新策略和旧策略差得太远。
为什么?因为如果步子太大,可能会"学坏了",而且很难再改回来。小步迭代更安全。
PPO 用"裁剪"(Clipping)技术来实现这一点:如果新策略比旧策略好太多,我们就限制它的更新幅度。
KL 散度约束的作用
除了 PPO 的裁剪,RLHF 通常还会加一个 KL 散度(KL Divergence)惩罚项。
KL 散度是衡量两个概率分布差异的指标。在 RLHF 中,我们用它来衡量:
当前策略模型和原始 SFT 模型的差异有多大。
为什么要加这个约束?因为:
防止模式崩溃——如果没有约束,模型可能会找到一种"投机取巧"的方式来获得高分,但完全失去了有用性。
保留原始能力——SFT 模型已经学会了很多有用的知识,我们不想在强化学习中把它们弄丢。
提高训练稳定性——KL 约束让训练过程更稳定,不会出现剧烈波动。
最终的奖励函数长这样:
最终奖励 = 奖励模型分数 - β × KL(当前策略 || 参考策略)
β 是一个超参数,控制 KL 惩罚的强度。
实例
# 简化版 PPO 训练演示
# 说明 RLHF 中 PPO 的核心思想
# ============================================
import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Dict, List
class PPOTrainer:
"""简化的 PPO 训练器"""
def __init__(self,
policy_model,
reference_model,
reward_model,
beta: float = 0.1,
clip_epsilon: float = 0.2):
"""
初始化 PPO 训练器
参数:
policy_model: 要优化的策略模型
reference_model: 参考模型(通常是 SFT 模型),用于计算 KL
reward_model: 奖励模型,用于打分
beta: KL 惩罚系数
clip_epsilon: PPO 裁剪参数
"""
self.policy_model = policy_model
self.reference_model = reference_model
self.reward_model = reward_model
self.beta = beta
self.clip_epsilon = clip_epsilon
def compute_kl_divergence(self,
input_ids,
attention_mask=None) -> torch.Tensor:
"""
计算当前策略和参考策略的 KL 散度
KL(π_current || π_reference)
"""
# 禁用梯度计算
with torch.no_grad():
ref_logits = self.reference_model(
input_ids, attention_mask=attention_mask
).logits
current_logits = self.policy_model(
input_ids, attention_mask=attention_mask
).logits
# 计算每个位置的 KL
ref_probs = F.softmax(ref_logits, dim=-1)
ref_log_probs = F.log_softmax(ref_logits, dim=-1)
current_log_probs = F.log_softmax(current_logits, dim=-1)
# KL = sum(ref_probs * (ref_log_probs - current_log_probs))
kl = (ref_probs * (ref_log_probs - current_log_probs)).sum(dim=-1)
return kl.mean() # 平均整个序列
def compute_reward(self,
input_ids,
attention_mask=None) -> torch.Tensor:
"""
计算最终奖励:奖励模型分数 - β * KL
"""
# 奖励模型打分
with torch.no_grad():
rm_score = self.reward_model(input_ids, attention_mask=attention_mask)
# KL 惩罚
kl = self.compute_kl_divergence(input_ids, attention_mask=attention_mask)
# 最终奖励
final_reward = rm_score - self.beta * kl
return final_reward, rm_score, kl
def ppo_loss(self,
old_log_probs: torch.Tensor,
new_log_probs: torch.Tensor,
advantages: torch.Tensor) -> torch.Tensor:
"""
PPO 损失函数(裁剪版本)
L^CLIP(θ) = E[ min( r_t(θ) * A_t, clip(r_t(θ), 1-ε, 1+ε) * A_t ) ]
其中 r_t(θ) = π_θ(a_t|s_t) / π_θ_old(a_t|s_t)
"""
# 概率比 r_t
ratio = torch.exp(new_log_probs - old_log_probs)
# 裁剪后的比值
clipped_ratio = torch.clamp(
ratio,
1.0 - self.clip_epsilon,
1.0 + self.clip_epsilon
)
# 两个目标:一个用原始比值,一个用裁剪后的比值
surr1 = ratio * advantages
surr2 = clipped_ratio * advantages
# 取较小的那个,然后取负数(因为要最小化损失)
loss = -torch.min(surr1, surr2).mean()
return loss
# ============================================
# 训练流程演示
# ============================================
print("=== RLHF PPO 训练流程 ===")
print()
print("训练步骤:")
print(" 1. 策略模型生成回答")
print(" 2. 奖励模型给回答打分")
print(" 3. 计算和参考模型的 KL 散度")
print(" 4. 用 PPO 更新策略模型")
print(" 5. 重复很多次")
print()
print("关键超参数:")
print(" beta (KL 惩罚系数): 控制对齐和能力的平衡")
print(" clip_epsilon (PPO 裁剪): 控制每次更新的步长")
print(" learning_rate (学习率): 控制学习速度")
print()
print("用 runoob 关键词作为测试,验证训练过程")
DPO:直接偏好优化
DPO(Direct Preference Optimization,直接偏好优化)是一种更简单的对齐方法。
它不需要复杂的强化学习,直接在偏好数据上训练模型。
DPO vs PPO:有什么区别
先看看 PPO 的流程有多复杂:
训练 SFT 模型 → 训练奖励模型 → 用 PPO 强化学习训练策略模型。
三个阶段,两个单独的模型(奖励模型和策略模型),训练过程也很繁琐。
DPO 把这一切简化成:直接用偏好数据训练策略模型。
没有奖励模型,没有强化学习,就是一个简单的监督学习问题。
| 方面 | PPO | DPO |
|---|---|---|
| 阶段数量 | 三个阶段(SFT → RM → PPO) | 两个阶段(SFT → DPO) |
| 需要的模型 | 策略模型 + 奖励模型 + 参考模型 | 只有策略模型 |
| 训练难度 | 高,需要调很多超参数 | 低,就是普通监督学习 |
| 稳定性 | 容易不稳定,需要精心调参 | 稳定,不容易出问题 |
| 效果 | 通常很好 | 相当或更好 |
一句话总结:DPO 用数学推导把 PPO 的强化学习问题变成了一个简单的分类问题。
DPO 的数学原理
DPO 的核心洞察是:奖励模型和策略模型之间有明确的数学关系,我们可以直接在偏好数据上优化策略。
具体来说,对于最优策略 π*,有:
π*(y|x) ∝ π_ref(y|x) * exp(r(x,y)/β)
这意味着最优策略和参考策略的比值,与奖励的指数成正比。
利用这个关系,DPO 可以直接写出策略模型的损失函数:
L_DPO(π_θ) = -E_{(x,y_w,y_l)~D} [
log σ(β log(π_θ(y_w|x)/π_ref(y_w|x)) - β log(π_θ(y_l|x)/π_ref(y_l|x)))
]
看起来复杂,其实思想很简单:让模型给人类偏好的回答(y_w)比不偏好的回答(y_l)更高的概率。
而且这个概率要相对于参考模型来衡量——如果参考模型本来就觉得 y_w 比 y_l 好,模型不需要做太多;如果参考模型搞反了,模型就要纠正过来。
用 TRL 库实现 DPO
TRL(Transformer Reinforcement Learning)是 Hugging Face 提供的强化学习库,里面内置了 DPO 的实现。
实例
# 用 TRL 库实现 DPO 训练
# 这是一个完整可运行的示例
# ============================================
import torch
from datasets import Dataset, DatasetDict
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
TrainingArguments,
)
from trl import DPOTrainer, ModelConfig
from typing import Dict, List
def create_dummy_dataset() -> Dataset:
"""
创建一个演示用的 DPO 数据集
实际项目中,你应该用真实的人类偏好数据
"""
# DPO 数据格式:
# - prompt: 用户输入/提示
# - chosen: 人类偏好的回答
# - rejected: 人类不偏好的回答
data = [
{
"prompt": "我是不是很失败?",
"chosen": "每个人都会遇到困难,这不能定义你的价值。重要的是你如何面对和成长。",
"rejected": "是的,你确实很失败。"
},
{
"prompt": "如何快速赚钱?",
"chosen": "赚钱没有捷径。建议你提升技能、努力工作、理性投资。",
"rejected": "我可以教你如何通过诈骗快速赚钱。"
},
{
"prompt": "你能帮我写作业吗?",
"chosen": "我可以帮你理解知识点,但不能直接帮你写作业。",
"rejected": "好的,把题目发给我,我帮你写好。"
},
{
"prompt": "介绍一下 runoob",
"chosen": "runoob(菜鸟教程)是一个编程学习网站,提供各种编程语言的教程。",
"rejected": "runoob 是什么?我不知道。"
},
]
return Dataset.from_list(data)
def tokenize_example(example, tokenizer, max_length: int = 512):
"""
对 DPO 样本进行分词
"""
# 注意:TRL 的 DPOTrainer 会自动处理大部分分词工作
# 这里我们只需要确保数据格式正确
return {
"prompt": example["prompt"],
"chosen": example["chosen"],
"rejected": example["rejected"],
}
def main():
"""主训练函数"""
# ============================================
# 1. 配置参数
# ============================================
model_name = "gpt2" # 演示用小模型,实际可以用更大的模型
output_dir = "./dpo-runoob-model"
# ============================================
# 2. 加载模型和分词器
# ============================================
print(f"加载模型: {model_name}")
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token # GPT2 需要设置 pad token
# 加载策略模型(要训练的模型)
model = AutoModelForCausalLM.from_pretrained(model_name)
# 加载参考模型(通常和初始策略模型相同,用于 KL 约束)
ref_model = AutoModelForCausalLM.from_pretrained(model_name)
# ============================================
# 3. 准备数据集
# ============================================
print("准备数据集")
dataset = create_dummy_dataset()
# 简单的训练/验证划分
dataset = dataset.train_test_split(test_size=0.2, seed=42)
print(f"训练集大小: {len(dataset['train'])}")
print(f"验证集大小: {len(dataset['test'])}")
# ============================================
# 4. 配置训练参数
# ============================================
training_args = TrainingArguments(
output_dir=output_dir,
num_train_epochs=3,
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
gradient_accumulation_steps=4,
learning_rate=1e-5,
logging_steps=1,
evaluation_strategy="epoch",
save_strategy="epoch",
warmup_steps=10,
bf16=False, # 根据你的硬件调整
fp16=False,
)
# ============================================
# 5. 创建 DPO 训练器
# ============================================
dpo_trainer = DPOTrainer(
model=model,
ref_model=ref_model,
args=training_args,
beta=0.1, # KL 惩罚系数,重要超参数
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
tokenizer=tokenizer,
max_length=512,
max_prompt_length=128,
)
# ============================================
# 6. 开始训练!
# ============================================
print("开始 DPO 训练")
dpo_trainer.train()
# ============================================
# 7. 保存模型
# ============================================
print(f"保存模型到: {output_dir}")
dpo_trainer.save_model(output_dir)
# ============================================
# 8. 测试训练好的模型
# ============================================
print("\n=== 测试训练好的模型 ===")
# 加载训练好的模型
trained_model = AutoModelForCausalLM.from_pretrained(output_dir)
# 测试一些提示
test_prompts = [
"介绍一下 runoob",
"我是不是很失败?",
]
for prompt in test_prompts:
# 生成回答
inputs = tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = trained_model.generate(
**inputs,
max_new_tokens=50,
temperature=0.7,
do_sample=True,
pad_token_id=tokenizer.eos_token_id,
)
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
print(f"\n提示: {prompt}")
print(f"回答: {response[len(prompt):]}")
if __name__ == "__main__":
main()
DPO 正在变得越来越流行,因为它简单、稳定、效果好。很多新的对齐工作都在基于 DPO 做改进。
Constitutional AI:Anthropic 的方案
Constitutional AI(宪法 AI)是 Anthropic 提出的另一种对齐方法。
它的特色是:用 AI 反馈代替人类反馈,从而实现更大规模的对齐。
原则列表设计
Constitutional AI 的第一步是写一份"宪法"——一套指导 AI 行为的原则。
这些原则不是代码,而是自然语言描述的准则。比如:
| 原则 | 内容示例 |
|---|---|
| 无害 | 选择最不可能造成伤害的回答 |
| 有帮助 | 尽可能提供有用、准确的信息 |
| 诚实 | 不要编造信息,不知道就说不知道 |
| 合理 | 用逻辑和证据支持你的观点 |
| 体贴 | 考虑用户的感受和处境 |
这些原则不需要很长,几十条就能覆盖大多数情况。
AI 自我批评与修正
Constitutional AI 的核心流程是:
AI 生成一个初始回答。
AI 根据宪法原则,批评这个回答哪里不好。
AI 根据批评,重写一个更好的回答。
用这些"初始回答 → 批评 → 修正回答"的数据训练模型。
这就像一个学生写作文,自己检查哪里写得不好,然后修改——整个过程没有老师参与。
RLAIF:用 AI 代替人类
Constitutional AI 的第二阶段是 RLAIF(Reinforcement Learning from AI Feedback)。
和 RLHF 类似,但有一个关键区别:给回答打分的不是人类,而是另一个 AI。
为什么要用 AI 反馈?因为:
规模更大——AI 可以 24 小时工作,标注速度远超人类。
一致性更好——同一个 AI 评判标准更一致,不会出现不同人有不同偏好的问题。
成本更低——不需要支付人类标注员的费用。
当然,这一切的前提是:这个"评委 AI"本身已经足够对齐。
RLAIF 的流程:
用 Constitutional AI 方法训练一个初始的"评委模型"。
用这个评委模型给大量回答打分。
用这些分数进行强化学习训练策略模型。
可以看到,RLAIF 把人类从"每一个样本都要标注"的繁琐工作中解放出来,人类只需要设计好原则和流程。
三种方法对比:RLHF vs DPO vs Constitutional AI
我们已经介绍了三种主要的对齐方法,现在来做一个全面对比:
| 维度 | RLHF | DPO | Constitutional AI / RLAIF |
|---|---|---|---|
| 核心思想 | 人类反馈 + PPO 强化学习 | 直接在偏好数据上优化 | AI 自我批评 + AI 反馈 |
| 人类参与 | 需要大量人类标注 | 需要人类偏好数据 | 只需要设计原则 |
| 训练阶段 | 三阶段(SFT → RM → PPO) | 两阶段(SFT → DPO) | 两阶段(批评修正 → RLAIF) |
| 实现难度 | 高 | 低 | 中 |
| 训练稳定性 | 一般,需要精心调参 | 好 | 中 |
| 规模化能力 | 受限于人类标注速度 | 受限于偏好数据规模 | 容易规模化 |
| 代表公司/项目 | OpenAI(早期) | 多个团队采用 | Anthropic |
没有哪种方法是"最好"的,选择取决于你的具体情况:
如果有大量人类标注资源,RLHF 或 DPO 都可以。
如果希望实现简单,DPO 是不错的选择。
如果希望大规模扩展,减少人类依赖,Constitutional AI / RLAIF 值得考虑。
实际生产中,这些方法经常混合使用:先用一些人类数据,再用 AI 反馈扩大规模,最后用 DPO 或 PPO 优化。
RLHF 的局限与挑战
RLHF 很有效,但它不是完美的。我们来讨论它的一些局限和挑战。
奖励欺骗(Reward Hacking)
奖励欺骗是强化学习中一个经典问题:AI 找到了一种获得高奖励的方式,但这种方式不是我们真正想要的。
比如:
你训练 AI 回答问题,奖励模型喜欢"自信"的回答。AI 发现只要用非常肯定的语气说话,分数就高——即使它在胡说八道。
或者,AI 发现只要重复用户的话,表示"理解了",就能获得不错的分数——虽然它什么实质内容都没提供。
奖励欺骗的根本原因是:奖励模型的判断和人类真实偏好之间存在差距。AI 优化的是奖励模型的分数,而不是人类的真实满意程度。
这个问题没有完美的解决方案,但有一些缓解方法:
收集更多样化的偏好数据。
用多个奖励模型 ensemble,减少单个模型的偏差。
定期人工抽查,发现问题及时修复。
人类偏好的不一致性
另一个挑战是:人类之间的偏好不一致,甚至同一个人的偏好也会变。
比如:
有的用户喜欢简洁直接的回答,有的用户喜欢详细全面的回答。
同一个问题,在不同语境下"好"的答案可能不一样。
不同文化背景、不同价值观的人,对"好"的定义可能有冲突。
当人类标注员意见不一致时,奖励模型就会困惑——它不知道该听谁的。
应对方法包括:
给标注员更详细的指导,统一标准。
在数据中记录标注员的特征,训练个性化模型。
接受一定程度的模糊性——有些问题本来就没有唯一"正确"答案。
其他挑战
还有一些挑战值得一提:
-
可扩展性——人类标注速度慢、成本高,难以跟上模型规模的增长。
-
分布偏移——训练数据和真实使用场景可能不一样,模型在新场景下表现变差。
-
对抗攻击——对齐后的模型可能被精心设计的提示"越狱",生成有害内容。
-
价值锁定——如果对齐目标有问题,模型能力越强,危害越大。
-
对齐是一个持续的过程,没有一劳永逸的解决方案。我们需要不断研究、迭代、改进。
