1. 为什么DAMO-YOLO值得单独拿出来聊
目标检测这个圈子里,YOLO系列一直是绕不开的存在。从最早的YOLOv1到后来的v5、v6、v7、v8,再到近两年各种变体,几乎每隔几个月就有一个新版本冒出来。但说实话,大部分版本之间的差异并没有宣传得那么大,很多时候只是在骨干网络、数据增强或者标签分配策略上做了微调。直到阿里达摩院把DAMO-YOLO开源出来,我才觉得这一波确实有点不一样的东西。
DAMO-YOLO的核心定位很明确:在保持YOLO系列一贯的推理速度优势的前提下,把精度往上再推一个台阶。它不是一个简单的“换骨干+调参”的产物,而是从搜索空间设计、特征融合方式到训练策略都做了系统性重新思考。我第一次跑通它的官方配置时,最直观的感受是:同样在T4上跑640分辨率的输入,它的mAP比同量级的YOLOv5高出好几个点,而帧率几乎没有下降。这个性价比在工业部署场景里是非常有吸引力的。
这篇文章适合谁看?如果你正在做目标检测相关的项目选型,或者已经在用YOLO系列但觉得精度不够、想找一个更优的替代方案,又或者你只是单纯想了解当前开源检测器的最新进展,那这篇内容应该能给你一些直接可用的参考。我会从设计思路、核心模块、实操部署、常见问题几个角度展开,尽量把我在实际使用中踩过的坑和总结的技巧都写出来。
2. DAMO-YOLO的整体设计思路拆解
2.1 它到底解决了YOLO系列的哪些痛点
YOLO系列最大的优势是速度快、结构简单、部署友好,但它的精度上限一直是个问题。尤其是在小目标检测、密集场景和类别不平衡的数据集上,YOLO的表现往往不如Faster R-CNN或者DETR这类方法。DAMO-YOLO要解决的核心矛盾就是:如何在不大幅增加计算量的前提下,把YOLO的精度拉到接近甚至超过两阶段检测器的水平。
达摩院团队的做法不是简单地堆叠更深的网络或者更大的输入分辨率,而是从三个层面同时入手。第一,重新设计骨干网络,用神经架构搜索的方式找到更适合检测任务的backbone结构。第二,改进特征融合模块,让不同尺度的特征能够更高效地交互。第三,引入更先进的标签分配策略和训练技巧,让模型在训练阶段就能学到更好的表示。
这三个层面是相互配合的,单独拿出任何一个都不足以解释DAMO-YOLO的性能提升。我个人的理解是,它本质上是在YOLO的框架内,把近年来目标检测领域的一些最佳实践做了系统性的整合和优化。
2.2 骨干网络的设计哲学
DAMO-YOLO的骨干网络叫MAE-NAS,这个名字听起来有点绕,但核心思想其实不复杂。传统的YOLO骨干比如CSPDarknet,是人工设计出来的,虽然经过多次迭代已经比较成熟,但终究受限于人的先验知识。MAE-NAS则是通过架构搜索的方式,在一个预定义的搜索空间里自动找到最优的结构组合。
这个搜索空间的设计很关键。如果搜索空间太大,搜索成本会高到无法接受;如果太小,又找不到真正有突破性的结构。达摩院的做法是聚焦在几个对检测任务最关键的维度上:卷积核大小、通道数、层数、以及不同阶段的连接方式。最终搜索出来的结构在ImageNet分类任务上可能不是最优的,但在检测任务上表现非常好。这说明分类和检测对骨干网络的需求确实存在差异,直接拿分类网络来当检测骨干并不总是最优解。
在实际使用中,你不需要自己去跑搜索过程,官方已经给出了搜索好的几个版本,分别对应不同的计算量预算。我建议根据自己的硬件条件直接选对应的版本就行,没必要从头搜索。
2.3 特征融合的改进:从FPN到更高效的结构
特征金字塔网络是目标检测里的标配组件,YOLO系列从v3开始就一直在用。但传统的FPN结构存在一个问题:不同尺度的特征在融合时,信息传递的效率不够高,尤其是深层语义信息和浅层细节信息之间的交互往往不够充分。
DAMO-YOLO在这方面做了一个叫RepGFPN的改进。它的核心思路是在特征融合路径上引入重参数化技术,让训练时的多分支结构在推理时可以合并成单分支,既保证了训练时的表达能力,又不增加推理时的计算量。这个思路其实和RepVGG那篇工作是一脉相承的,但DAMO-YOLO把它用在了特征融合环节,效果很明显。
我实测下来的感受是,RepGFPN对小目标的检测提升尤其明显。在一些密集小目标的场景里,比如无人机航拍或者监控画面,DAMO-YOLO的召回率比YOLOv5高出不少。这应该就是特征融合改进带来的直接收益。
2.4 标签分配与训练策略的细节
标签分配是目标检测训练中一个容易被忽视但影响很大的环节。简单来说,就是决定哪些预测框应该被分配为正样本、哪些是负样本。YOLOv5用的是基于宽高比的分配策略,YOLOv8换成了Task-Aligned Assignment,而DAMO-YOLO用的是SimOTA的改进版本。
SimOTA的核心思想是动态分配,根据每个真实框的上下文情况来决定正样本的数量和位置,而不是用固定的规则。这样做的好处是对于不同大小、不同遮挡程度的目标,都能找到合适的正样本匹配方式。我在训练自己的数据集时发现,用SimOTA的收敛速度确实比固定分配策略要快,而且最终精度也更稳定。
训练策略方面,DAMO-YOLO用了Mosaic增强、MixUp、余弦退火学习率等一套组合拳。这些技巧在YOLOv5里也有,但DAMO-YOLO在参数设置上做了一些调整,比如Mosaic的概率和关闭时机。官方配置里Mosaic会在最后15个epoch关闭,这个细节对最终精度有影响,建议不要随意改动。
3. 核心模块的深度解析与实操要点
3.1 MAE-NAS骨干的选型与替换
DAMO-YOLO官方提供了多个骨干版本,从轻量级的Tiny到标准版的Large,参数量和计算量跨度比较大。选哪个版本主要看你的部署硬件和精度要求。如果是在边缘设备上跑,比如Jetson Nano或者树莓派,建议选Tiny版本,输入分辨率也可以适当降低到416或者320。如果是在服务器GPU上跑,那Large版本能给你最好的精度。
替换骨干网络时需要注意一点:不同骨干的输出通道数不一样,后面的 neck 和 head 需要做对应的调整。官方代码里已经做好了配置映射,你只需要在配置文件里改一下模型名称就行。但如果你要自己换一个非官方的骨干,那就需要手动对齐通道数,否则会在特征融合时出现维度不匹配的错误。
提示:修改骨干网络后,建议先用小学习率跑几个epoch看看loss是否正常下降,确认没有维度错误后再用完整学习率训练。
3.2 RepGFPN的配置与调优
RepGFPN的配置主要在neck部分。官方默认用的是RepGFPN的完整版本,包含多个重参数化分支。如果你的硬件支持,建议保持默认配置。但如果你的推理设备对算子支持有限,比如某些国产NPU或者老款GPU,可能需要把重参数化分支简化掉,否则推理时可能会遇到算子不支持的问题。
我在一个国产边缘设备上部署时就遇到过这个问题:RepGFPN里的某些分支在转换模型时无法被正确识别,导致推理结果异常。后来把neck换成简化版之后才正常。所以如果你要做跨平台部署,建议提前确认目标平台对重参数化算子的支持情况。
调优方面,RepGFPN的通道数是一个可以调整的参数。默认配置下,neck的通道数和backbone的输出通道数是对齐的。如果你觉得模型太大,可以适当缩减neck的通道数,但要注意不要缩减得太狠,否则特征融合的能力会明显下降。我一般建议neck通道数不要低于backbone输出通道数的一半。
3.3 标签分配策略的实操细节
SimOTA的实现在官方代码里是封装好的,你一般不需要直接修改。但有几个参数会影响它的行为,需要了解。第一个是中心先验的半径,这个参数决定了正样本候选区域的大小。半径越大,参与分配的正样本越多,但噪声也会增加。第二个是cost函数的权重,包括分类loss和回归loss的权重比例。这个比例会影响分配时更看重分类准确性还是定位准确性。
我在训练一个类别极不平衡的数据集时,发现默认的cost权重会导致小类别被忽视。后来把分类loss的权重调高了一些,小类别的召回率有明显改善。所以如果你也在处理类似的问题,可以尝试调整这个参数。
另外,SimOTA对batch size比较敏感。如果batch size太小,每个batch里的正样本数量太少,分配会不稳定。官方建议的batch size是16以上,如果显存不够,可以用梯度累积来模拟大batch的效果。
3.4 数据增强的参数设置
DAMO-YOLO默认开启了Mosaic和MixUp,这两个增强对小目标检测帮助很大,但也会带来一些副作用。Mosaic会把四张图拼成一张,这会导致目标的尺度分布发生变化,有时候会让模型对某些尺度的目标过拟合。MixUp则是把两张图按比例混合,会引入一些不真实的纹理,对分类任务可能有帮助,但对检测任务有时候会干扰定位。
我的经验是,如果你的数据集本身已经足够大、场景足够丰富,可以适当降低Mosaic和MixUp的概率,甚至关掉MixUp。但如果你的数据集比较小,那这两个增强还是很有必要的。官方配置里Mosaic的概率是1.0,MixUp是0.1,这个比例在大多数情况下是合理的。
还有一个细节是Mosaic的关闭时机。官方配置是在最后15个epoch关闭Mosaic,让模型在真实分布上做最后的微调。这个策略我实测下来确实有效,关闭Mosaic之后loss会有一个小的波动,但最终精度会提升。如果你自己训练,建议保留这个设置。
4. 完整实操流程:从环境配置到模型部署
4.1 环境准备与依赖安装
DAMO-YOLO的官方代码是基于PyTorch的,对PyTorch版本有一定要求。我建议用PyTorch 1.10以上的版本,CUDA版本根据你的显卡驱动来选。如果是在T4或者1080Ti上跑,CUDA 11.3或者11.6都是可以的。
安装步骤大致如下:先创建虚拟环境,然后安装PyTorch和torchvision,接着安装DAMO-YOLO的依赖包。官方提供了一个requirements.txt,但里面有些包的版本比较旧,直接装可能会和你的PyTorch版本冲突。我的做法是手动安装核心依赖,比如pycocotools、opencv-python、tqdm这些,其他的让pip自动解决。
conda create -n damo-yolo python=3.8 conda activate damo-yolo pip install torch==1.12.0+cu113 torchvision==0.13.0+cu113 -f https://download.pytorch.org/whl/torch_stable.html pip install pycocotools opencv-python tqdm matplotlib安装完成后,下载官方代码仓库,然后编译一些C++扩展。DAMO-YOLO里有一些自定义的算子,比如可变形卷积,需要编译后才能用。编译过程可能会遇到gcc版本不兼容的问题,如果报错,可以尝试降低gcc版本或者用官方提供的预编译包。
4.2 数据集准备与格式转换
DAMO-YOLO支持COCO格式的数据集,如果你用的是VOC格式,需要先转换。转换脚本官方没有直接提供,但网上有很多现成的工具可以用。我一般用Python写一个简单的转换脚本,把VOC的XML标注转成COCO的JSON格式。
转换时需要注意几个点:第一,类别ID要从1开始,0是背景类,不要搞错。第二,图片的宽高信息要准确,否则会影响anchor的匹配。第三,如果数据集里有空标注的图片,建议直接过滤掉,否则训练时可能会报错。
数据集划分方面,我一般按8:1:1的比例分成训练集、验证集和测试集。如果数据量特别小,可以用交叉验证的方式,但DAMO-YOLO的训练时间比较长,交叉验证的成本会很高。所以如果数据量实在不够,建议先用预训练模型做微调,而不是从头训练。
4.3 训练配置与启动
训练配置主要在config文件里改。需要改的地方包括:数据集路径、类别数、batch size、学习率、训练epoch数。DAMO-YOLO的默认学习率是0.01,用的是SGD优化器。如果你的batch size和官方不一样,学习率需要按比例调整。一般来说,batch size翻倍,学习率也翻倍。
启动训练的命令很简单:
python tools/train.py -f configs/damo_yolo/damo_yolo_l.py -d your_dataset训练过程中可以用TensorBoard监控loss和mAP的变化。我一般会关注几个指标:分类loss、回归loss、以及验证集上的mAP。如果分类loss下降但mAP不涨,可能是过拟合了,需要增加数据增强或者提前停止。如果回归loss一直很高,可能是anchor设置不合理,需要调整。
训练时间方面,在单张T4上,用COCO数据集训练300个epoch大概需要3到4天。如果用自己的小数据集,epoch数可以适当减少,但建议不要少于100个epoch,否则模型可能还没收敛。
4.4 模型导出与推理部署
训练完成后,需要把PyTorch模型导出成推理格式。DAMO-YOLO支持导出ONNX和TensorRT。ONNX的导出比较简单,官方提供了脚本,直接运行就行。TensorRT的导出稍微复杂一些,需要先装TensorRT,然后用官方提供的转换脚本。
导出ONNX时需要注意opset版本。DAMO-YOLO里用了一些比较新的算子,opset版本太低可能不支持。我一般用opset 11或者12,兼容性比较好。导出后可以用onnxruntime做推理测试,确认输出和PyTorch一致。
TensorRT的转换能带来明显的速度提升。在T4上,640分辨率的输入,PyTorch推理大概能到30帧左右,转成TensorRT之后能到60帧以上。如果做多路视频分析,这个提升就很关键了。不过TensorRT的转换过程比较容易出错,尤其是遇到不支持的算子时,需要自己写plugin。我的建议是先用ONNX跑通,确认精度没问题后再转TensorRT。
推理部署时还有一个细节:预处理和后处理的速度。很多人只关注模型本身的速度,忽略了前后处理的开销。实际上,如果预处理用CPU做,可能会成为瓶颈。我一般会把预处理也放到GPU上做,用CUDA核函数实现,这样整体延迟会低很多。
5. 常见问题与排查技巧实录
5.1 训练不收敛或loss异常
这是最常见的问题之一。可能的原因有很多,我按排查顺序列一下。首先检查学习率是不是太大了,DAMO-YOLO对学习率比较敏感,如果loss一开始就爆炸,先把学习率降到0.001试试。其次检查数据标注有没有问题,比如类别ID越界、坐标超出图片范围等。第三检查anchor设置,如果数据集的目