3. 模型训练与导出:从标注到TensorRT的完整链路

这一章,我们正式进入YOLO部署的核心环节——模型训练与导出。说白了,就是把你手里的原始图片,变成能在嵌入式设备上跑起来的推理引擎。我做了这么多年嵌入式AI,见过太多人在这一步翻车:训练时精度很高,一导出就崩;或者导出的模型在PC上跑得好好的,上板就报错。嗯,这里面的坑,我一个个给你拆开讲。

3.1 自定义数据集标注——别在第一步偷懒

数据集的质量,直接决定模型的天花板。我刚开始做项目时,总觉得标注差不多就行,结果模型训练出来,检测框总是歪歪扭扭。后来花了三倍时间重新标注,才把精度拉回来。所以,标注这件事,值得你认真对待。

3.1.1 标注工具选择

我个人习惯用LabelImg,开源、轻量、支持YOLO格式直接导出。当然,LabelStudio、CVAT这些也都不错。选哪个不重要,关键是格式要对。

工具 输出格式 适用场景
LabelImg YOLO .txt / Pascal VOC .xml 小团队、快速标注
LabelStudio JSON / COCO / YOLO 多人协作、复杂标注
CVAT 多种格式 大规模标注、自动标注辅助

3.1.2 YOLO标注格式详解

YOLO的标注格式很简单:每张图片对应一个同名的.txt文件,每行代表一个目标。格式是:

<class_id> <x_center> <y_center> <width> <height>

注意,这里的坐标都是归一化的,范围0~1。x_center和y_center是目标框中心点相对于图片宽高的比例,width和height是框的宽高相对于图片宽高的比例。

我曾经踩过的坑: 标注时坐标格式搞混了,把坐标当成了归一化坐标。训练出来的模型,检测框全跑到图片边缘。检查了三天才发现是标注文件的问题。所以,标注完一定要抽检几份文件,用脚本可视化验证一下。

3.1.3 数据集划分

标注完成后,把数据集分成三份:训练集、验证集、测试集。我一般按8:1:1的比例划分。注意,测试集要完全独立,不能参与任何训练决策。

# 简单的划分脚本
import os, random, shutil

images = os.listdir('images/')
random.shuffle(images)
train = images[:int(0.8*len(images))]
val = images[int(0.8*len(images)):int(0.9*len(images))]
test = images[int(0.9*len(images)):]

3.2 YOLOv8训练流程——从配置文件到模型权重

YOLOv8的训练流程,比之前的版本简化了不少。你只需要准备好数据集和配置文件,一行命令就能跑起来。但简化不等于无脑,里面的参数调优还是很有讲究的。

3.2.1 准备数据集配置文件

创建一个data.yaml文件,告诉YOLOv8你的数据在哪:

train: /path/to/train/images
val: /path/to/val/images
test: /path/to/test/images

nc: 3  # 类别数量
names: ['person', 'car', 'bicycle']  # 类别名称

3.2.2 选择预训练模型

YOLOv8提供了多种规模的预训练模型:nano、small、medium、large、xlarge。对于嵌入式部署,我建议从nano或small开始。nano模型参数量最小,适合资源受限的设备;small模型在精度和速度之间取得了不错的平衡。

模型 参数量 mAP50-95 推理速度(FP16)
YOLOv8n 3.2M 37.3% ~1.5ms
YOLOv8s 11.2M 44.9% ~2.5ms
YOLOv8m 25.9M 50.2% ~4.5ms

3.2.3 开始训练

一行命令启动训练:

yolo train model=yolov8n.pt data=data.yaml epochs=100 imgsz=640 batch=16

这里有几个关键参数:

  • epochs:训练轮数。我一般设100~300轮,看验证集loss不再下降就停。
  • imgsz:输入图片尺寸。嵌入式设备建议用640或320,太大影响速度。
  • batch:批次大小。根据显存调整,能大尽量大。
我的经验: 训练时一定要用TensorBoard或WandB监控loss曲线。如果训练loss下降但验证loss上升,那就是过拟合了,赶紧加数据增强或早停。

3.3 模型量化——FP16和INT8

模型量化,说白了就是把模型参数的精度降低,换来更小的体积和更快的推理速度。嵌入式设备上,这一步几乎是必须的。

3.3.1 FP16量化

FP16量化是最简单的,几乎无损。把模型权重从FP32转成FP16,体积减半,速度提升明显。YOLOv8训练完成后,可以直接导出FP16模型:

yolo export model=best.pt format=onnx half=True

我在Jetson Nano上测试过,FP16比FP32快了将近一倍,精度只掉了不到0.5%。

3.3.2 INT8量化

INT8量化更激进,精度损失也更大。它需要校准数据集来统计激活值的分布。YOLOv8的INT8量化流程:

yolo export model=best.pt format=onnx int8=True data=data.yaml

注意,INT8量化需要你提供校准数据,一般从训练集里取几百张就够了。

我曾经踩过的坑: INT8量化后,模型在边缘设备上推理结果全乱套了。排查后发现是校准数据集和训练集分布不一致。校准数据一定要能代表真实场景,否则量化后的模型会“失明”。

3.4 导出为ONNX/TensorRT格式

训练好的模型,最终要导出成部署格式。ONNX是中间格式,TensorRT是NVIDIA平台上的优化引擎。

3.4.1 导出ONNX

ONNX是跨平台的模型交换格式。YOLOv8导出ONNX很简单:

yolo export model=best.pt format=onnx

导出的ONNX模型可以用Netron可视化查看结构。我建议你养成这个习惯,检查一下输入输出节点是否正确。

3.4.2 导出TensorRT

TensorRT是NVIDIA的推理优化引擎,能在Jetson系列设备上发挥极致性能。导出TensorRT引擎有两种方式:

  • 直接导出:YOLOv8支持直接导出TensorRT:yolo export model=best.pt format=engine
  • 通过ONNX转:先用ONNX导出,再用trtexec工具转换:trtexec --onnx=best.onnx --saveEngine=best.engine --fp16

我个人更推荐第二种方式,可控性更强,可以指定动态batch、精度模式等参数。

3.5 本章知识体系

下面这张图,把整个流程串起来了:

模型训练与导出流程 模型训练与导出完整流程 数据采集与标注 LabelImg / LabelStudio YOLOv8训练 预训练模型 + 自定义数据 模型量化 FP16 / INT8 模型导出 ONNX / TensorRT 关键决策点 • 数据集质量决定模型上限 • 量化精度与速度的权衡 • 导出格式需匹配目标平台 部署目标平台 Jetson Nano / Xavier / Orin | Raspberry Pi | RK3588 | 手机端NPU

3.6 常见问题与避坑指南

最后,分享几个我实际项目中遇到的坑,希望能帮你少走弯路:

  • 标注格式错误:标注文件里的坐标必须是归一化的,而且类别ID要从0开始。我曾经用1开始,结果模型把背景当成了第0类,检测结果全乱套。
  • 训练时显存溢出:减小batch size或图片尺寸。如果还不行,试试梯度累积。
  • 量化后精度骤降:检查校准数据集是否具有代表性。另外,INT8量化对某些层特别敏感,可以尝试逐层量化或混合精度量化。
  • TensorRT导出失败:确保ONNX模型结构正确,有些算子TensorRT不支持,需要手动替换。
核心要点: 模型训练与导出,本质上是“数据→权重→引擎”的转化过程。每一步都有坑,但只要你理解了背后的原理,就能从容应对。记住,嵌入式部署的终极目标是在有限的算力下,跑出尽可能高的精度和速度。