1. 项目概述
今天想和大家分享我在学习Advantage Actor-Critic(A2C)算法过程中的一些实战笔记。作为强化学习领域的一个重要算法,A2C结合了策略梯度(Actor)和价值函数(Critic)的优点,在很多连续控制任务中都表现优异。不同于传统的Actor-Critic算法,A2C引入了优势函数(Advantage Function)来减少方差,使得训练更加稳定。
我在实现过程中发现,很多教程都只停留在理论层面,对于实际调参和实现细节往往一笔带过。这篇笔记会重点记录我在PyTorch框架下实现A2C时遇到的各种坑和解决方案,包括:
- Actor和Critic网络的设计技巧
- 优势函数的计算方式选择
- 训练过程中的关键超参数设置
- 常见收敛问题的排查方法
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心算法解析
2.1 Actor-Critic框架基础
Actor-Critic框架本质上是一个混合架构:
- Actor(策略网络):负责生成动作
- Critic(价值网络):评估状态/动作的价值
传统实现中,Critic通常使用状态价值函数V(s),而A2C的关键改进在于使用优势函数A(s,a)=Q(s,a)-V(s)来指导策略更新。这种设计带来了几个好处:
- 减少了方差,使训练更稳定
- 可以更准确地评估动作的好坏
- 避免了单纯依赖TD误差带来的偏差
2.2 网络架构设计
在实践中,我发现共享底层特征提取层可以显著提升训练效率。典型的网络结构如下:
python复制class SharedBackbone(nn.Module):
def __init__(self, obs_dim):
super().__init__()
self.fc1 = nn.Linear(obs_dim, 64)
self.fc2 = nn.Linear(64, 64)
def forward(self, x):
x = F.relu(self.fc1(x))
return F.relu(self.fc2(x))
class Actor(nn.Module):
def __init__(self, backbone, action_dim):
super().__init__()
self.backbone = backbone
self.fc = nn.Linear(64, action_dim)
def forward(self, x):
x = self.backbone(x)
return torch.tanh(self.fc(x)) # 假设动作空间在[-1,1]
class Critic(nn.Module):
def __init__(self, backbone):
super().__init__()
self.backbone = backbone
self.fc = nn.Linear(64, 1)
def forward(self, x):
x = self.backbone(x)
return self.fc(x)
注意:对于连续动作空间,Actor输出通常使用tanh激活;对于离散动作空间则使用softmax
3. 关键实现细节
3.1 优势函数计算
优势函数的计算有几种常见方法:
- n-step return: A = ∑γ^i r_{t+i} + γ^n V(s_{t+n}) - V(s_t)
- GAE (Generalized Advantage Estimation): 加权混合不同n的估计
我最终选择了GAE实现,因为它在实践中表现最稳定:
python复制def compute_gae(next_value, rewards, masks, values, gamma=0.99, tau=0.95):
values = values + [next_value]
gae = 0
returns = []
for step in reversed(range(len(rewards))):
delta = rewards[step] + gamma * values[step+1] * masks[step] - values[step]
gae = delta + gamma * tau * masks[step] * gae
returns.insert(0, gae + values[step])
return returns
3.2 损失函数设计
Actor的损失使用策略梯度:
python复制policy_loss = -(log_probs * advantages.detach()).mean()
Critic的损失使用MSE:
python复制value_loss = F.mse_loss(returns, values)
重要技巧:记得对advantages进行标准化 (advantages - mean)/std,可以显著提升训练稳定性
4. 训练技巧与调参经验
4.1 超参数设置
经过多次实验,我发现以下参数组合在连续控制任务中效果较好:
- 学习率:3e-4(使用Adam优化器)
- GAE参数τ:0.95
- 折扣因子γ:0.99
- 批量大小:64-256
- 策略更新次数:每个batch更新4-10次
4.2 常见问题排查
-
回报不增长:
- 检查优势函数计算是否正确
- 尝试减小学习率
- 检查是否需要对观察值做归一化
-
策略过早收敛到局部最优:
- 增加熵正则项系数(通常0.01-0.1)
- 尝试不同的网络初始化方式
-
Critic损失震荡:
- 降低Critic的学习率(可以设为Actor的1/2)
- 使用梯度裁剪(norm=0.5)
5. 实战案例:Pendulum-v1
以Gym的Pendulum-v1环境为例,完整训练流程如下:
- 初始化环境和网络
- 收集轨迹数据(建议8-16个并行环境)
- 计算GAE和returns
- 更新网络参数(建议4-10次迭代)
- 重复2-4直到收敛
典型训练曲线:
- 前1000步:回报在-1000到-500之间波动
- 5000步左右:开始出现-200左右的episode
- 10000步后:稳定在-150以内
个人心得:在Pendulum环境中,我发现将动作空间的tanh输出乘以2(适应环境的最大扭矩)可以加速初期学习
6. 进阶优化方向
对于想要进一步提升性能的同学,可以考虑:
- 使用PPO的clip机制替代简单策略梯度
- 添加LSTM层处理部分可观测问题
- 实现分布式A2C(A3C)
- 结合好奇心驱动探索
我在机械臂控制项目中尝试了第3种方案,使用8个worker并行训练,收敛速度提升了约3倍。关键是要处理好各个worker之间的参数同步频率,太频繁会导致计算资源浪费,太稀疏会影响收敛。
