摘要:Q-Learning 的表格方法受限于状态空间的规模——游戏画面的像素组合几乎是无穷的,不可能用表格存 Q 值。深度强化学习(Deep RL)用神经网络替代 Q-Table,让 RL 能处理高维输入(图像、连续状态)。2013 年 DeepMind 的 DQN 在 Atari 游戏上超越人类,2017 年 OpenAI 的 PPO 成为策略梯度方法的标杆。这篇文章讲清楚这两个里程碑算法,以及它们如何从 Q-Learning 演进而来。


一、从 Q-Table 到神经网络

Q-Learning 的瓶颈

在上一篇文章的迷宫中,4×4 网格有 16 个状态,Q-Table 只有 64 个格子。但如果是 Atari 游戏呢?

Atari 游戏画面:
  分辨率:84×84 像素
  颜色:灰度(256 阶)

状态数 = 256^(84×84) ≈ 10^17000

→ 这个数字比宇宙中的原子数(10^80)还要大无数倍
→ 用表格存储绝对不可能

核心思路:用神经网络拟合 Q 函数

Q-Learning:     Q(s, a) ← 查 Q-Table[s][a]
                    ↓
DQN:            Q(s, a) ← 神经网络(s)[a]

神经网络输入:状态 s(如图像像素)
神经网络输出:每个动作 a 的 Q 值
import torch
import torch.nn as nn
import torch.nn.functional as F

# ===== DQN 的核心:用神经网络替代 Q-Table =====

class DQN(nn.Module):
    """深度 Q 网络——输入状态,输出各动作的 Q 值"""
    
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 128),
            nn.ReLU(),
            nn.Linear(128, action_dim)  # 输出每个动作的 Q 值
        )
    
    def forward(self, x):
        return self.net(x)

# Q-Learning 的做法:
#   q_value = q_table[state][action]
#
# DQN 的做法:
#   q_values = dqn_network(state)       # 一次输出所有动作的 Q 值
#   q_value = q_values[action]

二、DQN:Deep Q-Network

DQN 的两个关键创新

DQN(Deep Q-Network)在 2013 年由 DeepMind 提出,在 Atari 游戏上取得突破性成果。它解决了"用神经网络做 Q-Learning"的两个关键问题。

创新 1:经验回放(Experience Replay)
问题:RL 的数据是序列相关的
  在迷宫中刚刚走过的路和当前状态高度相关
  连续使用相关数据训练 → 神经网络"忘记"了过去 → 不稳定

