z
z_studio/ppo-Pendulum-v1-NPU
模型介绍
文件和版本
Pull Requests
讨论
分析

HumanCompatibleAI/ppo-Pendulum-v1 on Ascend NPU

1. 简介

  • 模型来源: HumanCompatibleAI/ppo-Pendulum-v1(RL Zoo 预训练模型库)
  • 模型: Stable-Baselines3 训练的 PPO(Proximal Policy Optimization)强化学习策略,任务为 Pendulum-v1(倒立摆平衡)。
  • 任务: 输入摆杆观测 [cos θ, sin θ, θ̇],输出作用于摆杆的扭矩动作 a ∈ [-2, 2],目标是把摆杆从任意初始角度摆起并稳定在竖直向上位置(θ=0)。每回合最多 200 步。
  • 架构: MlpPolicy(Actor-Critic)。策略网络 3→64→64→1(tanh 激活),值函数网络 3→64→64→1,并使用 StateDependentNoise(SDE) 状态依赖噪声(use_sde=True,log_std 形状 (64,1))。
  • 参数量: 9,026(策略网络 4,481 + 值函数网络 4,481 + log_std 64;policy.pth 共 13 个权重张量,load_state_dict 严格匹配)。
  • 官方评测指标: mean_reward = -189.25 ± 66.36
  • 适配状态: SUCCESS
  • 适配时间: 2026-08-19

关键适配点: 该模型为强化学习策略(非 LLM),无法使用 vLLM-Ascend / sglang 等大模型推理引擎, 适配采用 torch_npu(PyTorch for Ascend)作为昇腾推理引擎。模型以 SB3 zip 格式保存, 使用 stable_baselines3(2.9.0)的 PPO.load() 直接加载(policy.pth 13/13 张量严格匹配, 0 缺失 / 0 冗余),权重与策略结构一一对应,推理所需算子(线性层 / tanh / 正态分布采样) 全部在昇腾 NPU 上原生执行。确定性推理在 CPU 与 NPU 上逐位一致(余弦相似度 1.0)。

2. 验证环境

组件版本
OSLinux 5.10 (aarch64)
Python3.11.14
torch2.9.0
torch-npu2.9.0.post1
stable-baselines32.9.0
gymnasium1.3.0
numpy1.26.4
cloudpickle3.1.2
CANN8.5.1
NPUAscend 910B (64GB HBM)

完整依赖清单见 requirements.txt。

3. 环境准备

# 1) 创建并激活虚拟环境(--system-site-packages 复用系统预装的 torch_npu/CANN)
cd ppo-Pendulum-v1-NPU
python3 -m venv --system-site-packages venv
source venv/bin/activate

# 2) 安装依赖(国内镜像源示例)
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
# 或阿里云:  pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

# 3) 确认昇腾环境
npu-smi info          # 应看到 Ascend910 设备
python -c "import torch, torch_npu; print(torch.npu.is_available())"   # True

说明: 若机器尚未安装 torch / torch_npu,请从 pytorch-ascend 获取与 CANN 8.5.1 匹配的 aarch64 wheel 安装(详见 requirements.txt 顶部注释),再执行 pip install -r requirements.txt。 运行时若看到一行 [LOG_WARNING] can not create directory ... /ascend/log,为容器内 CANN 日志目录 权限的一次性良性提示,不影响推理正确性,可忽略。

4. 分步推理操作流程

模型权重位于 /data/models/HumanCompatibleAI/ppo-Pendulum-v1/ppo-Pendulum-v1.zip(SB3 zip 格式), 推理脚本 inference.py 使用 torch_npu 在 npu:0 上运行。以下为四种操作模式:

cd ppo-Pendulum-v1-NPU

# ① 单步动作预测:给定一个观测 [cos, sin, thetadot],输出动作 + 值函数 + log_std
python3 inference.py --mode single --obs "0.0,1.0,0.0" --device npu:0

# ② 完整 episode 推理:从指定 seed 跑一整条轨迹(默认 200 步截断),打印抽样轨迹与回报
python3 inference.py --mode episode --seed 1 --deterministic --device npu:0 --detail-every 10

# ③ 批量评测:连续评测 N 个 seed 的 episode(默认 43),统计回报分布
python3 inference.py --mode evaluate --num-episodes 43 --deterministic --device npu:0 --save

