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

PaulVialard/ppo-Huggy on Ascend NPU

昇腾 NPU 适配说明:本文档记录 PaulVialard/ppo-Huggy(Hugging Face Deep RL 课程经典示例 —— Huggy the Dog 🐶 "拥抱机器人")在昇腾 910B NPU 上的部署与验证 过程。推理引擎为 torch_npu(昇腾官方 PyTorch 后端,直接 forward ML-Agents PPO 策略网络),已在单卡 Ascend 910B 上完整跑通确定性 / 随机动作推理、批量推理、 精度对照、ONNX 交叉验证、闭环滚动统计与延迟基准,并完成 CPU fp32 数值对齐验证。

1. 模型简介

Huggy 是 Hugging Face Deep RL 课程 单元一的经典入门示例:一只用 PyBullet / Unity 物理引擎模拟的小狗,被训练去「扑向 并拥抱」投出的棍子(fetch & hug)。它由 Unity ML-Agents 使用 PPO 算法训练 (trainer_type: ppo,max_steps: 2_000_000),权重发布在 Hugging Face Hub:

  • 模型来源:https://huggingface.co/PaulVialard/ppo-Huggy
  • 参数量:566,805(fp32 权重约 2.3 MB)
  • 网络结构:MLP 策略网络(非 Transformer / 非 LLM / 非 VLM)

网络计算图(与官方 Huggy.onnx 逐算子一致,已交叉验证):

obs(59)
  │  obs 归一化: clamp((obs − running_mean) / sqrt(running_variance / steps), −5, 5)
  ▼
Linear(59→512) → SiLU → Linear(512→512) → SiLU → Linear(512→512) → SiLU
  │
  ▼  mu = Linear(512→21)                     log_sigma(21) 可学习标准差
确定性动作 = clip(mu, −3, 3) / 3        (输出落在 [−1, 1])
随机动作   = clip(mu + z·exp(log_sigma), −3, 3) / 3,  z ~ N(0, I)
项目数值
观测维度59(Huggy 环境连续向量观测)
动作维度21(Huggy 电机控制信号,连续)
隐藏层3 × 512(hidden_units=512, num_layers=3)
激活函数SiLU(Sigmoid × Linear,与 ONNX 导出一致)
观测归一化ML-Agents Normalizer(累计方差 / 步数),clip ±5
训练步数2,000,000(最终检查点 Huggy-2000049.pt)
适配状态SUCCESS
适配时间2026-08-19

引擎选型说明:vllm-ascend / sglang 面向自回归 token 生成(LLM/VLM),其 模型注册表(vllm/model_executor/models/registry.py)只包含文本/视觉生成架构, 无法加载强化学习策略网络;Huggy 是 PyTorch 原生的 MLP 策略,配合 torch_npu 后端即可在昇腾 NPU 上完整运行,故推理脚本选用 torch_npu 引擎。脚本同时以官方 Huggy.onnx(onnxruntime,CPU)作为参考实现做交叉验证,确认重建网络与官方 导出数值一致。

2. 验证环境

组件版本
操作系统Linux 5.10.0(aarch64)
Python3.11.14
CANN8.5.1
NPU 芯片Ascend 910B(Ascend910_9362,逻辑卡 ×2,单卡 HBM 64 GB)
torch2.9.0+cpu
torch-npu2.9.0.post1+gitee7ba04
numpy1.26.4
transformers4.57.6(系统预置,非本模型运行依赖)
onnx / onnxruntime / onnxscript1.22.0 / 1.29.0 / 0.7.1(交叉验证用)
推理引擎torch_npu
模型路径/data/models/PaulVialard/ppo-Huggy
检查点Huggy/Huggy-2000049.pt(最终 2M 步,与 Huggy.onnx 权重一致)
设备npu:0(验证时可切换 npu:1)

