跳转至

08-强化学习基础

学习时间: 约6-8小时 难度级别: ⭐⭐⭐⭐ 中高级 前置知识: 深度学习基础、概率论、PyTorch 学习目标: 理解强化学习的核心概念(MDP、策略、价值函数),掌握DQN与Policy Gradient的原理与实现

📎 交叉引用: - RLHF深入学习 → LLM学习/对齐技术(RLHF/DPO/GRPO完整教程) - 强化学习系统学习 → 强化学习教程 - 最新RLHF进展 → 强化学习/最新研究进展


目录


1. 强化学习概述

RL智能体环境交互

1.1 三大学习范式对比

范式 数据 信号 目标
监督学习 标注的输入-输出对 即时、精确的标签 学习映射函数
无监督学习 无标注数据 无外部信号 发现数据结构
强化学习 交互序列 延迟的标量奖励 最大化累积奖励

1.2 核心要素

Text Only
强化学习框架
    Agent(智能体)
      ↓ 动作 aₜ
    Environment(环境)
      ↓ 状态 sₜ₊₁, 奖励 rₜ
    Agent 根据(sₜ, rₜ)选择下一个动作
概念 符号 说明
状态 \(s \in \mathcal{S}\) 环境的描述
动作 \(a \in \mathcal{A}\) 智能体的行为
奖励 \(r \in \mathbb{R}\) 环境的即时反馈
策略 \(\pi(a \mid s)\) 状态到动作的映射
回报 \(G_t = \sum_{k=0}^{\infty} \gamma^k r_{t+k+1}\) 累积折扣奖励

1.3 应用领域

领域 代表
游戏 AlphaGo, Atari, StarCraft
机器人 机械臂操控、行走
推荐系统 长期用户满意度优化
LLM对齐 RLHF(人类反馈强化学习)
自动驾驶 路径规划与决策

2. 马尔可夫决策过程(MDP)

2.1 定义

MDP由五元组 \((\mathcal{S}, \mathcal{A}, P, R, \gamma)\) 定义:

元素 含义
\(\mathcal{S}\) 状态空间
\(\mathcal{A}\) 动作空间
\(P(s' \mid s,a)\) 转移概率
\(R(s,a)\) 奖励函数
\(\gamma \in [0,1)\) 折扣因子

2.2 马尔可夫性质

\[P(s_{t+1}|s_t, a_t, s_{t-1}, a_{t-1}, ...) = P(s_{t+1}|s_t, a_t)\]

未来仅取决于当前状态和动作,与历史无关。

2.3 策略的分类

类型 说明 代表
确定性策略 \(a = \pi(s)\) DQN的贪心策略
随机策略 \(a \sim \pi(a \mid s)\) Policy Gradient
在线策略 (On-policy) 用当前策略采样并更新 SARSA, PPO
离线策略 (Off-policy) 可用旧策略数据更新 Q-Learning, DQN

3. 价值函数与贝尔曼方程

3.1 状态价值函数

\[V^\pi(s) = \mathbb{E}_\pi\left[\sum_{k=0}^{\infty} \gamma^k r_{t+k+1} \bigg| s_t = s\right]\]

表示从状态 \(s\) 出发,按策略 \(\pi\) 行动,期望获得的累积回报。

3.2 动作价值函数(Q函数)

\[Q^\pi(s, a) = \mathbb{E}_\pi\left[\sum_{k=0}^{\infty} \gamma^k r_{t+k+1} \bigg| s_t = s, a_t = a\right]\]

3.3 贝尔曼方程

贝尔曼期望方程

\[V^\pi(s) = \sum_a \pi(a|s) \sum_{s'} P(s'|s,a)[R(s,a) + \gamma V^\pi(s')]\]

贝尔曼最优方程

\[V^*(s) = \max_a \sum_{s'} P(s'|s,a)[R(s,a) + \gamma V^*(s')]\]
\[Q^*(s,a) = R(s,a) + \gamma \sum_{s'} P(s'|s,a) \max_{a'} Q^*(s',a')\]

3.4 关系图

Text Only
V*(s) = max_a Q*(s,a)
π*(s) = argmax_a Q*(s,a)
Q*(s,a) = R(s,a) + γ·Σ P(s'|s,a)·V*(s')

4. Q-Learning与DQN

Q-Learning算法流程

4.1 Q-Learning

表格式方法,不需要环境模型(model-free):

\[Q(s,a) \leftarrow Q(s,a) + \alpha \left[r + \gamma \max_{a'} Q(s',a') - Q(s,a)\right]\]

局限:状态空间巨大时(如图像输入),Q表无法存储。

4.2 DQN:深度Q网络

用神经网络 \(Q(s,a;\theta)\) 近似Q函数:

\[\mathcal{L}(\theta) = \mathbb{E}\left[\left(r + \gamma \max_{a'} Q(s', a'; \theta^-) - Q(s, a; \theta)\right)^2\right]\]

4.3 DQN的三大创新

技术 作用
经验回放 (Replay Buffer) 打破样本相关性,提高数据利用率
目标网络 (Target Network) 使用延迟更新的网络计算目标值,稳定训练
ε-贪心探索 以概率ε随机探索,1-ε按Q值贪心选择

4.4 DQN的改进

变体 改进点
Double DQN 解耦动作选择和价值评估,减少过估计
Dueling DQN 分别估计V(s) 和 A(s,a),更高效
Prioritized Replay 优先回放TD误差大的样本
Rainbow 组合以上所有改进

5. 策略梯度方法

策略梯度方法

5.1 核心思想

直接参数化策略 \(\pi_\theta(a|s)\),通过梯度上升最大化期望回报:

\[J(\theta) = \mathbb{E}_{\pi_\theta}\left[\sum_t \gamma^t r_t\right]\]

5.2 策略梯度定理

\[\nabla_\theta J(\theta) = \mathbb{E}_{\pi_\theta}\left[\sum_t \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot G_t\right]\]

直觉:增大高回报轨迹中动作的概率,减小低回报轨迹中动作的概率

5.3 REINFORCE算法

Text Only
REINFORCE:
for 每个episode:
    1. 按π_θ采样完整轨迹 τ = (s₁,a₁,r₁,...,sₜ,aₜ,rₜ)
    2. 计算每步的回报 Gₜ = Σ γ^k r_{t+k}
    3. 更新: θ ← θ + α·Σ ∇log π_θ(aₜ|sₜ)·Gₜ

5.4 方差控制——基线

使用基线 \(b(s_t)\) 降低方差(不改变梯度的期望):

\[\nabla_\theta J(\theta) = \mathbb{E}\left[\sum_t \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot (G_t - b(s_t))\right]\]

常用基线:\(b(s_t) = V(s_t)\),此时 \(G_t - V(s_t)\) 称为优势函数 \(A(s_t, a_t)\)


6. Actor-Critic方法

Actor-Critic架构

6.1 思想

结合价值方法和策略方法的优点:

组件 作用 更新方式
Actor(策略网络) 输出动作概率 \(\pi_\theta(a \mid s)\) 策略梯度
Critic(价值网络) 评估状态价值 \(V_\phi(s)\) TD误差

6.2 优势Actor-Critic (A2C)

\[\delta_t = r_t + \gamma V_\phi(s_{t+1}) - V_\phi(s_t) \quad \text{(TD误差/优势估计)}\]

Actor更新:\(\theta \leftarrow \theta + \alpha \nabla_\theta \log \pi_\theta(a_t|s_t) \cdot \delta_t\)

Critic更新:\(\phi \leftarrow \phi - \beta \nabla_\phi (r_t + \gamma V_\phi(s_{t+1}) - V_\phi(s_t))^2\)

6.3 PyTorch实现

Python
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.distributions import Categorical

class ActorCritic(nn.Module):  # 继承nn.Module定义神经网络层
    """共享底层特征,分别输出策略和价值的Actor-Critic网络"""
    def __init__(self, state_dim, action_dim, hidden_dim=128):  # __init__构造方法,创建对象时自动调用
        super().__init__()  # super()调用父类方法
        # 共享特征提取层:Actor和Critic共用,减少参数量
        self.shared = nn.Sequential(
            nn.Linear(state_dim, hidden_dim),
            nn.ReLU(),
        )
        # Actor头:输出各动作的概率分布
        self.actor = nn.Linear(hidden_dim, action_dim)
        # Critic头:输出当前状态的价值估计V(s)
        self.critic = nn.Linear(hidden_dim, 1)

    def forward(self, state):
        features = self.shared(state)                           # 提取共享特征
        action_probs = F.softmax(self.actor(features), dim=-1)  # 动作概率(softmax归一化)
        state_value = self.critic(features)                     # 状态价值V(s)
        return action_probs, state_value

    def select_action(self, state):
        """根据策略分布采样动作,同时返回log概率和价值(用于后续梯度计算)"""
        action_probs, value = self.forward(state)
        dist = Categorical(action_probs)  # 构建离散分布
        action = dist.sample()            # 按概率采样动作
        return action.item(), dist.log_prob(action), value  # .item()将单元素张量转为Python数值

def train_actor_critic(env, model, optimizer, n_episodes=1000, gamma=0.99):
    """训练Actor-Critic模型"""
    for episode in range(n_episodes):
        state, _ = env.reset()
        log_probs, values, rewards = [], [], []  # 存储轨迹数据

        # 采样一个完整episode
        done = False
        while not done:
            state_tensor = torch.FloatTensor(state)
            action, log_prob, value = model.select_action(state_tensor)

            next_state, reward, terminated, truncated, _ = env.step(action)
            done = terminated or truncated

            log_probs.append(log_prob)   # 动作的log概率
            values.append(value)          # Critic的价值估计
            rewards.append(reward)        # 环境奖励
            state = next_state

        # 从后往前计算每步的折扣回报G_t,并标准化
        returns = []
        G = 0
        for r in reversed(rewards):
            G = r + gamma * G
            returns.insert(0, G)
        returns = torch.tensor(returns)
        returns = (returns - returns.mean()) / (returns.std() + 1e-8)  # 标准化降低方差

        # 计算Actor和Critic的损失
        policy_loss = 0
        value_loss = 0
        for log_prob, value, G in zip(log_probs, values, returns):  # zip按位置配对多个可迭代对象
            advantage = G - value.item()                       # 优势 = 实际回报 - 价值估计
            policy_loss -= log_prob * advantage                # Actor损失:增大高优势动作的概率
            value_loss += F.smooth_l1_loss(value.squeeze(), G) # Critic损失:让V(s)逼近实际回报

        # 联合优化:policy_loss + 0.5 * value_loss
        loss = policy_loss + 0.5 * value_loss
        optimizer.zero_grad()  # 清零梯度,防止梯度累积
        loss.backward()  # 反向传播计算梯度
        optimizer.step()  # 根据梯度更新模型参数

        if (episode + 1) % 100 == 0:
            print(f"Episode {episode+1}, Total Reward: {sum(rewards):.1f}")

7. PPO:近端策略优化

7.1 动机

策略梯度的步长难以控制——更新过大会导致策略崩溃。PPO通过裁剪限制策略更新幅度。

7.2 核心公式

重要性采样比:

\[r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}\]

裁剪目标:

\[L^{CLIP}(\theta) = \mathbb{E}\left[\min\left(r_t(\theta) A_t, \; \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) A_t\right)\right]\]

\(A_t > 0\)(好动作):限制 \(r_t\) 不超过 \(1+\epsilon\)\(A_t < 0\)(坏动作):限制 \(r_t\) 不低于 \(1-\epsilon\)

7.3 PPO的完整损失

\[L(\theta) = L^{CLIP} - c_1 L^{VF} + c_2 S[\pi_\theta]\]
作用
\(L^{CLIP}\) 策略目标(裁剪)
\(L^{VF}\) 价值函数损失
\(S[\pi_\theta]\) 熵正则化(鼓励探索)

7.4 为什么PPO重要

  • LLM对齐(RLHF)中广泛使用PPO作为RL优化器
  • 实现简单、调参少、训练稳定
  • 支持并行数据采集

8. 深度强化学习进阶话题

8.1 模型预测控制 (Model-Based RL)

方法 说明
World Model 学习环境动力学模型,在"想象"中规划
Dreamer 在潜在空间学习世界模型
MuZero 无需知道游戏规则,学习隐式模型

8.2 离线强化学习 (Offline RL)

只用预先收集的数据学习策略,不与环境交互: - 挑战:分布偏移(策略外推到未观测的状态-动作对) - 方法:CQL(保守Q学习)、Decision Transformer

8.3 RLHF:人类反馈强化学习

Text Only
LLM对齐的RLHF流程:
1. 监督微调 (SFT): 在高质量数据上微调基座模型
2. 训练奖励模型 (RM): 用人类偏好数据训练
3. PPO优化: 以RM评分为奖励,用PPO优化策略(LLM)
4. 加上KL惩罚: 防止偏离SFT模型过远

8.4 多智能体强化学习 (MARL)

范式 说明
合作 所有智能体共享目标
竞争 零和博弈
混合 既有合作又有竞争

9. 实战:DQN玩CartPole

Python
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from collections import deque
import random

class DQN(nn.Module):
    """深度Q网络:用神经网络近似Q(s,a)函数"""
    def __init__(self, state_dim, action_dim, hidden_dim=128):
        super().__init__()
        # 三层全连接网络:输入状态 → 输出每个动作的Q值
        self.net = nn.Sequential(
            nn.Linear(state_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, action_dim),  # 输出维度=动作数
        )

    def forward(self, x):
        return self.net(x)

class ReplayBuffer:
    """经验回放缓冲区:存储历史转移,随机采样打破时间相关性"""
    def __init__(self, capacity=10000):
        self.buffer = deque(maxlen=capacity)  # 固定容量的双端队列

    def push(self, state, action, reward, next_state, done):
        """存储一条转移记录 (s, a, r, s', done)"""
        self.buffer.append((state, action, reward, next_state, done))

    def sample(self, batch_size):
        """随机采样一批数据,转换为Tensor格式"""
        batch = random.sample(self.buffer, batch_size)
        states, actions, rewards, next_states, dones = zip(*batch)
        return (
            torch.FloatTensor(np.array(states)),  # np.array创建NumPy数组
            torch.LongTensor(actions),
            torch.FloatTensor(rewards),
            torch.FloatTensor(np.array(next_states)),
            torch.FloatTensor(dones),
        )

    def __len__(self):  # __len__定义len()的行为
        return len(self.buffer)

class DQNAgent:
    """DQN智能体:集成Q网络、目标网络、经验回放和ε-贪心探索"""
    def __init__(self, state_dim, action_dim, lr=1e-3, gamma=0.99,
                 epsilon_start=1.0, epsilon_end=0.01, epsilon_decay=0.995):
        self.action_dim = action_dim
        self.gamma = gamma               # 折扣因子
        self.epsilon = epsilon_start      # 探索率(从高到低衰减)
        self.epsilon_end = epsilon_end
        self.epsilon_decay = epsilon_decay

        self.q_net = DQN(state_dim, action_dim)         # 在线Q网络(实时训练)
        self.target_net = DQN(state_dim, action_dim)    # 目标网络(延迟更新,稳定训练)
        self.target_net.load_state_dict(self.q_net.state_dict())  # 初始参数同步

        self.optimizer = torch.optim.Adam(self.q_net.parameters(), lr=lr)
        self.buffer = ReplayBuffer()

    def select_action(self, state):
        """ε-贪心策略:以ε概率随机探索,1-ε概率选Q值最大的动作"""
        if random.random() < self.epsilon:
            return random.randrange(self.action_dim)  # 随机探索
        with torch.no_grad():  # 禁用梯度计算,节省内存
            q_values = self.q_net(torch.FloatTensor(state))
            return q_values.argmax().item()           # 贪心选择

    def update(self, batch_size=64):
        """从经验池采样一批数据,执行一步梯度更新"""
        if len(self.buffer) < batch_size:
            return  # 经验不足,跳过

        states, actions, rewards, next_states, dones = self.buffer.sample(batch_size)

        # 计算当前Q值:Q(s,a;θ)
        current_q = self.q_net(states).gather(1, actions.unsqueeze(1)).squeeze()

        # 计算TD目标:r + γ * max_a' Q(s',a';θ⁻),使用目标网络避免自举不稳
        with torch.no_grad():
            max_next_q = self.target_net(next_states).max(dim=1)[0]
            target_q = rewards + self.gamma * max_next_q * (1 - dones)

        # 最小化TD误差(Huber损失更鲁棒)
        loss = F.smooth_l1_loss(current_q, target_q)
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

        # 逐步衰减探索率:从探索为主过渡到利用为主
        self.epsilon = max(self.epsilon_end, self.epsilon * self.epsilon_decay)

    def update_target(self):
        """将在线网络的参数同步到目标网络(硬更新)"""
        self.target_net.load_state_dict(self.q_net.state_dict())

def train_dqn(env_name='CartPole-v1', n_episodes=500, target_update=10):
    """DQN训练主循环:在CartPole环境中训练智能体"""
    import gymnasium as gym
    env = gym.make(env_name)

    state_dim = env.observation_space.shape[0]   # 状态维度(CartPole=4)
    action_dim = env.action_space.n              # 动作数量(CartPole=2:左/右)
    agent = DQNAgent(state_dim, action_dim)

    rewards_history = []

    for episode in range(n_episodes):
        state, _ = env.reset()
        total_reward = 0
        done = False

        # 与环境交互,收集经验并实时学习
        while not done:
            action = agent.select_action(state)      # ε-贪心选动作
            next_state, reward, terminated, truncated, _ = env.step(action)
            done = terminated or truncated

            agent.buffer.push(state, action, reward, next_state, float(done))  # 存入经验池
            agent.update()  # 从经验池采样并更新Q网络

            state = next_state
            total_reward += reward

        # 每隔target_update个episode同步目标网络
        if (episode + 1) % target_update == 0:
            agent.update_target()

        rewards_history.append(total_reward)

        # 每50个episode打印训练进度
        if (episode + 1) % 50 == 0:
            avg_reward = np.mean(rewards_history[-50:])  # 切片操作取子序列
            print(f"Episode {episode+1}, Avg Reward: {avg_reward:.1f}, "
                  f"Epsilon: {agent.epsilon:.3f}")

    env.close()
    return agent

# agent = train_dqn()

10. 面试高频题

Q1: 价值方法(DQN)和策略方法(Policy Gradient)的区别?

:价值方法学习Q函数,从中间接导出策略(取argmax);适合离散动作空间,是off-policy的。策略方法直接参数化策略并通过梯度优化;天然支持连续动作空间和随机策略,但通常是on-policy的。DQN样本效率更高(经验回放),Policy Gradient方差较大但更通用。

Q2: 解释DQN中的经验回放和目标网络

经验回放:将\((s,a,r,s')\)存入缓冲区,训练时随机采样mini-batch,打破了时间相关性,提高数据利用率和训练稳定性。目标网络:使用一个延迟更新的网络计算TD目标 \(r + \gamma \max_{a'} Q(s',a';\theta^-)\),避免"自举"导致的训练不稳定(目标值随训练实时变化的问题)。

Q3: PPO的裁剪机制是如何工作的?

:PPO计算新旧策略的概率比 \(r_t(\theta)\),并将其裁剪到 \([1-\epsilon, 1+\epsilon]\) 范围。当动作好(优势>0)时,\(r_t\) 被限制不超过 \(1+\epsilon\),防止策略过度倾向该动作;当动作差(优势<0)时,\(r_t\) 被限制不低于 \(1-\epsilon\),防止过度远离该动作。这保证了策略更新的稳定性。

Q4: RLHF的流程是什么?为什么用PPO?

:三步流程:(1) 在高质量数据上做SFT得到基础策略;(2) 用人类偏好数据训练奖励模型RM;(3) 用PPO以RM得分为奖励优化LLM策略,同时加KL惩罚防止偏离SFT模型过远。用PPO因为它稳定、实现简单、支持大batch并行,适合大规模LLM微调。

Q5: on-policy和off-policy的区别?

:On-policy(如SARSA、PPO)用当前策略采样的数据来更新当前策略;数据用完即丢,样本效率低但理论简洁。Off-policy(如Q-Learning、DQN)可以用任意策略采集的数据更新目标策略;能使用经验回放,样本效率高,但需要处理分布不匹配问题。


11. 练习与自我检查

编程练习

  1. 基础:实现DQN玩CartPole-v1,调优至稳定达到满分(500步)
  2. 进阶:实现Actor-Critic并与DQN在CartPole上对比性能
  3. 挑战:实现PPO算法并应用到LunarLander-v2环境

检查清单

  • 能说明强化学习与监督学习的关键区别
  • 理解MDP的五元组及马尔可夫性质
  • 能写出贝尔曼最优方程
  • 理解Q-Learning的更新规则
  • 能解释DQN的三大创新(经验回放、目标网络、ε-贪心)
  • 理解策略梯度定理的直觉含义
  • 能实现Actor-Critic架构
  • 理解PPO的裁剪机制及其稳定训练的原理
  • 了解RLHF在LLM对齐中的应用
  • 能用PyTorch实现完整的DQN训练流程

RL应用场景

扩展阅读: - Sutton & Barto: Reinforcement Learning: An Introduction (经典教材) - Mnih et al., 2015: Human-level control through deep reinforcement learning (DQN) - Schulman et al., 2017: Proximal Policy Optimization Algorithms (PPO) - Ouyang et al., 2022: Training language models to follow instructions with human feedback (RLHF)