仓库地址https://github.com/ctfxffy/PPO

效果展示

10 万步冒烟训练曲线(早期学习阶段,蚂蚁学会“站立不倒”,回报约 850):

渲染评估(确定性策略,实时窗口支持鼠标旋转/缩放视角):

python evaluate.py --checkpoint checkpoints/best.pt --render --episodes 3

特性

  • 正确的 PPO 实现:Clipped Surrogate + GAE(λ=0.95)+ 优势归一化 + 价值损失 + 梯度裁剪,所有组件均有单元测试(含 GAE 手算对照)。
  • 现代技术栈:Gymnasium 1.x + MuJoCo 3.x 纯 pip 安装,Windows 免编译,告别 mujoco-py
  • 断点续训:checkpoint 完整保存网络/优化器/归一化器/步数/RNG 状态,--resume 一键恢复,可边续训边改超参(配合分阶段调参)。
  • 训练监控:TensorBoard 全指标(回报/损失/KL/clip_frac/lr)+ CSV 日志 + tqdm 实时进度条 + matplotlib 出图。
  • 评估工具:确定性策略评估、--render 实时渲染、离屏视频录制(mp4)。
  • 工程细节:观测 RunningMeanStd 归一化(Welford 算法)、正交初始化、terminated/truncated 分别处理、NaN 保护、原子化 checkpoint 保存。
  • 兼容性修复:内置 gymnasium 1.x + mujoco 3.x 渲染鼠标回调不兼容的 shim(ppo/render_fix.py)。

任务与奖励机制

Ant-v5 是 MuJoCo 经典连续控制环境:一只躯干 + 四腿(8 关节)的蚂蚁机器人,目标是在不摔倒的前提下沿 x 轴方向尽快移动。

要素 内容
观测空间 105 维连续向量(躯干姿态、关节角度/角速度、78 维足底接触力)
动作空间 8 维连续向量,[-1, 1],各关节电机力矩
回合长度 上限 1000 步(每步 0.05s 仿真时间,共 50s)
终止条件 terminated:躯干高度掉出 [0.2, 1.0](摔倒);truncated:超时(仍可 bootstrap)

奖励函数(环境内置,本项目未做自定义 shaping):

r_t = v_x(前进速度 × 1.0)+ 1.0(存活奖励)− 0.5 × Σ a_i²(控制代价)− 5×10⁻⁴ × Σ F_contact²(接触代价,裁剪后)

环境与依赖

开发与验证环境(Windows 11 + conda):

版本 说明
Python 3.10 3.9–3.12 均可
PyTorch 2.13 (cu126) >=2.0 即可,CPU 也能训
numpy 2.2.6 2.x 是关键约束
gymnasium[mujoco] 1.3.0 必须 >=1.0(numpy 2 兼容从 1.0 起)
mujoco 3.11 随 gymnasium 自动安装,纯 wheel
tensorboard >=2.17 2.17 起支持 numpy 2
matplotlib >=3.9 3.9 起支持 numpy 2
tqdm >=4.66 进度条
moviepy 任意 可选,仅录视频需要(注意会降级 pillow 到 11.x)

安装:

pip install -r requirements.txt

验证环境可用:

python -c "import gymnasium; env = gymnasium.make('Ant-v5'); print(env.observation_space.shape, env.action_space.shape); env.close()"
# 期望输出: (105,) (8,)

快速开始

训练

python train.py --total-steps 3000000
推荐:带学习率退火
python train.py --total-steps 3000000 --lr-anneal

断点续训

# 从上次 checkpoint 继续,一切状态(网络/优化器/归一化器/RNG/步数)原样恢复
python train.py --total-steps 6000000 --resume checkpoints/latest.pt
续训同时调整超参(分阶段调参,见下文)
python train.py --total-steps 6000000 --resume checkpoints/latest.pt --lr 1e-4 --lr-anneal

监控

tensorboard --logdir runs          # 浏览器打开 http://localhost:6006
python plot.py runs/<run-dir>/metrics.csv   # 生成 reward_curve.png

评估与渲染

# 无头评估 10 回合,输出均值/方差
python evaluate.py --checkpoint checkpoints/best.pt --episodes 10
实时渲染(窗口里可鼠标拖拽旋转、滚轮缩放)
python evaluate.py --checkpoint checkpoints/best.pt --render --episodes 3

录制视频

import gymnasium as gym
env = gym.make("Ant-v5", render_mode="rgb_array")
env = gym.wrappers.RecordVideo(env, video_folder="videos",
                               episode_trigger=lambda e: True, disable_logger=True)
