1. 策略梯度算法基础解析
在强化学习领域,策略梯度(Policy Gradients,简称PG)算法代表了一种与价值函数方法截然不同的范式。与DQN等基于价值的方法不同,PG直接对策略进行建模和优化,这种差异带来了独特的优势和挑战。
1.1 策略梯度与价值方法的本质区别
传统Q-learning系列算法(如DQN)的核心思想是通过学习状态-动作价值函数Q(s,a)来间接指导行为选择。这种方法需要评估所有可能的动作价值,然后选择价值最高的动作。然而,当动作空间变得连续或维度极高时,这种"先估值再选择"的机制就会遇到严重瓶颈。
PG算法则采用了更为直接的思路:它直接参数化策略π(a|s),即给定状态下选择各个动作的概率分布。这种方法的优势主要体现在三个方面:
- 天然支持连续动作空间:网络可以直接输出高斯分布的参数(均值和方差),在无限的动作空间中优雅地进行采样
- 避免了argmax操作:不需要在每一步计算所有可能的Q值,计算效率更高
- 支持随机策略:可以学习到概率性的行为模式,这在部分博弈场景中至关重要
提示:在机械控制等连续动作场景中,PG方法通常比DQN表现更好,因为DQN需要对无限的动作空间进行离散化处理,既损失精度又增加计算负担。
1.2 策略参数化的常见形式
在实际实现中,策略通常通过神经网络进行参数化。根据动作空间的不同,输出层的设计也有所区别:
-
离散动作空间:采用softmax输出层,每个神经元对应一个动作的概率
python复制# 离散动作空间输出层示例 self.fc = nn.Sequential( nn.Linear(state_dim, 64), nn.ReLU(), nn.Linear(64, action_dim), nn.Softmax(dim=-1) # 确保输出是有效的概率分布 ) -
连续动作空间:输出高斯分布的参数(均值和方差)
python复制# 连续动作空间输出层示例 self.mean_layer = nn.Sequential( nn.Linear(state_dim, 64), nn.ReLU(), nn.Linear(64, action_dim) ) self.log_std = nn.Parameter(torch.zeros(action_dim)) # 对数标准差作为可学习参数
在实际应用中,CartPole这类简单环境通常采用离散动作空间,而MuJoCo等复杂物理仿真环境则需要连续动作空间的处理方式。
2. 蒙特卡洛方法与G值计算
2.1 回合制更新的必要性
PG算法的一个显著特点是它采用回合制(episodic)的更新方式,这与DQN的步进式(step-by-step)更新形成鲜明对比。这种差异源于PG算法缺乏即时评估动作价值的机制。
考虑一个简单的游戏场景:前90步可能都是看似无效的探索(即时奖励为0),但在第100步突然获得关键胜利(最终奖励+1)。如果在前几步就急于更新策略,算法会错误地认为这些探索动作没有价值。PG必须等待整个回合结束,获得最终结果后,才能客观评估每个动作的真实贡献。
这种"先体验后学习"的模式被称为蒙特卡洛方法,它虽然增加了学习延迟,但能更准确地评估动作的长期价值。
2.2 G值的递归计算
G值(回报)的计算是PG算法的核心环节。它通过递归方式将最终奖励合理地分配到每个动作上。以一个三步回合为例:
code复制即时奖励序列:[0.0, 0.0, 1.0]
折扣因子γ=0.9
计算过程如下:
-
第3步(t=2):
G₂ = R₂ = 1.0 -
第2步(t=1):
G₁ = γG₂ + R₁ = 0.91.0 + 0.0 = 0.9 -
第1步(t=0):
G₀ = γG₁ + R₀ = 0.90.9 + 0.0 = 0.81
最终得到的G值序列为:[0.81, 0.9, 1.0]。这个结果反映了早期动作对最终胜利的贡献,尽管它们没有获得即时奖励。
2.3 数据归一化的关键作用
在实际实现中,G值的归一化处理对训练稳定性至关重要。考虑以下两种情况:
- 未归一化:如果所有G值都是正数,网络会倾向于提高所有动作的概率,缺乏对比性学习
- 归一化后:通过减去均值、除以标准差,形成有正有负的G值,明确区分好动作和坏动作
归一化实现代码示例:
python复制rewards = np.array([0.81, 0.9, 1.0])
reward_mean = np.mean(rewards)
reward_std = np.std(rewards)
normalized_rewards = (rewards - reward_mean) / reward_std
# 结果可能为:[-1.24, 0.04, 1.2]
这种处理使得表现优于平均的动作获得正反馈,而表现差的动作受到抑制,大大加快了学习速度。
3. 策略梯度的实现细节
3.1 策略模型的架构设计
以CartPole环境为例,典型的策略网络架构如下:
python复制class PolicyModel(nn.Module):
def __init__(self, input_dim, output_dim):
super(PolicyModel, self).__init__()
self.fc = nn.Sequential(
nn.Linear(input_dim, 128),
nn.ReLU(),
nn.Linear(128, 128),
nn.ReLU(),
nn.Linear(128, output_dim),
nn.Sigmoid() # 二分类问题使用sigmoid
)
def forward(self, x):
return self.fc(x)
这个网络有几点关键设计:
- 输入维度对应环境状态维度(CartPole为4)
- 隐藏层使用ReLU激活函数,平衡表达能力和训练稳定性
- 输出层使用sigmoid,因为CartPole只有两个离散动作(左/右)
对于更复杂的环境,可能需要调整网络深度和宽度,或使用更先进的架构如Actor-Critic。
3.2 动作选择与采样
PG算法通过概率采样选择动作,这既保证了探索性,又能逐渐聚焦到优秀动作上。实现代码如下:
python复制def choose_action(self, state):
state = torch.FloatTensor(state).unsqueeze(0)
probs = self.policy_model(state)
distribution = torch.distributions.Bernoulli(probs)
action = distribution.sample()
return action.item()
这里使用伯努利分布进行采样,因为CartPole是二分类问题。对于多离散动作,应改用Categorical分布;连续动作则使用Normal分布。
注意:在测试阶段,通常会改为选择概率最高的动作(即argmax操作),以获得更稳定的表现。
3.3 策略更新的数学原理
PG算法的损失函数设计非常巧妙,它解决了强化学习中缺乏明确监督信号的难题。其核心思想是:
- 将实际采样的动作视为"正确"标签
- 计算该动作的对数概率
- 用G值作为权重,放大或缩小该动作概率的更新幅度
数学表达式为:
[ \text{loss} = -\log \pi(a|s) \cdot G ]
PyTorch实现示例:
python复制def update(self, states, actions, rewards):
# 计算G值并归一化...
self.optimizer.zero_grad()
loss = 0
for state, action, reward in zip(states, actions, rewards):
prob = self.policy_model(state)
dist = torch.distributions.Bernoulli(prob)
log_prob = dist.log_prob(action)
loss += -log_prob * reward
loss.backward()
self.optimizer.step()
这种设计使得:
- 获得高G值的轨迹中的动作概率会被提升
- 低G值轨迹中的动作概率会被抑制
- 更新的幅度与G值的大小成正比
4. 实战技巧与常见问题
4.1 训练不稳定问题及解决方案
PG算法以训练不稳定著称,常见问题及对策包括:
-
学习率设置:
- 问题:过大导致震荡,过小导致收敛慢
- 方案:初始设为1e-3到1e-4,配合学习率衰减
-
梯度爆炸:
- 问题:长期回报导致梯度幅值过大
- 方案:梯度裁剪(
torch.nn.utils.clip_grad_norm_)
-
探索不足:
- 问题:过早收敛到次优策略
- 方案:添加熵正则项,鼓励探索
python复制entropy = -torch.sum(prob * torch.log(prob)) loss -= 0.01 * entropy # 熵正则系数通常较小
4.2 超参数调优经验
基于CartPole环境的实践经验:
| 超参数 | 推荐值 | 作用 |
|---|---|---|
| 折扣因子γ | 0.95-0.99 | 平衡即时与未来奖励 |
| 学习率 | 1e-3到1e-4 | 控制更新步长 |
| 隐藏层大小 | 64-256 | 网络表达能力 |
| 批次大小 | 整个回合 | PG通常使用完整回合 |
4.3 性能监控与调试
有效的训练监控应包括:
- 回合奖励曲线:观察是否稳定上升
- 动作概率分布:检查是否合理变化
- 梯度幅值:防止梯度消失/爆炸
- 探索率:通过动作熵值监控
调试时可采用的检查清单:
- 奖励归一化是否正确实现
- G值计算是否按时间逆序
- 梯度更新是否在回合结束后进行
- 动作采样是否基于概率而非确定性
5. 进阶扩展与变体算法
5.1 基准测试:PG在CartPole中的表现
在标准CartPole-v1环境中,一个正确实现的PG算法通常能在100-300个训练回合内达到最大奖励(500分)。典型的学习曲线呈现以下特征:
- 初期(0-50回合):随机探索期,奖励波动大
- 中期(50-150回合):快速提升期,策略明显改善
- 后期(150+回合):稳定期,偶尔出现性能下降(探索导致)
与DQN相比,PG算法通常:
- 训练初期收敛更快
- 最终性能相当
- 对超参数更敏感
5.2 主流变体算法比较
-
REINFORCE:最基础的PG算法,使用完整回合的蒙特卡洛回报
- 优点:实现简单
- 缺点:高方差
-
Actor-Critic:引入价值函数作为基线减少方差
- 优点:更稳定
- 缺点:实现复杂
-
PPO:加入策略更新约束
- 优点:训练稳定
- 缺点:超参数多
-
TRPO:理论保证的策略优化
- 优点:数学严谨
- 缺点:计算复杂
对于初学者,建议从REINFORCE开始,掌握基本原理后再转向Actor-Critic等进阶算法。
5.3 连续动作空间的扩展
当处理连续动作空间时,PG算法需要做以下调整:
-
策略网络输出高斯分布的均值和方差:
python复制mean = self.mean_layer(state) std = torch.exp(self.log_std) # 确保方差为正 -
使用正态分布进行采样:
python复制
dist = torch.distributions.Normal(mean, std) action = dist.sample() -
对数概率计算需考虑方差:
python复制log_prob = dist.log_prob(action).sum(dim=-1)
这种参数化方式可以优雅地处理机械控制等连续动作任务,是PG算法的重要优势之一。