昇腾 NPU 上手:ResNet-50 从 ONNX 到 OM 的完整踩坑记录

昇腾 NPU 上手:ResNet-50 从 ONNX 到 OM 的完整踩坑记录

环境:Ascend 910B2C + CANN 8.5.1

目标:把 resnet_50.onnx 转成 resnet_50.om,在 NPU 上跑起来,并和转换前的 ONNX 结果做对比。

最近在昇腾机器上跑了一次经典的 ResNet-50 转换流程。看起来只是「ONNX → OM → 推理」三步,实际踩了不少坑:Python 依赖、opset 版本、精度对比方式都不简单。本文把完整过程记下来,方便以后自己复用,也希望帮到同样入门的同学。


1. 最终产物

文件 说明
resnet_50.onnx ONNX Model Zoo 的 ResNet-50 v2(约 98MB)
resnet_50.om ATC 转换后的昇腾离线模型(约 50MB)
run_resnet50_om.py ACL Python 推理脚本

模型 I/O:

  • 输入 :data,float32,[1, 3, 224, 224](NCHW)
  • 输出 :[1, 1000](ImageNet 1000 类 logits)

2. 环境确认

先确认 NPU 和 CANN 是否正常:

bash 复制代码
npu-smi info
which atc
source /usr/local/Ascend/ascend-toolkit/set_env.sh

本次机器信息:

  • NPU:910B2C
  • CANN:8.5.1
  • 转换时 soc_version:Ascend910B2C

3. 下载 ONNX 模型

从 ONNX Model Zoo 下载 ResNet-50 v2:

bash 复制代码
cd /home/compiler_projects

curl -L -o resnet_50.onnx \
  "https://media.githubusercontent.com/media/onnx/models/main/validated/vision/classification/resnet/model/resnet50-v2-7.onnx"

这个模型有两个小特点,后面转换会碰到:

  1. opset = 7(比较老)
  2. 权重被写在 graph.input 里 (老式 ONNX 写法),真正的网络输入只有 data

4. ONNX → OM(ATC)

4.1 标准命令

bash 复制代码
source /usr/local/Ascend/ascend-toolkit/set_env.sh

atc --model=resnet_50.onnx \
    --framework=5 \
    --output=resnet_50 \
    --soc_version=Ascend910B2C \
    --input_format=NCHW \
    --input_shape="data:1,3,224,224" \
    --log=error

参数含义:

  • --framework=5:ONNX
  • --soc_version:必须和芯片匹配
  • --input_shape:静态 shape;这里把动态 batch 固定成 1

成功后会生成 resnet_50.om。

4.2 坑 1:No module named 'numpy'

第一次跑 ATC 直接失败:

text 复制代码
Environment_Error_Import_Python_Module_Failed(EC0010):
ModuleNotFoundError: No module named 'numpy'

原因 :ATC 编译算子时会走 TBE(Python),它用的是 /usr/bin/python3,不是你日常装包的那个 Python。

即使 /usr/local/python3.11.xxx 里有 numpy,ATC 也看不到。

解决:给 ATC 实际使用的 Python 装依赖:

bash 复制代码
/usr/bin/python3 -m ensurepip --upgrade
/usr/bin/python3 -m pip install 'numpy<2' decorator attrs cloudpickle \
    ml-dtypes psutil scipy tornado sympy

装完 numpy 后,往往还会继续缺 decorator、scipy 等,建议一次性装齐。

4.3 坑 2:所有算子都报 No operator plugin

依赖装好后,又出现:

text 复制代码
Not_Supported_Operator(E13010):
No operator plugin is registered for Op: xxx, optype: ai.onnx::7::Conv

连 Conv、Relu 都报不支持,看起来很吓人。查日志后发现:

  • ONNX 插件其实加载成功了
  • 但 CANN 8.5.1 注册的是 ai.onnx::8 及以上
  • 当前模型是 ai.onnx::7

所以不是「昇腾不支持卷积」,而是 opset 太旧,对不上注册表。

4.4 处理方式:清洗模型 + 提升 opset

先把权重从 graph.input 里摘掉,只保留真正输入 data,并把 batch 固定为 1;再把 opset 提升到 11:

python 复制代码
import onnx

m = onnx.load("resnet_50.onnx")

# 1) 权重 initializer 不再作为 graph input
name_to_input = {i.name: i for i in m.graph.input}
for init in m.graph.initializer:
    if init.name in name_to_input:
        m.graph.input.remove(name_to_input[init.name])

# 2) 动态 batch -> 1
for i in m.graph.input:
    if i.name == "data":
        for d in i.type.tensor_type.shape.dim:
            d.ClearField("dim_param")
            if d.dim_value == 0:
                d.dim_value = 1

# 3) 提升 opset(对本模型直接改 version 即可)
for o in m.opset_import:
    if o.domain in ("", "ai.onnx"):
        o.version = 11

onnx.save(m, "resnet_50_op11.onnx")

然后再转:

bash 复制代码
atc --model=resnet_50_op11.onnx \
    --framework=5 \
    --output=resnet_50 \
    --soc_version=Ascend910B2C \
    --input_format=NCHW \
    --input_shape="data:1,3,224,224" \
    --log=error

这次输出:

text 复制代码
ATC run success, welcome to the next use.

5. 怎么跑 resnet_50.om