# ... 之后按 evaluate.py 的方式加载模型跑回合即可,视频自动保存到 videos/

项目结构

PPO/
├── ppo/                     # 核心算法包(建议按此顺序阅读)
│   ├── utils.py             #   种子固定、设备检测、环境工厂(v5/classic 观测模式)
│   ├── config.py            #   argparse 全部超参数(先看它混个脸熟)
│   ├── normalizer.py        #   RunningMeanStd 观测归一化(Welford 并行更新)
│   ├── models.py            #   ActorCritic:高斯策略 + 价值网络,正交初始化
│   ├── buffer.py            #   RolloutBuffer:数据存储 + GAE 计算 + minibatch 迭代
│   ├── agent.py             #   PPOAgent:clipped surrogate 更新逻辑(算法核心)
│   └── render_fix.py        #   gymnasium+mujoco3 渲染鼠标回调兼容 shim
├── train.py                 # 训练入口:采样→GAE→更新循环,日志/checkpoint/续训
├── evaluate.py              # 评估入口:确定性策略,支持 --render
├── plot.py                  # 从 metrics.csv 画回报曲线
├── requirements.txt
├── assets/                  # README 配图(训练曲线)
├── runs/                    # TensorBoard 事件 + metrics.csv(.gitignore)
├── checkpoints/             # latest.pt / best.pt(.gitignore)
└── videos/                  # 评估录像

PPO 算法要点与代码对照

1. 策略:高斯分布(ppo/models.py)

连续动作空间的策略输出分布参数而非动作值:Actor 头输出均值 μ(s),log_std 为独立可学习参数(不随状态变),打包成 torch.distributions.Normal

  • dist.sample() → 训练时采样(探索)
  • dist.mean → 评估时确定性输出
  • dist.log_prob(a).sum(-1) → 动作的对数概率(8 维独立求和),PPO ratio 的原料

2. GAE 优势估计(ppo/buffer.py)

δ_t = r_t + γV(s_{t+1})·m_t − V(s_t),A_t = Σ_l (γλ)^l δ_{t+l}

critic 的训练目标是 returns = advantages + values(GAE 修正后的回报估计),用 MSE 拟合。

3. Clipped Surrogate(ppo/agent.py)

L^CLIP = E[min(r_t·A_t, clip(r_t, 1±ε)·A_t)],r_t = π_new(a_t|s_t) / π_old(a_t|s_t)

ratio = (new_log_probs - old_log_probs).exp()      # log 域做除法
surr1 = ratio * adv
surr2 = torch.clamp(ratio, 1.0 - clip_eps, 1.0 + clip_eps) * adv
policy_loss = -torch.min(surr1, surr2).mean()

4. 完整损失与更新

loss = policy_loss + 0.5 * value_loss - entropy_coef * entropy

每批 2048 步数据复用 10 个 epoch,每 epoch 切成 8 个 256 的 minibatch 随机打乱;每个 minibatch 内做优势归一化;反向传播后梯度裁剪到 0.5。

超参数说明

python train.py -h 查看全部,常用项:

参数 默认值 说明
--total-steps 3,000,000 总环境步数
--rollout-steps 2048 每次 PPO 更新前采样步数
--minibatch-size 256 minibatch 大小(建议为 rollout 的 1/8)
--n-epochs 10 每批数据复用轮数
--lr 3e-4 Adam 学习率
--lr-anneal 线性退火到 0,长训练强烈建议开
--gamma 0.99 折扣因子
--lam 0.95 GAE λ(偏差-方差权衡)
--clip-eps 0.2 PPO 剪切系数 ε
--value-coef 0.5 价值损失权重
--entropy-coef 0.0 熵奖励(Ant 通常为 0)
--max-grad-norm 0.5 梯度裁剪
--seed 0 随机种子
--resume 续训 checkpoint 路径
--obs-mode v5 v5=105 维;classic=107 维(补回全局坐标)
--save-interval 10 每 N 次更新存一次 checkpoint

分阶段训练建议

阶段 步数 lr clip-ε rollout GAE λ
探索 0–1M 3e-4 0.3 2048 0.95
提升 1M–3M 1e-4 0.2 4096 0.90
微调 >3M 5e-5 0.1 8192 0.80

阶段间通过 --resume + 覆盖超参切换:

python train.py --total-steps 1000000
python train.py --total-steps 3000000 --resume checkpoints/latest.pt \
    --lr 1e-4 --clip-eps 0.2 --rollout-steps 4096 --lam 0.9

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