torch / torch-npu / transformers 为昇腾环境系统级预装(本机 /usr/local/python3.11.14),venv 通过 --system-site-packages 复用; 仅 onnx / onnxruntime / onnxscript 需在 venv 内单独安装(交叉验证可选)。

3. 环境依赖

3.1 创建虚拟环境(本目录 venv/)

cd /opt/atomgit/model_adapt/ppo-Huggy-NPU
/usr/local/python3.11.14/bin/python3 -m venv --system-site-packages venv

3.2 安装交叉验证依赖(可选,NPU 推理可不装)

./venv/bin/pip install -r requirements.txt \
  -i https://pypi.tuna.tsinghua.edu.cn/simple
# 备用阿里云源:
# ./venv/bin/pip install -r requirements.txt \
#   -i https://mirrors.aliyun.com/pypi/simple

torch / torch-npu 为昇腾系统预置组件,依赖 CANN 8.5.1 + Ascend 910B, 普通 pip 无法重装,请勿在 venv 中覆盖。完整依赖清单见 requirements.txt。

3.3 校验环境

./venv/bin/python -c "import torch, torch_npu; \
print(torch.__version__, torch_npu.__version__); \
print('npu_available =', torch.npu.is_available())"

预期输出(版本号以本机为准,npu_available 必须为 True):

2.9.0+cpu 2.9.0.post1+gitee7ba04
npu_available = True

4. 分步推理操作流程

4.1 第 1 步:确认 NPU 可用

npu-smi info
# 需能看到 Health=OK 的逻辑卡(本机 Phy-ID 0 / 1)

4.2 第 2 步:进入目录并查看帮助

cd /opt/atomgit/model_adapt/ppo-Huggy-NPU
./venv/bin/python inference.py --help
# 方式二:激活 venv 后直接 python3
source venv/bin/activate
python inference.py --help

4.3 第 3 步:环境 / 模型信息

./venv/bin/python inference.py --mode info

预期输出关键行:

[env] 设备 npu:0 | NPU 可用: True
[load] checkpoint = /data/models/PaulVialard/ppo-Huggy/Huggy/Huggy-2000049.pt
[load] 参数量 = 566,805 | 权重加载耗时 0.03s
[load] dtype = float32 | 结构 = obs(59)->Linear(512)->SiLU x3->Linear(21)->clip(±3)/3
[load] obs 归一化步数 = 2000050
[policy] deterministic_actions[:6] = [-0.009379000402987003, 0.8172829747200012, 1.0, -1.0, 0.03073599934577942, 0.7599239945411682]
SUCCESS

4.4 第 4 步:运行推理

# 确定性动作(给定 59 维观测,默认 npu:0 / float32)
./venv/bin/python inference.py --mode action --obs random --seed 0
./venv/bin/python inference.py --mode action --obs zeros

# 随机采样动作(带探索噪声,同 seed 可复现)
./venv/bin/python inference.py --mode sample --obs random --seed 1

# 批量推理 / 延迟基准 / 精度对照 / ONNX 交叉验证
./venv/bin/python inference.py --mode batch --batch 32
./venv/bin/python inference.py --mode benchmark --runs 200
./venv/bin/python inference.py --mode precision --dtype float32
./venv/bin/python inference.py --mode onnx-compare --batch 64 --seed 7

# 动作序列指纹(验证 NPU 推理确定性)/ 闭环滚动统计
./venv/bin/python inference.py --mode fingerprint --seed 7 --steps 64
./venv/bin/python inference.py --mode stats --rollouts 5 --steps 50

# 导出重建模型为 ONNX(与官方导出逐位一致)
./venv/bin/python inference.py --mode export-onnx

# 指定设备 / 精度(fp16 / bf16 为低精度实验,生产推荐 float32)
./venv/bin/python inference.py --mode action --obs random --device npu:1
./venv/bin/python inference.py --mode sample --obs random --dtype float16

