图解ChatGPT强化学习过程:从PPO算法到奖励模型设计

3次阅读
没有评论

共计 2947 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

背景痛点:为什么传统监督学习不够用?

传统监督微调(Supervised Fine-Tuning, SFT)虽然能教会模型模仿人类回答,但存在三个致命缺陷:

图解 ChatGPT 强化学习过程:从 PPO 算法到奖励模型设计

  • 缺乏全局优化目标:逐句匹配标准答案时,模型无法理解整体对话质量(如连贯性、信息量)
  • 数据天花板效应:标注数据量有限时,模型容易过拟合到表面语言模式
  • 价值观对齐困难:无法通过简单分类处理安全性、偏见等复杂语义问题

这时候就需要引入强化学习(Reinforcement Learning),让模型通过试错学习更接近人类偏好的对话策略。而 RLHF(Reinforcement Learning from Human Feedback)正是结合人类判断的强化学习框架。

RLHF 全流程技术图解

用 Mermaid 绘制的三阶段训练流程如下:

graph TD
    A[预训练语言模型] -->| 监督微调 | B[SFT 模型]
    B -->| 人类排序数据 | C[奖励模型训练]
    C -->|PPO 优化 | D[RL 微调模型]

    subgraph 阶段 1: 监督微调
    A --> B
    end

    subgraph 阶段 2: 奖励建模
    B --> C
    style C fill:#ffe6e6
    end

    subgraph 阶段 3: RL 微调
    C --> D
    style D fill:#e6ffe6
    end

关键数据流说明:
1. 阶段 1 输入:海量对话语料(如 OpenAI 的 WebText)
2. 阶段 2 输入:人工标注的答案对(标注员选择更优回答)
3. 阶段 3 输入:SFT 模型生成 + 奖励模型打分

PPO 算法的对话场景实战技巧

KL 散度约束:防止模型 ” 放飞自我 ”

直接优化奖励会导致模型生成乱码(因为乱码可能意外获得高分)。解决方案是在损失函数中加入 KL 惩罚项:

$$\mathcal{L}^{\text{CLIP}}(\theta) = \mathbb{E}\left[\min\left(r_t(\theta)\hat{A}t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_t\right)\right] – \beta \text{KL}[\pi\theta|\pi_{\text{SFT}}]$$

其中:
– $r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\text{old}}(a_t|s_t)}$ 为策略比例
– $\beta$ 一般取 0.1~0.2

奖励模型设计三原则

  1. 对比学习框架
  2. 输入:(好回答, 差回答) 对
  3. 输出:确保 $R(\text{好}) – R(\text{差}) > \text{margin}$
  4. 人工数据清洗
  5. 剔除标注不一致样本(Inter-annotator disagreement)
  6. 平衡不同主题领域分布
  7. 分数标准化
  8. 对每批数据做 Z -score 归一化
  9. 防止不同 batch 间奖励尺度漂移

关键代码实现

带 KL 约束的 PPO 核心代码

import torch
from transformers import GPT2LMHeadModel

class PPOTrainer:
    def __init__(self, sft_model):
        self.policy = GPT2LMHeadModel.from_pretrained(sft_model)
        self.old_policy = GPT2LMHeadModel.from_pretrained(sft_model)
        self.kl_coef = 0.15  # KL 惩罚系数

    def compute_loss(self, batch):
        # batch 输入: input_ids, attention_mask, rewards
        outputs = self.policy(input_ids=batch['input_ids'],
            attention_mask=batch['attention_mask'],
            return_dict=True
        )
        logits = outputs.logits

        # 计算新旧策略概率比
        old_log_probs = self.old_policy(...).log_softmax(dim=-1)
        new_log_probs = torch.log_softmax(logits, dim=-1)
        ratio = (new_log_probs - old_log_probs).exp()

        # PPO-Clip 目标
        surr1 = ratio * batch['advantages']
        surr2 = torch.clamp(ratio, 1.0-0.2, 1.0+0.2) * batch['advantages']
        policy_loss = -torch.min(surr1, surr2).mean()

        # KL 散度惩罚
        kl = (old_log_probs - new_log_probs).mean()
        total_loss = policy_loss + self.kl_coef * kl

        return total_loss

奖励模型对比学习实现

from torch import nn

class RewardModel(nn.Module):
    def __init__(self, base_model):
        super().__init__()
        self.transformer = base_model.transformer  # 共享底层参数
        self.head = nn.Linear(base_model.config.n_embd, 1)

    def forward(self, input_ids):
        hidden = self.transformer(input_ids)[0]
        # 取 EOS token 对应的隐藏状态作为句子表示
        eos_pos = (input_ids == 50256).nonzero()[:, 1]
        pooled = hidden[torch.arange(hidden.size(0)), eos_pos]
        return self.head(pooled)

    def contrastive_loss(self, good_ids, bad_ids, margin=1.0):
        good_rewards = self(good_ids)
        bad_rewards = self(bad_ids)
        return torch.relu(margin - (good_rewards - bad_rewards)).mean()

开发者避坑指南

对抗奖励黑客(Reward Hacking)

  1. 正则化方法
  2. 在奖励模型中加入文本长度惩罚项
  3. 使用 Ensemble 模型减少过拟合
  4. 数据层面
  5. 人工检查高分样本中的异常模式
  6. 添加对抗样本到训练数据
  7. 训练技巧
  8. 动态调整 KL 系数 $\beta$
  9. 设置最大生成长度限制

分布式训练同步策略

当使用多 GPU 时需注意:

  1. 梯度同步
  2. 使用torch.nn.parallel.DistributedDataParallel
  3. 每 K 步同步一次全局梯度(而非每步)
  4. 经验池同步
  5. 各 worker 独立收集数据
  6. 通过共享内存或 Redis 同步经验

开放问题与延伸思考

当前 RLHF 仍存在未解决的挑战:

  1. 长期一致性评估
  2. 如何衡量多轮对话中的逻辑连贯性?
  3. 能否设计延迟奖励(Delayed Reward)机制?
  4. 多目标优化
  5. 安全性、趣味性、信息量如何平衡?
  6. 帕累托最优解是否存在?

建议实践方向:
– 在 HuggingFace 的 trl 库基础上修改奖励函数
– 使用 wandb 记录训练过程中的 KL 散度变化曲线

正文完
 0
评论(没有评论)