Ascend-SACT/Yolov8x
模型介绍文件和版本Pull Requests讨论分析
下载使用量0

YOLOv8x 昇腾 NPU 迁移项目

YOLOv8x (68.2M params, 258.5 GFLOPS) 目标检测模型在昇腾 910B3 NPU 上的完整迁移,包含训练、推理验证与 ONNX/OM 导出。

📋 目录

  • 环境要求
  • 依赖安装
  • 训练
  • 多卡训练(DDP)
  • 推理验证
  • 导出 ONNX / OM
  • 精度对比(CPU vs NPU)
  • 性能对比(NPU vs GPU)
  • 性能与精度结果
  • 已知问题与解决方案

📦 环境要求

组件版本
CANN8.5.0
torch2.7.1
torch_npu2.7.1
torchvision0.22.1
ultralytics8.3.0
Python3.11.14
NPUAscend 910B3 × 8

⚠️ 重要: 必须使用 source /usr/local/Ascend/cann-8.5.0/set_env.sh(不是 ascend-toolkit/set_env.sh)


📥 依赖安装

# 从华为云镜像安装核心依赖
pip install torch==2.7.1 torchvision==0.22.1 --no-deps \
  -i https://mirrors.huaweicloud.com/repository/pypi/simple

pip install torch_npu==2.7.1 --no-deps \
  -i https://mirrors.huaweicloud.com/repository/pypi/simple

pip install ultralytics==8.3.0 pycocotools onnx \
  -i https://mirrors.huaweicloud.com/repository/pypi/simple

# ⚠️ ultralytics 会自动升级 torch,安装后必须回退
pip install torch==2.7.1 --no-deps \
  -i https://mirrors.huaweicloud.com/repository/pypi/simple

完整依赖列表见 src/requirements.txt


🏋️ 训练

source /usr/local/Ascend/cann-8.5.0/set_env.sh
python3 src/ascend_train.py
产物路径说明
训练权重yolov8x-trained.pt来自 YOLO.train last.pt (119MB)
timing JSONsrc/npu_timing.json7字段性能数据
loss 日志src/npu_loss_b1.logbox/cls/dfl loss 曲线

核心: 脚本开头必须 from torch_npu.contrib import transfer_to_npu,它将所有 torch.cuda.* 全局替换为 torch.npu.*,解决 ultralytics 与 torch+cpu 不兼容。


🏋️ 多卡训练(DDP)

环境要求

多卡训练需要在每台机器上启动多个进程,使用 HCCL(华为集合通信库)进行通信。

# 确认 NPU 数量 ≥ 2
python3 -c "import torch; print(torch.npu.device_count())"

多卡训练命令

source /usr/local/Ascend/cann-8.5.0/set_env.sh

# 启动 4 卡训练
# WORLD_SIZE=4 表示总卡数,RANK=0/1/2/3 表示当前卡编号
python3 -m torch.distributed.launch \
    --nproc_per_node=4 \
    --nnodes=1 \
    --node_rank=0 \
    --master_addr=127.0.0.1 \
    --master_port=29500 \
    src/ascend_train_ddp.py

多卡训练脚本(ascend_train_ddp.py)

核心改动:

import torch.distributed as dist
import torch_npu
from torch_npu.contrib import transfer_to_npu

# HCCL 后端(NPU 专用)
dist.init_process_group(backend="hccl")

# 每个进程绑定对应 NPU 设备
local_rank = int(os.environ["LOCAL_RANK"])
torch.npu.set_device(f"npu:{local_rank}")

# 加载模型并分布式包装
model = YOLO('yolov8x.pt').model
model = model.to(f"npu:{local_rank}")
model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[local_rank])

# 训练时使用 local_rank 对应的设备
model.train(data='coco128.yaml', ..., device=str(local_rank), ...)

多卡关键参数

参数单卡多卡(4卡)说明
batch_size416(每卡4)总 batch = 单卡batch × 卡数
lr(学习率)0.010.04按线性缩放规则:lr × √卡数
workers00建议 workers=0 避免数据加载冲突
ampFalseFalseCANN 8.5.0 限制

多卡产物

产物路径说明
DDP checkpointyolov8x-trained-ddp.pt多卡训练权重
DDP timingddp_timing.json7字段性能数据
DDP loss 日志ddp_loss.log每卡 loss 曲线

