08-强化学习基础¶
学习时间: 约6-8小时 难度级别: ⭐⭐⭐⭐ 中高级 前置知识: 深度学习基础、概率论、PyTorch 学习目标: 理解强化学习的核心概念(MDP、策略、价值函数),掌握DQN与Policy Gradient的原理与实现
📎 交叉引用: - RLHF深入学习 → LLM学习/对齐技术(RLHF/DPO/GRPO完整教程) - 强化学习系统学习 → 强化学习教程 - 最新RLHF进展 → 强化学习/最新研究进展
目录¶
- 1. 强化学习概述
- 2. 马尔可夫决策过程(MDP)
- 3. 价值函数与贝尔曼方程
- 4. Q-Learning与DQN
- 5. 策略梯度方法
- 6. Actor-Critic方法
- 7. PPO:近端策略优化
- 8. 深度强化学习进阶话题
- 9. 实战:DQN玩CartPole
- 10. 面试高频题
- 11. 练习与自我检查
1. 强化学习概述¶
1.1 三大学习范式对比¶
| 范式 | 数据 | 信号 | 目标 |
|---|---|---|---|
| 监督学习 | 标注的输入-输出对 | 即时、精确的标签 | 学习映射函数 |
| 无监督学习 | 无标注数据 | 无外部信号 | 发现数据结构 |
| 强化学习 | 交互序列 | 延迟的标量奖励 | 最大化累积奖励 |
1.2 核心要素¶
| 概念 | 符号 | 说明 |
|---|---|---|
| 状态 | \(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 马尔可夫性质¶
未来仅取决于当前状态和动作,与历史无关。
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 状态价值函数¶
表示从状态 \(s\) 出发,按策略 \(\pi\) 行动,期望获得的累积回报。
3.2 动作价值函数(Q函数)¶
3.3 贝尔曼方程¶
贝尔曼期望方程:
贝尔曼最优方程:
3.4 关系图¶
4. Q-Learning与DQN¶
4.1 Q-Learning¶
表格式方法,不需要环境模型(model-free):
局限:状态空间巨大时(如图像输入),Q表无法存储。
4.2 DQN:深度Q网络¶
用神经网络 \(Q(s,a;\theta)\) 近似Q函数:
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)\),通过梯度上升最大化期望回报:
5.2 策略梯度定理¶
直觉:增大高回报轨迹中动作的概率,减小低回报轨迹中动作的概率。
5.3 REINFORCE算法¶
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)\) 降低方差(不改变梯度的期望):
常用基线:\(b(s_t) = V(s_t)\),此时 \(G_t - V(s_t)\) 称为优势函数 \(A(s_t, a_t)\)。
6. Actor-Critic方法¶
6.1 思想¶
结合价值方法和策略方法的优点:
| 组件 | 作用 | 更新方式 |
|---|---|---|
| Actor(策略网络) | 输出动作概率 \(\pi_\theta(a \mid s)\) | 策略梯度 |
| Critic(价值网络) | 评估状态价值 \(V_\phi(s)\) | TD误差 |
6.2 优势Actor-Critic (A2C)¶
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实现¶
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 核心公式¶
重要性采样比:
裁剪目标:
当 \(A_t > 0\)(好动作):限制 \(r_t\) 不超过 \(1+\epsilon\) 当 \(A_t < 0\)(坏动作):限制 \(r_t\) 不低于 \(1-\epsilon\)
7.3 PPO的完整损失¶
| 项 | 作用 |
|---|---|
| \(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:人类反馈强化学习¶
LLM对齐的RLHF流程:
1. 监督微调 (SFT): 在高质量数据上微调基座模型
2. 训练奖励模型 (RM): 用人类偏好数据训练
3. PPO优化: 以RM评分为奖励,用PPO优化策略(LLM)
4. 加上KL惩罚: 防止偏离SFT模型过远
8.4 多智能体强化学习 (MARL)¶
| 范式 | 说明 |
|---|---|
| 合作 | 所有智能体共享目标 |
| 竞争 | 零和博弈 |
| 混合 | 既有合作又有竞争 |
9. 实战:DQN玩CartPole¶
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. 练习与自我检查¶
编程练习¶
- 基础:实现DQN玩CartPole-v1,调优至稳定达到满分(500步)
- 进阶:实现Actor-Critic并与DQN在CartPole上对比性能
- 挑战:实现PPO算法并应用到LunarLander-v2环境
检查清单¶
- 能说明强化学习与监督学习的关键区别
- 理解MDP的五元组及马尔可夫性质
- 能写出贝尔曼最优方程
- 理解Q-Learning的更新规则
- 能解释DQN的三大创新(经验回放、目标网络、ε-贪心)
- 理解策略梯度定理的直觉含义
- 能实现Actor-Critic架构
- 理解PPO的裁剪机制及其稳定训练的原理
- 了解RLHF在LLM对齐中的应用
- 能用PyTorch实现完整的DQN训练流程
扩展阅读: - 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)