Arun Nanda 2025-02-13
学习强化学习(RL)中的策略梯度定理,了解其数学推导过程,并使用基于策略梯度的算法在 Gymnasium 环境中解决一个简单的 RL 任务。
强化学习中的策略梯度(Policy Gradient)是一类算法,它们通过直接估计期望奖励关于策略参数的梯度来优化智能体的策略。
在本教程中,我们将详细解释策略梯度定理及其推导过程,并展示如何使用 PyTorch 实现策略梯度算法。
什么是策略梯度定理?
在强化学习中,智能体的策略指的是它根据对环境的观察来决定采取何种动作的算法。RL 问题的目标是最大化智能体与环境交互过程中所获得的奖励。能够带来最大奖励的策略即为最优策略。
用于最大化回报的算法大致可分为两类:基于策略的方法和基于价值的方法:
- 基于策略的方法(如策略梯度算法)通过在期望奖励上执行梯度上升,直接学习最优策略。它们不依赖于价值函数。策略通常以参数化形式表达。当策略由神经网络实现时,策略参数即指网络权重。网络通过在策略参数上执行梯度上升来学习最优策略。
- 基于价值的方法(如 Q-learning)通过估计状态或状态-动作对的价值来间接推导策略,即选择具有最高价值的动作。最终选择能导出最优价值函数的策略作为最优策略。贝尔曼方程描述了最优状态价值函数和状态-动作价值函数。
根据策略梯度定理,期望回报的导数等于回报与策略对数导数乘积的期望值(通常策略被表示为概率分布)。
策略通常被建模为一个参数化函数。当策略由神经网络建模时,策略参数即指网络权重。因此,计算期望回报(累积奖励)关于策略参数的梯度,即可更新策略以提升其性能。该梯度可用于迭代地沿增加期望回报的方向更新策略参数。训练应能收敛到最大化期望回报的最优策略。
在后续章节中,我们将详细解释该定理并展示其推导过程。
为什么使用策略梯度方法?
策略梯度方法的一个关键优势是其处理复杂动作空间的能力,在这类空间中传统的基于价值的方法往往难以应对。
处理高维动作空间
基于价值的方法(如 Q-learning)通过估计所有可能动作的价值函数来工作。当环境的动作空间是连续的或离散但规模很大时,这种方法变得非常困难。
策略梯度方法对策略进行参数化,并估计累积奖励关于策略参数的梯度。它们利用该梯度直接优化策略参数。因此,它们可以高效处理高维或连续动作空间。策略梯度也是基于人类反馈的强化学习(RLHF)方法的基础。
通过参数化策略并基于梯度调整其参数,策略梯度能高效处理连续和高维动作。这种直接方法带来了更好的泛化能力和更灵活的探索能力,使其非常适合机器人控制等复杂任务。
学习随机策略
给定一组观测:
- 确定性策略明确规定了智能体应采取的动作。
- 随机策略给出一组动作以及智能体选择每个动作的概率。
在遵循随机策略时,相同的观测在不同迭代中可能导致选择不同的动作。这促进了动作空间的探索,防止策略陷入局部最优。因此,在探索对发现最大回报路径至关重要的环境中,随机策略非常有用。
在基于策略的方法中,策略输出被转换为概率分布,每个可能的动作都被分配一个概率。智能体通过从该分布中采样来选择动作,从而实现随机策略。因此,策略梯度方法结合了探索与利用,在具有复杂奖励结构的环境中尤为有效。
策略梯度定理的推导
在深入推导之前,有必要先建立推导过程中使用的数学符号和关键概念。
数学符号与预备知识
如前文所述,策略梯度定理指出:期望回报的导数等于回报与策略对数导数乘积的期望值。
在推导策略梯度定理之前,我们先介绍以下符号:
- 表示随机变量 的概率期望。
- 策略在数学上表示为一个概率矩阵,给出在不同观测下选择不同动作的概率。策略通常被建模为参数化函数,参数记为 。
- 表示由参数 参数化的策略。在实践中,这些参数即为建模策略的神经网络的权重。
- 轨迹(trajectory) 指的是从随机初始状态开始,直到当前时间步或终止状态的一系列状态序列。
- 表示函数 关于参数 的梯度。
- 表示智能体遵循策略 所获得的期望回报。这也是梯度上升的目标函数。
- 环境在每个时间步根据智能体的动作给出奖励。回报(return)指从初始状态到当前时间步的累积奖励。
- 表示在轨迹 上生成的回报。
推导步骤
我们将从第一性原理出发,推导并证明策略梯度定理,从目标函数的展开开始,并使用对数导数技巧(log-derivative trick)。
目标函数(公式 1)
策略梯度方法中的目标函数是智能体遵循策略 所累积的回报 。该目标函数表示为:
在上述公式中:
- 左边(LHS)是遵循策略 所获得的期望回报。
- 右边(RHS)是在遵循策略 生成的轨迹 上对回报 的期望。
目标函数的微分(公式 2)
对上述等式两边关于 求导:
期望的梯度(公式 3)
右边的期望可表示为轨迹概率与回报乘积的积分:
梯度与积分可交换顺序,因此可表示为:
因此,公式 2 可重写为:
轨迹的概率(公式 4)
现在我们仔细分析 ,即在给定策略参数 (从而策略 )下智能体遵循轨迹 的概率。轨迹由一系列步骤组成,因此:
- 轨迹 的概率是所有单个步骤概率的乘积。
- 在时间步 ,智能体从状态 通过动作 转移到状态 。该事件发生的概率为:
- 策略在状态 下预测动作 的概率;
- 在状态 和动作 下转移到状态 的概率。
因此,从初始状态 开始,智能体遵循策略 生成轨迹 的概率为:
为简化表达,我们对两边取对数:
对数概率的导数(公式 5)
现在对上述对数概率关于 求导:
在右边:
- 第一项 与 无关,导数为 0。
- 求和项中的 也与 无关,其导数也为 0。
去除上述零项后,得到(公式 5):
回顾公式 2:
公式 5 计算了公式 2 右边第一部分的对数。我们需要将一个项的导数与其对数联系起来。我们使用链式法则和对数导数技巧来实现这一点。
对数导数技巧
我们先回顾微积分中的一个结果:
这有时被称为对数导数技巧。
链式法则
根据链式法则,若 是 的函数,而 本身是 的函数 ,则:
在此情况下,令 ,,则:
应用链式法则
我们从微积分中知道:
将此结果代入上述右侧(RHS)的第一个表达式中。
将 移到等式左侧,并使用梯度符号表示:
这里, 代表 。因此,上述等式等价于:
应用对数导数技巧
将上述结果代入公式 2 的右边:
即,
重新排列右边积分内的项:
推导最终结果
注意到上述表达式包含了期望的积分展开形式:
因此,上述右侧(RHS)可以表示为一个期望:
我们将对数概率的导数代入期望回报的表达式中:
在上述等式中,将公式 5 中的 的值代入,得到:
这就是根据策略梯度定理得出的回报函数梯度的表达式。
策略梯度的直观理解
策略梯度方法将策略输出转换为概率分布。智能体通过从该分布中采样来选择动作。策略梯度方法调整策略参数,从而在每次迭代中更新该概率分布。更新后的概率分布更倾向于选择能带来更高奖励的动作。
策略梯度算法计算期望回报关于策略参数的梯度。通过沿该梯度方向移动策略参数,智能体在训练过程中提高选择高回报动作的概率。
本质上,那些带来更好结果的动作在未来被选择的可能性会更高,从而逐步改进策略以最大化长期回报。
使用 Python 实现策略梯度
在讨论了策略梯度的基本原理后,我们展示如何使用 PyTorch 和 Gymnasium 实现它。
环境设置
首先,我们需要安装 gymnasium 及一些支持库,如 NumPy 和 PyTorch。
在服务器或本地机器上安装 gymnasium 及其依赖项,运行:
$ pip install gymnasium
在 Google Colab 或 DataLab 等 Notebook 中,使用:
!pip install gymnasium
在 Python 环境中导入这些包:
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torch.distributions as distributions
import numpy as np
import gymnasium as gym
编写一个简单的策略梯度智能体
使用 .make() 方法创建环境实例:
env = gym.make('CartPole-v1')
与其他机器学习方法一样,我们使用神经网络实现策略梯度智能体。
CartPole-v1 是一个简单环境,因此我们设计一个包含 64 个神经元的单隐藏层网络。输入层维度等于观测空间的维度,输出层维度等于环境动作空间的大小。因此,策略网络将观测状态映射到动作。给定一个观测作为输入,网络根据策略输出预测的动作。
以下代码实现了策略网络:
class PolicyNetwork(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim, dropout):
super().__init__()
self.layer1 = nn.Linear(input_dim, hidden_dim)
self.layer2 = nn.Linear(hidden_dim, output_dim)
self.dropout = nn.Dropout(dropout)
def forward(self, x):
x = self.layer1(x)
x = self.dropout(x)
x = F.relu(x)
x = self.layer2(x)
return x
训练智能体
环境在每个时间步根据智能体的状态和动作给出奖励。策略梯度方法对累积奖励(回报)执行梯度下降。目标是最大化总回报。
要计算一个 episode 的回报,你需要(带折扣因子)累积该 episode 中所有时间步的奖励。此外,对回报进行归一化有助于确保训练平稳稳定。以下代码展示了如何实现:
def calculate_stepwise_returns(rewards, discount_factor):
returns = []
R = 0
for r in reversed(rewards):
R = r + R * discount_factor
returns.insert(0, R)
returns = torch.tensor(returns)
normalized_returns = (returns - returns.mean()) / returns.std()
return normalized_returns
在每次前向传播迭代中,我们执行以下步骤:
- 使用当前策略通过
.step()函数运行智能体。策略预测每个时间步选择所选动作的概率。 - 根据智能体的动作从环境接收奖励。
- 累积逐步奖励和动作的对数概率,直到智能体达到终止状态。
以下代码实现了前向传播:
def forward_pass(env, policy, discount_factor):
log_prob_actions = []
rewards = []
done = False
episode_return = 0
policy.train()
observation, info = env.reset()
while not done:
observation = torch.FloatTensor(observation).unsqueeze(0)
action_pred = policy(observation)
action_prob = F.softmax(action_pred, dim=-1)
dist = distributions.Categorical(action_prob)
action = dist.sample()
log_prob_action = dist.log_prob(action)
observation, reward, terminated, truncated, info = env.step(action.item())
done = terminated or truncated
log_prob_actions.append(log_prob_action)
rewards.append(reward)
episode_return += reward
log_prob_actions = torch.cat(log_prob_actions)
stepwise_returns = calculate_stepwise_returns(rewards, discount_factor)
return episode_return, stepwise_returns, log_prob_actions
使用反向传播和梯度上升更新策略
在传统机器学习中:
- 损失(Loss)指预测输出与实际输出之间的差异。
- 我们使用梯度下降最小化损失。
在强化学习中:
- 损失是梯度下降(或上升)所应用量的代理。
- 我们使用梯度上升最大化回报(累积奖励)。
- 期望回报值被用作梯度下降的损失代理。期望回报值是以下两项的乘积:
- 每个步骤的预期回报;
- 每个步骤中选择采样动作的概率。
- 为了使用反向传播执行梯度上升,我们使用损失的负值。
以下代码计算损失:
def calculate_loss(stepwise_returns, log_prob_actions):
loss = -(stepwise_returns * log_prob_actions).sum()
return loss
与标准机器学习算法类似,要更新策略,你需要对损失函数执行反向传播。下面的 update_policy() 方法调用 calculate_loss() 方法,然后对该损失执行反向传播以更新策略参数(即策略网络的模型权重)。
def update_policy(stepwise_returns, log_prob_actions, optimizer):
stepwise_returns = stepwise_returns.detach()
loss = calculate_loss(stepwise_returns, log_prob_actions)
optimizer.zero_grad()
loss.backward()
optimizer.step()
return loss.item()
训练循环
我们使用前面定义的函数来训练策略。在开始训练之前,我们需要:
- 一个未训练的策略,初始化为
PolicyNetwork类的随机实例。 - 使用 Adam 算法的优化器。
- 折扣因子、学习率、dropout 率、奖励阈值和最大训练轮数等超参数。
我们迭代训练循环,直到平均回报超过奖励阈值。在每次迭代中,我们执行以下步骤:
- 对每个 episode,运行一次前向传播。收集动作的对数概率、逐步回报和该 episode 的总回报。将 episodic 回报累积到数组中。
- 使用对数概率和逐步回报计算损失。对该损失运行反向传播。使用优化器更新策略参数。
- 检查最近
N_TRIALS个 episode 的平均回报是否超过奖励阈值。
以下代码实现了这些步骤:
def main():
MAX_EPOCHS = 500
DISCOUNT_FACTOR = 0.99
N_TRIALS = 25
REWARD_THRESHOLD = 475
PRINT_INTERVAL = 10
INPUT_DIM = env.observation_space.shape[0]
HIDDEN_DIM = 128
OUTPUT_DIM = env.action_space.n
DROPOUT = 0.5
episode_returns = []
policy = PolicyNetwork(INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM, DROPOUT)
LEARNING_RATE = 0.01
optimizer = optim.Adam(policy.parameters(), lr=LEARNING_RATE)
for episode in range(1, MAX_EPOCHS+1):
episode_return, stepwise_returns, log_prob_actions = forward_pass(env, policy, DISCOUNT_FACTOR)
_ = update_policy(stepwise_returns, log_prob_actions, optimizer)
episode_returns.append(episode_return)
mean_episode_return = np.mean(episode_returns[-N_TRIALS:])
if episode % PRINT_INTERVAL == 0:
print(f'| Episode: {episode:3} | Mean Rewards: {mean_episode_return:5.1f} |')
if mean_episode_return >= REWARD_THRESHOLD:
print(f'Reached reward threshold in {episode} episodes')
break
通过调用 main() 函数运行训练程序:
main()
策略梯度方法的优势与挑战
策略梯度方法具有多项优势,例如:
处理连续动作空间
基于价值的方法(如 Q-learning)在连续动作空间中效率低下,因为它们需要在整个动作空间上估计价值。策略梯度方法可以直接使用期望回报的梯度优化策略。这种方法适用于连续动作分布,因此非常适合机器人控制等基于连续动作空间的任务。
随机策略
策略梯度方法可以学习随机策略——即给出选择每个可能动作的概率。这允许智能体尝试多种动作,降低陷入局部最优的风险。在智能体需要探索动作空间以找到最优策略的复杂环境中尤其有用。随机性有助于平衡探索(尝试新动作)和利用(选择已知最佳动作),这在具有不确定性或稀疏奖励的环境中至关重要。
直接策略优化
策略梯度直接优化策略,而不是使用价值函数。在连续或高维动作空间中,为每个动作近似价值可能计算成本高昂。因此,基于策略的方法在这些环境中表现良好。
尽管有诸多优势,策略梯度方法也存在一些固有挑战:
梯度估计的高方差
策略梯度方法通过从概率分布中采样动作来选择动作。实际上,它们通过采样轨迹来估计期望回报。由于采样过程本质上是随机的,后续迭代中估计的回报可能具有高方差。这会使智能体难以高效学习,因为策略更新可能在迭代之间大幅波动。
训练不稳定
策略梯度方法对超参数(如学习率)敏感。如果学习率过高,策略参数的更新可能过大,导致训练错过最优参数。反之,如果学习率过小,收敛速度会很慢。
策略梯度方法需要平衡探索与利用。如果智能体探索不足,可能无法接近最优策略;反之,如果探索过多,则无法收敛到最优策略,而是在动作空间中振荡。
样本效率低下
策略梯度方法通过完整执行每个策略直到终止并累积每一步的奖励来估计回报。因此,它们需要与环境进行大量交互以绘制大量样本轨迹。对于状态或动作空间较大的环境,这是低效且昂贵的。
稳定性解决方案
由于不稳定性是策略梯度方法中相对常见的问题,开发者采用了多种解决方案来稳定训练过程。下面介绍几种常见的策略梯度训练稳定性解决方案:
使用基线函数
由于采样效率低下,训练迭代中估计的回报梯度可能具有高方差,导致训练不稳定且缓慢。一种常见的降低方差的方法是使用基线函数,例如优势演员-评论家(A2C)方法。其思想是使用优势函数(advantage function)而非估计回报作为目标函数的代理。
优势计算为采样轨迹的实际回报与给定初始状态的预期回报之间的差值。这种方法涉及使用价值函数作为状态和状态-动作对的期望值。通过将损失表示为实际回报与预期回报之差(而非仅回报本身),A2C 降低了损失函数及梯度的方差,从而使训练更加稳定。
使用熵正则化
在某些环境中(如稀疏奖励环境——只有极少数状态给予奖励),策略会迅速采用确定性方法,并采取贪婪策略,仅利用已探索的路径。这会阻碍进一步探索,常导致收敛到局部最优和次优策略。
解决方案是通过在策略变得过于确定性时对其进行惩罚来鼓励探索。这通过在目标函数中添加基于熵的项来实现。熵衡量策略中的随机性程度。熵越大,智能体选择动作的随机性越高。该基于熵的项是熵系数与当前策略熵的乘积。
将熵纳入目标函数有助于在探索与利用之间取得平衡。
策略梯度的扩展
在策略梯度方法的各种扩展中,最基本的是 REINFORCE 算法。它提供了策略梯度定理的直接实现,是更高级技术的基础。
REINFORCE 算法
REINFORCE 算法(也称为蒙特卡洛 REINFORCE)是策略梯度定理的基本实现之一。它使用蒙特卡洛方法估计回报和策略梯度。在遵循 REINFORCE 算法时,智能体直接从环境中采样从初始状态到终止状态的所有动作。这与 TD 学习和动态规划等基于价值函数估计引导动作的方法形成对比。
以下是 REINFORCE 算法的基本步骤:
- 使用随机参数初始化策略。
- 重复多个训练 episode。对于每个 episode:
- 生成 episode 的每一步:
- 将状态传递给策略函数。
- 策略函数为每个可能动作生成概率。
- 从该概率分布中随机采样一个动作。
- 对于 episode 中的每个状态,估计直到该步的回报(折扣累积奖励)。
- 根据策略梯度定理估计目标函数的梯度,表示为逐步回报与各步动作概率的乘积。
- 通过应用梯度更新策略参数。
- 生成 episode 的每一步:
对于每个策略,你可以采样单条轨迹来估计梯度(如上所示),或对同一策略下采样的多条轨迹的梯度取平均。
演员-评论家方法(Actor-Critic Methods)
演员-评论家方法将策略梯度方法(如 REINFORCE)与价值函数相结合。
- 演员(Actor)的工作方式类似于策略梯度方法。演员实现策略,根据策略在每一步选择动作。它通过遵循期望回报的梯度来更新策略。
- 评论家(Critic)实现价值函数,用作基线(如前所述)。这有助于使训练更高效和稳定。
像 REINFORCE 这样的策略梯度方法使用原始回报沿每条轨迹估计梯度。由于这些轨迹通过采样过程绘制,可能导致回报和梯度的方差较大。使用优势函数(advantage function)代替原始回报可解决此问题。优势函数是实际回报与预期回报(即价值函数)之间的差值。
演员-评论家方法是一类算法。当评论家使用优势函数实现时(最常见的方式),也称为优势演员-评论家(A2C)。
近端策略优化(PPO)
在复杂环境中,仅靠 A2C 等演员-评论家方法不足以控制回报和梯度的方差。在这种情况下,人为限制每次迭代中策略可改变的幅度有助于稳定训练。这迫使更新后的策略(梯度上升后)位于旧策略的邻域内。
近端策略优化(Proximal Policy Optimization, PPO)对策略梯度做了两项修改:
- 使用优势函数。通常,该优势函数使用价值函数作为基线。在这方面,它与 A2C 方法类似。
- 限制每次迭代中策略参数的改变量。这是通过裁剪的替代目标函数(clipped surrogate objective function)实现的。算法指定新策略与旧策略比率必须位于的范围。当比率(梯度更新后)超出这些预设值时,会被裁剪到范围内。
因此,PPO 显著改进了原始策略梯度方法,提高了复杂环境中的稳定性。裁剪的目标函数防止了回报和梯度的大方差破坏策略更新。为在探索与利用之间取得平衡,还可以修改 PPO 以使用熵正则化。这是通过在目标函数中添加熵项(缩放参数乘以策略熵)来实现的。
最新进展
策略梯度是最早用于解决 RL 问题的方法之一。随着高速 GPU 的出现,各种新方法被提出,将现代机器学习技术应用于策略梯度。
梯度提升强化学习(Gradient-Boosted Reinforcement Learning)
近年来,在将梯度提升等方法应用于 RL 算法方面取得了进展。梯度提升通过组合多个弱模型的预测来生成一个强模型。这被称为梯度提升强化学习(GBRL)。GBRL 是一个类似于 XGBoost 的 Python 包,为 RL 算法实现了这些技术。
迁移强化学习(Transfer Reinforcement Learning)
迁移学习(TL)是一种将一个模型获得的知识应用于提升另一个模型性能的技术。迁移学习很有用,因为从头训练 ML 模型成本高昂。TL 方法已与策略梯度结合使用,以提升 RL 模型的性能。这种方法称为迁移强化学习(TRL)。
结论
策略梯度是解决 RL 问题的最基本方法之一。
在本文中,我们介绍了策略梯度的第一性原理,展示了策略梯度定理的推导过程,并演示了如何在 Gymnasium 环境中使用 PyTorch 实现一个简单的基于梯度的算法。最后,我们讨论了基本策略梯度算法的实际挑战和常见扩展。