多卡注意事项

  • 先绑设备后初始化:必须先 torch.npu.set_device() 再 init_process_group
  • float64 禁止:HCCL 不支持 float64,全部使用 float32
  • 设备一致性:所有进程必须使用相同的 backend="hccl"
  • 数据打乱:DDP 默认对数据做 shuffle,不需额外处理

🔍 推理验证

source /usr/local/Ascend/cann-8.5.0/set_env.sh
python3 src/ascend_infer.py

产物:src/perf_summary.json(性能)、src/accuracy_summary.json(精度)


📤 导出 ONNX / OM

导出 ONNX:

import torch
from ultralytics import YOLO
model = YOLO('yolov8x.pt').model.eval()
torch.onnx.export(model, torch.randn(1, 3, 640, 640),
    'yolov8x.onnx', opset=11,
    input_names=['images'], output_names=['output'],
    dynamic_axes={'images': {0: 'batch'}, 'output': {0: 'batch'}})

导出 OM:

atc --model=yolov8x.onnx --framework=5 --output=yolov8x \
    --soc_version=Ascend910B3 --input_shape="images:1,3,640,640"

📊 精度对比(CPU vs NPU)

对比目标

在相同数据集(COCO128, 128张)上,对比 CPU 推理和 NPU 推理的端到端精度指标:

  • mAP@50、mAP@50-95
  • precision、recall
  • bbox 坐标误差
  • 置信度误差

对比方法

Step 1: CPU 推理

from ultralytics import YOLO

model = YOLO('yolov8x.pt')
cpu_results = model.val(data='coco128.yaml', device='cpu', imgsz=640, batch=8)
print(f"CPU mAP50: {cpu_results.box.map50:.6f}")
print(f"CPU mAP50-95: {cpu_results.box.map:.6f}")

产物:cpu_val_result.json

Step 2: NPU 推理

from ultralytics import YOLO

model = YOLO('yolov8x.pt')
npu_results = model.val(data='coco128.yaml', device='npu:0', imgsz=640, batch=8)
print(f"NPU mAP50: {npu_results.box.map50:.6f}")
print(f"NPU mAP50-95: {npu_results.box.map:.6f}")

产物:npu_val_result.json

Step 3: 精度差异统计

import json

cpu = json.load(open('cpu_val_result.json'))
npu = json.load(open('npu_val_result.json'))

metrics = ['mAP50', 'mAP50-95', 'precision', 'recall']
for m in metrics:
    diff = npu[m] - cpu[m]
    rel_pct = diff / cpu[m] * 100 if cpu[m] != 0 else 0
    print(f"{m}: CPU={cpu[m]:.6f}, NPU={npu[m]:.6f}, diff={diff:.6f} ({rel_pct:.4f}%)")

精度对比判定标准

指标差异评级说明
< 0.1%极优秀NPU 与 CPU 几乎完全一致
0.1% ~ 1%优秀可接受,存在正常数值误差
1% ~ 5%可接受需要检查是否由 NPU 算子精度差异导致
> 5%不合格需排查 NPU 适配问题

常见精度差异原因

现象可能原因排查方法
mAP 差异 > 1%NPU 算子与 CPU 计算顺序不同检查 loss 值是否正常
bbox 坐标漂移数据类型不匹配(fp16 vs fp32)确认 amp=False
recall 明显下降激活函数精度问题检查 activation 是否在 fp32

📊 性能对比(NPU vs GPU)

对比目标

对比 YOLOv8x 在 NPU(Ascend 910B3)和 GPU(NVIDIA A100/V100)上的训练和推理性能。

训练性能对比

指标NPU (910B3)GPU (A100)说明
1 epoch 耗时~95s (batch=4)参考值相同 batch 配置
吞吐量实测参考值img/s
AMP 加速不支持(CANN 8.5.0)支持NPU 需 fp32

测量方法:

import time, json

# NPU 训练计时
start = time.time()
# ... NPU 训练代码 ...
npu_time = time.time() - start

# 产出 timing JSON
timing = {
    "total_train_ms": int(npu_time * 1000),
    "avg_step_ms": int(npu_time * 1000 / steps),
    "throughput_samples_per_sec": round(batch * steps / npu_time, 2),
}

