一、前言:为什么要做这个转换?

YOLOv5作为当前最流行的目标检测模型之一,在昇腾AI处理器上部署能获得显著的推理加速。但这个过程需要将PyTorch训练的模型转换为昇腾专用的离线模型(.om文件)。

转换的价值:

  • 性能提升:相比CPU/GPU,在昇腾芯片上有10倍以上的推理加速
  • 部署简化:.om文件可直接用于边缘设备(如Atlas 200DK)
  • 资源优化:支持INT8量化,大幅降低内存占用

二、环境准备:确保你的工具链完整

2.1 环境检查清单

# 1. 确认ATC已安装
atc --version
# 应输出类似:ATC version: 7.0.0.alpha

# 2. 检查Python环境
python3 --version
pip3 list | grep -E "(torch|onnx|numpy)"

# 3. 确认芯片型号(选择正确的soc_version)
# Ascend310, Ascend310P, Ascend710等
# 如果不知道,默认用Ascend310(最常用)

2.2 获取YOLOv5源码和模型

# 克隆YOLOv5官方仓库
git clone https://github.com/ultralytics/yolov5.git
cd yolov5

# 安装依赖(建议使用虚拟环境)
pip install -r requirements.txt onnx>=1.12.0

# 下载预训练权重
wget https://github.com/ultralytics/yolov5/releases/download/v7.0/yolov5s.pt

三、第一步:从PyTorch到ONNX的转换

3.1 标准导出方法

# 基本导出命令
python export.py --weights yolov5s.pt \
                 --include onnx \
                 --img-size 640 640 \
                 --batch-size 1 \
                 --opset 12

# 参数详解:
# --weights: 预训练权重文件
# --img-size: 输入图像尺寸(必须与训练时一致)
# --batch-size: 批处理大小(建议从1开始)
# --opset: ONNX算子集版本(建议12或13,兼容性更好)

3.2 验证ONNX模型

import onnx
import onnxruntime
import numpy as np

# 加载并验证ONNX模型
onnx_model = onnx.load("yolov5s.onnx")
onnx.checker.check_model(onnx_model)
print("✓ ONNX模型结构验证通过")

# 使用ONNX Runtime测试推理
ort_session = onnxruntime.InferenceSession("yolov5s.onnx")

# 创建随机输入测试
x = np.random.randn(1, 3, 640, 640).astype(np.float32)
ort_inputs = {ort_session.get_inputs()[0].name: x}
ort_outputs = ort_session.run(None, ort_inputs)

print(f"输入形状: {x.shape}")
print(f"输出数量: {len(ort_outputs)}")
print(f"第一个输出形状: {ort_outputs[0].shape}")

四、第二步:关键步骤 - ONNX模型优化

4.1 为什么要优化?

原始的YOLOv5 ONNX模型包含一些昇腾不支持的算子或结构,需要进行调整。

# 方法一:使用YOLOv5自带的简化工具
python -m onnxsim yolov5s.onnx yolov5s-sim.onnx

# 方法二:使用ONNX官方优化器(更推荐)
python -c """
import onnx
from onnxsim import simplify

model = onnx.load('yolov5s.onnx')
model_simp, check = simplify(model)
assert check, "简化验证失败"
onnx.save(model_simp, 'yolov5s-sim.onnx')
print(f'简化完成,原始节点数:{len(model.graph.node)},简化后:{len(model_simp.graph.node)}')
"""

4.2 常见问题修复

如果遇到不支持的算子,可以尝试以下方法:

# 创建自定义算子映射文件(custom_op.yaml)
custom_ops = """
op_properties:
  - op_name: "NonMaxSuppression"
    custom_op_flag: true
    compute_capacity: "high"
"""
with open("custom_op.yaml", "w") as f:
    f.write(custom_ops)

五、第三步:核心环节 - ATC模型转换

5.1 基础转换命令

# 基础ATC转换命令
atc --model=yolov5s-sim.onnx \
    --framework=5 \
    --output=yolov5s_ascend \
    --soc_version=Ascend310 \
    --input_format=NCHW \
    --input_shape="images:1,3,640,640" \
    --insert_op_conf=aipp_yolov5.config \
    --log=info \
    --out_nodes="output:0;422:0"