解决方案:经验回放
  把每次交互 (s, a, r, s') 存到一个"经验池"中
  训练时从池中随机采样(打乱相关性)
  就像人类学习时不只是复习刚学过的一题,而是随机抽题

  经验池大小:通常 10000-100000
  每次训练采样:32-256 条
from collections import deque
import random

class ReplayBuffer:
    """经验回放缓冲区"""
    
    def __init__(self, capacity=10000):
        self.buffer = deque(maxlen=capacity)
    
    def push(self, state, action, reward, next_state, done):
        self.buffer.append((state, action, reward, next_state, done))
    
    def sample(self, batch_size=64):
        batch = random.sample(self.buffer, batch_size)
        states, actions, rewards, next_states, dones = zip(*batch)
        return (np.array(states), np.array(actions), 
                np.array(rewards, dtype=np.float32),
                np.array(next_states), np.array(dones))
    
    def __len__(self):
        return len(self.buffer)
创新 2:目标网络(Target Network)
问题:Q-Learning 更新中,"目标"也在随训练变化
  
  Q(s, a) ← Q(s, a) + α × [R + γ × max Q(s', a') - Q(s, a)]
                                  ↑
                          这个目标值也在变!
  
  就像追着一个移动的靶子射箭——永远打不中

解决方案:使用两个网络
  1. 主网络(Policy Net):不断更新,用于选择动作
  2. 目标网络(Target Net):定期从主网络复制,用于计算目标 Q 值
  
  让目标网络"冻结"一段时间,训练更稳定
# ===== DQN 训练的关键部分 =====
def train_dqn_step(main_net, target_net, optimizer, batch, gamma=0.99):
    """DQN 单步训练"""
    states, actions, rewards, next_states, dones = batch
    
    # 转为 Tensor
    states = torch.FloatTensor(states)
    actions = torch.LongTensor(actions)
    rewards = torch.FloatTensor(rewards)
    next_states = torch.FloatTensor(next_states)
    dones = torch.FloatTensor(dones)
    
    # 1. 主网络计算当前 Q 值
    current_q = main_net(states)                         # [batch, n_actions]
    current_q = current_q.gather(1, actions.unsqueeze(1)) # [batch, 1]
    
    # 2. 目标网络计算目标 Q 值(固定!不传播梯度)
    with torch.no_grad():
        next_q = target_net(next_states)                  # [batch, n_actions]
        max_next_q = next_q.max(1)[0]                     # [batch]
        target_q = rewards + gamma * max_next_q * (1 - dones)
    
    # 3. 损失 = MSE(当前 Q, 目标 Q)
    loss = F.mse_loss(current_q.squeeze(), target_q)
    
    # 4. 反向传播更新主网络
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
    return loss.item()

# 定期(每 100-1000 步)将主网络参数复制到目标网络
def update_target_network(main_net, target_net):
    target_net.load_state_dict(main_net.state_dict())

DQN 完整训练循环

import torch.optim as optim

class DQNAgent:
    """完整的 DQN 智能体"""
    
    def __init__(self, state_dim, action_dim):
        self.main_net = DQN(state_dim, action_dim)
        self.target_net = DQN(state_dim, action_dim)
        self.target_net.load_state_dict(self.main_net.state_dict())
        
        self.optimizer = optim.Adam(self.main_net.parameters(), lr=0.001)
        self.replay_buffer = ReplayBuffer(capacity=10000)
        
        self.epsilon = 1.0
        self.action_dim = action_dim
        self.update_counter = 0
    
    def choose_action(self, state):
        """ε-贪婪选择动作"""
        if np.random.random() < self.epsilon:
            return np.random.randint(self.action_dim)
        
        state_tensor = torch.FloatTensor(state).unsqueeze(0)
        with torch.no_grad():
            q_values = self.main_net(state_tensor)
        return q_values.argmax().item()
    
    def train_step(self, batch_size=64):
        """用经验回放中的一个 batch 训练"""
        if len(self.replay_buffer) < batch_size:
            return 0
        
        batch = self.replay_buffer.sample(batch_size)
        loss = train_dqn_step(self.main_net, self.target_net,
                              self.optimizer, batch)
        
        # 定期更新目标网络
        self.update_counter += 1
        if self.update_counter % 100 == 0:
            update_target_network(self.main_net, self.target_net)
        
        return loss

# ===== DQN vs Q-Learning =====
# Q-Learning:    Q-Table   + 贝尔曼方程更新   + ε-贪婪
# DQN:          神经网络   + 经验回放          + ε-贪婪
#                           + 目标网络
#                           + 贝尔曼方程更新

三、从 DQN 到策略梯度

价值方法 vs 策略方法

DQN 是价值方法——先学 Q 值,再根据 Q 值选动作。但价值方法有局限:

价值方法的局限:
  1. 只能处理离散动作(从 Q 值中选最大的)
  2. 策略是确定性的(同一状态永远选同一个动作)
  3. 对连续动作(如机器人关节角度)无能为力

→ 策略梯度方法直接学习策略 π(a|s)
  而不是通过 Q 值间接得到策略

REINFORCE 算法

策略梯度中最简单的方法:

REINFORCE 的核心思想:
  1. 用神经网络直接输出动作概率 π(a|s)
  2. 让"好"的动作概率增大,"坏"的动作概率减小
  3. 用整条轨迹的累积奖励作为"好坏"的判断标准
class PolicyNetwork(nn.Module):
    """策略网络——直接输出动作概率"""
    
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, 128),
            nn.ReLU(),
            nn.Linear(128, action_dim),
            nn.Softmax(dim=-1)        # 输出概率分布
        )
    
    def forward(self, x):
        return self.net(x)

# REINFORCE 更新
# 目标:最大化 log π(a|s) × G
# 其中 G 是这条轨迹的总奖励

四、PPO:近端策略优化

PPO 要解决的问题

REINFORCE 有一个严重的问题:不稳定

REINFORCE 的问题:
  如果你用旧的交互数据更新了策略,
  新策略和旧策略可能差异很大
  → 更新一步,策略"坍塌"了
  
  就像"步子迈太大,直接摔倒了"

PPO(Proximal Policy Optimization)在 2017 年由 OpenAI 提出,核心思想:每次更新不要改变太多

PPO 的核心创新:Clip 目标

PPO 的目标函数:

L = min( r(θ) × A, clip(r(θ), 1-ε, 1+ε) × A )

其中:
  r(θ) = π_新(a|s) / π_旧(a|s)  ← 新旧策略的比率
  A = 优势函数(这个动作比平均好多少?)
  ε = clip 范围(通常 0.2)

关键机制:
  - 当 r(θ) 太大时(新策略太激进),clip 把它拉回来
  - 当 r(θ) 太小时(新策略太保守),clip 把它拉回来
  - 更新被限制在 [1-ε, 1+ε] 的范围内
  
  → "每次只学一点,不贪多"

为什么 PPO 是 2026 年最主流的 RL 算法?

