共计 2189 个字符,预计需要花费 6 分钟才能阅读完成。
为什么 A2C 实现总是不稳定?
刚接触 A2C 时,最让人头疼的就是模型训练过程中的剧烈波动。通过实践发现,主要原因集中在三个方面:

- 优势估计偏差 :简单的时序差分(TD)估计会引入较大方差
- 策略更新幅度失控 :没有像 PPO 那样的更新约束机制
- 探索不足 :智能体容易陷入局部最优策略
这些痛点直接导致 reward 曲线像坐过山车——好不容易上升又突然崩盘。接下来我们通过对比主流算法,看看 A2C 的特性在哪。
算法横向对比
| 维度 | A2C | PPO | DQN |
|---|---|---|---|
| 样本效率 | 中等 | 高 | 低 |
| 超参数敏感度 | 高(尤其学习率) | 中等 | 高 |
| 并行能力 | 强(同步更新) | 一般 | 弱 |
| 适用场景 | 连续 / 离散动作空间 | 连续动作空间优势 | 离散动作空间 |
从表格可以看出,A2C 在并行训练方面有天然优势,适合作为入门 Actor-Critic 框架的首选。
PyTorch 实现详解
网络架构设计
class ActorCritic(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
# 共享特征提取层
self.feature = nn.Sequential(nn.Linear(state_dim, 64),
nn.ReLU(),
nn.Linear(64, 64),
nn.ReLU())
# 策略头
self.actor = nn.Linear(64, action_dim)
# 价值头
self.critic = nn.Linear(64, 1)
def forward(self, x):
features = self.feature(x)
return F.softmax(self.actor(features), dim=-1), self.critic(features)
关键点在于共享底层网络(feature 层),这样既能保证策略和价值函数对状态理解的一致性,又能提高训练效率。
GAE 优势计算
def compute_gae(rewards, values, dones, gamma=0.99, lam=0.95):
"""
rewards: 轨迹中的即时奖励序列
values: 价值网络输出的状态估值
dones: 轨迹终止标记
"""
advantages = []
last_advantage = 0
# 反向计算 GAE
for t in reversed(range(len(rewards))):
delta = rewards[t] + gamma * values[t+1] * (1-dones[t]) - values[t]
last_advantage = delta + gamma * lam * (1-dones[t]) * last_advantage
advantages.insert(0, last_advantage)
return torch.tensor(advantages)
这里 lam 参数控制偏差与方差的权衡,通常设置在 0.9~0.95 之间。注意要反向遍历时间步,这是 GAE 计算的标准做法。
损失函数实现
def a2c_loss(states, actions, advantages, old_log_probs, entropy_coef=0.01):
# 新版策略概率
new_probs, state_values = model(states)
new_log_probs = torch.log(new_probs.gather(1, actions))
# 策略梯度损失(带重要性采样)policy_ratio = (new_log_probs - old_log_probs).exp()
policy_loss = -(policy_ratio * advantages).mean()
# 价值函数损失
value_loss = F.mse_loss(returns, state_values)
# 熵正则项(鼓励探索)entropy = -(new_probs * torch.log(new_probs)).sum(dim=1).mean()
return policy_loss + 0.5 * value_loss - entropy_coef * entropy
熵正则化系数需要小心调整,太小不起作用,太大会干扰主目标。建议从 0.01 开始尝试。
五大避坑经验
- 折扣因子 γ 陷阱 :
- 现象:设置 >0.99 时早期训练剧烈震荡
-
解决:CartPole 这类简单任务先用 0.95-0.98
-
学习率爆炸 :
- 现象:策略网络梯度突然暴涨
-
解决:添加梯度裁剪(
torch.nn.utils.clip_grad_norm_(model.parameters(), 0.5)) -
优势值尺度失控 :
- 现象:不同 episode 间 advantage 量级差异大
- 解决:对每个 batch 的优势值做标准化(减去均值除以标准差)
CartPole 实验对比
通过对比实验可以清晰看到:
- 无熵正则化:约 150episode 后 reward 卡在 150 左右
- 带熵正则化(系数 0.01):稳定增长到 500 满分的速度加快 40%
延伸思考方向
-
如何设计动态调整的熵系数?可以考虑随着训练进度线性衰减,或根据策略的确定性程度自适应调整
-
多步 TD 与 GAE 如何结合能进一步提升样本效率?尝试在 GAE 计算中混合 n 步 TD 回报
实现完整 A2C 就像搭积木,每个模块都需要精心调试。建议先用 CartPole 这类简单环境验证基础实现,再逐步挑战更复杂环境。代码中所有关键点我都添加了详细注释,完整实现已放在 GitHub(伪链接:github.com/xxx/a2c-tutorial)
