Actor-Critic深度强化学习训练框架:从理论到PyTorch实战指南

1次阅读
没有评论

共计 3508 个字符,预计需要花费 9 分钟才能阅读完成。

image.webp

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

Actor-Critic 深度强化学习训练框架:从理论到 PyTorch 实战指南

更关键的是,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)

协同训练机制

  1. 采样阶段:Actor 与环境交互生成轨迹(s,a,r,s’),Critic 评估每个状态的 V(s)
  2. 优势计算 :使用 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}$$
  3. 策略更新:沿着优势函数方向提升高回报动作的概率:
    $$\nabla J(\theta) \approx \mathbb{E}\left[\nabla_\theta \log \pi_\theta(a|s) A_t^{GAE}\right]$$
  4. 价值更新:最小化 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)

常见陷阱与解决方案

  1. 策略过早收敛:监控策略熵 $H(\pi) = -\sum \pi(a|s)\log \pi(a|s)$,如果低于阈值,在损失函数中添加熵正则项:
    $$L_{actor} = -\mathbb{E}[\log \pi(a|s)A_t] – \beta H(\pi)$$
  2. 价值过估计:使用 Clipped Value 更新:
    $$L_{critic} = \mathbb{E}[\max((V – R)^2, (\text{clip}(V, V_{old}-\epsilon, V_{old}+\epsilon) – R)^2)]$$
  3. 参数同步问题 :在分布式训练中,使用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 如何像舞蹈搭档般相互配合,共同逼近最优策略。

正文完
 0
评论(没有评论)