共计 2251 个字符,预计需要花费 6 分钟才能阅读完成。
核心概念:策略梯度与 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)$ 来改进价值估计
新手三大致命痛点
- 探索不足 :策略过早收敛到局部最优,智能体停止尝试新动作
- 高方差 :蒙特卡洛采样导致梯度估计波动剧烈
- 网络耦合 :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()
优势函数计算双流派
- 蒙特卡洛法 :使用完整轨迹的累计回报 $G_t$ 减去基线值
advantages = returns - values.detach() - 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)
超参数敏感性应对
- 使用自动学习率调整:
optimizer = torch.optim.Adam([{'params': actor.parameters(), 'lr': 3e-4}, {'params': critic.parameters(), 'lr': 1e-3} ]) - 实现早停机制(当连续 10 轮无提升时终止)
CartPole 环境验证
训练曲线分析要点:
– 理想情况:Critic 损失平稳下降,Episode Reward 阶梯式上升
– 危险信号:Reward 剧烈波动或 Critic 损失突然飙升

完整代码与延伸思考
完整可运行代码已上传 GitHub(链接见文末)。最后留个思考题: 如何改造 Actor 网络使其适合连续动作空间? 提示:可以考虑将输出层改为高斯分布参数。
通过这次实践,我深刻体会到 Actor-Critic 就像教孩子骑自行车:Critic 是扶着后座的家长(价值指导),Actor 是不断尝试的孩子(策略探索),只有两者配合得当,才能快速奔向强化学习的星辰大海。
正文完
发表至: 人工智能
近一天内
