深度强化学习实战:从零构建Actor-Critic模型及其避坑指南

1次阅读
没有评论

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

image.webp

核心概念:策略梯度与 TD 误差的化学反应

Actor-Critic 的精髓在于结合了策略梯度(Policy Gradient)和时序差分(Temporal Difference)两大方法。用数学公式表示策略梯度定理:

$$\nabla_\theta J(\theta) = \mathbb{E}{\pi\theta}[\nabla_\theta \log \pi_\theta(a|s) Q^{\pi_\theta}(s,a)]$$

而 TD 误差则通过优势函数 $A(s,a) = Q(s,a) – V(s)$ 来优化策略更新,形成以下协同机制:

  • Actor(策略网络)负责根据 $\nabla_\theta \log \pi_\theta(a|s) A(s,a)$ 更新策略
  • Critic(价值网络)通过最小化 TD 误差 $\delta = r + \gamma V(s’) – V(s)$ 来改进价值估计

新手三大致命痛点

  1. 探索不足 :策略过早收敛到局部最优,智能体停止尝试新动作
  2. 高方差 :蒙特卡洛采样导致梯度估计波动剧烈
  3. 网络耦合 :Critic 的误差会通过优势函数传导给 Actor,形成恶性循环

PyTorch 双网络架构实现

先看网络结构定义(完整代码见文末):

import torch
import torch.nn as nn

class Actor(nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.fc1 = nn.Linear(state_dim, 64)
        self.fc2 = nn.Linear(64, action_dim)

    def forward(self, state):
        x = torch.relu(self.fc1(state))
        return torch.softmax(self.fc2(x), dim=-1)

class Critic(nn.Module):
    def __init__(self, state_dim):
        super().__init__()
        self.fc1 = nn.Linear(state_dim, 64)
        self.fc2 = nn.Linear(64, 1)

    def forward(self, state):
        x = torch.relu(self.fc1(state))
        return self.fc2(x)

熵正则化策略更新

在策略更新中添加熵正则项可防止过早收敛,关键代码如下:

def update_actor(states, actions, advantages):
    probs = actor(states)
    log_probs = torch.log(probs.gather(1, actions))

    # 计算策略梯度(含熵正则项)entropy = -torch.sum(probs * torch.log(probs), dim=1).mean()
    policy_loss = -(log_probs * advantages).mean() - 0.01 * entropy

    actor_optimizer.zero_grad()
    policy_loss.backward()
    actor_optimizer.step()

优势函数计算双流派

  1. 蒙特卡洛法 :使用完整轨迹的累计回报 $G_t$ 减去基线值
    advantages = returns - values.detach()
  2. TD 法 :采用 n 步 TD 误差作为优势估计
    delta = rewards + gamma * next_values * (1 - dones) - values
    advantages = discount_rewards(delta, gamma * tau)

工程优化策略

异步采样技巧

  • 同步更新:等所有 worker 完成采样后统一更新(稳定但慢)
  • 异步更新:各 worker 独立更新(快但需要处理梯度冲突)

推荐折中方案:

# 使用多个环境并行采样
envs = [gym.make("CartPole-v1") for _ in range(4)]

学习率衰减实践

# 线性衰减学习率
scheduler = torch.optim.lr_scheduler.LambdaLR(
    optimizer, 
    lr_lambda=lambda epoch: 1 - epoch / total_epochs
)

避坑指南

梯度爆炸防护

# 梯度裁剪(L2 范数阈值设为 0.5)nn.utils.clip_grad_norm_(actor.parameters(), 0.5)
nn.utils.clip_grad_norm_(critic.parameters(), 0.5)

超参数敏感性应对

  1. 使用自动学习率调整:
    optimizer = torch.optim.Adam([{'params': actor.parameters(), 'lr': 3e-4},
        {'params': critic.parameters(), 'lr': 1e-3}
    ])
  2. 实现早停机制(当连续 10 轮无提升时终止)

CartPole 环境验证

训练曲线分析要点:
– 理想情况:Critic 损失平稳下降,Episode Reward 阶梯式上升
– 危险信号:Reward 剧烈波动或 Critic 损失突然飙升

深度强化学习实战:从零构建 Actor-Critic 模型及其避坑指南

完整代码与延伸思考

完整可运行代码已上传 GitHub(链接见文末)。最后留个思考题: 如何改造 Actor 网络使其适合连续动作空间? 提示:可以考虑将输出层改为高斯分布参数。

通过这次实践,我深刻体会到 Actor-Critic 就像教孩子骑自行车:Critic 是扶着后座的家长(价值指导),Actor 是不断尝试的孩子(策略探索),只有两者配合得当,才能快速奔向强化学习的星辰大海。

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