维度 REINFORCE DQN PPO
训练稳定性 ❌ 差 ⚠️ 中等 稳定
样本效率 ❌ 低 ⚠️ 中等 ✅ 高
连续动作 ✅ 支持 ❌ 不支持 支持
离散动作 ✅ 支持 ✅ 支持 支持
实现复杂 ✅ 简单 ⚠️ 中等 ⚠️ 中等
调参难度 ❌ 难 ⚠️ 中
PPO 的"好"体现在:它是 RL 领域的"随机森林"

  不像 DQN 那样只有特定场景好用
  不像 REINFORCE 那样不稳定
  PPO 几乎在所有场景下都能工作
  是 2026 年强化学习的第一选择

PPO 的 3 行核心代码

# PPO 最核心的部分只有 3 行伪代码:

# 1. 计算新旧策略比率
ratio = torch.exp(new_log_prob - old_log_prob)

# 2. 裁剪比率(限制更新幅度)
clipped_ratio = torch.clamp(ratio, 1 - clip_eps, 1 + clip_eps)

# 3. 取最小值(不要让激进更新主导)
loss = -torch.min(ratio * advantage, clipped_ratio * advantage).mean()

# 其他都是工程优化

五、三大算法对比总结

强化学习算法演进:

Q-Learning ──→ DQN ──→ PPO
(2013以前)    (2013)  (2017)
    │           │        │
  表格方法     深度网络    策略梯度
  有限状态     Atari游戏  通用RL基准

核心思想对比

Q-Learning DQN PPO
核心概念 Q-Table 神经网络 + 经验回放 + 目标网络 裁剪策略更新
学习对象 Q(s, a) Q(s, a) 直接学 π(a|s)
状态空间 有限离散 连续/高维 连续/高维
动作空间 有限离散 有限离散 连续或离散
探索方式 ε-贪婪 ε-贪婪 策略自带随机性

2026 年的选型指南

任务类型 → 推荐算法

离散动作 + 小状态空间(迷宫、棋盘)
  → Q-Learning ✅

离散动作 + 高维状态输入(Atari 游戏)
  → DQN ✅(或改进版 Rainbow DQN)

连续动作 + 机器人控制(倒立摆、机械臂)
  → PPO ✅

大规模并行训练(如 DOTA、StarCraft)
  → PPO + 大规模分布式 ✅(如 OpenAI Five)

需要从演示中学习(模仿学习)
  → 先行为克隆 + 再 PPO 微调 ✅

六、强化学习的现实应用

领域 应用 使用的算法
游戏 AI AlphaGo、OpenAI Five、AlphaStar 深度 RL + 蒙特卡洛树搜索
机器人 机械臂抓取、四足机器人行走 PPO / SAC
自动驾驶 路径规划、车辆控制 PPO / DDPG
推荐系统 新闻推荐、广告出价 多臂老虎机 / 离线 RL
大模型 RLHF(人类反馈强化学习) PPO(训练 ChatGPT 的算法)
芯片设计 芯片布局优化 PPO / 图神经网络 + RL

RLHF:大模型中的强化学习

2024-2026 年最广为人知的 RL 应用是 RLHF(Reinforcement Learning from Human Feedback)——ChatGPT 训练的核心步骤之一。

RLHF 的三步:

1. 监督微调(SFT)
   用高质量对话数据微调预训练模型

2. 训练奖励模型
   让人类对模型的输出进行排序
   训练一个"打分模型"来模拟人类偏好

3. PPO 优化
   用 PPO 微调语言模型
   → 目标是最大化奖励模型的分数
   → 同时用 KL 散度约束,防止模型偏离初始分布

→ PPO 在这里起到了"每次不要改变太多"的关键作用!
  如果不用 PPO 而用普通的策略梯度,模型可能会"疯掉"

七、总结

概念 一句话理解
DQN 用神经网络替代 Q-Table + 经验回放打乱相关性 + 目标网络稳定训练
经验回放 把经历存起来,随机采样训练——打破序列相关性
目标网络 固定目标 Q 值的计算网络——追固定靶而不是移动靶
策略梯度 直接学 π(a|s) 而非 Q(s, a)——天然支持连续动作和随机策略
PPO 每次更新都限制幅度——"步子大了会扯到蛋"

核心三句话

  1. DQN = Q-Learning + 神经网络 + 经验回放 + 目标网络——它让 RL 能处理图像等高维输入
  2. PPO = 策略梯度 + "每次不要改太多"的约束——它用 3 行核心代码解决了 RL 训练不稳定的根本问题
  3. PPO 是 2026 年最通用的 RL 算法——从 Atari 游戏到机器人控制到 ChatGPT 的 RLHF,都是它
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