4.5 第 5 步:查看结果

  • 每个模式的关键数值、精度指标、耗时实时打印在终端;
  • --mode export-onnx 将重建模型导出到 assets/huggy_rebuilt.onnx;
  • 全部 50 组测试用例的可复现命令见第 5 节(同目录 run_tests.sh 一键重跑, 日志落盘到 logs/test_cases.log)。

5. 完整测试用例与输出结果

测试输入全部为确定性构造(np.random.RandomState(seed),无随机成分), 输出可直接复现。共 50 组用例,覆盖环境/模型信息、确定性动作、随机采样、 批量推理、精度对照、ONNX 交叉验证、动作序列指纹、闭环滚动统计、延迟基准与 ONNX 导出,全部以 SUCCESS 通过。一键重跑:

bash run_tests.sh   # 输出写入 logs/test_cases.log

5.1 环境与模型信息(用例 1–2)

用例命令结果
1inference.py --mode infoSUCCESS ✅
2inference.py --mode checkpoints201 个检查点,默认用最终 Huggy-2000049.pt ✅

用例 1 输出:

[env] torch 2.9.0+cpu | torch_npu 2.9.0.post1+gitee7ba04 | numpy 1.26.4
[env] onnxruntime 1.29.0(CPU 参考引擎)
[env] onnx 1.22.0
[env] 设备 npu:0 | NPU 可用: True
[npu-smi] | 5     Ascend910           | OK            | 175.8       45                0    / 0
[load] checkpoint = /data/models/PaulVialard/ppo-Huggy/Huggy/Huggy-2000049.pt
[load] 参数量 = 566,805 | 权重加载耗时 0.03s
[load] dtype = float32 | 结构 = obs(59)->Linear(512)->SiLU x3->Linear(21)->clip(±3)/3
[load] obs 归一化步数 = 2000050
[load] log_sigma[:6] = [-0.0286, -0.0217, 0.0028, -0.0025, -0.0036, -0.1371]
[policy] obs(random,seed=0)[:6] = [1.7641, 0.4002, 0.9787, 2.2409, 1.8676, -0.9773]
[policy] deterministic_actions[:6] = [-0.009379000402987003, 0.8172829747200012, 1.0, -1.0, 0.03073599934577942, 0.7599239945411682]
SUCCESS

用例 2 输出(节选,共 201 个 .pt 检查点):

[files] 共发现 201 个 PyTorch 检查点:
  - Huggy-1009955.pt
  - Huggy-1019938.pt
  ...
  - Huggy-2000049.pt    ← 最终 2M 步检查点(默认加载)
  - checkpoint.pt
[files] 顶层 ONNX 导出: Huggy.onnx(与最终检查点权重一致)
[load] 默认使用最终检查点: Huggy-2000049.pt
SUCCESS

5.2 确定性动作推理(用例 3–17)

