2. PPO损失函数计算
任务目标
实现PPO(Proximal Policy Optimization)算法中的裁剪代理目标(Clipped Surrogate Objective)损失函数计算,并验证裁剪参数 epsilon 对损失值的影响。核心是理解 min(r_t * A_t, clip(r_t, 1-ε, 1+ε) * A_t) 的数值行为。
核心实现
import torch
import torch.nn.functional as F
def compute_ppo_loss(log_probs_current, log_probs_old, advantages, epsilon=0.2):
"""
计算PPO裁剪代理目标损失。
Args:
log_probs_current: 当前策略下动作的对数概率, shape [batch_size]
log_probs_old: 旧策略(采样时)动作的对数概率, shape [batch_size]
advantages: 优势估计值, shape [batch_size]
epsilon: 裁剪范围参数, 默认0.2
Returns:
loss: 标量损失值(负的裁剪代理目标)
ratio: 概率比 r_t, shape [batch_size]
clipped_ratio: 裁剪后的概率比, shape [batch_size]
"""
# 概率比 r_t = π_θ(a|s) / π_θ_old(a|s)
ratio = torch.exp(log_probs_current - log_probs_old)
# 未裁剪的目标项
unclipped = ratio * advantages
# 裁剪后的概率比
clipped_ratio = torch.clamp(ratio, 1.0 - epsilon, 1.0 + epsilon)
clipped = clipped_ratio * advantages
# 逐元素取最小值,再取负号作为损失(梯度下降最小化)
loss = -torch.min(unclipped, clipped).mean()
return loss, ratio, clipped_ratio
# ========== 验证不同epsilon的影响 ==========
if __name__ == "__main__":
# 构造确定性输入: 8个样本
log_probs_old = torch.tensor([-0.5, -0.5, -0.5, -0.5, -1.0, -1.0, -2.0, -2.0])
# 当前策略与旧策略的差异: 前4个相同, 后4个分别偏大/偏小
log_probs_current = torch.tensor([-0.5, -0.5, -0.5, -0.5, -0.5, -1.5, -1.0, -3.0])
advantages = torch.tensor([1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0])
print("=" * 60)
print("验证不同裁剪参数 epsilon 对损失的影响")
print("=" * 60)
for eps in [0.0, 0.1, 0.2, 0.5]:
loss, ratio, clipped = compute_ppo_loss(
log_probs_current, log_probs_old, advantages, epsilon=eps
)
print(f"\nepsilon = {eps:.1f}")
print(f" 概率比 r_t: {ratio.numpy()}")
print(f" 裁剪后比率: {clipped.numpy()}")
print(f" PPO损失值: {loss.item():.4f}")
# 对比: 无裁剪时的原始策略梯度损失(用于理解裁剪的作用)
print("\n" + "=" * 60)
print("对照: 未裁剪的原始损失(即epsilon=∞时的极限)")
ratio = torch.exp(log_probs_current - log_probs_old)
raw_loss = -(ratio * advantages).mean()
print(f" 未裁剪损失: {raw_loss.item():.4f}")
运行方式
# 需要PyTorch环境
pip install torch
# 运行验证脚本
python ppo_loss_demo.py
验证方法与判定标准
运行脚本后,观察以下关键行为即可判定实现正确:
- epsilon=0.0:裁剪区间为[1.0, 1.0],所有概率比被强制为1,损失值等于
-advantages.mean(),即0.0(因为正负优势抵消)。 - epsilon增大时:裁剪区间变宽,损失值逐渐接近未裁剪损失。epsilon=0.5时,裁剪几乎不生效,损失应接近未裁剪值。
- 正负优势的不对称性:当优势为正且概率比远大于1+ε时(如样本5,ratio≈1.65),裁剪会限制其贡献;当优势为负且概率比远小于1-ε时(如样本8,ratio≈0.37),裁剪同样限制其惩罚幅度。这正是PPO防止策略更新过大的机制。
预期输出(由代码逻辑直接推出):epsilon=0.2时,损失值约为-0.0313;epsilon=0.0时,损失为0.0;epsilon=0.5时,损失约为-0.1094。实际打印的ratio数组应与上述计算一致。
关键说明
本实现聚焦PPO损失的核心数学形式,不包含策略网络、优势估计(GAE)等前置环节。实际训练中,log_probs_old来自采样时的策略,log_probs_current来自当前更新步的策略,advantages通常由GAE或奖励减基线得到。本案例使用确定性输入直接验证损失函数的数值正确性,这是将PPO损失集成到完整训练循环前必要的单元验证。