共计 2392 个字符,预计需要花费 6 分钟才能阅读完成。
为什么需要 Actor-Critic?
强化学习新手常会遇到两个经典算法的局限:

- Policy Gradient:直接优化策略函数,但依赖蒙特卡洛采样导致梯度估计方差大,训练过程像坐过山车一样不稳定。
- Q-Learning:通过价值函数间接学习策略,但处理连续动作空间时(比如方向盘转角),需要遍历所有动作计算 Q 值,计算量爆炸。
Actor-Critic 就像把两个算法拼成瑞士军刀:用 Actor 网络解决动作选择问题(Policy Gradient 的强项),用 Critic 网络评估状态价值降低方差(Q-Learning 的优势)。
算法横向对比
| 方法 | 收敛性 | 适用场景 | 缺点 |
|---|---|---|---|
| Monte Carlo | 高方差慢收敛 | 完整回合的离散任务 | 必须等回合结束才能更新 |
| TD Learning | 中等方差 | 连续决策场景 | 价值估计可能偏颇 |
| Actor-Critic | 低方差快收敛 | 连续 / 离散动作空间 | 需要调两个网络参数 |
PyTorch 实现核心代码
1. 网络结构定义
import torch
import torch.nn as nn
class Actor(nn.Module):
"""策略网络,输出动作概率分布"""
def __init__(self, state_dim: int, action_dim: int):
super().__init__()
self.fc = nn.Sequential(nn.Linear(state_dim, 64),
nn.LayerNorm(64), # 缓解梯度消失
nn.ReLU(),
nn.Linear(64, action_dim),
nn.Softmax(dim=-1) # 动作概率归一化
)
def forward(self, state: torch.Tensor) -> torch.Tensor:
# state 形状: [batch_size, state_dim]
return self.fc(state)
class Critic(nn.Module):
"""价值网络,评估状态价值"""
def __init__(self, state_dim: int):
super().__init__()
self.fc = nn.Sequential(nn.Linear(state_dim, 64),
nn.LayerNorm(64),
nn.ReLU(),
nn.Linear(64, 1) # 输出标量价值
)
def forward(self, state: torch.Tensor) -> torch.Tensor:
# 输出形状: [batch_size, 1]
return self.fc(state)
2. 关键训练逻辑
def compute_advantages(rewards: List[float],
values: torch.Tensor,
gamma: float = 0.99
) -> torch.Tensor:
"""计算优势函数:实际回报 - 预估价值"""
returns = []
R = 0
# 反向计算折扣回报
for r in reversed(rewards):
R = r + gamma * R
returns.insert(0, R)
returns = torch.tensor(returns)
# 形状对齐并归一化
advantages = returns - values.squeeze()
return (advantages - advantages.mean()) / (advantages.std() + 1e-8)
# 在训练循环中
for _ in range(epochs):
# 采样轨迹数据
states, actions, rewards = env.sample(batch_size)
# Critic 评估状态价值
values = critic(states)
# 计算策略梯度(含熵正则项)probs = actor(states)
dist = torch.distributions.Categorical(probs)
entropy = dist.entropy().mean() # 鼓励探索
advantages = compute_advantages(rewards, values)
policy_loss = -(dist.log_prob(actions) * advantages).mean() - 0.01 * entropy
# 价值函数损失(MSE)value_loss = F.mse_loss(values.squeeze(), torch.tensor(rewards))
六大避坑经验
- 学习率设置:
- Actor 网络通常需要比 Critic 更小的学习率(如 1e-4 vs 3e-4)
-
可以先用 Adam 优化器自动适应,后期转 SGD 精调
-
梯度消失对策:
- 在 MLP 层后添加 LayerNorm(如代码示例)
-
慎用 ReLU,可尝试 Swish 激活函数
-
折扣因子 γ :
- CartPole 这类短期任务用 0.99
-
长期规划任务可降到 0.9~0.95
-
批次大小:
- 简单任务 batch_size=32 足够
-
复杂环境需要增大到 256 以上
-
探索策略:
- 初期用 epsilon-greedy(如从 1.0 衰减到 0.1)
-
后期依赖策略网络自身的概率分布
-
监控指标:
- 每轮记录 episode_reward 和 entropy
- 如果 entropy 趋近 0,说明探索不足
在 CartPole 中的表现
不同 batch_size 下的训练曲线对比:
– batch_size=32:100 轮后收敛,但后期波动大
– batch_size=128:150 轮稳定收敛
– 加入熵正则项后:探索更充分,最高分可达 500
扩展连续动作空间
若要处理像机器人控制这类连续动作空间,需要:
1. 修改 Actor 网络输出高斯分布的均值和方差
2. 使用 PPO 算法限制策略更新幅度
3. 在计算 log_prob 时考虑概率密度函数
完整代码已开源在 GitHub(伪代码示例需替换为真实链接),包含更多工程实践细节如:
– 经验回放 (Replay Buffer) 实现
– 多环境并行采样
– 自动学习率调整
希望这篇笔记能帮你少走弯路,如果有问题欢迎在评论区交流!
正文完
发表至: 人工智能
近一天内