昇腾侧用 ACL(Ascend Computing Language)加载 OM。核心流程:

  1. acl.init() / set_device
  2. acl.mdl.load_from_file("resnet_50.om")
  3. Host → Device 拷贝输入
  4. acl.mdl.execute(...)
  5. Device → Host 取输出
  6. 释放资源

仓库里放了一个可直接跑的脚本 run_resnet50_om.py:

bash 复制代码
source /usr/local/Ascend/ascend-toolkit/set_env.sh
cd /home/compiler_projects

# 随机输入冒烟
python3 run_resnet50_om.py

# 真实图片(需 Pillow,会做 ImageNet 预处理)
python3 run_resnet50_om.py --image cat.jpg

冒烟结果示例:

text 复制代码
model=/home/compiler_projects/resnet_50.om
input dims=[1, 3, 224, 224], bytes=602112
output dims=[1, 1000], bytes=4000
top-5:
  #1: class=490, score=...
  ...

6. 和转换前的 ONNX 对比

转换完最关心的还是:结果有没有漂?

方法很简单:同一份输入,分别用

  • onnxruntime(CPU)跑 ONNX
  • ACL 跑 OM

然后比:

  • max_abs_diff / mean_abs_diff
  • 相对 L2
  • cosine 相似度
  • top1 / top5

对比结果(固定随机种子 seed=0)

指标 ONNX vs OM
cosine 相似度 0.999998
相对 L2 0.00175
max abs diff ≈ 0.0097
mean abs diff ≈ 0.0024
top1 一致
top5 5/5 完全一致

top5 类别完全相同:[490, 904, 556, 794, 599],分数也非常接近。

这类微小数值差通常来自昇腾图编译后的 FP16 混合精度计算,对分类任务基本无感。只要 topk 稳定、cosine 接近 1,就可以认为转换是成功的。


7. 一张流程图总结

text 复制代码
resnet50-v2-7.onnx (opset7)
        │
        ▼
 清洗 input / 固定 shape / 提升 opset→11
        │
        ▼
      ATC 转换
   (soc=Ascend910B2C)
        │
        ▼
   resnet_50.om
        │
        ├──────────────► ACL 推理(NPU)
        │
onnxruntime (CPU) ──────► 同输入对比精度

8. 经验清单

  1. 先 source set_env.sh ,否则 atc / ACL 环境变量容易缺。
  2. ATC 用的 Python ≠ 你终端默认 Python ,缺包要装到 /usr/bin/python3 对应环境。
  3. 老 ONNX(opset 7)在新 CANN 上可能全员报不支持,先升到 11+。
  4. 转换前最好确认:
    • 真正输入名(这里是 data)
    • 静态 shape
    • 权重不要混在 graph.input 里
  5. 精度验收别只看单个 logit,cosine + topk 更有参考价值。

9. 参考命令速查

bash 复制代码
# 环境
source /usr/local/Ascend/ascend-toolkit/set_env.sh

# 转换
atc --model=resnet_50_op11.onnx \
    --framework=5 \
    --output=resnet_50 \
    --soc_version=Ascend910B2C \
    --input_format=NCHW \
    --input_shape="data:1,3,224,224" \
    --log=error

# 推理
python3 run_resnet50_om.py
python3 run_resnet50_om.py --image your.jpg

结语

这次把 ResNet-50 在昇腾上跑通后,最大的感受是:

ATC 本身不难,难的是环境对齐和模型版本兼容。

只要把 Python 依赖、soc_version、ONNX opset 这三件事处理好,经典 CNN 转 OM 还是很顺的;再补一个同输入精度对比,心里就踏实了。

如果你也在做昇腾模型迁移,欢迎把你的 soc_version、CANN 版本和报错贴出来一起对一下。

相关推荐
隔窗听雨眠2 小时前
开源为骨自研为魂:存储统一管理平台的混合架构实践
性能优化·实战教程·原理解析·踩坑记录
科威网络10 小时前
企业网站技术方案:响应式、SEO 结构与性能优化实践
性能优化·需求分析·软件需求
爱喝水的鱼丶11 小时前
SAP-ABAP:ECC→S/4HANA ABAP 迁移升级指南——程序改造、语法兼容与性能优化 导语
运维·开发语言·性能优化·sap·abap·经验交流·开发交流
传奇开心果编程16 小时前
【Compose Multiplatform跨端声明式UI实用技术】第17课:性能优化与基准测试
性能优化·移动开发·跨平台·编程语言理论·开发范式·编程框架·声明式ui
科威网络18 小时前
APP 技术选型:原生、React Native、Flutter 怎么选?
性能优化·软件需求·web app
硅基手札20 小时前
SPI技术系列 12 · SPI 设备驱动实战
linux·驱动开发·单片机·嵌入式硬件·mcu·性能优化·系统架构
ZhengEnCi20 小时前
多 Agent 系统如何复用 KV 缓存-前缀对齐比缓存开关更重要
人工智能·性能优化·llm
利威尔xu1 天前
Go GMP 调度器 10 个高频追问:3 个线上故障让 P99 从 80ms 飙到 2s、10 万 goroutine 泄漏
开发语言·后端·面试·性能优化·golang
Sayai1 天前
Elasticsearch Rollup 汇算索引实战:监控指标多粒度预聚合查询,附 painless 时间窗取每日最新值
大数据·运维·elasticsearch·搜索引擎·性能优化·监控
yunwei371 天前
eBPF 实例教程:跟踪 Intel NPU 内核驱动操作
linux·后端·性能优化