概述

RLHF(Reinforcement Learning from Human Feedback,人类反馈强化学习)是一种将人类偏好融入强化学习的技术,是GPT-4、ChatGPT、Claude等大语言模型对齐(alignment)的核心技术。1

核心思想:用人类偏好数据训练奖励模型,然后用RL优化策略使模型输出符合人类意图。

为什么需要RLHF?

预训练语言模型的问题

仅用大规模文本预训练的模型存在:

  1. 有害输出:可能生成有害、歧视性内容
  2. 无效回答:可能不遵循用户指令
  3. 幻觉:生成看似合理但错误的答案

传统方法的局限

方法问题
规则过滤只能处理已知模式,过于死板
监督微调(SFT)需要大量标注数据,难以覆盖所有场景
提示工程不改变模型本身,能力有限

RLHF的优势

  1. 从人类偏好学习:无需指定奖励函数
  2. 泛化能力强:能处理未见过的指令
  3. 价值对齐:输出符合人类期望和价值观

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.3B63%
PPO 1.3B71%
SFT 175B73%
PPO 175B79%
PPO + PT 175B80%

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 preference

2. 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
    }
}

参考


后续主题

Footnotes

  1. Ouyang et al., “Training language models to follow instructions with human feedback”, NeurIPS, 2022 2