5.2 关键参数详解

参数1:图像预处理配置(--insert_op_conf)

创建aipp_yolov5.config文件:

aipp_op {
    aipp_mode: static
    input_format : RGB888_U8
    src_image_size_w : 640
    src_image_size_h : 640
  
    # 归一化参数 (x/255)
    mean_chn_0 : 0
    mean_chn_1 : 0
    mean_chn_2 : 0
    var_reci_chn_0 : 0.003921568627451
    var_reci_chn_1 : 0.003921568627451
    var_reci_chn_2 : 0.003921568627451
  
    # YOLOv5需要的transpose (HWC->CHW)
    matrix_r0c0: 1; matrix_r0c1: 0; matrix_r0c2: 0
    matrix_r1c0: 0; matrix_r1c1: 1; matrix_r1c2: 0
    matrix_r2c0: 0; matrix_r2c1: 0; matrix_r2c2: 1
    input_bias_0: 0; input_bias_1: 0; input_bias_2: 0
}
参数2:动态形状支持(多Batch/多分辨率)
# 支持动态Batch(1-8,4最优)
atc --model=yolov5s-sim.onnx \
    --framework=5 \
    --output=yolov5s_dynamic_batch \
    --soc_version=Ascend310 \
    --input_format=NCHW \
    --input_shape="images:-1,3,640,640" \
    --dynamic_batch_size="1,2,4,8" \
    --dynamic_image_size="640,640" \
    --insert_op_conf=aipp_yolov5.config
参数3:INT8量化(性能提升关键)
# 准备校准数据(需要少量标注数据)
python3.7 calibrate_yolo.py \
    --model yolov5s-sim.onnx \
    --data ./coco128.yaml \
    --output yolov5s_calibration

# 带量化的ATC转换
atc --model=yolov5s-sim.onnx \
    --framework=5 \
    --output=yolov5s_int8 \
    --soc_version=Ascend310 \
    --input_format=NCHW \
    --input_shape="images:1,3,640,640" \
    --insert_op_conf=aipp_yolov5.config \
    --quantize_calibration_file=yolov5s_calibration.json \
    --quantization_algorithms=weight_quantization

六、第四步:转换结果验证

6.1 检查输出文件

# 查看生成的OM文件
ls -lh yolov5s_ascend.om
# 应该能看到类似:yolov5s_ascend.om (约45MB)

# 使用msame工具验证推理
git clone https://gitee.com/ascend/tools.git
cd tools/msame
./build.sh
./msame --model ../yolov5s_ascend.om --input input.bin --output output

6.2 Python推理验证脚本

import numpy as np
from ais_bench.infer.interface import InferSession

# 初始化昇腾推理会话
device_id = 0
model = InferSession(device_id, "yolov5s_ascend.om")

# 准备输入数据
fake_input = np.random.randn(1, 3, 640, 640).astype(np.float32)

# 执行推理
outputs = model.infer([fake_input])

# 解析YOLOv5输出
print("推理成功!输出信息:")
for i, out in enumerate(outputs):
    print(f"  输出{i}: shape={out.shape}, dtype={out.dtype}, min={out.min():.4f}, max={out.max():.4f}")

# 与ONNX结果对比(可选)
def compare_results(onnx_output, ascend_output, threshold=1e-3):
    # 实现精度对比逻辑
    pass

七、常见错误与解决方案

错误1:Unsupported op type: NonMaxSuppression

原因:YOLOv5后处理中的NMS算子在昇腾上需要自定义实现

解决方案:

  1. 方案A(推荐):导出时移除后处理
  2. python export.py --weights yolov5s.pt \
                     --include onnx \
                     --img-size 640 640 \
                     --batch-size 1 \
                     --opset 12 \
                     --simplify \
                     --nms  # 使用内置NMS或导出不带NMS的模型
  3. 方案B:使用ATC自定义算子功能
