skills/atc-model-converter/SKILL.md
Complete toolkit for Huawei Ascend NPU model conversion and end-to-end inference adaptation. Workflow 1 auto-discovers input shapes and parameters from user source code. Workflow 2 exports PyTorch models to ONNX. Workflow 3 converts ONNX to .om via ATC with multi-CANN version support. Workflow 4 adapts the user's full inference pipeline (preprocessing + model + postprocessing) to run end-to-end on NPU. Workflow 5 verifies precision between ONNX and OM outputs. Workflow 6 generates a reproducible README. Supports any standard PyTorch/ONNX model. Use when converting, testing, or deploying models on Ascend AI processors.
npx skillsauth add Ascend/agent-skills atc-model-converterInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
华为昇腾 NPU 上完整的 PT -> ONNX -> OM 模型转换与端到端推理适配工具链。支持任意标准 PyTorch 或 ONNX 模型。
支持的 CANN 版本: 8.1.RC1, 8.3.RC1, 8.5.0+
⚠️ 环境兼容性警告: Python 必须 ≤ 3.10(推荐 3.10),NumPy 必须 < 2.0,ONNX opset 推荐 11。 违反这三条是最常见的转换失败原因。详见 FAQ.md。
Agent 在执行本 Skill 的任何 Workflow 之前,必须先向用户收集以下信息。 缺少任何一项时,Agent 应主动询问用户,而不是猜测或跳过。
| 必需信息 | 说明 | 示例 |
|---------|------|------|
| 模型权重路径 | .pt / .pth / .onnx 文件的本地路径或下载地址 | /home/user/models/yolo26n.pt |
| 代码仓库地址 | 模型所属项目的 Git 仓库 URL 或本地路径 | https://github.com/ultralytics/ultralytics |
| 可选信息 | 说明 | 默认行为 | |---------|------|---------| | 目标任务类型 | 分类/检测/分割/姿态/OBB 等 | Agent 从代码仓中自动识别 | | 目标输入尺寸 | 模型推理时的输入分辨率 | Agent 通过 Workflow 1 自动发现 | | 测试图片/数据 | 用于验证端到端推理的样例输入 | Agent 使用随机数据或从仓库中寻找 |
Agent 收集信息后的标准开场白模板:
收到以下信息:
- 模型权重:<path>
- 代码仓库:<url_or_path>
- 任务类型:<type_or_待确认>
我将按以下流程执行:
1. 分析代码仓库,发现模型参数 (Workflow 1)
2. 导出 ONNX (Workflow 2)
3. ATC 转换为 OM (Workflow 3)
4. 端到端推理适配 (Workflow 4)
5. 精度与推理验证 (Workflow 5)
6. 生成可复现 README (Workflow 6)
Anti-Hardcoding Rule (反硬编码铁律): Agent 在执行本 Skill 时,绝对禁止猜测或使用任何"常见默认值"(如 640x640、224x224、batch_size=1)作为转换参数。 所有
input_shape、input_names、opset_version等关键参数,必须有用户项目代码层面的证据支撑。 如果无法从代码中确认,必须明确询问用户,而不是静默填入默认值。
收到用户的项目路径后,Agent 应按以下优先级搜索代码线索,提取 input_shape 和预处理参数:
1.1 搜索预处理管道 (Preprocessing Pipeline)
在项目代码中搜索以下模式,提取目标分辨率:
# 搜索关键词(按优先级排列)
cv2.resize(img, (W, H)) # OpenCV resize -> 提取 (W, H)
transforms.Resize((H, W)) # torchvision -> 提取 (H, W),注意 HW 顺序
transforms.CenterCrop(size) # torchvision -> 提取 crop size
Image.resize((W, H)) # PIL -> 提取 (W, H)
F.interpolate(x, size=(H, W)) # torch functional -> 提取 (H, W)
albumentations.Resize(height=H, width=W) # albumentations -> 提取 H, W
# Agent 应执行的搜索命令示例
grep -rn "cv2.resize\|transforms.Resize\|Image.resize\|F.interpolate\|\.resize(" /path/to/project --include="*.py"
grep -rn "img_size\|image_size\|input_size\|imgsz\|resolution" /path/to/project --include="*.py" --include="*.yaml" --include="*.json"
1.2 搜索配置入口 (Configuration Entry Points)
查找 CLI 参数定义和配置文件中的 shape 信息:
# argparse 定义中的线索
parser.add_argument('--img-size', type=int, default=640) # -> input H/W = 640
parser.add_argument('--batch-size', type=int, default=1) # -> batch dimension
parser.add_argument('--input-size', nargs=2, default=[224,224]) # -> H, W
# 配置文件中的线索 (.yaml / .json / .cfg)
input_size: [3, 224, 224] # -> C, H, W
image_shape: [640, 640] # -> H, W
# Agent 应执行的搜索命令示例
grep -rn "add_argument.*size\|add_argument.*shape\|add_argument.*resolution\|add_argument.*dim" /path/to/project --include="*.py"
find /path/to/project -name "*.yaml" -o -name "*.yml" -o -name "*.json" -o -name "*.cfg" | head -20
1.3 搜索模型前向传播 (Model Forward Pass)
查找代码中已有的 dummy_input 或 forward 函数签名:
# 已有的导出代码
dummy_input = torch.randn(1, 3, 224, 224) # -> 直接提取 shape
torch.onnx.export(model, dummy, ...) # -> 查看 dummy 的定义
# forward 函数的类型注解或 docstring
def forward(self, x: Tensor) -> Tensor:
"""Args: x: (B, 3, H, W) input tensor""" # -> 提取 channel=3
# 数据加载器中的线索
DataLoader(dataset, batch_size=8) # -> batch dimension
# Agent 应执行的搜索命令示例
grep -rn "dummy_input\|dummy\|torch.randn\|torch.zeros\|torch.ones" /path/to/project --include="*.py"
grep -rn "def forward" /path/to/project --include="*.py"
1.4 证据汇总模板
Agent 在完成静态分析后,必须以如下格式输出证据链:
=== Parameter Discovery Report ===
input_shape: [1, 3, 640, 640]
Evidence: found in config.yaml line 12: "input_size: [3, 640, 640]"
Evidence: confirmed by transforms.py line 45: "transforms.Resize((640, 640))"
input_names: ["images"]
Evidence: found in export.py line 30: 'input_names=["images"]'
opset_version: 11
Evidence: found in export.py line 31: "opset_version=11"
CANN compatibility: OK (CANN 8.3.RC1 supports opset 11)
Confidence: HIGH (multiple consistent sources)
当且仅当静态分析无法确定 input_shape 时(如逻辑嵌套过深、动态计算 shape),Agent 应编写并运行一个临时探针脚本来捕获真实的运行时张量信息。
方案 A: 数据集探针 — 从 DataLoader 中获取真实输入 shape
#!/usr/bin/env python3
"""Probe: Extract input tensor shape from the project's data pipeline."""
import sys
sys.path.insert(0, '/path/to/project')
# Import the project's dataset/dataloader
# (Agent 需要根据实际项目代码调整以下 import)
from dataset import create_dataloader # 或其他数据加载入口
loader = create_dataloader(split='val', batch_size=1)
batch = next(iter(loader))
# Handle common batch formats
if isinstance(batch, (list, tuple)):
tensor = batch[0]
elif isinstance(batch, dict):
# Common keys: 'image', 'img', 'input', 'pixel_values'
for key in ['image', 'img', 'input', 'pixel_values', 'x']:
if key in batch:
tensor = batch[key]
break
else:
tensor = batch
print(f"PROBE_RESULT: input_shape={list(tensor.shape)}, dtype={tensor.dtype}")
方案 B: 模型追踪探针 — 通过 forward hook 捕获输入
#!/usr/bin/env python3
"""Probe: Capture model input shape via forward hook."""
import torch
import sys
sys.path.insert(0, '/path/to/project')
# Load model (Agent 需要根据实际项目代码调整)
model = torch.load('model.pt', map_location='cpu')
model.eval()
# Register hook on the first layer to capture input
captured = {}
def hook_fn(module, input, output):
captured['input_shape'] = list(input[0].shape)
captured['input_dtype'] = str(input[0].dtype)
first_layer = list(model.children())[0]
first_layer.register_forward_hook(hook_fn)
# Try common input shapes to see which one doesn't crash
for shape in [(1,3,224,224), (1,3,256,256), (1,3,384,384), (1,3,512,512), (1,3,640,640)]:
try:
with torch.no_grad():
model(torch.randn(*shape))
print(f"PROBE_RESULT: shape {shape} -> SUCCESS, captured={captured}")
break
except Exception as e:
print(f"PROBE_RESULT: shape {shape} -> FAILED ({e})")
方案 C: ONNX 模型自检 — 如果已有 ONNX 文件
# 直接从已有 ONNX 模型提取全部 I/O 信息
python3 scripts/get_onnx_info.py model.onnx
探针安全规则:
- 探针脚本必须是只读的,不修改用户项目的任何文件
- 探针执行完毕后,Agent 应向用户报告发现结果并请求确认
- 如果探针也无法确定 shape,Agent 必须明确询问用户,而不是猜测
Agent 应识别用户项目中的后处理类型,为后续 Workflow 4(端到端推理适配)做准备。
# 搜索后处理关键词
grep -rn "nms\|non_max_suppression\|softmax\|argmax\|sigmoid\|postprocess\|decode" /path/to/project --include="*.py"
| 任务类型 | 典型后处理 | 搜索关键词 |
|---------|-----------|-----------|
| 分类 (Classification) | softmax/argmax -> label | softmax, argmax, topk, class_names |
| 检测 (Detection) | decode + NMS -> boxes | nms, non_max_suppression, bbox, anchor |
| 分割 (Segmentation) | argmax/threshold -> mask | argmax, threshold, mask, palette |
| 关键点 (Pose) | decode -> keypoints | keypoint, heatmap, joint, skeleton |
| 生成 (Generation) | denormalize -> image | denormalize, clamp, to_pil, save_image |
注意: 此阶段仅做识别和记录,实际的推理脚本生成在 Workflow 4 中完成。
使用 export_onnx.py 将任意标准 PyTorch 模型导出为 ONNX 格式。
# 导出 PyTorch 模型 (.pt / .pth)
python3 scripts/export_onnx.py \
--pt_model model.pt \
--output model.onnx \
--input_shape 1,3,224,224
# 指定 opset 版本(默认 11,CANN 兼容性最佳)
python3 scripts/export_onnx.py \
--pt_model model.pt \
--output model.onnx \
--input_shape 1,3,224,224 \
--opset 13
# 动态 batch size
python3 scripts/export_onnx.py \
--pt_model model.pt \
--output model.onnx \
--input_shape 1,3,224,224 \
--dynamic_axes '{"input": {"0": "batch"}, "output": {"0": "batch"}}'
python3 scripts/export_onnx.py \
--torchvision resnet50 \
--output resnet50.onnx \
--input_shape 1,3,224,224
对于特定框架的模型,优先使用框架自带的导出方式:
# HuggingFace Transformers example
from transformers import AutoModel
import torch
model = AutoModel.from_pretrained("bert-base-uncased")
model.eval()
dummy = torch.randint(0, 1000, (1, 128))
torch.onnx.export(model, dummy, "bert.onnx", opset_version=13,
input_names=["input_ids"], output_names=["output"])
运行 python3 scripts/export_onnx.py --help 查看完整参数列表。支持的格式:完整模型(torch.save)、TorchScript、含 "model" 键的 checkpoint dict。纯 state dict 不支持(需要模型架构定义)。
# 查看模型输入输出信息,获取推荐的 ATC 命令
python3 scripts/get_onnx_info.py model.onnx
# 自动检测 CANN 版本并配置环境
./scripts/setup_env.sh
# 验证环境
./scripts/check_env_enhanced.sh
手动配置及多版本共存请参考 CANN_VERSIONS.md。
ATC 转换中的
--soc_version必须与目标设备完全一致!
# 查询设备的 SoC 版本
npu-smi info | grep Name
# 输出: Name: 910B3 -> 使用: --soc_version=Ascend910B3
# 输出: Name: 310P3 -> 使用: --soc_version=Ascend310P3
| 设备 | SoC Version | 查询方式 |
|--------|-------------|--------------|
| Atlas 910B3 | Ascend910B3 | npu-smi info \| grep Name |
| Atlas 310P | Ascend310P1/P3 | npu-smi info \| grep Name |
| Atlas 200I DK A2 | Ascend310B4 | npu-smi info \| grep Name |
常见报错:
[ACL ERROR] EE1001: supported socVersion=Ascend910B3, but the model socVersion=Ascend910B修复: 使用
npu-smi info输出的精确 SoC 版本,不要使用缩写。
# 基本转换(ONNX 中为静态 shape 时可自动推断)
atc --model=model.onnx --framework=5 --output=model_om \
--soc_version=Ascend910B3
# 显式指定 input shape
atc --model=model.onnx --framework=5 --output=model_om \
--soc_version=Ascend910B3 \
--input_shape="input:1,3,224,224"
# 使用 FP16 精度以提升性能
atc --model=model.onnx --framework=5 --output=model_om \
--soc_version=Ascend910B3 \
--input_shape="input:1,3,224,224" \
--precision_mode=force_fp16
# 动态 batch size
atc --model=model.onnx --framework=5 --output=model_om \
--soc_version=Ascend910B3 \
--input_shape="input:-1,3,224,224" \
--dynamic_batch_size="1,2,4,8"
加速转换:
export TE_PARALLEL_COMPILER=16 # 并行编译
完整 ATC 参数参考见 PARAMETERS.md。AIPP 配置见 AIPP_CONFIG.md。
目标: 基于用户提供的代码仓库,生成一个完整的端到端 NPU 推理脚本。 该脚本复用原始仓库的预处理和后处理逻辑,仅将模型推理部分替换为 OM 模型。 最终用户获得的是一个可以直接输入原始数据(图片/文本/音频)并输出业务结果的脚本, 而非仅输出原始张量的
infer_om.py。
Agent 需要从用户仓库中找到完整的推理入口,理解其三段式结构:
# 搜索推理入口文件
grep -rn "def predict\|def infer\|def forward\|def __call__\|def run\b" /path/to/repo --include="*.py"
grep -rn "if __name__\|argparse\|click\|typer" /path/to/repo --include="*.py" | grep -i "infer\|predict\|detect\|demo\|test"
Agent 应识别以下三个阶段并记录其代码位置:
| 阶段 | 需识别的内容 | 搜索关键词 |
|------|------------|-----------|
| 预处理 | 输入数据 → 模型输入张量 | preprocess, transform, resize, normalize, letterbox, pad |
| 模型推理 | 张量 → 原始输出张量 | model(, forward(, session.run, torch.no_grad |
| 后处理 | 原始输出 → 业务结果 | postprocess, nms, decode, softmax, argmax, draw, visualize |
输出要求: Agent 必须生成推理流程分析报告:
=== Inference Pipeline Analysis ===
入口文件: ultralytics/engine/predictor.py
预处理: ultralytics/data/augment.py :: LetterBox (line 420)
- resize to imgsz with letterbox padding
- BGR→RGB, HWC→CHW, /255.0 normalize
模型推理: ultralytics/nn/autobackend.py :: AutoBackend.forward() (line 180)
- input: (1, 3, 640, 640) float32
- output: (1, 84, 8400) float32
后处理: ultralytics/utils/ops.py :: non_max_suppression (line 200)
- transpose if shape[1] < shape[2]
- conf threshold + NMS
- scale boxes back to original image size
Agent 基于分析结果,生成 e2e_infer_om.py 脚本,遵循以下原则:
核心原则:最大化复用,最小化重写
#!/usr/bin/env python3
"""End-to-end OM inference script — reuses <repo_name> pre/post-processing."""
import sys
import numpy as np
from ais_bench.infer.interface import InferSession
# ===== 复用原始仓库的预处理/后处理 =====
# Agent 应优先 import 用户仓库中的函数
# 仅当原始代码与 GPU/特定框架强耦合时,才改写为 NumPy 版本
sys.path.insert(0, '/path/to/repo')
# from <repo>.preprocess import preprocess_fn # 优先复用
# from <repo>.postprocess import postprocess_fn # 优先复用
def main():
# 1. 加载输入数据(使用原始仓库的方式)
# img = load_image(input_path)
# 2. 预处理(复用原始仓库逻辑)
# input_tensor = preprocess_fn(img)
# 3. OM 推理(替换原始模型推理)
session = InferSession(device_id=0, model_path="model.om")
outputs = session.infer([input_tensor], mode='static')
# 4. 后处理(复用原始仓库逻辑)
# results = postprocess_fn(outputs, original_shape=img.shape)
# 5. 输出业务结果
# visualize(img, results, save_path="result.jpg")
if __name__ == "__main__":
main()
Agent 生成脚本时的决策树:
原始仓库的预处理/后处理函数能否直接 import?
├── YES → 直接 import 并调用(首选)
├── 需要少量修改(如 GPU→CPU)→ 复制函数并修改 tensor → numpy
└── 与框架深度耦合 → 基于原始逻辑用 numpy/opencv 重写,保留注释标注来源
Agent 必须验证端到端推理脚本的输出与原始推理流程的输出一致:
# 1. 使用原始仓库运行推理,保存结果
python3 original_infer.py --input test.jpg --save-result original_result.json
# 2. 使用 e2e_infer_om.py 运行推理,保存结果
python3 e2e_infer_om.py --input test.jpg --save-result om_result.json
# 3. 对比业务结果(不是张量对比,是最终输出对比)
# 检测任务:对比 bounding box 坐标、类别、置信度
# 分类任务:对比 top-K 类别和概率
# 分割任务:对比 mask IoU
验证通过标准:
| 任务类型 | 验证指标 | 通过阈值 | |---------|---------|---------| | 检测 | box坐标差 / 类别一致 / 检出数一致 | 坐标差 < 1px, 类别100%一致 | | 分类 | Top-1一致 / 概率差 | Top-1一致, 概率差 < 0.01 | | 分割 | mask IoU | > 0.99 | | 姿态 | 关键点坐标差 | < 2px |
# 端到端延迟(含预处理+推理+后处理)
python3 e2e_infer_om.py --input test.jpg --benchmark --warmup 5 --loops 100
Agent 应报告:
=== End-to-End Performance ===
Preprocess: X.XX ms
OM Inference: X.XX ms
Postprocess: X.XX ms
Total E2E: X.XX ms (X.X FPS)
本 Workflow 包含两个验证层面:原始张量级精度对比(ONNX vs OM)和 OM 模型推理冒烟测试。
转换完成后,使用 infer_om.py 配合 ais_bench 快速验证 OM 模型可正常推理:
# 仅打印模型信息
python3 scripts/infer_om.py --model model.om --info
# 使用随机输入推理(shape 从模型元数据获取)
python3 scripts/infer_om.py --model model.om
# 使用实际输入数据推理
python3 scripts/infer_om.py --model model.om --input test.npy --output result.npy
# 性能基准测试(预热 + 多次迭代)
python3 scripts/infer_om.py --model model.om --warmup 5 --loop 100
Python API(快速参考):
from ais_bench.infer.interface import InferSession
import numpy as np
session = InferSession(device_id=0, model_path="model.om")
input_data = np.random.randn(*session.get_inputs()[0].shape).astype(np.float32)
outputs = session.infer([input_data], mode='static')
for i, out in enumerate(outputs):
print(f"Output[{i}]: shape={out.shape}, dtype={out.dtype}")
session.free_resource()
详细的 ais_bench API 用法和参数说明见 INFERENCE.md。
通过比较 ONNX(CPU)与 OM(NPU)的推理输出,验证转换精度:
# 使用默认容差对比
python3 scripts/compare_precision.py \
--onnx model.onnx --om model.om --input test.npy
# 自定义容差
python3 scripts/compare_precision.py \
--onnx model.onnx --om model.om --input test.npy \
--atol 1e-3 --rtol 1e-2
# 保存对比报告为 JSON
python3 scripts/compare_precision.py \
--onnx model.onnx --om model.om --input test.npy \
--output precision_report.json
# 保存差异数组用于分析
python3 scripts/compare_precision.py \
--onnx model.onnx --om model.om --input test.npy \
--save-diff diff_output/
指标说明:
| 指标 | 说明 | 良好值 |
|--------|-------------|------------|
| cosine_similarity | 1.0 = 完全一致 | > 0.99 |
| max_abs_diff | 最大绝对误差 | < 1e-3 (FP32) |
| mean_abs_diff | 平均绝对误差 | < 1e-5 (FP32) |
| outlier_ratio | 超出容差的元素占比 | < 1% |
| is_close | 基于 atol/rtol 的通过/失败判定 | True |
结果解读:
--precision_mode=force_fp32注意: 对于 end2end 模型(如 YOLOv10 输出
(1,300,6)),原始张量对比可能显示较低的 cosine_similarity, 因为低置信度占位槽的值在 CPU 和 NPU 间存在差异。此时应以 Workflow 4 Step 3 的业务结果对比为准。
在完成 Workflow 1–5 的全部步骤后,Agent 必须生成一份用户可直接跟随复现的 README 文档。 这是 Skill 执行的最后一步,不可跳过。
生成的 README 面向没有参与当前会话的用户,他们只需照着文档从头到尾执行,即可复现整个模型转换与推理流程。
环境信息 → 模型简介 → Quick Start → 详细步骤(环境准备、获取模型、导出 ONNX、ATC 转换、端到端推理)→ 关键挑战与解决方案 → 文件结构 → 已知限制
| 要求 | 说明 |
|------|------|
| 可复制粘贴 | 所有命令必须是完整的、可直接执行的,不能有 ... 省略或 <placeholder> |
| 有预期输出 | 关键步骤需附上预期输出示例 |
| 记录踩坑 | 每个报错和绕行方案都要写入「关键挑战」章节 |
| 版本钉死 | 所有 pip install 必须带版本号 |
| 自包含 | README + 同目录的脚本/配置文件 = 完整可复现 |
完整 README 模板及示例见:references/EXAMPLE_README.md
详见 FAQ.md,涵盖 Python 版本、NumPy 兼容性、opset 版本、算子缺失等常见问题的完整排查决策树。
导出 & 转换:
export_onnx.py - 通用 PyTorch → ONNX 导出工具get_onnx_info.py - 查看 ONNX 模型输入输出信息setup_env.sh - 自动配置 CANN 环境check_env_enhanced.sh - 全面环境兼容性检查推理 & 测试:
infer_om.py - 通用 OM 模型推理(基于 ais_bench)compare_precision.py - ONNX vs OM 精度对比testing
Kubernetes 集群健康检查与安全修复 — 诊断问题,用户确认后执行修复
tools
昇腾NPU CANN Toolkit+Kernels+NNAL安装部署技能。支持从官网下载run包安装和从Docker镜像提取两种方式,覆盖驱动检查、包下载、安装、环境变量配置与验证全流程。当用户需要安装CANN全套组件或指定版本CANN到自定义路径时调用。
development
编译 ATB (Ascend Transformer Boost) 测试框架。当用户需要编译 ATB 测试框架、 运行 CSV 测试、或构建 atb_test_framework 时调用。支持全量编译(含第三方依赖克隆与源替换) 和增量编译两种模式。需在 Docker 容器内配合 CANN 环境执行。
databases
ATB OPS→ACLNN 迁移标准化工作流主模板。整合前置学习、设计文档生成、CSV用例设计、 实际迁移、编译验证、测试验证全流程,提供明确的阶段 Gates 和用户确认机制。