共计 3398 个字符,预计需要花费 9 分钟才能阅读完成。
背景与痛点
强化学习中的策略梯度(Policy Gradient)方法直接优化策略,能够处理连续动作空间,但存在高方差问题,导致训练不稳定。而价值函数方法(如 Q -Learning)通过估计状态或状态 - 动作的价值函数来间接优化策略,方差较低但容易产生估计偏差。这两种方法各自存在明显的局限性,特别是在复杂环境中表现不佳。

Actor-Critic 框架结合了两种方法的优点:Actor 负责策略优化,Critic 负责价值估计,两者相互促进,显著提升了训练稳定性和效率。
框架结构解析
Actor-Critic 框架的核心在于两个组件的协同工作:
- Actor(策略网络):输入当前状态,输出动作的概率分布。目标是最大化预期回报,通过策略梯度进行更新。
- Critic(价值网络):输入当前状态(或状态 - 动作对),输出价值估计。目标是减少价值估计的误差,通常通过时序差分(TD)误差更新。
两者的交互流程如下:
- Actor 根据当前策略选择动作。
- 环境执行动作并返回新状态和奖励。
- Critic 评估当前状态的价值,并计算 TD 误差。
- TD 误差用于调整 Actor 的策略梯度更新幅度(作为基线减少方差)。
- Critic 根据 TD 误差更新自身的价值估计。
这种交互使得 Actor 能够更稳定地优化策略,而 Critic 的价值估计则逐渐趋于准确。
PyTorch 实现
以下是基于 PyTorch 的 Actor-Critic 实现代码:
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
# 定义 Actor 策略网络
class Actor(nn.Module):
def __init__(self, state_dim, action_dim, hidden_dim=128):
super(Actor, self).__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, hidden_dim)
self.fc3 = nn.Linear(hidden_dim, action_dim)
def forward(self, state):
x = F.relu(self.fc1(state))
x = F.relu(self.fc2(x))
action_probs = F.softmax(self.fc3(x), dim=-1)
return action_probs
# 定义 Critic 价值网络
class Critic(nn.Module):
def __init__(self, state_dim, hidden_dim=128):
super(Critic, self).__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, hidden_dim)
self.fc3 = nn.Linear(hidden_dim, 1)
def forward(self, state):
x = F.relu(self.fc1(state))
x = F.relu(self.fc2(x))
value = self.fc3(x)
return value
# 经验回放缓冲区
class ReplayBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = []
def push(self, state, action, reward, next_state, done):
if len(self.buffer) >= self.capacity:
self.buffer.pop(0)
self.buffer.append((state, action, reward, next_state, done))
def sample(self, batch_size):
return random.sample(self.buffer, batch_size)
# 训练过程示例
def train(env, actor, critic, buffer, batch_size=64, gamma=0.99):
# 从缓冲区采样
transitions = buffer.sample(batch_size)
states, actions, rewards, next_states, dones = zip(*transitions)
# 转换为张量
states = torch.FloatTensor(states)
actions = torch.LongTensor(actions)
rewards = torch.FloatTensor(rewards)
next_states = torch.FloatTensor(next_states)
dones = torch.FloatTensor(dones)
# Critic 更新
current_values = critic(states)
next_values = critic(next_states)
target_values = rewards + gamma * next_values * (1 - dones)
critic_loss = F.mse_loss(current_values, target_values.detach())
critic_optimizer.zero_grad()
critic_loss.backward()
critic_optimizer.step()
# Actor 更新
action_probs = actor(states)
selected_action_probs = action_probs.gather(1, actions.unsqueeze(1))
advantages = target_values - current_values.detach()
actor_loss = -torch.log(selected_action_probs) * advantages
actor_loss = actor_loss.mean()
actor_optimizer.zero_grad()
actor_loss.backward()
actor_optimizer.step()
调优技巧
- 学习率设置 :Actor 和 Critic 通常需要不同的学习率,Critic 的学习率可以略高于 Actor(如 0.001 vs 0.0001),以确保价值估计更快收敛。
- 折扣因子选择 :对于回合制任务,gamma 可以设置较高(0.99);对于连续任务,可能需要更低的值(0.9-0.95)。
- 探索策略 :初始阶段可以加入较高的随机探索(如 ε -greedy),随着训练逐渐降低。
- 批量大小 :较大的批量(128-256)有助于稳定训练,但会增加内存需求。
- 网络结构 :Critic 网络可以比 Actor 稍深一些,以更好地拟合价值函数。
避坑指南
- 梯度消失 :使用 ReLU 激活函数时,注意初始化权重,避免过小的梯度。可以尝试 LeakyReLU 或 ELU。
- 探索不足 :定期检查策略的熵,如果熵过低,说明探索不足,可以增加熵正则化项。
- 价值估计偏差 :使用多步 TD 或 Retrace 方法可以减少 Critic 的估计偏差。
- 训练不稳定 :定期保存模型检查点,使用梯度裁剪(clip_grad_norm_)防止梯度爆炸。
- 过拟合 :在 Critic 网络中加入 dropout 或 L2 正则化。
性能考量
- 计算资源 :对于图像输入的环境,可以考虑使用 CNN 作为特征提取器,这会显著增加 GPU 需求。
- 训练效率 :使用并行环境(如 VectorEnv)可以大幅提升样本收集效率。
- 内存管理 :经验回放缓冲区大小需要根据可用内存调整,通常 10000-100000 个样本为宜。
- 分布式训练 :对于大规模任务,可以考虑 A3C 或 IMPALA 等分布式框架。
思考题
- 如何修改当前框架使其支持连续动作空间?
- 在多智能体环境中,Actor-Critic 框架需要做哪些调整?
- 当 Critic 的价值估计持续偏高或偏低时,可能是什么原因导致的?如何解决?
- 如何设计一个自适应的探索策略,使其能够根据训练进度自动调整探索率?
希望这篇文章能帮助你理解 Actor-Critic 框架的核心思想并实现自己的强化学习模型。在实际应用中,可能需要根据具体问题调整网络结构和超参数,但理解基本原理是成功的关键。
正文完
发表至: 人工智能
近一天内