推理性能对比

import time
from ultralytics import YOLO

# 准备测试图像
img_files = sorted(Path('/workspace/Yolov8x/dataset/coco128/images/train2017').glob('*.jpg'))[:50]

# NPU 推理计时
model_npu = YOLO('yolov8x.pt')
npu_times = []
for img in img_files:
    t0 = time.time()
    model_npu(img, device='npu:0', verbose=False)
    npu_times.append((time.time() - t0) * 1000)

npu_mean = sum(npu_times) / len(npu_times)
print(f"NPU mean latency: {npu_mean:.2f}ms")

# GPU 推理计时(同理,device='0' on GPU machine)
# gpu_mean = ...

性能对比判定标准

对比项正常范围异常阈值
NPU vs GPU 推理延迟比0.8x ~ 1.5x> 2x(需优化)
NPU 吞吐取决于 batchbatch=8 时约 3.9 img/s

📊 性能与精度结果

推理性能(NPU, batch=8, 640×640)

指标值
mean 延迟23.74 ms/image
p50 延迟23.74 ms/image
吞吐3.9 img/s

精度对比(COCO128, 128张)

指标CPU 基线NPU差异
mAP@500.87130.8713< 0.001%
mAP@50-950.67340.6734< 0.001%
precision0.72000.7200< 0.001%
recall0.76070.7607< 0.001%

结论: NPU 推理精度与 CPU 完全一致,Grade = 极优秀

训练性能(1 epoch, batch=4, COCO128)

指标值
训练耗时~95s
权重文件yolov8x-trained.pt (119MB)
AMP禁用(fp32 训练)

⚠️ 已知问题与解决方案

1. torch+cpu + ultralytics CUDA lazy_init 崩溃

现象: RuntimeError: Could not run 'aten::empty_strided' with arguments from the 'CUDA' backend 根因: torch+cpu 没有 CUDA backend,ultralytics 内部 torch.cuda.FloatTensor 触发 C++ 层 _lazy_init() 解决: 在 ultralytics 之前添加 from torch_npu.contrib import transfer_to_npu

2. CANN OPP 路径不存在

现象: ASCEND_OPP_PATH: /usr/local/Ascend/ascend-toolkit/latest/opp does not exist 解决: source /usr/local/Ascend/cann-8.5.0/set_env.sh(不是 ascend-toolkit/set_env.sh)

3. CANN 8.5.0 AICPU NonZero 507018

现象: AMP 训练时触发 NonZero 算子错误 解决: 训练时 amp=False(已由脚本自动处理)

4. ultralytics 覆盖 torch 版本

现象: pip install ultralytics 自动升级 torch 到 2.11.0 解决: 安装后回退 pip install torch==2.7.1 --no-deps


📁 仓库结构

Yolov8x/
├── README.md                           # 迁移指导书(本文件)
├── src/
│   ├── ascend_train.py                  # NPU 单卡训练脚本
│   ├── ascend_train_ddp.py              # NPU 多卡 DDP 训练脚本(≥2卡时使用)
│   ├── ascend_infer.py                  # NPU 推理脚本
│   ├── ascend_infer_benchmark.py       # NPU 推理性能评测脚本
│   ├── coco128.yaml                    # 数据集配置
│   ├── requirements.txt                 # 核心依赖清单
│   ├── npu_timing.json                  # 单卡训练 timing(7字段)
│   ├── ddp_timing.json                  # 多卡 DDP 训练 timing(7字段)
│   ├── npu_loss_b1.log                 # 单卡训练 loss 日志
│   ├── ddp_loss.log                    # 多卡训练 loss 日志
│   ├── npu_phase4_val_report.json     # Phase 4 精度验证报告
│   ├── four_phase_accuracy_summary.json # 四阶段精度汇总
│   ├── benchmark_results.json           # 推理性能统计
│   ├── accuracy_summary.json            # CPU vs NPU 精度统计
│   ├── perf_summary.json                # 推理性能(原始)
│   ├── cpu_val_result.json              # CPU 推理结果(精度基线)
│   ├── npu_val_result.json              # NPU 推理结果(精度验证)
│   └── single_vs_multicard.json         # 单卡 vs 多卡性能对比

适配时间: 2026-04-23 | 适配目录: /workspace/Yolov8x-npu-migration