用例命令关键输出(action[:8])范围L2
3--mode action --obs random --seed 0[-0.0094, 0.8173, 1.0, -1.0, 0.0307, 0.7599, -0.6733, -1.0][-1,1] ✅2.9219
4--mode action --obs random --seed 1[0.1819, 0.7452, 1.0, 1.0, -0.7113, 0.1507, -0.9836, -1.0][-1,1] ✅3.2625
5--mode action --obs random --seed 2[-0.4098, 0.7568, 0.8626, -0.6022, -0.5743, 0.7448, -0.1221, -0.6314][-0.73,1] ✅2.6987
6--mode action --obs zeros[-0.2696, -0.0536, 0.0999, 0.4499, -0.1415, 0.2865, 0.0706, 0.0116][-0.42,1] ✅1.4680
7--mode action --obs ones[0.6783, -0.9792, 0.3976, 0.2190, -0.2624, 0.2594, -1.0, -0.8258][-1,1] ✅2.6679
8--mode action --obs ones_neg[-0.0881, -0.3326, 1.0, 1.0, -0.9107, 0.5000, 0.6505, -0.2832][-0.91,1] ✅3.0028
9--mode action --obs boundary[1.0, -0.1537, 1.0, 0.0501, -0.3112, 1.0, -1.0, -0.7206][-1,1] ✅2.9931
10--mode action --obs 0.5[0.0277, -0.5232, 0.0765, 0.0443, -0.2493, 0.5120, -1.0, -0.4866][-1,1] ✅2.0863
11--mode action --obs -0.5[-0.1398, -0.2284, 0.9097, 1.0, -0.4845, 0.3462, 0.5725, -0.4314][-0.81,1] ✅2.9354
12--mode action --obs 1[0.6783, -0.9792, 0.3976, 0.2190, -0.2624, 0.2594, -1.0, -0.8258][-1,1] ✅2.6679
13--mode action --obs -1[-0.0881, -0.3326, 1.0, 1.0, -0.9107, 0.5000, 0.6505, -0.2832][-0.91,1] ✅3.0028
14--mode action --obs <59 维自定义> --seed 5[0.5489, 0.8179, 1.0, -1.0, -0.0828, 0.3761, -0.4723, -1.0][-1,1] ✅2.4379
15--mode action --obs random --seed 0 --dtype float16[-0.0094, 0.8169, 1.0, -1.0, 0.0306, 0.7598, -0.6733, -1.0][-1,1] ✅2.9221
16--mode action --obs random --seed 0 --dtype bfloat16[-0.0115, 0.8164, 1.0, -1.0, 0.0297, 0.7617, -0.6719, -1.0][-1,1] ✅2.9224
17--mode action --obs 2[1.0, -1.0, 0.7766, 0.9828, -0.9027, -0.0081, -1.0, -0.9296][-1,1] ✅3.3291

用例 3 完整输出(其余同结构):

===== 确定性动作推理 (obs=random, device=npu:0, dtype=float32) =====
  obs[:8]        = [1.7640520334243774, 0.40015700459480286, 0.978738009929657, 2.2408928871154785, 1.8675580024719238, -0.9772779941558838, 0.9500880241394043, -0.1513569951057434]
  action[:8]     = [-0.009379000402987003, 0.8172829747200012, 1.0, -1.0, 0.03073599934577942, 0.7599239945411682, -0.673259973526001, -1.0]
  action 范围    = [-1.0000, 1.0000](应在 [-1,1])
  action L2 范数 = 2.9219
SUCCESS

5.3 随机采样动作推理(用例 18–23)

用例命令deterministic[:6]sampled#1[:6]同 seed 可复现
18--mode sample --obs random --seed 0[-0.0094, 0.8173, 1.0, -1.0, 0.0307, 0.7599][-0.3741, 0.4414, 0.9196, -1.0, 0.3126, 0.3080]是 ✅
19--mode sample --obs random --seed 1[0.1819, 0.7452, 1.0, 1.0, -0.7113, 0.1507][-0.3123, 0.5005, 0.8493, 0.9297, -0.7446, 0.2217]是 ✅
20--mode sample --obs zeros[-0.2696, -0.0536, 0.0999, 0.4499, -0.1415, 0.2865][-0.6342, -0.4295, 0.0162, 0.3056, 0.1404, -0.1654]是 ✅
21--mode sample --obs ones --seed 2[0.6783, -0.9792, 0.3976, 0.2190, -0.2624, 0.2594][0.3411, -0.6803, -0.0384, -0.1499, -0.6671, 0.2449]是 ✅
22--mode sample --obs random --seed 3 --dtype float16[0.2810, 1.0, 1.0, -0.7134, -0.5537, 1.0][0.2561, 1.0, 1.0, -0.6898, -0.3329, 1.0]是 ✅
23--mode sample --obs random --seed 3 --dtype bfloat16[0.2832, 1.0, 1.0, -0.7148, -0.5547, 1.0][0.2577, 1.0, 1.0, -0.6898, -0.3339, 1.0]是 ✅

用例 18 完整输出:

===== 随机采样动作推理 (obs=random, seed=0, device=npu:0, dtype=float32) =====
  obs[:8]        = [1.7640520334243774, 0.40015700459480286, 0.978738009929657, 2.2408928871154785, 1.8675580024719238, -0.9772779941558838, 0.9500880241394043, -0.1513569951057434]
  deterministic  = [-0.009379000402987003, 0.8172829747200012, 1.0, -1.0, 0.03073599934577942, 0.7599239945411682, -0.673259973526001, -1.0]
  sampled#1      = [-0.3740620017051697, 0.4414159953594208, 0.9195690155029297, -1.0, 0.3126179873943329, 0.3079589903354645, -0.7862300276756287, -0.6573699712753296]
  sampled#2      = [-0.3740620017051697, 0.4414159953594208, 0.9195690155029297, -1.0, 0.3126179873943329, 0.3079589903354645, -0.7862300276756287, -0.6573699712753296]
  两次采样一致性 = 相同(同seed)
  sigma[:6]      = [0.9717640280723572, 0.9785130023956299, 1.0028480291366577, 0.9974939823150635, 0.9963899850845337, 0.8719059824943542]
SUCCESS

说明:sample 模式在 CPU 上按 seed 生成高斯噪声再搬运到 NPU(NPU 无随机数 生成器),因此同 seed 两次采样逐位一致;不同 seed 或不同 obs 则产生不同的 探索动作。sigma = exp(log_sigma),本模型训练收敛后 log_sigma≈0(σ≈1)。

5.4 批量推理(用例 24–28)

用例命令batch单次批量前向耗时输出范围
24--mode batch --batch 1616143.07 ms[-1,1] ✅
25--mode batch --batch 3232143.06 ms[-1,1] ✅
26--mode batch --batch 6464139.40 ms[-1,1] ✅
27--mode batch --batch 128128135.45 ms[-1,1] ✅
28--mode batch --batch 512512142.76 ms[-1,1] ✅

用例 25 完整输出:

===== 批量推理 (batch=32, device=npu:0, dtype=float32) =====
  单次批量前向耗时 = 143.0582 ms (batch=32)
  deterministic[:, :4] =
    batch[0] [-0.009379000402987003, 0.8172829747200012, 1.0, -1.0, 0.03073599934577942, 0.7599239945411682]
    batch[1] [1.0, 1.0, 1.0, -1.0, -0.7775319814682007, 1.0]
    batch[2] [0.10846299678087234, 0.1815209984779358, 0.211776003241539, -0.41998401284217834, -0.48460298776626587, 0.37777701020240784]
    batch[3] [0.7471129894256592, 0.39087799191474915, 0.8367679715156555, 0.18831799924373627, 0.0004560000088531524, 0.3632200062274933]
    batch[4] [-0.5962759852409363, -0.5917180180549622, 0.2943800091743469, 0.967270016670227, -0.5609040260314941, 0.07912400364875793]
  mu 最大幅值 = 11.3423
  输出范围 = [-1.0000, 1.0000]
SUCCESS

说明:batch 模式的耗时包含进程冷启动 + 首次前向算子编译 / 图捕获开销 (约 140 ms 为单次前向计时,未预热);稳态单步延迟见 5.9 节 benchmark (--mode benchmark 预热后 avg ≈ 0.37 ms)。相同 seed 下批量输出与单样本一致, 验证批量维度无关性。

5.5 精度对照:NPU vs CPU fp32 参考(用例 29–31)

用例命令余弦相似度最大绝对误差判定
29--mode precision --dtype float321.000000001.132e-06通过 ✅
30--mode precision --dtype float161.000000001.113e-03通过 ✅
31--mode precision --dtype bfloat160.999994581.070e-02未达标 ❌

用例 29 完整输出:

