1. 策略梯度方法的核心思想
在强化学习领域,策略梯度方法代表了一种直接优化策略函数的范式。与传统的值函数方法不同,策略梯度方法通过参数化策略本身,直接对策略进行优化。这种方法特别适合处理连续动作空间和需要随机策略的场景。
1.1 从值函数到策略函数的转变
传统值函数方法(如Q-learning)通过迭代更新值函数来间接优化策略。这种方法存在几个固有局限:
- 离散动作空间的限制:当动作空间连续或维度很高时,寻找使Q值最大的动作变得计算不可行
- 确定性策略的局限:某些环境需要随机策略才能达到最优(如剪刀石头布游戏)
- 收敛性问题:函数近似下的值函数方法可能不收敛
策略梯度方法通过直接参数化策略πθ(a|s)来解决这些问题。策略通常表示为状态s和参数θ的函数,输出动作a的概率分布。这种参数化方式带来了几个显著优势:
- 自然处理连续动作空间
- 可以学习随机策略
- 通常有更好的收敛性质
- 实现端到端的策略优化
1.2 策略参数化的常见形式
根据动作空间的特性,我们可以采用不同的策略参数化方式:
离散动作空间的Softmax策略:
πθ(a|s) = exp(φ(s)^Tθ_a) / ∑a' exp(φ(s)^Tθ_a')
其中φ(s)是状态特征,θ_a是动作a的参数向量
连续动作空间的高斯策略:
πθ(a|s) = N(μθ(s), Σθ(s))
其中均值μθ(s)和协方差Σθ(s)都是参数化函数
在实际应用中,这些策略通常由神经网络实现。例如,对于连续控制任务,可以使用两层MLP输出动作分布的均值和方差:
python复制class GaussianPolicy(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.fc1 = nn.Linear(state_dim, 64)
self.fc2 = nn.Linear(64, 64)
self.mean = nn.Linear(64, action_dim)
self.log_std = nn.Parameter(torch.zeros(action_dim))
def forward(self, state):
x = F.relu(self.fc1(state))
x = F.relu(self.fc2(x))
mean = torch.tanh(self.mean(x)) # 限制在[-1,1]范围内
std = torch.exp(self.log_std)
return torch.distributions.Normal(mean, std)
2. 策略梯度定理的推导
策略梯度定理是策略梯度方法的理论基础,它建立了策略性能指标与策略参数梯度之间的明确关系。
2.1 目标函数的定义
我们首先定义策略性能的度量指标。对于带有折扣的无限时域问题:
J(θ) = E_{τ∼πθ}[∑_{t=0}^∞ γ^t r_t]
其中τ表示轨迹(s0,a0,r0,s1,a1,...),γ∈(0,1)是折扣因子。
这个目标函数可以等价地表示为:
J(θ) = E_{s∼dπθ, a∼πθ}[Qπθ(s,a)]
其中dπθ是折扣状态访问分布:
dπθ(s) = ∑_{t=0}^∞ γ^t P(s_t=s|πθ)
2.2 策略梯度定理的表述
策略梯度定理指出:
∇θJ(θ) = E_{s∼dπθ, a∼πθ}[∇θlogπθ(a|s) Qπθ(s,a)]
这个结果非常优雅,它将策略性能的梯度表示为策略对数梯度与动作值函数的乘积的期望。注意这个期望是在策略πθ下的状态-动作分布上取的。
2.3 定理的详细证明
让我们一步步推导这个重要结果。
步骤1:展开目标函数的梯度
J(θ) = ∫ πθ(τ) R(τ) dτ
其中πθ(τ) = p(s0) ∏{t=0}^∞ πθ(a_t|s_t) p(s|s_t,a_t)
R(τ) = ∑_{t=0}^∞ γ^t r_t
因此:
∇θJ(θ) = ∫ ∇θπθ(τ) R(τ) dτ
步骤2:应用对数导数技巧
∇θπθ(τ) = πθ(τ) ∇θlogπθ(τ)
而:
logπθ(τ) = logp(s0) + ∑{t=0}^∞ [logπθ(a_t|s_t) + logp(s|s_t,a_t)]
因此:
∇θlogπθ(τ) = ∑_{t=0}^∞ ∇θlogπθ(a_t|s_t)
因为只有策略项依赖于θ。
步骤3:组合结果
∇θJ(θ) = ∫ πθ(τ) [∑{t=0}^∞ ∇θlogπθ(a_t|s_t)] R(τ) dτ
= E[∑_{t=0}^∞ ∇θlogπθ(a_t|s_t) R(τ)]
步骤4:回报分解
注意到R(τ)可以分解为:
R(τ) = ∑{t'=0}^∞ γ^{t'} r = ∑{t'=t}^∞ γ^{t'-t} r (对于任意t)
因此可以重写为:
∇θJ(θ) = E_{τ∼πθ}[∑{t=0}^∞ ∇θlogπθ(a_t|s_t) (∑^∞ γ^{t'-t} r_{t'})]
= E_{τ∼πθ}[∑_{t=0}^∞ ∇θlogπθ(a_t|s_t) Qπθ(s_t,a_t)]
最后一步是因为:
Qπθ(s_t,a_t) = E[∑{t'=t}^∞ γ^{t'-t} r | s_t,a_t]
2.4 策略梯度的直观理解
策略梯度定理有一个非常直观的解释:它通过增加导致高回报动作的概率,同时减少导致低回报动作的概率来改进策略。具体来说:
- ∇θlogπθ(a|s) 给出了参数θ变化时,在状态s选择动作a的概率变化方向
- Qπθ(s,a) 作为权重,决定了这个变化应该被加强还是减弱
- 期望操作确保我们在策略访问的所有状态-动作对上进行平均
这种"加权概率调整"的机制使得策略能够逐渐向更高回报的方向进化。
3. 基线技术与方差缩减
虽然策略梯度定理给出了无偏的梯度估计,但直接使用它会导致高方差问题。基线技术是解决这一问题的关键方法。
3.1 基线的基本概念
基线b(s)是一个只依赖于状态(不依赖于动作)的函数。我们可以将策略梯度修改为:
∇θJ(θ) = E[∇θlogπθ(a|s) (Qπθ(s,a) - b(s))]
关键性质:引入基线不影响梯度的无偏性,因为:
E[∇θlogπθ(a|s) b(s)] = b(s) E[∇θlogπθ(a|s)] = b(s) ∫ πθ(a|s) ∇θlogπθ(a|s) da
= b(s) ∫ ∇θπθ(a|s) da = b(s) ∇θ ∫ πθ(a|s) da = b(s) ∇θ1 = 0
3.2 最优基线的选择
理论上,可以推导出使梯度估计方差最小的最优基线。对于每个状态s,最优基线为:
b*(s) = E[ (∇θlogπθ(a|s))^2 Qπθ(s,a) ] / E[ (∇θlogπθ(a|s))^2 ]
在实际中,我们通常使用状态值函数Vπθ(s)作为基线,因为:
- 它接近最优基线
- 它自然地反映了状态的"基准"价值
- 它与优势函数Aπθ(s,a) = Qπθ(s,a) - Vπθ(s)的概念一致
3.3 优势函数的估计
使用优势函数Aπθ(s,a) = Qπθ(s,a) - Vπθ(s)的策略梯度为:
∇θJ(θ) = E[∇θlogπθ(a|s) Aπθ(s,a)]
在实际算法中,我们需要估计优势函数。常见的方法包括:
-
蒙特卡洛估计:
Â_t = ∑_{k=t}^∞ γ^{k-t} r_k - V(s_t) -
TD残差估计:
δ_t = r_t + γV(s_{t+1}) - V(s_t)
Â_t = δ_t -
GAE(广义优势估计):
Â_t^{GAE(λ)} = ∑{l=0}^∞ (γλ)^l δ
其中λ∈[0,1]控制偏差-方差的权衡。λ=1对应蒙特卡洛估计,λ=0对应TD(0)估计。
4. REINFORCE算法实现
REINFORCE是最基础的策略梯度算法,直接使用蒙特卡洛回报作为Q估计。
4.1 算法描述
REINFORCE算法的核心步骤如下:
- 使用当前策略πθ采样完整轨迹τ=(s0,a0,r0,...,sT)
- 计算每个时间步t的回报G_t = ∑_{k=t}^T γ^{k-t} r_k
- 估计策略梯度:ĝ = ∑_{t=0}^T ∇θlogπθ(a_t|s_t) G_t
- 更新策略参数:θ ← θ + α ĝ
4.2 代码实现细节
以下是PyTorch实现的REINFORCE算法关键部分:
python复制class REINFORCE:
def __init__(self, state_dim, action_dim, gamma=0.99, lr=3e-4):
self.policy = PolicyNetwork(state_dim, action_dim)
self.optimizer = optim.Adam(self.policy.parameters(), lr=lr)
self.gamma = gamma
def update(self, states, actions, rewards):
# 计算折扣回报
returns = []
G = 0
for r in reversed(rewards):
G = r + self.gamma * G
returns.insert(0, G)
returns = torch.tensor(returns)
returns = (returns - returns.mean()) / (returns.std() + 1e-8) # 标准化
# 计算策略梯度
policy_loss = []
for log_prob, G in zip(self.log_probs, returns):
policy_loss.append(-log_prob * G)
self.optimizer.zero_grad()
policy_loss = torch.stack(policy_loss).sum()
policy_loss.backward()
self.optimizer.step()
4.3 方差缩减技巧
在REINFORCE实现中,我们采用了几个重要的方差缩减技术:
- 回报标准化:将每个episode的回报减去均值并除以标准差,保持梯度更新的稳定性
- 基线使用:虽然标准REINFORCE不使用基线,但实践中可以添加状态值函数作为基线
- 批量更新:收集多个episode的数据后进行批量更新,而不是单episode更新
重要提示:REINFORCE虽然概念简单,但由于使用完整轨迹的蒙特卡洛估计,方差通常很高。在实际应用中,建议使用带基线的版本或转向Actor-Critic方法。
5. Actor-Critic架构
Actor-Critic方法结合了策略梯度和值函数近似的优点,是当前最主流的策略优化框架。
5.1 算法原理
Actor-Critic框架包含两个核心组件:
- Actor:策略函数πθ(a|s),负责选择动作
- Critic:值函数Vw(s),评估状态或状态-动作对的价值
它们协同工作:
- Critic评估当前策略的表现
- Actor根据Critic的反馈调整策略
策略梯度使用Critic提供的优势估计:
∇θJ(θ) ≈ E[∇θlogπθ(a|s) Â(s,a)]
其中Â(s,a)是Critic提供的优势估计。
5.2 优势Actor-Critic (A2C)
A2C是同步版本的Actor-Critic算法,其更新步骤如下:
- 使用当前策略采样若干步(如5步)的数据
- 计算每个步骤的TD误差:δ_t = r_t + γV(s_{t+1}) - V(s_t)
- 用δ_t作为优势估计更新Actor:
∇θJ(θ) ≈ ∇θlogπθ(a_t|s_t) δ_t - 最小化Critic的均方误差:
L(w) = (r_t + γVw(s_{t+1}) - Vw(s_t))^2
5.3 代码实现
以下是A2C的核心实现:
python复制class A2C:
def __init__(self, state_dim, action_dim, gamma=0.99, lr=3e-4):
self.actor = PolicyNetwork(state_dim, action_dim)
self.critic = ValueNetwork(state_dim)
self.optimizer = optim.Adam(list(self.actor.parameters()) +
list(self.critic.parameters()), lr=lr)
self.gamma = gamma
def update(self, states, actions, rewards, next_states, dones):
# 计算TD目标和TD误差
values = self.critic(states)
next_values = self.critic(next_states)
targets = rewards + self.gamma * next_values * (1 - dones)
td_errors = targets - values
# Actor损失(策略梯度)
log_probs = self.actor.get_log_prob(states, actions)
actor_loss = -(log_probs * td_errors.detach()).mean()
# Critic损失(值函数拟合)
critic_loss = td_errors.pow(2).mean()
# 总损失
loss = actor_loss + 0.5 * critic_loss
# 更新参数
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
5.4 实现技巧
在实际实现Actor-Critic时,有几个关键技巧:
- 共享网络架构:通常让Actor和Critic共享前几层的特征提取层,提高样本效率
- 熵正则化:在策略损失中添加熵项,鼓励探索:
L_entropy = β ∑ πθ(a|s) logπθ(a|s) - 梯度裁剪:对Critic的梯度进行裁剪,防止训练不稳定
- 多步TD:使用n步TD回报而不是单步TD,平衡偏差和方差
6. 策略梯度的进阶主题
6.1 自然策略梯度
自然策略梯度考虑了策略空间的几何结构,使用Fisher信息矩阵F(θ)作为度量:
F(θ) = E[∇θlogπθ(a|s) ∇θlogπθ(a|s)^T]
自然梯度方向为:
∇̃θJ(θ) = F(θ)^{-1} ∇θJ(θ)
这相当于在策略空间的黎曼流形上进行最速上升,而不是在原始参数空间。
6.2 信赖域方法(TRPO)
TRPO通过限制策略更新的幅度来保证单调改进:
maxθ E[πθ(a|s)/πθ_old(a|s) A(s,a)]
s.t. E[KL(πθ_old(·|s) || πθ(·|s))] ≤ δ
这通过二阶近似和共轭梯度法高效实现。
6.3 近端策略优化(PPO)
PPO是TRPO的简化版本,通过裁剪概率比来近似实现信赖域约束:
L^{CLIP}(θ) = E[min(r_t(θ)Â_t, clip(r_t(θ),1-ε,1+ε)Â_t)]
其中r_t(θ) = πθ(a_t|s_t)/πθ_old(a_t|s_t)。
PPO实现简单且效果优秀,已成为当前最流行的策略梯度算法。
7. 实践建议与常见问题
7.1 超参数调优
策略梯度方法对超参数敏感,以下是一些指导原则:
- 学习率:通常设置在1e-4到1e-3之间,Actor和Critic可以使用不同学习率
- 折扣因子γ:对于episodic任务接近1(如0.99),对于连续任务可以小些(如0.95)
- GAE参数λ:通常0.9到0.97之间,平衡偏差和方差
- 熵系数:开始时可以设为0.01,随着训练逐渐减小
7.2 常见问题排查
-
策略不更新:
- 检查梯度是否流动(特别是自定义策略网络时)
- 确认优势估计没有错误标准化
- 尝试增大学习率或熵系数
-
训练不稳定:
- 添加梯度裁剪(特别是Critic)
- 减小学习率
- 使用更保守的算法(如PPO而不是A2C)
-
策略探索不足:
- 增加熵系数
- 检查策略初始化是否合理
- 考虑添加噪声或使用随机性更强的策略类
7.3 性能优化技巧
- 并行采样:使用多个环境并行采样数据,大幅提高样本收集效率
- 经验回放:对于off-policy算法,使用经验回放缓冲池提高数据效率
- 状态归一化:对观察状态进行运行均值和方差归一化
- 框架选择:使用JAX或PyTorch等支持自动微分和GPU加速的框架
8. 应用案例:连续控制任务
让我们以MuJoCo的HalfCheetah环境为例,展示策略梯度方法的实际应用。
8.1 环境设置
HalfCheetah是一个典型的连续控制任务,具有:
- 状态空间:17维
- 动作空间:6维,每个维度在[-1,1]之间
- 奖励函数:前进速度减去控制成本
8.2 策略网络设计
对于连续动作空间,我们使用高斯策略:
python复制class GaussianPolicy(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.fc1 = nn.Linear(state_dim, 64)
self.fc2 = nn.Linear(64, 64)
self.mean = nn.Linear(64, action_dim)
self.log_std = nn.Parameter(torch.zeros(action_dim))
def forward(self, state):
x = torch.tanh(self.fc1(state))
x = torch.tanh(self.fc2(x))
mean = torch.tanh(self.mean(x))
std = torch.exp(self.log_std.clamp(-20, 2))
return torch.distributions.Normal(mean, std)
8.3 训练曲线分析
典型的训练过程会呈现以下特点:
- 初期:回报快速上升,策略学习基本运动技能
- 中期:进步放缓,策略优化运动效率
- 后期:趋于稳定,可能偶尔有突破性改进
实际技巧:在训练连续控制任务时,合理设置动作缩放和奖励缩放非常重要。通常需要将原始动作和奖励调整到[-1,1]范围附近。
9. 与其他方法的比较
9.1 与值函数方法的对比
| 特性 | 策略梯度 | 值函数方法 |
|---|---|---|
| 动作空间 | 适合连续/高维 | 适合离散/低维 |
| 策略类型 | 可表示随机策略 | 通常确定性策略 |
| 收敛性 | 通常更好 | 可能振荡 |
| 方差 | 较高(但可用基线降低) | 通常较低 |
9.2 与进化策略的对比
| 策略梯度 | 进化策略 |
|---|---|
| 使用梯度信息 | 仅使用函数评估 |
| 需要可微分策略 | 适用于不可微分策略 |
| 样本效率较高 | 通常需要更多样本 |
| 对噪声敏感 | 对噪声更鲁棒 |
10. 扩展阅读与资源
10.1 经典论文
- Williams (1992) - REINFORCE算法原始论文
- Sutton et al. (2000) - 策略梯度定理的严格表述
- Mnih et al. (2016) - A3C算法
- Schulman et al. (2015,2017) - TRPO和PPO算法
10.2 开源实现
- OpenAI Baselines
- Stable Baselines3
- Spinning Up (OpenAI)
- Tianshou (PyTorch库)
10.3 推荐实验
- 在CartPole上比较REINFORCE、带基线的REINFORCE和A2C
- 调整GAE的λ参数,观察对训练稳定性的影响
- 在MuJoCo环境中实现PPO并调参
- 尝试不同的策略参数化方式(如Beta分布代替高斯分布)
策略梯度方法为强化学习提供了强大而灵活的工具集。通过深入理解策略梯度定理及其实现细节,研究者可以针对不同任务设计合适的策略优化算法。当前最先进的算法如PPO、SAC等,都是在基本策略梯度框架上的扩展和创新。
