概述
RLHF(Reinforcement Learning from Human Feedback,人类反馈强化学习)是一种将人类偏好融入强化学习的技术,是GPT-4、ChatGPT、Claude等大语言模型对齐(alignment)的核心技术。1
核心思想:用人类偏好数据训练奖励模型,然后用RL优化策略使模型输出符合人类意图。
为什么需要RLHF?
预训练语言模型的问题
仅用大规模文本预训练的模型存在:
- 有害输出:可能生成有害、歧视性内容
- 无效回答:可能不遵循用户指令
- 幻觉:生成看似合理但错误的答案
传统方法的局限
| 方法 | 问题 |
|---|---|
| 规则过滤 | 只能处理已知模式,过于死板 |
| 监督微调(SFT) | 需要大量标注数据,难以覆盖所有场景 |
| 提示工程 | 不改变模型本身,能力有限 |
RLHF的优势
- 从人类偏好学习:无需指定奖励函数
- 泛化能力强:能处理未见过的指令
- 价值对齐:输出符合人类期望和价值观
RLHF三阶段流程
┌─────────────────────────────────────────────────────────────────┐
│ RLHF 三阶段流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 阶段1: 监督微调 (SFT) │
│ ┌───────────────────────┐ │
│ │ 预训练语言模型 │──▶ 微调语言模型 (SFT Model) │
│ │ + 人类示范数据 │ │
│ └───────────────────────┘ │
│ │ │
│ ▼ │
│ 阶段2: 训练奖励模型 (RM) │
│ ┌───────────────────────┐ │
│ │ 人类偏好数据 │──▶ 奖励模型 (Reward Model) │
│ │ (比较标注) │ │
│ └───────────────────────┘ │
│ │ │
│ ▼ │
│ 阶段3: 强化学习优化 │
│ ┌───────────────────────┐ │
│ │ SFT Model + RM │──▶ 对齐模型 (Aligned Model) │
│ │ + PPO │ │
│ └───────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
阶段1:监督微调(SFT)
目的
让模型初步具备遵循指令的能力。
数据格式
{
"prompt": "用Python写一个快速排序",
"response": "def quicksort(arr):\n if len(arr) <= 1:\n return arr\n ..."
}模型选择
通常使用预训练的GPT、LLaMA等基座模型。
实现
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
def sft_training():
# 加载预训练模型
model = AutoModelForCausalLM.from_pretrained("gpt2")
tokenizer = AutoTokenizer.from_pretrained("gpt2")
# 添加pad token
tokenizer.pad_token = tokenizer.eos_token
# 准备数据集
def tokenize_function(examples):
result = tokenizer(
examples["text"],
truncation=True,
max_length=512,
padding="max_length"
)
result["labels"] = result["input_ids"].copy()
return result
# 训练
training_args = TrainingArguments(
output_dir="./sft_model",
num_train_epochs=3,
per_device_train_batch_size=4,
learning_rate=1e-5,
warmup_steps=100,
logging_steps=10,
save_steps=500,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
tokenizer=tokenizer,
)
trainer.train()阶段2:训练奖励模型(RM)
核心思想
将”人类偏好”转化为可学习的奖励信号。
数据收集
让模型对同一prompt生成多个response,由人类标注哪个更好:
{
"prompt": "解释量子纠缠",
"responses": [
"量子纠缠是...",
"量子纠缠是指..."
],
"preference": [0, 1] // 索引1更好
}Bradley-Terry模型
假设人类偏好概率:
奖励模型训练
import torch
import torch.nn as nn
from transformers import AutoModel, AutoConfig
class RewardModel(nn.Module):
"""奖励模型:输出标量奖励"""
def __init__(self, model_name):
super().__init__()
self.model = AutoModel.from_pretrained(model_name)
self.value_head = nn.Linear(self.model.config.hidden_size, 1)
def forward(self, input_ids, attention_mask):
outputs = self.model(input_ids=input_ids, attention_mask=attention_mask)
# 使用最后一层[CLS] token的表示
hidden_state = outputs.last_hidden_state[:, 0, :]
reward = self.value_head(hidden_state)
return reward.squeeze(-1)
def train_reward_model(reward_model, train_data, epochs=3, batch_size=8):
"""训练奖励模型"""
optimizer = torch.optim.AdamW(reward_model.parameters(), lr=1e-5)
for epoch in range(epochs):
for batch in train_data:
chosen_ids = batch["chosen_ids"]
rejected_ids = batch["rejected_ids"]
chosen_mask = batch["chosen_mask"]
rejected_mask = batch["rejected_mask"]
# 计算奖励
chosen_reward = reward_model(chosen_ids, chosen_mask)
rejected_reward = reward_model(rejected_ids, rejected_mask)
# 偏好损失(cross-entropy)
logits = chosen_reward - rejected_reward
loss = -torch.log(torch.sigmoid(logits)).mean()
optimizer.zero_grad()
loss.backward()
optimizer.step()
print(f"Loss: {loss.item():.4f}")损失函数详解
其中:
- :输入prompt
- :人类偏好的response
- :不被偏好的response
- :奖励模型对的打分
阶段3:强化学习优化(PPO)
使用PPO微调
from transformers import AutoModelForCausalLM
import torch
class PPOTrainer:
"""PPO训练器"""
def __init__(self, ref_model, reward_model, sft_model):
self.policy = sft_model # 当前策略
self.ref_model = ref_model # 参考模型(SFT)
self.reward_model = reward_model # 奖励模型
self.optimizer = torch.optim.AdamW(self.policy.parameters(), lr=1e-5)
# PPO超参数
self.gamma = 1.0
self.lambd = 0.95
self.epsilon = 0.2
self.kl_coef = 0.02 # KL惩罚系数
def compute_kl_divergence(self, log_probs, ref_log_probs, mask):
"""计算KL散度"""
kl = log_probs - ref_log_probs
kl = (kl * mask).sum() / mask.sum()
return kl
def ppo_step(self, batch):
"""一次PPO更新"""
# 获取响应
responses = batch["responses"]
prompts = batch["prompts"]
# 1. 计算参考模型的log概率(冻结)
with torch.no_grad():
ref_log_probs = self.get_log_probs(self.ref_model, prompts, responses)
# 2. 计算当前策略的log概率
log_probs = self.get_log_probs(self.policy, prompts, responses)
# 3. 计算奖励
rewards = self.reward_model(prompts, responses)
# 4. 计算KL惩罚
kl = self.compute_kl_divergence(log_probs, ref_log_probs)
# 5. PPO裁剪损失
ratio = torch.exp(log_probs - batch["old_log_probs"])
advantages = self.compute_advantages(rewards) # GAE
surr1 = ratio * advantages
surr2 = torch.clamp(ratio, 1 - self.epsilon, 1 + self.epsilon) * advantages
policy_loss = -torch.min(surr1, surr2).mean()
# 6. 总损失
total_loss = policy_loss + self.kl_coef * kl
# 7. 更新
self.optimizer.zero_grad()
total_loss.backward()
torch.nn.utils.clip_grad_norm_(self.policy.parameters(), 1.0)
self.optimizer.step()
return {
"policy_loss": policy_loss.item(),
"kl": kl.item(),
"reward": rewards.mean().item()
}
def compute_advantages(self, rewards):
"""简单优势估计"""
return rewards # 简化的优势函数InstructGPT详细流程
InstructGPT的完整流程:1
1. 收集人类偏好数据
- 标注员对模型输出排序
- 13K prompts(イン)
2. SFT训练
- 16 epochs
- lr = 9.65e-6
- 训练12B参数模型
3. 奖励模型训练
- 基于SFT模型初始化
- 1 epoch on human preference data
4. PPO微调
- KL penalty系数 β = 0.02
- 批量大小 512
- 训练4个epoch
实验结果
| 模型 | 人类偏好率(Helpful) |
|---|---|
| SFT 1.3B | 63% |
| PPO 1.3B | 71% |
| SFT 175B | 73% |
| PPO 175B | 79% |
| PPO + PT 175B | 80% |
RLHF的挑战
1. 标注成本高
| 问题 | 解决方案 |
|---|---|
| 人工标注慢 | 合成数据、RL from AI Feedback (RLAIF) |
| 标注一致性 | 专业培训、明确标准 |
| 长文本标注难 | 摘要、分段标注 |
2. 奖励黑客(Reward Hacking)
模型找到”欺骗”奖励模型的方法,而非真正满足人类意图。
解决方案:
- KL散度约束
- 混合奖励(多样性、安全性、有用性)
- Constitutional AI等方法
3. 分布偏移
| 问题 | 现象 |
|---|---|
| Reward model过拟合 | 对齐税(Alignment Tax) |
| 策略分布偏移 | 训练不稳定 |
RLHF变体
1. RLAIF(AI Feedback)
用AI模型替代人类偏好标注:
class RLAIFTrainer:
"""RL from AI Feedback"""
def __init__(self, judge_model):
self.judge = judge_model # 评判模型(如GPT-4)
def generate_preference(self, response1, response2):
"""用AI生成偏好"""
prompt = f"""
Which response is better?
Response A: {response1}
Response B: {response2}
Output: A or B
"""
preference = self.judge.generate(prompt)
return preference2. DPO(Direct Preference Optimization)
DPO直接优化偏好,避免RL训练:
class DPOTrainer:
"""Direct Preference Optimization"""
def __init__(self, model, ref_model):
self.model = model
self.ref_model = ref_model
self.beta = 0.1 # KL惩罚系数
def dpo_loss(self, chosen_logps, rejected_logps,
ref_chosen_logps, ref_rejected_logps):
"""DPO损失函数"""
chosen_rewards = self.beta * (chosen_logps - ref_chosen_logps)
rejected_rewards = self.beta * (rejected_logps - ref_rejected_logps)
loss = -torch.log(torch.sigmoid(chosen_rewards - rejected_rewards)).mean()
return loss
def train_step(self, batch):
chosen_logps = self.get_logps(self.model, batch["chosen"])
rejected_logps = self.get_logps(self.model, batch["rejected"])
with torch.no_grad():
ref_chosen_logps = self.get_logps(self.ref_model, batch["chosen"])
ref_rejected_logps = self.get_logps(self.ref_model, batch["rejected"])
loss = self.dpo_loss(chosen_logps, rejected_logps,
ref_chosen_logps, ref_rejected_logps)
loss.backward()
return loss.item()3. GRPO(Group Relative Policy Optimization)
DeepSeek使用的方法:
其中 是同一问题的多个采样response。
实践指南
数据收集
# 偏好数据格式
preference_data = [
{
"prompt": "Write a haiku about coding",
"responses": [
"Code flows swift, / bugs disappear in night, / coffee fuels all",
"coding haiku: bugs vanish, tests pass, deploy with grace"
],
"preference": 0 # 第一个更好
},
# ... 更多数据
]
def create_preference_dataset(model, prompts, n_samples=4):
"""为每个prompt生成多个response并收集偏好"""
dataset = []
for prompt in prompts:
responses = []
for _ in range(n_samples):
response = model.generate(prompt, temperature=0.7)
responses.append(response)
# 人类偏好标注
preference = get_human_preference(prompt, responses)
dataset.append({
"prompt": prompt,
"responses": responses,
"preference": preference
})
return dataset训练配置
# RLHF训练配置示例
config = {
"sft": {
"lr": 1e-5,
"epochs": 3,
"batch_size": 4,
"max_len": 512
},
"reward_model": {
"lr": 1e-5,
"epochs": 1,
"batch_size": 8
},
"ppo": {
"lr": 1e-5,
"epochs": 4,
"batch_size": 1, # per sample
"ppo_epochs": 4,
"kl_coef": 0.02,
"clip_epsilon": 0.2,
"value_loss_coef": 0.1,
"entropy_coef": 0.0
}
}参考
后续主题
- Transformer与注意力机制:LLM基础架构
- LLM评估:如何评估LLM能力
- 对比学习:CLIP等模型的核心