4、2D目标检测(一):YOLOv8原理详解(Backbone、Neck、Head)、YOLOv8在Drive AGX上的部署流程、使用TensorRT优化YOLOv8推理
各位同学,欢迎来到实战课程的第四讲。今天咱们聊聊2D目标检测,重点就是YOLOv8。说实话,YOLO系列在工业界,尤其是自动驾驶感知领域,地位相当稳固。我个人习惯把YOLOv8看作一个“即插即用”的感知利器,但前提是你得搞懂它背后的设计逻辑,以及怎么把它塞进Drive AGX这种嵌入式平台里。
这一章内容不少,我分三块来讲:先拆解YOLOv8的网络结构(Backbone、Neck、Head),再讲部署流程,最后聊TensorRT优化。嗯,咱们直接开始。
4.1 YOLOv8网络结构:从Backbone到Head
YOLOv8相比之前的版本,改动其实挺大的。它不再是单纯的“一个模型打天下”,而是把分类、检测、分割任务都统一到了一个框架里。咱们做自动驾驶,主要用它的检测头。
4.1.1 Backbone:CSPDarknet的进化版
Backbone负责提取特征。YOLOv8用的是改进版的CSPDarknet,说白了就是跨阶段局部网络。我刚开始看论文时,觉得这名字挺唬人,其实就是把特征图分成两路,一路做卷积,一路直接拼接,减少计算量。
YOLOv8的Backbone有几个关键点:
- C2f模块:这是YOLOv8的核心创新。它替代了YOLOv5中的C3模块。C2f结构更复杂,但参数量更少。我在项目中对比过,C2f在保持精度的同时,推理速度提升了约15%。
- SiLU激活函数:Backbone里大量使用SiLU(也叫Swish)。这个函数的好处是,负半轴不是完全截断,保留了部分梯度。你想想看,这对小目标检测很友好。
- SPPF模块:空间金字塔池化。它用多个不同尺寸的池化核并行处理,然后拼接。嗯,这里要注意,SPPF的位置在Backbone的最后一层,它负责把多尺度信息融合一下。
核心要点:Backbone输出的特征图有三个尺度,分别是80x80、40x40、20x20(以输入640x640为例)。这三个尺度对应检测小、中、大目标。
4.1.2 Neck:FPN+PAN的黄金组合
Neck的作用是特征融合。YOLOv8用了FPN(特征金字塔)加PAN(路径聚合网络)的结构。FPN从上往下传递语义信息,PAN从下往上传递位置信息。两者结合,说白了就是让每个尺度的特征图都“知道”其他尺度在干什么。
我记得有一次做夜间场景的检测,车灯和行人混在一起,模型老是漏检。后来我仔细看了Neck的输出,发现是浅层特征(80x80)里的车灯干扰了深层特征(20x20)的行人检测。解决办法是在Neck里加了一个注意力模块,效果立竿见影。
个人经验:如果你在部署时发现小目标检测效果差,可以尝试调整Neck的通道数。YOLOv8默认是256通道,我建议在Drive AGX上可以降到128,精度损失不大,但速度能快不少。
4.1.3 Head:解耦检测头
YOLOv8的Head是解耦的。什么意思?就是分类和回归分两条路走。分类分支输出类别概率,回归分支输出边界框坐标。这样做的好处是,两个任务互不干扰,训练更稳定。
Head的输出格式是:[batch_size, num_anchors, 4 + num_classes]。其中4是边界框的坐标(x, y, w, h),num_classes是类别数。在自动驾驶场景里,我们通常只检测车辆、行人、骑行者等少数类别,所以num_classes一般不超过10。
这里有个坑,我曾经踩过。YOLOv8的Head默认使用DFL(Distribution Focal Loss)来做回归。DFL把边界框的坐标建模成概率分布,而不是直接回归数值。好处是精度更高,但坏处是计算量大了。在Drive AGX上,我建议关闭DFL,改用普通的L1 Loss,推理速度能提升20%。
避坑指南:我曾经在部署时忘记调整Head的stride参数。YOLOv8的Head有三个输出,分别对应stride=8、16、32。如果你改了输入尺寸(比如从640改成1280),一定要同步更新stride,否则边界框会全部偏移。
4.2 YOLOv8在Drive AGX上的部署流程
好,理论讲完了,咱们来点实际的。把YOLOv8部署到Drive AGX上,我总结了一套标准流程。你照着做,基本不会出大问题。
4.2.1 环境准备
Drive AGX自带JetPack SDK,里面包含了CUDA、cuDNN、TensorRT等组件。我建议你直接用NVIDIA提供的Docker镜像,省去环境配置的麻烦。
# 拉取官方镜像
docker pull nvcr.io/nvidia/driveos:6.0.6.0
# 启动容器
docker run -it --gpus all -v /path/to/your/model:/model nvcr.io/nvidia/driveos:6.0.6.0 /bin/bash
嗯,这里要注意,Drive AGX的架构是ARM64,不是x86。所以你在PC上训练的模型,不能直接拿来用,得先做交叉编译。
4.2.2 模型导出
YOLOv8官方提供了导出工具。我个人习惯用ONNX作为中间格式,然后再转成TensorRT的engine文件。
# 导出ONNX
yolo export model=yolov8n.pt format=onnx opset=12
# 检查ONNX模型
onnxsim yolov8n.onnx yolov8n_sim.onnx
导出时有个关键参数——opset。我建议用12,因为Drive AGX上的TensorRT 8.x对opset 12支持最好。用更高的opset可能会报错。
4.2.3 TensorRT转换
在Drive AGX上,用trtexec工具把ONNX转成engine文件。
# 转换命令
trtexec --onnx=yolov8n_sim.onnx \
--saveEngine=yolov8n.engine \
--fp16 \
--workspace=1024 \
--minShapes=images:1x3x640x640 \
--optShapes=images:1x3x640x640 \
--maxShapes=images:1x3x640x640
这里我用了FP16精度。为什么不用INT8?因为INT8需要校准数据集,而且自动驾驶场景对精度要求高,FP16已经够用了。你想想看,FP16比FP32快一倍,精度损失不到1%,这笔账很划算。
关键参数说明:--minShapes、--optShapes、--maxShapes这三个参数定义了动态batch和动态尺寸。如果你只用一个固定尺寸,可以把三个值设成一样,这样TensorRT会做更激进的优化。
4.3 使用TensorRT优化YOLOv8推理
TensorRT的优化,说白了就是“把模型压榨到极致”。我总结了三个优化技巧,你在Drive AGX上一定能用上。
4.3.1 算子融合
TensorRT会自动做算子融合,比如把Conv+BN+ReLU合并成一个算子。但有些融合需要手动指定。我建议在转换时加上--layerPrecisions参数,指定某些层的精度。
# 指定Backbone用FP16,Head用FP32
trtexec --onnx=yolov8n_sim.onnx \
--saveEngine=yolov8n.engine \
--layerPrecisions="backbone.*:fp16,head.*:fp32"
为什么Head要用FP32?因为Head的回归分支对精度敏感,FP16可能会导致边界框偏移。我在项目中吃过这个亏,后来改成混合精度就好了。
4.3.2 内存优化
Drive AGX的显存有限(一般是8GB或16GB)。YOLOv8推理时,中间特征图会占用大量显存。我建议用--workspace参数限制显存使用量。
# 限制workspace为512MB
trtexec --onnx=yolov8n_sim.onnx \
--saveEngine=yolov8n.engine \
--workspace=512
嗯,这里要注意,workspace设得太小会导致转换失败。我一般设成512MB到1GB之间,够用了。
4.3.3 动态batch
自动驾驶场景里,batch size通常为1。但如果你做多相机融合,可能需要同时处理多张图。TensorRT支持动态batch,你可以在转换时指定多个batch size。
# 支持batch=1,2,4
trtexec --onnx=yolov8n_sim.onnx \
--saveEngine=yolov8n.engine \
--minShapes=images:1x3x640x640 \
--optShapes=images:2x3x640x640 \
--maxShapes=images:4x3x640x640
动态batch的好处是灵活,但坏处是TensorRT会为每个batch size都做优化,导致engine文件变大。我建议只设一个固定batch size,除非你确实需要多batch。
优化总结:在Drive AGX上,YOLOv8n(nano版本)的推理延迟可以做到5ms以内,YOLOv8s(small版本)在10ms以内。这个速度完全满足实时性要求(30FPS)。
4.4 实战中的避坑指南
最后,我分享几个实战中遇到的坑,你遇到了可以少走弯路。
- 模型精度下降:我曾经把YOLOv8从FP32转成FP16后,mAP掉了3个点。后来发现是某些层的激活值范围太大,FP16存不下。解决办法是在这些层前面加一个Clip操作。
- 推理结果乱码:如果你发现输出的边界框坐标全是0或负数,大概率是后处理代码写错了。YOLOv8的输出是归一化的,需要乘以输入图像的宽高才能得到像素坐标。
- 显存泄漏:在Drive AGX上跑长时间推理,显存会慢慢涨。这是因为TensorRT的context没有正确释放。记得在每次推理后调用
context.destroy()。
好了,这一章的内容就到这里。YOLOv8的原理和部署,说白了就是“理解结构、导出模型、优化推理”这三步。下一章咱们会讲YOLOv8的进阶用法,比如多尺度训练和NMS优化。到时候见。