===== 精度对照 NPU(npu:0, float32) vs CPU(float32) 参考 =====
  样本数 = 100(每个样本 59 维,正态采样观测)
  deterministic-actions 余弦相似度 = 1.00000000
  最大绝对误差 = 1.132e-06
  最大相对误差 = 2.048e-04
  bit 级一致率 = 21.6667%
  是否满足要求 (cos>=0.999 且 max_abs<0.01): 是 ✅
SUCCESS

用例 30 / 31 输出(关键行):

# 30: float16
  余弦相似度 = 1.00000000 | 最大绝对误差 = 1.113e-03 | 是否满足要求: 是 ✅
# 31: bfloat16
  余弦相似度 = 0.99999458 | 最大绝对误差 = 1.070e-02 | 是否满足要求: 否 ❌

结论:float32 下 NPU 与 CPU fp32 参考最大绝对误差仅 1.1e-6(余弦相似度 1.0),与官方 ONNX 推理一致,昇腾 NPU 数值正确 ✅。float16 精度可接受 (误差 < 动作范围 1%);bfloat16 在本机 910B 上相对误差略超阈值,故生产 部署推荐 --dtype float32。另外在 npu:1 上复测 float32 结果与 npu:0 逐位 一致(用例 49),验证多卡数值一致性。

5.6 ONNX 交叉验证:torch 重建 vs 官方 ONNX(用例 32–34)

用例命令batch/seed最大绝对误差余弦相似度判定
32--mode onnx-compare --batch 64 --seed 764/76.557e-071.00000000通过 ✅
33--mode onnx-compare --batch 32 --seed 032/07.749e-071.00000000通过 ✅
34--mode onnx-compare --batch 128 --seed 42128/427.153e-071.00000000通过 ✅

用例 32 完整输出:

===== ONNX 交叉验证 (batch=64, seed=7) =====
  onnx  deterministic[0][:6] = [0.6132569909095764, 0.0065379999577999115, 0.7251830101013184, 1.0, 0.028953999280929565, -0.26625001430511475]
  torch deterministic[0][:6] = [0.6132569909095764, 0.0065379999577999115, 0.7251840233802795, 1.0, 0.028954999521374702, -0.26625001430511475]
  max_abs_err = 6.557e-07 | mean_abs_err = 1.238e-07 | cos = 1.00000000
  是否满足要求 (max_abs<1e-4 且 cos>=0.999999): 是 ✅
SUCCESS

本验证确认:脚本按官方 Huggy.onnx 计算图重建的 torch 网络,与 onnxruntime 参考输出最大偏差 < 8e-7(float32 舍入级别),模型结构与数值完全正确。

5.7 动作序列指纹:NPU 推理确定性(用例 35–37)

用例命令首步 action[:6]两次运行一致sha256
35--mode fingerprint --seed 7 --steps 64[0.3476, 0.1356, 1.0, 1.0, -0.0265, -0.0390]是 ✅9ac2e6…ccb13
36--mode fingerprint --seed 42 --steps 32 --deterministic[0.7430, 1.0, 0.7363, -0.8938, -1.0, 0.9869]是 ✅d72ce8…ff9b8
37--mode fingerprint --seed 0 --steps 128[-0.3741, 0.4414, 0.9196, -1.0, 0.3126, 0.3080]是 ✅e30cb3…a6755

用例 35 完整输出:

===== 动作序列指纹 (seed=7, steps=64, device=npu:0, dtype=float32, deterministic=False) =====
  run1 首步 action[:6] = [0.34759798645973206, 0.13558200001716614, 1.0, 1.0, -0.026510000228881836, -0.03895200043916702]
  run2 首步 action[:6] = [0.34759798645973206, 0.13558200001716614, 1.0, 1.0, -0.026510000228881836, -0.03895200043916702]
  两次运行完全一致     = 是 ✅
  sha256(动作序列)     = 9ac2e685eac2ec64913cd2d7bb34e1a3ea94259d963c0ee442dc80a62acccb13
SUCCESS

5.8 策略闭环滚动统计(用例 38–40)

