Files
AI-tianyan/docs/CONVERSION_GUIDE.md
master ee9be85292 fix: ACL bytes_to_ptr GC bug + 添加 NPU 模型转换指南
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: 一键启动脚本
2026-05-06 23:30:49 +08:00

166 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 昇腾 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 步:安装依赖
```bash
# 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
```bash
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关键步骤
```bash
# 设置环境变量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 步:部署模型
```bash
# 复制模型到指定位置
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需要
1. `pip install ultralytics yolov10`
2. 导出:`YOLOv10('yolov10n.pt').export(format='onnx', ...)`
3. 修改 `OUTPUT_FORMAT=nms_free` (YOLOv10 输出格式不同)
4. 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`**
```python
# 错误写法 - 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
```python
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)
```