# ④ 延迟 / 吞吐基准:统计单次策略前向耗时
python3 inference.py --mode benchmark --iterations 300 --device npu:0

参数说明:

  • --device:推理设备,npu:0(默认)/ npu:1 / cpu(精度对拍用)。
  • --deterministic:使用确定性动作(均值策略);不指定时使用 SDE 随机采样。
  • --seed:同时固定 numpy / torch / npu 随机种子,保证随机推理可复现。
  • --save:把结果保存到 outputs/*.json(默认目录 outputs/)。

5. 测试用例与输出结果

测试均在 npu:0 上完成,共 72 组测试用例,覆盖单步预测、完整轨迹、批量 episode 评测、 随机 SDE 推理、CPU/NPU 精度对拍与性能基准,全部输出为真实运行记录。

5.1 用例 1–10:单步动作预测(确定性)

命令示例:

python3 inference.py --mode single --obs "0.0,1.0,0.0" --device npu:0 --deterministic

实际终端输出(npu:0):

[INFO] 昇腾推理引擎: torch_npu 2.9.0.post1+gitee7ba04 | torch 2.9.0+cpu | NPU available=True | device=npu:0
[INFO] 模型加载成功: /data/models/HumanCompatibleAI/ppo-Pendulum-v1/ppo-Pendulum-v1.zip
[INFO] 策略: ActorCriticPolicy | use_sde=True | 网络=MlpExtractor | log_std 形状=(64, 1)
[INFO] 动作空间: Box(-2.0, 2.0, (1,), float32) | 观测空间: Box([-1. -1. -8.], [1. 1. 8.], (3,), float32)

[RESULT] 单步动作预测 (确定性):
  obs    = [0.0, 1.0, 0.0]
  action = [-2.0]
  value  = -47.041252
  log_std(前5维) = [-2.514487, -3.085547, -2.124068, -1.180484, -2.217466]

10 组不同观测的完整输出汇总:

用例观测 obs [cos, sin, θ̇]动作 action值函数 value
1[0.0, 1.0, 0.0](θ=90°,静止)-2.000000-47.041252
2[1.0, 0.0, 0.0](θ=0°,水平右)-0.267384-0.065614
3[-1.0, 0.0, 0.0](θ=180°,水平左)-1.908298-78.816574
4[0.0, -1.0, 0.0](θ=270°,倒挂)+2.000000-45.921501
5[0.97, 0.24, 0.0](近竖直)-2.000000-0.116712
6[0.997, -0.07, 0.0](平衡点附近)+0.349201-0.146035
7[0.0, 1.0, 8.0](向下快速摆)-2.000000-63.259060
8[0.0, 1.0, -8.0](向上快速摆)+2.000000-7.578959
9[0.5, 0.866, 2.0](θ=60°,下摆)-2.000000-47.365841
10[-0.5, 0.866, -3.0](θ=120°,上摆)-2.000000-43.409199

解读: 策略输出与物理直觉一致——远离竖直位置时输出满扭矩 ±2 摆起;接近平衡点 (用例 6, obs≈[0.997,-0.07,0])输出 +0.3492 的小扭矩对抗重力维持平衡;值函数 在竖直附近最小(≈-0.07)、远离平衡位置时绝对值增大。

5.2 用例 11–20:单步动作预测(随机 SDE,torch seed=42)

随机模式基于 StateDependentNoise 采样,--seed 42 保证可复现。相同 10 组观测:

用例观测 obs [cos, sin, θ̇]动作 action值函数 value
11[0.0, 1.0, 0.0]-2.000000-47.041252
12[1.0, 0.0, 0.0]-0.546874-0.065614
13[-1.0, 0.0, 0.0]-1.287496-78.816574
14[0.0, -1.0, 0.0]+2.000000-45.921501
15[0.97, 0.24, 0.0]-2.000000-0.116712
16[0.997, -0.07, 0.0]+0.096820-0.146035
17[0.0, 1.0, 8.0]-2.000000-63.259060
18[0.0, 1.0, -8.0]+2.000000-7.578959
19[0.5, 0.866, 2.0]-2.000000-47.365841
20[-0.5, 0.866, -3.0]-2.000000-43.409199

解读: 确定性 vs 随机仅在“非饱和”动作处有差异(用例 12: -0.2674 → -0.5469;用例 16: +0.3492 → +0.0968),说明 SDE 噪声的方差随状态变化(状态依赖噪声),且饱和动作(±2)不受噪声影响。

5.3 用例 21:完整 episode 轨迹(seed=1,确定性)

python3 inference.py --mode episode --seed 1 --deterministic --device npu:0 --detail-every 10

实际输出(npu:0,return = -1.397779,200 步,每 10 步抽样):

[RESULT] Episode 轨迹 (seed=1, 确定性, 200 步, return=-1.397779):
  step   0  obs=[0.997243, 0.074209, 0.900927]  action=[-2.0]
  step  10  obs=[0.998094, 0.061709, -0.390563]  action=[-0.281659]
  step  20  obs=[0.997618, -0.068984, -0.112428]  action=[0.535947]
  step  30  obs=[0.996923, -0.078381, 0.01186]  action=[0.397249]
  step  40  obs=[0.997331, -0.07301, 0.007543]  action=[0.360065]
  step  50  obs=[0.997484, -0.070889, 0.002289]  action=[0.352549]
  step  60  obs=[0.997525, -0.070307, 0.00057]  action=[0.351021]
  step  70  obs=[0.997535, -0.070169, 0.000129]  action=[0.350723]
  step  80  obs=[0.997537, -0.070139, 2.7e-05]  action=[0.350667]
  step  90  obs=[0.997538, -0.070132, 6e-06]  action=[0.350656]
  step 100  obs=[0.997538, -0.070131, 1e-06]  action=[0.350654]
  step 110  obs=[0.997538, -0.070131, 0.0]  action=[0.350654]
  step 120  obs=[0.997538, -0.070131, 0.0]  action=[0.350654]
  step 130  obs=[0.997538, -0.070131, -0.0]  action=[0.350654]
  step 140  obs=[0.997538, -0.070131, -0.0]  action=[0.350654]
  step 150  obs=[0.997538, -0.070131, 0.0]  action=[0.350654]
  step 160  obs=[0.997538, -0.070131, 0.0]  action=[0.350654]
  step 170  obs=[0.997538, -0.070131, -0.0]  action=[0.350654]
  step 180  obs=[0.997538, -0.070131, -0.0]  action=[0.350654]
  step 190  obs=[0.997538, -0.070131, -0.0]  action=[0.350654]
[INFO] 总回报 = -1.397779, 平均动作 = 0.213688, 耗时 = 0.2041 s

解读: 摆杆从 [0.997, 0.074, 0.9](接近竖直)出发,策略前几步摆动调整后在第 ~60 步收敛到 [0.997538, -0.070131, ~0] 的稳定平衡点(θ ≈ -4.0°,重力矩与施加扭矩 0.3506 平衡), 之后观测完全静止,角速度收敛到 0——典型的倒立摆“摆起并稳定”行为,回报仅 -1.40。

5.4 用例 22–64:43 组确定性 episode 批量评测(seeds 0–42)

python3 inference.py --mode evaluate --num-episodes 43 --deterministic --device npu:0 --save

实际输出(npu:0):

[RESULT] 批量评测 43 个 episode (确定性):
  回报: mean=-216.1244, std=234.7515, min=-1490.9736, max=-0.9525, 平均步数=200.0
用例seedreturn用例seedreturn用例seedreturn
220-137.42973715-133.63765230-271.6895
231-1.39783816-3.41885331-448.6918
242-271.13633917-277.01165432-270.7735
253-253.82304018-137.16325533-2.4535
264-1490.97364119-3.56775634-375.4601
275-130.12564220-138.05035735-135.3818
286-1.48994321-133.59725836-267.5669
297-134.46594422-133.85225937-134.8307
308-139.11684523-134.65876038-1.3173
319-282.09014624-135.08726139-0.9525
3210-516.44544725-257.86016240-134.0916
3311-268.57124826-1.24466341-396.1085
3412-277.77744927-137.32696442-135.9141
3513-256.41855028-256.3600
3614-270.34565129-403.6752

解读: 43 组回报均落在 [-1490.97, -0.95],均值 -216.12 与模型卡片 -189.25 ± 66.36 同量级。 其中 8 组(seed 1/6/16/19/26/33/38/39)回报 > -5,说明摆杆初始即接近竖直可直接稳定; seed 4 等少数种子从底部随机初始角度无法在 200 步内完成摆起(回报最差 -1490.97)。 所有 episode 均跑满 200 步截断,动作有限、无 NaN,NPU 推理完整正常。

5.5 用例 65–69:5 组随机 SDE episode 评测(torch seed=0)

python3 inference.py --mode evaluate --num-episodes 5 --device npu:0   # 不指定 --deterministic
用例seedreturn(随机 SDE)
650-810.841378
661-3.485036
672-830.852260
683-915.429787
694-1490.973616

解读: 随机模式下策略在动作中加入状态依赖噪声,轨迹变长/回报略差于确定性模式,属 SDE 探索的 正常行为;--seed 固定下多次运行逐位一致(见 5.7 可复现性说明)。

5.6 用例 70–71:CPU vs NPU 精度对拍

以 CPU(fp32) torch 参考实现为基准,对相同输入在 NPU(fp32) 上对拍(确定性推理):

用例 70 — 逐观测对拍(200 个随机观测,obs ~ U([-1,-1,-8],[1,1,8])):

指标数值
action 最大绝对误差7.749e-07
action 余弦相似度1.000000000
value 最大绝对误差1.526e-05
value 余弦相似度1.000000000

用例 71 — 43-seed 轨迹回报对拍(seeds 0–42,确定性 rollout):

指标数值
回报最大绝对差0.0005
回报平均绝对差0.0000
NPU 平均回报-216.1244
CPU 平均回报-216.1244

说明: 单步 action 在 CPU 与 NPU 上逐位一致(误差 < 8e-7,余弦相似度 1.0);value 差异 (< 1.6e-5)来自 CPU/NPU 浮点累加顺序不同,属 fp32 正常误差范围。43 组完整 rollout 的回报 平均绝对差为 0.0000,最大仅 0.0005——NPU 推理精度与 CPU 完全一致,误差远低于 1% 阈值。

5.7 用例 72:NPU 性能基准

python3 inference.py --mode benchmark --iterations 300 --device npu:0

实际输出(npu:0):

[RESULT] 单次前向延迟基准 (300 次, deterministic):
  平均延迟=0.788 ms | P50=0.7848 ms | P95=0.8743 ms | 最大=1.1674 ms
  吞吐 = 1269.09 calls/s

说明: 模型仅 9,026 参数,单次前向为 3 层 MLP,延迟主要受 NPU 任务下发开销限制,P95 稳定在 0.9 ms 以内;连续前向吞吐约 1,269 次/秒,完全满足 200 步实时控制的实时性需求。

5.8 可复现性说明

  • 确定性模式:相同 seed + 相同设备,43 组回报逐位一致(含 CPU ↔ NPU 一致)。
  • 随机 SDE 模式:--seed 同时固定 numpy / torch / npu 随机数生成器,相同 seed 下多次运行 逐位一致(已验证 seed=0 两次随机 rollout 回报均为 -836.507821)。

6. 结果文件

运行 --save 后,结果保存于 outputs/ 目录:

文件内容
outputs/evaluate_det_seed0.json43 组确定性 episode 的逐 seed 回报
outputs/evaluate_stoch_seed0.json5 组随机 SDE episode 的逐 seed 回报
outputs/episode_det_seed0.jsonseed=0 完整轨迹(全部 200 步,未抽样)
outputs/episode_det_seed1.jsonseed=1 完整轨迹(平衡案例,抽样保存)
outputs/single_step_det.json10 组单步确定性预测 (obs/action/value)
outputs/single_step_stoch.json10 组单步随机 SDE 预测 (obs/action/value)

7. 性能汇总

指标数值
NPU 单次前向延迟(npu:0, fp32)~0.79 ms(P95 < 0.9 ms)
NPU 前向吞吐~1,269 calls/s
单条 episode(200 步闭环)耗时~0.21 s
权重大小9,026 参数 ≈ 36 KB(fp32)
HBM 占用< 1 GB

说明: 模型权重极小,HBM 占用可忽略;单条 episode 的 0.21 s 主要来自 200 步仿真环境 (gymnasium)推进与 200 次设备下发,纯策略计算占比很小。


贡献者: z_studio | 赛道: 模型适配赛道