无 Unity 可执行环境时,脚本在 59 维观测空间上做确定性有阻尼随机游走(动作 padding 到观测空间反馈演化),演示策略前向在连续状态上的稳定性与输出范围。

用例命令关键指标
38--mode stats --rollouts 5 --steps 505 局全 in[-1,1] ✅,平均单步 1.05 ms
39--mode stats --rollouts 8 --steps 1008 局全 in[-1,1] ✅,平均单步 0.69 ms
40--mode stats --rollouts 3 --steps 30 --deterministic3 局全 in[-1,1] ✅,平均单步 1.91 ms

用例 38 完整输出:

===== 策略闭环滚动统计 (rollouts=5, steps=50, seed 起始=0, device=npu:0, dtype=float32) =====
  rollout[00] steps=  50 | mean|a|=0.5235 | max|a|=1.0000 | in[-1,1]=True | avg=3.2568ms
  rollout[01] steps=  50 | mean|a|=0.4754 | max|a|=1.0000 | in[-1,1]=True | avg=0.5041ms
  rollout[02] steps=  50 | mean|a|=0.5425 | max|a|=1.0000 | in[-1,1]=True | avg=0.4998ms
  rollout[03] steps=  50 | mean|a|=0.6721 | max|a|=1.0000 | in[-1,1]=True | avg=0.5008ms
  rollout[04] steps=  50 | mean|a|=0.5243 | max|a|=1.0000 | in[-1,1]=True | avg=0.5028ms
  → 全部 rollouts 动作均落在 [-1,1]: 是 ✅
  → 平均单步推理耗时: 1.0528 ms
SUCCESS

5.9 延迟基准(用例 41–44)

用例命令runsavgp50p95p99max
41--mode benchmark --runs 200 --seed 02000.3698 ms0.36750.39530.40030.4145
42--mode benchmark --runs 100 --seed 0 --dtype float161000.4359 ms0.42880.45620.48740.8678
43--mode benchmark --runs 300 --seed 0 --dtype bfloat163000.4320 ms0.42490.45610.72150.7914
44--mode benchmark --runs 100 --seed 1 --device npu:11000.3649 ms0.36330.38750.40100.4043

用例 41 完整输出:

===== 延迟基准 (runs=200, warmup=20, device=npu:0, dtype=float32) =====
  avg = 0.3698 ms | p50 = 0.3675 ms | p95 = 0.3953 ms | p99 = 0.4003 ms | max = 0.4145 ms
SUCCESS

5.10 ONNX 导出(用例 45)+ 双卡 / 扩展用例(46–50)

用例命令结果
45--mode export-onnx导出 assets/huggy_rebuilt.onnx(与官方逐位一致)✅
46--mode sample --obs -0.5 --seed 4deterministic=[-0.1398, -0.2284, …],sampled 可复现 ✅
47--mode sample --obs boundary --seed 5deterministic=[1.0, -0.1537, …],sampled 可复现 ✅
48--mode batch --batch 512单次 142.76 ms,输出范围 [-1,1] ✅
49--mode precision --dtype float32 --device npu:1cos=1.0,max_abs=1.132e-06(与 npu:0 逐位一致)✅
50--mode action --obs random --seed 9 --device npu:1action=[0.0997, 0.2071, 1.0, …],范围 [-0.74,1] ✅

用例 45 输出:

===== 导出重建模型为 ONNX =====
  已导出: /opt/atomgit/model_adapt/ppo-Huggy-NPU/assets/huggy_rebuilt.onnx
SUCCESS

重建 ONNX 与官方 Huggy.onnx 输入输出对齐(obs_0: [batch, 59] → deterministic_continuous_actions: [batch, 21]),同一输入下两模型输出 最大偏差 = 0.0(逐位一致)。

50 组用例汇总:

分组用例编号数量通过
A 环境/模型信息1–222 ✅
B 确定性动作3–171515 ✅
C 随机采样动作18–23, 46–4788 ✅
D 批量推理24–28, 4866 ✅
E 精度对照29–31, 4943 ✅ + 1 未达标(bf16,见 5.5)
F ONNX 交叉验证32–3433 ✅
G 动作序列指纹35–3733 ✅
H 闭环滚动统计38–4033 ✅
I 延迟基准41–4444 ✅
J ONNX 导出 + 双卡45, 5022 ✅
合计1–505049 ✅ / 1 未达标(bf16 非推荐精度)

6. 性能参考

测试条件:单卡 Ascend 910B,dtype=float32,单样本前向(含一次预热后计时)。

指标数值
单步推理平均延迟(npu:0,runs=200)0.3698 ms
单步推理 p95 延迟0.3953 ms
单步推理 p99 延迟0.4003 ms
批量 512 单次前向142.76 ms(含首次算子编译)
权重加载耗时≈ 0.03 s
参数量 / 权重体积566,805 / ≈ 2.3 MB(fp32)
峰值显存< 100 MB(模型极小)

本模型为单样本 59×512×512×512×21 的轻量 MLP,稳态单步延迟约 0.37 ms; 吞吐受限场景可配合批量推理(单次批量前向开销基本与 batch 无关,详见 5.4)。 耗时随系统负载小幅波动1。

7. 注意事项与常见问题

  1. 引擎选型:Huggy 是强化学习策略网络(MLP),不是 LLM/VLM,无法用 vllm-ascend / sglang 的 token 生成链路加载;请使用 torch_npu 直接 forward (见第 1 节引擎选型说明)。若在真实 Huggy 环境中闭环,需配合 Unity 训练环境可执行文件(env_path),本适配聚焦策略网络的 NPU 推理与数值验证。
  2. float32 为生产推荐精度:fp32 与 CPU/ONNX 参考最大误差 1e-6(余弦相似度 1.0);fp16 误差 ~1e-3 可接受;bf16 在 910B 上相对误差略超阈值(见 5.5), 不建议生产使用。
  3. 归一化统计量必须保持 float32:ML-Agents 的 running_variance 是累计和 (可达 1e5)、normalization_steps 为 2e6,强转 float16 会溢出为 inf → 归一化 NaN。脚本已在 HuggyAgent 内强制将缓冲统计量保留 float32(详见 inference.py 中 normalize_obs 注释)。
  4. NPU 随机数:NPU 无随机数生成器,sample 模式在 CPU 按 seed 生成高斯噪声 再搬运,保证同 seed 可复现;deterministic 模式完全无随机成分。
  5. 首次前向延迟较高:batch 模式单次前向约 140 ms 包含进程冷启动 + 算子编译 / 图捕获;benchmark 模式内置预热(warmup = runs/10),稳态约 0.37 ms。
  6. 双卡一致性:单机 2 张逻辑卡(Phy-ID 0/1),--device npu:1 可切换;fp32 下两卡输出逐位一致(用例 49)。
  7. torch_npu 日志噪音:脚本已通过 warnings.filterwarnings 屏蔽 Ascend 目录 属主告警,并将 CANN 日志级别设为 ERROR(ASCEND_GLOBAL_LOG_LEVEL=3)。首次 forward 时 stderr 可能仍会打印 1~2 行算子捕获的源码行与 path string is NULL, 均为无害环境提示,不影响 stdout 结果与推理正确性。

8. 参考链接

  • 权重(HuggingFace):https://huggingface.co/PaulVialard/ppo-Huggy
  • Deep RL 课程(Huggy 教程):https://huggingface.co/learn/deep-rl-course/unitbonus1/introduction
  • Unity ML-Agents:https://github.com/Unity-Technologies/ml-agents
  • vllm-ascend 文档

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

Footnotes

  1. 单步延迟数值为实测结果,会随系统负载小幅波动(本机实测范围约 0.36~0.46 ms); 动作/数值输出为确定性结果,可精确复现(见 logs/test_cases.log)。 ↩