python/infer_server.py: - 修复 acl.util.bytes_to_ptr() 后 bytes 被 GC 回收导致全零输出的 bug - 保持 bytes 引用直到 memcpy 完成 - 正确解析 FP16 模型输出 (half precision -> float32) docs/CONVERSION_GUIDE.md: - 完整的 YOLOv8 ONNX->OM 转换流程文档 - ATC 转换参数详解及低内存设备配置 - 已知问题排查表 (OOM/全零输出/算子不支持) - 其他 YOLOv8 尺寸模型参考 start.sh: 一键启动脚本
4.6 KiB
4.6 KiB
昇腾 NPU 模型转换指南
硬件: Atlas 200I DK2 / 昇腾 310B4 | CANN 6.2.RC2 | YOLOv8n
方案概要
YOLOv8n.pt → YOLOv8n.onnx → yolov8n.om (ACL原生推理)
| 步骤 | 工具 | 耗时 | 资源需求 |
|---|---|---|---|
| 1. 下载权重 | ultralytics | ~1min | 无 |
| 2. 导出 ONNX | ultralytics | ~85s | 需安装 onnx/onnxslim |
| 3. ATC 转换 OM | CANN atc | ~16min | 3GB RAM + swap, 单核编译 |
环境要求
Ubuntu 22.04 aarch64
Python 3.9+ (miniconda)
CANN 6.2.RC2 (已安装于 /usr/local/Ascend/ascend-toolkit/latest)
Go 1.22+ (编译 edge-agent)
详细步骤
第 1 步:安装依赖
# Python 依赖
pip3 install "numpy>=1.24.0,<2.0.0"
pip3 install ultralytics opencv-python-headless
# Go (系统源版本太低, 需手动安装)
wget https://go.dev/dl/go1.22.5.linux-arm64.tar.gz
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.5.linux-arm64.tar.gz
rm go1.22.5.linux-arm64.tar.gz
export PATH=/usr/local/go/bin:$PATH
# 验证
python3 -c "import ultralytics; print('OK')"
go version # 应输出 go1.22.5
第 2 步:导出 ONNX
cd /path/to/AI-tianyan
python3 -c "
from ultralytics import YOLO
model = YOLO('yolov8n.pt') # 或自定义模型路径
model.export(format='onnx', imgsz=640, dynamic=False, opset=11, simplify=True)
"
输出:yolov8n.onnx (约 13MB)
注意事项:
dynamic=False:静态输入尺寸,ATC 转换必须opset=11:CANN 6.2 最稳定的 ONNX opset 版本simplify=True:使用 onnxslim 简化模型图
第 3 步:ATC 转换 OM(关键步骤)
# 设置环境变量(3GB RAM 设备必须)
export TBE_IMPL_MODE=AI_CORE # 使用 AI Core 模式
export TE_PARALLEL_COMPILER=1 # 单线程编译,避免内存溢出
# 执行转换
atc \
--model=yolov8n.onnx \
--framework=5 \ # 5 = ONNX
--output=yolov8n \
--soc_version=Ascend310B4 \ # 昇腾 310B4
--input_format=NCHW \
--input_shape="images:1,3,640,640" \
--output_type=FP16 # FP16 更快,精度够用
# 约 16 分钟后得到 yolov8n.om (约 7.1MB)
常见失败原因:
| 错误 | 原因 | 解决 |
|---|---|---|
| EOFError / 进程崩溃 | TBE 多线程编译 OOM | 设置 TE_PARALLEL_COMPILER=1 |
| 输出全零 | bytes_to_ptr() 后 bytes 被 GC |
保持 bytes 引用见 infer_server.py |
| 算子不支持 | ONNX opset 版本过高 | 改用 opset=11 |
第 4 步:部署模型
# 复制模型到指定位置
cp yolov8n.om /opt/tianyan-edge/model/model.om
# 更新类别文件(COCO 80 类)
cp model/names.txt /opt/tianyan-edge/model/names.txt
# 启动服务
cd /opt/tianyan-edge
python3 python/infer_server.py # 推理服务
./bin/edge-agent -config config/edge.yaml # 主控 Agent
其他模型
| 模型 | 导出命令 | 输出尺寸 |
|---|---|---|
| yolov8s | YOLO('yolov8s.pt').export(...) |
22.5MB onnx → 11MB om |
| yolov8m | YOLO('yolov8m.pt').export(...) |
51.9MB onnx → 25MB om |
| yolov8l | YOLO('yolov8l.pt').export(...) |
88.3MB onnx → 43MB om |
| yolov8n | YOLO('yolov8n.pt').export(...) |
12.3MB onnx → 7.1MB om |
注意: 大模型转换需要更多内存,建议 8GB+ RAM。
YOLOv10 注意事项
如果用 YOLOv10,需要:
pip install ultralytics yolov10- 导出:
YOLOv10('yolov10n.pt').export(format='onnx', ...) - 修改
OUTPUT_FORMAT=nms_free(YOLOv10 输出格式不同) - ATC 命令完全相同
已知 Bug 修复
ACL bytes_to_ptr 提前 GC 问题
问题描述: acl.util.bytes_to_ptr(blob.tobytes()) 返回的指针,
由于 blob.tobytes() 创建的临时 bytes 对象被 Python GC 回收,
导致后续 acl.rt.memcpy 读到全零数据。
修复方案(见 python/infer_server.py):
# 错误写法 - bytes 临时对象会被 GC
host_addr = acl.util.bytes_to_ptr(blob.tobytes())
acl.rt.memcpy(in_buf, in_sz, host_addr, in_sz, ...) # 读到空数据
# 正确写法 - 保持引用
host_bytes = blob.tobytes() # 保持引用
host_addr = acl.util.bytes_to_ptr(host_bytes)
acl.rt.memcpy(in_buf, in_sz, host_addr, in_sz, ...) # 数据正确
FP16 输出解析
ATC 转换时指定 --output_type=FP16 后,模型输出为 half precision。
需要按半精度读取并转为 float32:
host_bytes = host.tobytes()
acl.rt.memcpy(acl.util.bytes_to_ptr(host_bytes), sz, out_buf, sz, ...)
host_from_bytes = np.frombuffer(host_bytes, dtype=np.uint8)
out_fp16 = host_from_bytes.view(np.float16)
output = out_fp16.astype(np.float32).reshape(shape)