共计 3508 个字符,预计需要花费 9 分钟才能阅读完成。
在连续动作空间的控制任务中(比如机器人行走或机械臂抓取),传统的 DQN 方法会遇到明显瓶颈——它只能处理离散的动作选择。想象一下要让机械臂平滑移动到精确位置,如果简单粗暴地把每个关节角度离散成 10 档,动作空间会爆炸式增长(10^ 关节数),而 Actor-Critic 框架通过输出连续的动作分布完美解决了这个问题。

更关键的是,Actor-Critic 融合了策略梯度(直接优化策略)和值函数估计(评估状态价值)的双重优势。相比之下,纯策略梯度方法方差过高容易震荡,而纯值函数方法(如 DQN)在连续空间会遭遇最大化操作难以计算的困境。Actor 负责探索动作空间,Critic 则像一位实时评分员,指引策略向高回报方向更新,这种分工协作让训练既稳定又高效。
核心架构解剖
我们用一个控制类任务典型架构为例(假设是 OpenAI Gym 的 Pendulum-v1 环境):
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
class Actor(nn.Module):
"""输出连续动作的正态分布参数(均值 + 标准差)"""
def __init__(self, state_dim, action_dim, hidden_dim=256):
super().__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc_mu = nn.Linear(hidden_dim, action_dim) # 均值
self.fc_std = nn.Linear(hidden_dim, action_dim) # 对数标准差
def forward(self, x):
x = F.relu(self.fc1(x))
mu = torch.tanh(self.fc_mu(x)) * 2 # 假设动作范围[-2,2]
std = F.softplus(self.fc_std(x)) + 1e-5 # 保证正值
return torch.distributions.Normal(mu, std)
class Critic(nn.Module):
"""评估状态价值 V(s)"""
def __init__(self, state_dim, hidden_dim=256):
super().__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, 1)
def forward(self, x):
x = F.relu(self.fc1(x))
return self.fc2(x)
协同训练机制
- 采样阶段:Actor 与环境交互生成轨迹(s,a,r,s’),Critic 评估每个状态的 V(s)
- 优势计算 :使用 GAE(广义优势估计) 综合多步 TD 误差:
$$\delta_t = r_t + \gamma V(s_{t+1}) – V(s_t)$$
$$A_t^{GAE} = \sum_{l=0}^{\infty}(\gamma\lambda)^l \delta_{t+l}$$ - 策略更新:沿着优势函数方向提升高回报动作的概率:
$$\nabla J(\theta) \approx \mathbb{E}\left[\nabla_\theta \log \pi_\theta(a|s) A_t^{GAE}\right]$$ - 价值更新:最小化 Critic 的均方误差:
$$L(\phi) = \mathbb{E}\left[(V_\phi(s_t) – R_t)^2\right]$$
关键代码实现
def compute_gae(rewards, values, dones, gamma=0.99, lam=0.95):
"""计算广义优势估计"""
advantages = torch.zeros_like(rewards)
last_advantage = 0
for t in reversed(range(len(rewards))):
if dones[t]:
delta = rewards[t] - values[t]
last_advantage = delta
else:
delta = rewards[t] + gamma * values[t+1] - values[t]
last_advantage = delta + gamma * lam * last_advantage
advantages[t] = last_advantage
return advantages
# 策略梯度更新示例
actor_optimizer.zero_grad()
dist = actor(states)
log_probs = dist.log_prob(actions)
policy_loss = -(log_probs * advantages.detach()).mean() # 关键:阻止梯度流过 advantages
policy_loss.backward()
actor_optimizer.step()
性能优化实战
并行环境采样技巧
使用 Python 的 multiprocessing 模块创建多个环境实例:
from multiprocessing import Process, Queue
def worker(env_fn, queue):
env = env_fn()
while True:
state = env.reset()
queue.put(('reset', state))
# ... 交互逻辑省略
envs = [lambda: gym.make('Pendulum-v1') for _ in range(4)]
queues = [Queue() for _ in envs]
processes = [Process(target=worker, args=(env, queue))
for env, queue in zip(envs, queues)]
学习率自适应
对 Actor 和 Critic 使用不同的学习率通常更有效:
actor_optimizer = optim.Adam(actor.parameters(), lr=3e-4)
critic_optimizer = optim.Adam(critic.parameters(), lr=1e-3)
常见陷阱与解决方案
- 策略过早收敛:监控策略熵 $H(\pi) = -\sum \pi(a|s)\log \pi(a|s)$,如果低于阈值,在损失函数中添加熵正则项:
$$L_{actor} = -\mathbb{E}[\log \pi(a|s)A_t] – \beta H(\pi)$$ - 价值过估计:使用 Clipped Value 更新:
$$L_{critic} = \mathbb{E}[\max((V – R)^2, (\text{clip}(V, V_{old}-\epsilon, V_{old}+\epsilon) – R)^2)]$$ - 参数同步问题 :在分布式训练中,使用
torch.nn.utils.clip_grad_norm_限制梯度范数,避免各 worker 更新幅度差异过大
完整训练示例
以下是 Pendulum-v1 环境的训练循环骨架:
env = gym.make('Pendulum-v1')
for episode in range(1000):
state = env.reset()
episode_reward = 0
while True:
dist = actor(torch.FloatTensor(state))
action = dist.sample()
next_state, reward, done, _ = env.step(action.numpy())
# 存储 transition 到 buffer
buffer.append(state, action, reward, next_state, done)
if len(buffer) > batch_size:
# 从 buffer 采样并计算 GAE
states, actions, rewards, next_states, dones = buffer.sample()
values = critic(states)
advantages = compute_gae(rewards, values, dones)
# 更新网络
update_actor_critic(states, actions, advantages)
state = next_state
episode_reward += reward
if done:
break
试着修改奖励函数(比如给角度偏差更高的惩罚),观察智能体如何调整平衡策略。这个框架同样适用于更复杂的任务,只需调整网络结构和超参数——关键在于理解 Actor 和 Critic 如何像舞蹈搭档般相互配合,共同逼近最优策略。
正文完
发表至: 人工智能
近一天内