# 在ATC命令中添加
--op_name_map=op_name_map.json

创建op_name_map.json

{
  "NonMaxSuppression": "CustomNonMaxSuppression"
}

错误2:Shape inference failed

原因:动态形状或维度不匹配

解决方案:

# 明确指定所有维度
--input_shape="images:1,3,640,640" \
--output_type="FP16" \
--precision_mode=allow_fp32_to_fp16

错误3:内存不足

解决方案:

# 使用内存优化参数
--auto_tune_mode="RL,GA" \
--buffer_optimize=off_optimize \
--enable_small_channel=1

八、性能优化技巧

8.1 转换参数调优

# 完整优化参数示例
atc --model=yolov5s-sim.onnx \
    --framework=5 \
    --output=yolov5s_optimized \
    --soc_version=Ascend310 \
    --input_format=NCHW \
    --input_shape="images:1,3,640,640" \
    --insert_op_conf=aipp_yolov5.config \
    --precision_mode=allow_mix_precision \
    --fusion_switch_file=fusion_switch.cfg \
    --op_select_implmode=high_precision \
    --optypelist_for_implmode="Gelu,Add" \
    --enable_small_channel=1

8.2 创建融合规则文件(fusion_switch.cfg)

{
    "switch": {
        "GraphFusion": {
            "enable": true,
            "pass_list": ["ConcatOptimize", "ReshapeFusion"]
        },
        "MemoryOptimization": {
            "enable": true,
            "memory_optimization_priority": "memory_first"
        }
    }
}

九、完整脚本:一键转换工具

创建convert_yolov5.sh

#!/bin/bash
# YOLOv5一键转换脚本
# 用法:./convert_yolov5.sh yolov5s.pt Ascend310

MODEL_PT=$1
SOC_VERSION=${2:-Ascend310}
MODEL_NAME=$(basename $MODEL_PT .pt)

echo "开始转换 ${MODEL_NAME} ..."

# 步骤1:导出ONNX
python export.py --weights $MODEL_PT \
                 --include onnx \
                 --img-size 640 640 \
                 --batch-size 1 \
                 --opset 12 \
                 --simplify

# 步骤2:简化模型
python -m onnxsim ${MODEL_NAME}.onnx ${MODEL_NAME}-sim.onnx

# 步骤3:ATC转换
atc --model=${MODEL_NAME}-sim.onnx \
    --framework=5 \
    --output=${MODEL_NAME}_${SOC_VERSION} \
    --soc_version=$SOC_VERSION \
    --input_format=NCHW \
    --input_shape="images:1,3,640,640" \
    --insert_op_conf=aipp_yolov5.config \
    --log=info

# 步骤4:验证
if [ -f "${MODEL_NAME}_${SOC_VERSION}.om" ]; then
    echo "✓ 转换成功!"
    echo "  输入模型: ${MODEL_NAME}.pt"
    echo "  输出模型: ${MODEL_NAME}_${SOC_VERSION}.om"
    echo "  芯片版本: ${SOC_VERSION}"
else
    echo "✗ 转换失败,请检查日志"
fi

十、进阶:不同YOLOv5版本的注意事项

版本 关键区别 ATC转换建议
YOLOv5 v6.0+ 输出层改变 使用--out_nodes指定正确输出节点
YOLOv5 v7.0 支持分类头 确认是否需要多任务输出
YOLOv5n/s/m/l/x 模型大小不同 大模型可能需要分片转换

十一、资源与下一步

学习资源

  1. 昇腾官方模型仓- 查看官方YOLOv5转换脚本
  2. ATC命令参考- 完整参数说明
  3. 昇腾论坛- 提问和交流

下一步行动

  1. 在Atlas 200DK上部署转换后的模型
  2. 尝试YOLOv6/YOLOv8的转换
  3. 实现端到端的完整应用(图像输入 → 检测结果输出)

成果展示

成功转换后,你将获得:

  • yolov5s_ascend.om- 昇腾离线模型
  • ✅ 比CPU快10-20倍的推理速度
  • ✅ 支持边缘设备部署的能力
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