昇腾 ATC 模型转换入门指南:把训练模型变成 NPU 能跑的 OM
一句话让Agent变成昇腾专家,昇腾任务轻松搞定。评测入口:请按这个开源仓接入昇腾图谱 https://gitcode.com/agent0/kg-tools
你花几天训练出一个深度学习模型,满怀期待地拷到昇腾开发板上,然后发现:板子根本不认识这个文件。
这不是板子的问题。训练框架产出的模型(ONNX、Caffe、TensorFlow 等)是为通用计算世界准备的,而昇腾 NPU 有一套自己的"方言"——数据要按它喜欢的格式排布、算子要用它能执行的形态、内存要提前规划好。中间必须有人做一次"翻译 + 备考",这个角色就是ATC。
这篇入门指南把 ATC 的使用场景、命令用法、内部流程、关键原理、常用参数和避坑事项一次讲清。所有关键事实都经昇腾知识图谱官方文档核实(文末附来源清单)。
一、ATC 是什么:模型的"编译器"
ATC(Ascend Tensor Compiler,昇腾张量编译器)是昇腾 CANN 异构计算架构下的模型转换工具。官方定义一句话:
ATC 可以将开源框架的网络模型以及 Ascend IR 定义的单算子描述文件(JSON 格式),转换为 AI 处理器支持的
.om格式离线模型。
理解它最快的方式是类比 gcc:C 源码要经过 gcc 编译成可执行文件才能跑;同样,训练产出的模型文件要经过 ATC 编译成.om(Offline Model,离线模型)才能在昇腾设备上高效推理。
.c 源文件 --gcc--> 可执行文件 .onnx/.pb/.air 模型 --ATC--> .om 离线模型而且 ATC 不只是"翻译"。转换过程中它会做算子调度优化、权重数据重排、内存使用优化——相当于翻译的同时还替你把考试重点复习了一遍。这也是它叫"编译器"而不是"格式转换器"的原因。
支持哪些输入
--framework参数告诉 ATC 输入模型来自哪个框架,注意填的是数字枚举,不是字符串:
| –framework 取值 | 对应格式 | 说明 |
|---|---|---|
| 0 | Caffe | 双文件(结构 + 权重)输入,命令写法请查所用版本的官方 ATC 文档 |
| 1 | MindSpore | .air格式模型 |
| 3 | TensorFlow | .pb等 |
| 5 | ONNX | 最常用的路径,官方文档口径支持 ONNX 1.12.0(以所用 CANN 版本文档为准) |
二、它能干什么:典型使用场景
场景一:训练到部署的主线。这是 ATC 最经典的用法:在训练机(比如 910B)上训练并导出 ONNX,用 ATC 转成 OM,拷到推理设备(比如香橙派等 310 系列板子)上,通过推理接口加载执行。CANN 官方教程里就有这样一条端到端实验路径。
场景二:没有 NPU 的机器上也能转。官方文档明确:模型转换阶段不依赖 AI 处理器。你可以在一台普通 x86 开发机上完成转换,把 OM 拷到目标设备运行。两个约束要记住:
--soc_version必须填转换后实际运行的芯片型号,转给谁就要填谁;- 只要
--soc_version相同,不同产品只需转一次,同一份 OM 可以分别部署。
场景三:单算子编译。ATC 不只能转整网,还能把一个描述算子的 JSON 文件编译成单算子 OM(--singleop),算子开发调试时会用到。
什么时候不需要 ATC?也有不经过 ATC 的推理路径:在线推理可以用 GE(图引擎)接口构图后由 GeSession 直接编译执行;PyTorch 走图模式时由 TorchAir 组件对接 GE 编译。但对入门部署来说,"ATC 离线转 OM → 推理接口加载执行"是最主线、最通用的路径。
三、上手三步走:环境、芯片型号、第一条命令
第 1 步:装好 CANN,让 atc 命令可用
CANN 装好后,先 source 环境变量(以实际安装路径为准,不同版本路径略有差异):
# 常见路径(FAQ 口径):source/usr/local/Ascend/ascend-toolkit/set_env.sh# 新版 GE 文档口径为 /usr/local/Ascend/cann/set_env.sh# 带版本号目录的写法如 /usr/local/Ascend/cann-{version}/set_env.shecho$ASCEND_HOME_PATH# 有输出说明环境变量已生效两个易踩的环境点:
- CANN 8.5.0 及之后版本,转换时必须安装与目标 AI 处理器匹配的 ops 算子包,否则编译失败(文档两处一致强调);
- 在非昇腾设备上装 Toolkit 做转换,还需把
devlib目录加进LD_LIBRARY_PATH(详见官方环境准备文档)。
atc命令本体位于 CANN 安装目录的bin下(如/usr/local/Ascend/cann/bin),source 之后直接敲atc --help能出帮助就说明装好了。
第 2 步:查芯片型号,确定 --soc_version
npu-smi info看回显里的Name字段,--soc_version的值就是Ascend+ Name。例如 Name 显示 910B1,参数就填--soc_version=Ascend910B1;常见值还有Ascend310P3、Ascend310B1等。
少数产品(如 950PR/950DT、A3 系列)要用npu-smi info -t board取 Chip Name 与 NPU Name 组合填写,具体以官方--soc_version文档为准。
第 3 步:跑第一条转换命令
最简 ONNX 示例(官方文档原文示例的整理版):
atc--model=$HOME/module/resnet50.onnx\--framework=5\--output=$HOME/module/out/resnet50\--soc_version=Ascend910B1| 参数 | 含义 |
|---|---|
--model | 原始模型文件路径与文件名 |
--framework=5 | 5 = ONNX(见第一节取值表) |
--output | 输出路径与文件名(不带.om后缀,ATC 自动补上) |
--soc_version | 目标芯片型号,必填 |
MindSpore 的.air模型同理,只需把 framework 换成 1:
atc--model=$HOME/module/ResNet50.air\--framework=1\--output=$HOME/module/out/ResNet50_air\--soc_version=Ascend910B1一个更接近实战的例子
真实部署往往还要固定输入尺寸、开启日志、甚至把预处理塞进模型。下面这条 YOLOv7 命令来自官方样例,一次展示了这些常用参数:
atc--framework=5\--model=yolov7.onnx\--output=yolov7_bs1\--input_format=NCHW\--input_shape="images:1,3,640,640"\--log=error\--insert_op_conf=aipp.cfg\--soc_version=Ascend310P3--input_format:输入数据格式。Caffe/MindSpore/ONNX 默认 NCHW,TensorFlow 默认 NHWC,与模型实际不符时显式指定;--input_shape:把动态输入固定下来,格式"输入名:N,C,H,W"。ATC 是离线编译,原则上需要确定的 shape(动态分档见第五节);--insert_op_conf:AIPP 预处理配置文件,见第五节详解。
转完怎么确认没翻车
atc--mode=1--om=$HOME/module/out/resnet50.om--mode=1会把 OM 模型的输入输出 shape 和数据类型打出来,转换后先跑一下,核对输入名、维度、dtype 与预期一致,再上板。推理侧的标准流程是:aclmdlLoadFromFile加载 OM →aclmdlExecute执行推理(C/C++ 用 ACL 接口,Python 用 pyACL),本文不展开。
给新手的工程建议(来自官方迁移实践):转换前先用 Netron 打开模型核对输入输出名/维度/dtype;转换后做最小验证——OM 能加载 + 用 1~3 个固定输入跑通前向并保存输出范围,再去对精度。
四、ATC 里面发生了什么:运行流程与关键原理
一条atc命令敲下去到 OM 落盘,内部是一条完整的编译流水线。先看全景图,再逐段拆解:
逐阶段拆解
Parser 解析。把 ONNX/TensorFlow/MindSpore 的模型读进来,统一翻译成中间态 IR Graph——类似编译器把各种语言先变成统一的中间表示(IR),后面所有优化都在这份统一表示上做。ATC 本身是 GE(Graph Engine,图引擎,昇腾计算图编译和运行的控制中心)对外提供的命令行入口,这套流水线就是 GE 的编译器。
① 图准备。做规范化、Shape 推理、量化准备等预处理,把图整理成"标准体位"。
② 图优化。编译器的核心收益环节,重点认识三件事:
- 算子融合。把多个小算子合成一个大算子,分硬件无关与硬件相关两类:图融合做数学等价替换(比如 Convolution + BiasAdd 融合后直接在片上完成累加,Conv2D + BatchNorm 用数学推导合成一个算子);UB 融合则消除相邻算子之间"片上缓存 → 内存 → 片上缓存"的往返搬运,让数据留在 Unified Buffer 里流转。往返搬运是性能杀手,融合是图模式相对逐算子执行的核心优势。
- 常量折叠。输入全是常量的算子,编译期就在主机侧提前算好,替换成一个常量节点。典型的如维度计算——Shape/Reshape/Transpose 这类操作编译期就能求值,没必要留到运行时。进阶用户可用
--ge.oo.level=O1 --ge.oo.constantFolding=true控制,融合 pass 级开关可用--fusion_switch_file。 - 数据布局转换。框架模型常用的 NCHW/NHWC 会被转成 AI 处理器统一的NC1HWC0五维格式(C0 对应矩阵单元 32B 对齐的元素数,fp16 时 C0=16),卷积权重则排成FRACTAL_Z分形格式送进 Cube 矩阵单元。入门篇记住"数据被重新排成硬件最喜欢的队形"即可,细节官方概述文档有图解。
此外还有公共子表达式消除(CSE)、死代码消除(DCE)等常规编译优化。
③ 图拆分 / 引擎分区。按引擎把图切分成子图——大部分算子跑在 AI Core 上,部分算子跑在 AI CPU 上,分区器负责划界。
④ 图构建。完成流分配、内存规划、任务生成,产出编译产物并序列化成 OM。你在部署后感受到的"加载即跑",就是因为这些调度决策都在这一步提前做完了。
OM 文件里装了什么
转出来的.om不是一个简单的权重容器,而是一个自包含的部署包,按分区组织:
| 分区 | 内容 |
|---|---|
| 文件头 | ModelFileHeader,模型元信息 |
| MODEL_DEF | 模型定义:图结构、算子属性 |
| WEIGHTS_DATA | 权重数据(已按目标格式重排) |
| TBE_KERNELS | 编译后的算子二进制(kernel) |
| TILING_DATA | 预计算好的 tiling 参数(切块策略) |
| SO_BINS / CUSTOM_OPS | 算子 so 与自定义算子数据(按需打包) |
用atc --mode=6 --om=model.om可以查看 OM 占用的关键资源信息和编译运行环境(包括当初的 atc 命令行和 soc_version)——排查"这模型是谁在哪儿转的"很有用。
为什么要"提前编译"
把编译挪到部署前完成,收益不是省的那几秒启动时间,而是:
- 全局视角做优化。逐算子下发执行时每个算子各自为战;整图离线编译能看到全貌,融合、内存复用、多流并行、模型下沉这些优化才做得起来,有效减少 Host 与 Device 的调度交互;
- 部署侧自包含。传统部署要在目标机装完整算子包(数百个 so)、严格保证编译运行环境算子包版本一致、还要管理环境变量;OM 把需要的都打包带走,部署形态干净得多;
- 确定性与稳定性。一个真实的反例:某推理框架的 CANN 后端在运行时编译生成 OM 缓存,多卡并发写同一个缓存路径产生竞态问题——预编译 OM 文件可以完全避免这类运行时竞态。
(顺带一答常见疑问:“GPU 上不是运行时 JIT 吗,昇腾为啥搞离线?”——官方文档没有做这样的对照论述,这一段对比属于笔者的理解,供参考:两条路线本质都是"把硬件差异和优化时机放在不同的位置",昇腾选择把优化前置到转换期,换取部署期的确定性与极致性能。)
遇到不支持的算子怎么办
ATC 遇到算子库里没有的算子会直接报错、转换失败,报错会点名算子类型,长这样:
Op type NonMaxSuppression is not supported别慌,按官方排错路径逐级尝试:
- 先化简模型:用 onnx-simplifier 或 auto_optimizer 消除/化简,有希望把不支持的融合算子拆成基本算子组合(官方推荐按 auto_optimizer → onnxslim → 原模型 逐个回退尝试);
- 等价算子替换:如 GroupNorm 换 InstanceNorm + LayerNorm 组合、bicubic 插值换 bilinear;
- 让 AIPP 承担:某些预处理类算子(如离线 resize)可下沉到 AIPP;
- 升级 CANN:算子支持随版本持续完善;
- 自定义算子:用 Ascend C 写 kernel 注册进算子库,ATC 即可使用(进阶话题)。
另外一个冷知识:dtype 不匹配导致的编译失败,可以通过自动插入 Cast 打通(AI CPU Cast 自动插入模式),详见官方文档。
五、主要参数速查
必选四件套
--model、--framework、--output、--soc_version,见第三节,不再赘述。
常用可选参数
| 参数 | 干什么 | 备注 / 默认 |
|---|---|---|
--input_format | 输入数据格式 | Caffe/MindSpore/ONNX 默认 NCHW;TensorFlow 默认 NHWC |
--input_shape | 固定输入 shape,如"images:1,3,640,640" | 动态维度用-1占位,配合下面的动态参数 |
--dynamic_batch_size | batch 分档,如"1,2,4,8" | 至少 2 档、最多 100 档;只支持 N 在首位;OM 会新增一个输入用于喂实际 batch 值(新手常踩) |
--dynamic_image_size | 分辨率分档,如"416,416;832,832" | 与--input_shape配用(H、W 位填 -1);与 dynamic_batch_size 互斥 |
--output_type | 指定输出数据类型 | — |
--out_nodes | 指定输出节点 | — |
--core_type | 使用的 Core 类型 | 默认 AiCore;含 Cube 算子的网络只能 AiCore |
--external_weight | 权重外置为单独文件 | 默认 0;适合超大模型拆分管理 |
--log | 转换日志级别 | 默认 null(不出调试日志);排错加--log=debug |
--insert_op_conf | AIPP 预处理配置文件 | 见下文 |
--singleop | 单算子 JSON 编译 | 算子开发场景 |
精度一族
| 参数 | 取值 | 默认 | 说明 |
|---|---|---|---|
--precision_mode | force_fp32 / force_fp16 / allow_fp32_to_fp16 / allow_mix_precision / must_keep_origin_dtype | force_fp16 | 老版精度参数 |
--precision_mode_v2 | fp16 / origin / cube_fp16in_fp32out / mixed_float16 / mixed_bfloat16 / mixed_hif8 | fp16 | 官方推荐改用,与老参数互斥 |
--op_select_implmode | high_precision / high_performance | high_performance | 算子实现选高精度还是高性能;某算子只实现一种模式时该参数对其不生效 |
一个诚实提醒:--optypelist_for_implmode(按算子列表指定 implmode)在网上老教程里出镜率不低,但官方文档已明示该功能停止演进、后续版本会废弃,请勿使用。另外--compression_optimize_conf是压缩优化的配置入口(含首层量化融合、训练后量化等特性),注意配了其中的 calibration 量化就不能再配高精度模式,两者会互相抵消收益。
AIPP:把预处理"焊"进模型
AIPP(AI Pre-Processing)把推理前的图像处理——色域转换(YUV→RGB)、抠图、归一化——以 Aipp 算子的形式固化进 OM,推理时用专用加速模块完成,省掉 CPU 上的预处理代码。开启方式就是--insert_op_conf=aipp.cfg,一份官方静态 AIPP 配置示例(YUV420SP 转 RGB + 通道减均值):
aipp_op { aipp_mode : static related_input_rank : 0 # 作用于第几个输入 src_image_size_w : 608 src_image_size_h : 608 crop : false input_format : YUV420SP_U8 csc_switch : true # 色域转换开关 + 转换矩阵 matrix_r0c0 : 298 matrix_r0c1 : 0 matrix_r0c2 : 409 matrix_r1c0 : 298 matrix_r1c1 : -100 matrix_r1c2 : -208 matrix_r2c0 : 298 matrix_r2c1 : 516 matrix_r2c2 : 0 input_bias_0 : 16 input_bias_1 : 128 input_bias_2 : 128 mean_chn_0 : 104 # 各通道减均值 mean_chn_1 : 117 mean_chn_2 : 123 }约束提一句:静态 AIPP 与动态分辨率共用时,cfg 里不能开 Crop/Padding 且src_image_size_w/h须设 0。完整配置项见官方《AIPP 配置参考》。
–mode:ATC 的六种运行模式
| 取值 | 作用 |
|---|---|
| 0 | 默认,执行转换生成.om |
| 1 | 查看模型信息(输入输出 shape/dtype)——自检常用 |
| 3 | 只做模型合法性预检查(生成 check_result.json) |
| 5 | 把 dump 出的图结构文件转 JSON,定位图问题用 |
| 6 | 查看已有 OM 的资源占用与编译运行环境 |
| 30 | 生成感知硬件调度的.exeom(加载更快、内存峰值更低;仅特定芯片支持) |
顺手的两个环境变量
exportTE_PARALLEL_COMPILER=16# 算子并行编译进程数(1~32,默认 8),大网转换提速exportASCEND_SLOG_PRINT_TO_STDOUT=1# atc 日志直接打屏(默认只落盘)六、避坑指南:常见报错与注意事项
排错总表(现象 → 原因 → 对策)
| # | 现象 | 原因 | 对策 |
|---|---|---|---|
| 1 | Op type XXX is not supported | 算子库无该算子 | 见第四节五步排错路径 |
| 2 | E10003 ... Value 1.1,2,4,8 for parameter --dynamic_batch_size is invalid | 档位值含非法字符(小数点) | 按提示改参数值,错误信息里的 Reason 写得很直白 |
| 3 | EZ0005 OP[xx] Nth input has incorrect shape size | 算子输入维度数不对 | 核对--input_shape;用 DUMP_GE_GRAPH(见下)在图里找问题节点 |
| 4 | 转换巨慢 / 内存耗尽失败 | 动态档位过多过大;整网过大 | 减少档位/调低数值;超大模型拆成多段分别转换(官方大模型实战做法);转换期可swapoff -a防止 swap 拖慢 |
| 5 | 转换成功但推理结果不对 | 精度模式、输入 dtype/layout、AIPP 配置问题 | 同输入对比 onnxruntime 与 NPU 输出(余弦相似度/最大绝对误差);换 precision_mode 对照;日志里 grepreplace/fusion/unsupported |
| 6 | OM 无法加载 / 执行报acl.mdl.execute error 507011 | soc_version 与实际设备不符 | npu-smi info查实际型号重转;atc --mode=1核对 OM 信息;勿复用旧 OM |
| 7 | ModuleNotFoundError: No module named 'decorator'(或'te') | Python 依赖缺失 | 按提示 pip 安装;te报错说明装 CANN 时没带--pylocal,建议带参数重装 |
错误码怎么认
ATC 生态的错误码按前缀分家,认前缀就知道该往哪查:
| 前缀 | 归属 |
|---|---|
| E1xxxx | GE 图编译/校验 |
| E20101 | FE 算子融合 |
| EE1011 | RTS 运行时 |
| EH0001 | ACL |
| EZxxxx | ATC 工具专属(离线模型编译) |
E*9*** 一类内部错误按报错信息排查无果的话,收集日志联系技术支持。各家族的 Symptom/Solution 结构说明都在官方 troubleshooting 手册里。
日志去哪看
- 转换失败第一反应:加
--log=debug重跑; - 日志格式形如
[ERROR] GE(30741,atc.bin):2021-12-09-16:10:22.539 [error_manager.cc:263]...——方括号里的模块名就是定位思路:GE 开头查图编译/参数,FE 查算子融合,TEFUSION 查融合算子编译,TBE 查算子编译; - 落盘路径:
$HOME/ascend/log/debug/plog/plog-<pid>-*.log(调试日志)、$HOME/ascend/log/run/plog/(运行日志); - 进阶:
--op_debug_level=1生成 kernel 编译中间文件用于定位 AICore Error(会影响性能,仅排错时开); - 看图结构:
export DUMP_GE_GRAPH=1各阶段图描述生成ge_onnx*.pbtxt,可以用 Netron 直接打开——肉眼看融合/拆分后的图,排 shape 问题神器。
版本与兼容(最容易忽略的坑)
- 升级顺序不能乱:固件 → 驱动 → CANN,顺序不可颠倒;升驱动后必须重启。查版本:
cat /usr/local/Ascend/ascend-toolkit/latest/version.cfg、npu-smi info -v。 - OM 与 CANN 版本绑定:官方规则是运行环境 CANN 版本不低于转换环境即可——低版本 CANN 转出的 OM 可在高版本 CANN 上运行,兼容 4 个版本周期;社区经验进一步补充:同一大版本内小升级一般兼容,跨大版本不兼容。最稳的做法永远是在目标部署环境用当地 CANN 重转一次。
- OM 不跨芯片:910B 转的 OM 不能拿到 910A 上用;芯片型号 + CANN 版本共同决定模型支持范围。
- 两个特例场景要求更严:动态 shape 算子场景和昇腾虚拟化实例(vNPU)场景,转换环境的 CANN 版本必须与运行环境相同。
新手好习惯清单
- 转换前 Netron 核对输入输出名/维度/dtype,转后
atc --mode=1自检; - 固定输入跑通前向、对完精度(余弦相似度等)再上真实业务;msame、ais_bench 等工具可做 OM 精度/性能对比;
- 模型与输出路径用纯英文、不含空格——这是经验习惯(官方未将其列为通用约束),但能少一类诡异问题;
- 大网转换前评估内存,必要时分段转换。
七、写在最后
ATC 是昇腾部署链路的第一道门:一条命令背后,是"解析 → 图准备 → 图优化 → 分区 → 构建 → 序列化"的完整编译流水线,把框架模型变成针对目标芯片深度定制的自包含 OM。入门阶段记住三件事就够了——soc_version 填运行环境的芯片、shape 策略想清楚再转、报错先看模块名和日志。
想动手走一遍完整链路的话,CANN 官方 learning-hub 有现成实验:在 910B3 上训练导出 ONNX,再到香橙派(310B)上用 ATC 编译为 OM 并用推理接口执行——从训练板到边缘板,正好把本文所有知识点串起来。
本文涉及的 API 语义、参数默认值、报错样例与兼容规则,均经昇腾知识图谱(ascend.wiki)的官方文档节点核实,主要来源包括:GE 仓
atc_tools/overview/atc_overview.md、atc_tools/CLI_options/系列参数文档、GE 编译器设计文档compiler.md、so_in_om.md、常量折叠与融合模式设计文档、AIPP 配置示例、官方 troubleshooting 错误码手册及 cann-recipes 实战样例。接入昇腾知识图谱 https://gitcode.com/agent0/kg-tools