3. YOLO模型训练与部署准备

在FPGA上实现YOLO实时检测加速器之前,必须先在PC端完成一个“可训练、可验证、可导出”的模型准备闭环。这一章的核心目标不是追求最高mAP,而是得到一个结构干净、算子友好、量化可落地的YOLO模型,并把它稳定地导出为ONNX,为后续的定点量化、硬件映射和上板调试提供输入。很多上板失败的问题,根源其实在训练与导出阶段就已经埋下。

一、数据集构建:从采集到划分

YOLO是监督学习模型,数据质量直接决定部署上限。数据集构建通常包含采集、清洗、标注、划分四个步骤。采集时要覆盖真实部署场景的光照、角度、遮挡和运动模糊;清洗时剔除重复帧、严重失焦和错误标注;划分时按场景或时间段切分训练集、验证集和测试集,避免同一视频的相邻帧同时出现在训练和验证中造成“数据泄漏”。

一个典型的数据集目录结构如下:

dataset/
├── images/
│   ├── train/  (约70%)
│   ├── val/    (约20%)
│   └── test/   (约10%)
├── labels/
│   ├── train/
│   ├── val/
│   └── test/
└── data.yaml

其中 data.yaml 描述类别数与路径:

path: ./dataset
train: images/train
val: images/val
test: images/test
nc: 3
names: ['person', 'car', 'bicycle']

二、标注格式:YOLO txt 与坐标归一化

YOLO使用每张图一个 .txt 的标注文件,每行表示一个目标,格式为:

class_id  x_center  y_center  width  height

四个坐标均为相对图像宽高的归一化值,范围0~1。例如图像宽640、高480,目标框中心在(320,240)、宽128、高96,则标注为:

0  0.500000  0.500000  0.200000  0.200000

常见误区是把像素坐标直接写入标注文件,导致训练时框全部偏移或缩放异常。若原始数据是VOC XML或COCO JSON,可用脚本批量转换。转换后务必用可视化脚本抽查若干张,确认框与目标对齐。

标注完成后建议统计每个类别的实例数量与目标尺寸分布。若小目标占比很高,后续在FPGA上要特别关注浅层特征图的分辨率与带宽开销。

三、训练环境搭建与模型训练

推荐使用Python 3.9+、PyTorch 2.x、CUDA 11.8/12.x,并安装Ultralytics YOLO框架。环境安装命令示例:

conda create -n yolo python=3.10 -y
conda activate yolo
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118
pip install ultralytics onnx onnxruntime opencv-python

以YOLOv8n为例,训练命令如下:

yolo detect train \
  model=yolov8n.pt \
  data=dataset/data.yaml \
  epochs=100 \
  imgsz=640 \
  batch=16 \
  device=0 \
  project=runs/train name=yolo_fpga

训练过程中重点关注三类曲线:损失下降是否平稳、mAP@0.5与mAP@0.5:0.95是否持续上升、验证集是否过早饱和或过拟合。若验证损失上升而训练损失继续下降,应减小模型规模或增强数据增广。

YOLO训练与部署准备流程 数据采集与清洗 images / labels 标注与格式转换 YOLO txt 归一化 训练环境搭建 PyTorch + CUDA 模型训练 epochs / imgsz 验证与调优 mAP / 混淆矩阵 导出ONNX opset / 动态轴 ONNX检查 onnxruntime 推理 输出:结构干净、算子友好、可量化、可映射到FPGA的ONNX模型 为后续定点量化与硬件加速器设计提供输入

四、模型验证与导出ONNX

训练完成后,用验证集评估模型:

yolo detect val model=runs/train/yolo_fpga/weights/best.pt data=dataset/data.yaml imgsz=640

确认mAP与推理速度满足预期后,导出ONNX。FPGA部署通常要求固定输入尺寸、固定batch=1、opset版本与推理引擎匹配:

yolo export model=best.pt format=onnx imgsz=640 opset=12 simplify=True dynamic=False

导出后必须用onnxruntime做一次数值一致性检查,确认ONNX输出与PyTorch输出误差在可接受范围内:

import onnxruntime as ort
import numpy as np
sess = ort.InferenceSession("best.onnx")
x = np.random.randn(1,3,640,640).astype(np.float32)
out = sess.run(None, {sess.get_inputs()[0].name: x})
print(out[0].shape)

五、常见问题与复盘清单

问题现象 可能原因 处理建议
训练loss不下降 标注坐标未归一化、类别ID越界 检查txt格式与data.yaml类别数
mAP正常但ONNX输出异常 导出时dynamic轴或opset不兼容 固定输入尺寸,opset设为11/12
ONNX包含大量自定义算子 使用了非标准后处理或插件 导出时simplify,剥离后处理
量化后精度骤降 激活分布极端、缺少校准集 增加校准样本,检查BN融合
上板后检测框错位 预处理归一化方式不一致 统一letterbox与归一化参数
导出ONNX时不要保留NMS等后处理算子。FPGA加速器通常只负责卷积与特征提取,NMS应在CPU或软核中完成。把后处理留在ONNX里会显著增加硬件映射难度。

完成本章后,你应当得到一个经过验证的ONNX模型,并清楚它的输入输出张量形状、算子组成和量化敏感层。这些信息将直接决定后续定点量化策略与FPGA数据通路的位宽设计。