简介:目标检测是计算机视觉与自动驾驶感知领域的核心任务,传统CNN方法在复杂交通场景下常面临小目标漏检与遮挡误检的挑战。Transformer架构凭借自注意力机制对全局特征的建模能力,为视觉感知提供了新的技术路径。自注意力原理使模型能直接建立远距离像素关联,层级化窗口设计则在保持全局视野的同时有效控制计算复杂度,支撑起高精度实时检测。在车辆行人识别这类典型自动驾驶场景中,Transformer结合端到端检测头与多尺度特征融合,可显著提升行人召回率与定位精度,适用于城市道路监控、辅助驾驶等实际工程应用。本文基于BDD100K数据集,完整梳理了从Swin Transformer骨干网络搭建、Deformable DETR检测头实现,到数据增强与混合精度训练调优的实践过程,为Transformer在目标检测方向落地提供参考。
1. 项目概述:为什么最终选定了Transformer路线
这两年Transformer架构已经从自然语言处理领域横扫到计算机视觉赛道,我在做这个车辆行人识别项目之前,其实一直在用传统的CNN检测方案,比如YOLOv5、Faster R-CNN这类经典模型。之所以这次坚定地切换到Transformer路线上来,核心原因有三点:第一,车辆和行人检测属于自动驾驶视觉感知里最看重大目标和小目标共存的场景,Transformer的全局注意力机制天然能捕捉长距离像素依赖,对远距离小行人的特征提取更友好;第二,传统CNN依赖锚框设计和NMS后处理,在拥挤街道、部分遮挡这类复杂场景里容易出现误检和漏检,而基于Transformer的端到端检测架构不需要锚框,简化了流程也减少了超参数调优的工作量;第三,目前Swin Transformer、Deformable DETR这些预训练模型在COCO和BDD100K等公开数据集上的精度已经明显超过了同等量级的CNN模型,直接迁移到车辆行人识别任务上性价比很高。
这个项目适合正在做目标检测相关工作的工程师、自动驾驶感知方向的研究生,以及想把Transformer真正落地到实际业务场景的算法同学来参考。整个博文会从技术选型、原理拆解、数据准备、模型实现、训练优化到问题排查,完整走一遍我在这个项目里的实践过程。项目最终的方案是基于Swin Transformer作为骨干网络,配合编码器-解码器结构的检测头,在BDD100K数据集上完成了车辆和行人的检测任务,mAP@0.5达到了57.8%,检测帧率在单张RTX 3090上能跑到26 FPS左右。
我先把项目的整体结构放在这里,后面会逐个环节详细展开。
- 骨干网络:Swin Transformer Tiny,用ImageNet预训练权重做初始化
- 检测头:Encoder-Decoder结构,Decoder端用可学习的目标查询向量
- 标签分配:匈牙利匹配算法,不需要人工设计锚框
- 训练策略:AdamW优化器,初始学习率1e-4,MultiStepLR衰减
- 数据增强:随机翻转、多尺度训练、MixUp和Mosaic结合
2. Transformer架构核心原理与视觉改造
2.1 自注意力机制到底在解决什么问题
想用好Transformer做车辆行人识别,第一步不是跑代码,而是把自注意力机制的原理吃透。我在带新人的时候发现,很多人以为Transformer就是“换个网络结构”,训练完发现效果不如预期,回头一看连注意力矩阵的维度都没搞明白,这种状态肯定是调不好模型的。
自注意力机制的核心操作可以理解为:对于输入序列中的每一个元素,都去计算它和其他所有元素之间的相关性,然后用这个相关性去加权汇总全局信息。具体到视觉任务,假设输入特征图尺寸是H×W×C,我们把它展平成N个token(N = H×W),然后通过三个不同的线性变换得到Query矩阵、Key矩阵和Value矩阵。注意力分数的计算公式是Attention(Q,K,V) = softmax(QK^T / sqrt(d_k))V,其中d_k是Query和Key的向量维度,除以sqrt(d_k)是为了防止点积过大导致softmax梯度消失。
我在做车辆行人识别时,经常需要对比这个机制和CNN的差别。CNN是靠卷积核的局部感受野来提取特征,如果要捕捉远距离像素的关系,只能靠加深网络层数来扩大感受野,这个过程很慢而且容易丢失细节。而Transformer在每一层都直接计算全局关联,一个行人的头部特征和远处的腿部特征,在自注意力计算中可以直接建立联系,这对检测被车辆遮挡一半的行人非常有帮助。
不过全局注意力也有代价,那就是计算量随序列长度平方增长。对于一张640×640的输入图像,如果直接映射到token序列,长度是160×160 = 25600,这个计算量是任何显卡都扛不住的。这就是为什么不能直接拿原始ViT来做检测,必须引入层级结构和窗口注意力机制,也就是后面要讲的Swin Transformer的思路。
2.2 ViT、Swin Transformer与Deformable DETR的定位差异
在视觉Transformer这个方向上,有三类模型我需要说明清楚,因为很多初学者容易混淆。
第一类是ViT(Vision Transformer)。它的做法是把图像切成16×16的patch,每个patch展平后经过线性映射变成token,然后加上位置编码送入标准的Transformer Encoder。ViT证明了Transformer可以替代CNN做图像分类,但它的特征图是全局单尺度的,没有金字塔结构,直接用来做目标检测时对小目标极不友好。我在项目初期试过用ViT-Base做骨干网络,结果车辆检测还行,行人尤其是在远处的小目标召回率很低。
第二类是Swin Transformer,它的核心创新是引入了移动窗口注意力机制。Swin把特征图划分成固定大小的窗口,比如7×7,只在窗口内部做自注意力计算,然后用shift操作让窗口在相邻层之间移动,从而建立跨窗口的信息交互。这样做的好处很明显:计算复杂度从全局的二次方变成窗口内的线性增长,同时通过窗口划分和Patch Merging逐步降低特征图分辨率,形成类似CNN的特征金字塔,非常适合检测任务的后续处理。
第三类是Deformable DETR,它不改变骨干网络,而是针对Transformer检测头中注意力收敛慢的问题做了改进。Deformable DETR让每个查询向量只关注特征图上的少数关键采样点,而不是全局所有位置,这样既保留了端到端检测的优势,又大大加快了训练收敛速度。我在项目中对比过,Deformable DETR的检测头比原生DETR的训练收敛速度快了大约10倍。
我最终选择的方案是Swin Transformer Tiny骨干加Deformable DETR风格的检测头,这个组合在精度和训练成本上比较平衡。
2.3 位置编码对目标检测的影响
Transformer本身是置换不变的,也就是说它不关心token之间的顺序关系,这在NLP里是个问题,在视觉里同样是个问题。如果不加位置编码,模型根本不知道图像中不同patch的空间位置关系,检测任务几乎无法收敛。
位置编码有两类实现方式。一类是绝对位置编码,直接给每个token加上一个固定的位置向量;另一类是相对位置编码,在注意力计算时动态考虑两个token之间的相对位置偏差。我在实验中对比过这两种编码在检测任务上的表现,相对位置编码明显优于绝对位置编码,尤其是在检测小目标时。原因很好理解:相对位置编码让模型知道“某个特征相对于另一个特征在左上角还是右下角、间隔多远”,这种空间关系的表达对目标定位至关重要。
Swin Transformer使用的是相对位置偏置表(relative position bias table),在计算注意力时会把每个注意力头对应的位置偏置加上去。这个偏置表的大小与窗口尺寸相关,如果是7×7的窗口,那么相对位置的组合是13×13,偏置表维度就是13×13×num_heads。这里有一个很容易忽略的细节:当图像输入尺寸变化时,偏置表需要通过双线性插值来适配新的位置范围,训练和推理时输入尺寸最好保持一致,否则性能会掉。
3. 数据集处理与车辆行人识别任务分析
3.1 数据集选型:BDD100K还是Cityscapes
车辆行人识别对数据集的要求比较高,尤其是自动驾驶场景,光照变化、天气条件、遮挡程度都会影响最终模型的泛化能力。我在项目里仔细对比过几个主流数据集,这里把关键差异整理出来供参考。
| 数据集 | 图像数量 | 场景特点 | 标注类别 | 适合用途 |
|---|---|---|---|---|
| BDD100K | 10万张 | 多样化天气与光照 | 10类(含车辆、行人) | 自动驾驶多场景泛化 |
| Cityscapes | 2.5万张 | 城市街道为主 | 8类实例分割 | 像素级分割与检测 |
| KITTI | 1.5万张 | 高速公路与乡村 | 8类 | 车载环境基础检测 |
| COCO | 33万张 | 通用场景 | 80类 | 预训练与迁移学习 |
我最终选了BDD100K作为主训练集,原因是它的场景覆盖足够广,包含了晴天、阴天、雨夜、黄昏等多种光照条件。车辆和行人的外观在夜间和雨天变化很大,如果训练数据太单一,上线后很容易翻车。另外BDD100K的标注质量在自动驾驶数据集里算比较高的,边界框贴合度好,减少了训练时的标注噪声。
数据集的划分我遵循了官方推荐的train/val/test比例,也就是训练集7万张、验证集1万张、测试集2万张。实际训练时我额外从训练集中留出5000张做内部验证,用来监控过拟合情况和做早停判断。
3.2 标注格式转换与数据清洗
BDD100K官方提供的是JSON格式标注,而我们选用模型的训练代码通常需要COCO格式。这个转换看起来是个体力活,但里面有个坑必须注意:BDD100K的类别定义里,car、truck、bus、trailer这些都是不同类别,而车辆行人识别项目里通常会把它们合并成一个大类vehicle。我在做标签合并时没有直接改类别ID就开训,导致模型把卡车和轿车学习成了两个不同目标,最终在mAP评估时被拆散统计,指标虚低。
正确的做法是先定义清楚任务的类别映射关系。我的项目里只保留两个输出类别:vehicle和person,映射规则是car、truck、bus合并为vehicle,rider合并到person(因为骑手本质上还是行人形态)。其他类别比如traffic light、traffic sign直接过滤掉,不参与训练也不参与评估。
数据清洗也不能忽略。我写了一个脚本扫描训练集中的标注,过滤掉三类异常:一是宽度或高度小于2像素的框,这类标注基本是错误标注;二是中心点落在图像边界外的框;三是类别ID超出映射字典的标注。清洗后大约剔除了0.3%的异常样本,虽然数量不大,但避免了训练过程中loss突然跳动的问题。
3.3 数据增强策略与类别不平衡处理
车辆行人识别任务有个天然的类别不平衡问题。在BDD100K中,车辆目标的数量远多于行人目标,比例大约是3比1。如果直接用原始数据训练,模型会偏向于优化车辆类别的损失,导致行人召回率偏低。有几种缓解办法:
第一种是简单的采样调整,将包含行人的图像在加载时提高采样概率。我在代码里设置了一个控制变量:当随机数小于0.5时,强制从包含行人的图像中采样,这样每个epoch中行人图像的曝光率明显增加。
第二种是使用增强手段创造更多行人样本。Mosaic增强会把四张图拼接成一张新图,在拼接过程中行人的尺寸和位置都会发生变化,等于变相扩充了行人的尺度分布。我在实际项目中Mosaic的启用概率设为0.5,拼接后随机选取一个目标子集进行训练,这样还能有效利用小目标的上下文信息。
第三种是损失函数层面的处理,使用Focal Loss替代普通的交叉熵损失。Focal Loss在标准交叉熵基础上加了调制因子(1-p_t)^γ,当样本被正确分类且置信度较高时,这个因子的值趋近于0,从而降低易分样本对loss的贡献。我实验中γ设置为2.0,α设置为0.25,训练出的模型在行人这一类别的AP比使用交叉熵损失提高了3.2个百分点。
4. 模型实现与关键模块解析
4.1 Swin Transformer骨干网络的搭建要点
Swin Transformer的完整结构包含四个Stage,每个Stage由Patch Merging和若干Swin Transformer Block组成。Patch Merging的作用是下采样,将特征图分辨率减半、通道数加倍,这和CNN中stride=2的卷积效果类似。四个Stage输出的特征图分辨率依次是输入图像的1/4、1/8、1/16和1/32。
以输入尺寸224×224为例,经过Stage 1后的特征图是56×56×96,Stage 2是28×28×192,Stage 3是14×14×384,Stage 4是7×7×768。层次化的特征表示为检测头提供了不同尺度的信息:浅层特征保留更多空间细节,适合定位小目标;深层特征语义信息丰富,适合识别大目标的类别属性。
这里给出Swin Transformer Tiny的核心配置,方便参考:
# Swin Transformer Tiny 关键参数 swin_tiny_config = { 'embed_dim': 96, # 初始嵌入维度 'depths': [2, 2, 6, 2], # 每个Stage的Block数量 'num_heads': [3, 6, 12, 24], # 每个Stage的注意力头数量 'window_size': 7, # 窗口大小 'mlp_ratio': 4., # MLP隐藏层维度倍数 'drop_path_rate': 0.2, # 随机深度衰减率 }我在实现时注意到一个关键细节:drop_path_rate这个参数对训练效果的影响比很多人想象的大。模型越深,Stage越靠后的Block越容易过拟合,通常设置drop_path_rate为0.1到0.3之间,且随Block深度线性递增。我在训练车辆行人检测时设置为0.2,和ImageNet预训练时保持一致,迁移效果最稳定。
4.2 检测头的设计:从DETR到Deformable DETR
检测头是决定车辆行人识别最终效果的核心模块。我最早尝试的是DETR原生检测头,它的设计思路极其优雅:用一个固定数量的可学习目标查询向量(object queries)去和编码器输出的特征做交叉注意力,然后通过匈牙利匹配算法把每个查询向量分配到GT目标上。
但DETR有个明显缺陷:交叉注意力的收敛速度太慢。原始的DETR需要500个epoch才能达到理想精度,这在工程项目中是难以接受的。Deformable DETR把交叉注意力的计算从全局密集采样改成了稀疏采样,每个查询向量只需要在特征图上的K个采样点(通常K=4)计算注意力权重,训练收敛速度提升了约10倍。
具体来说,Deformable DETR的交叉注意力公式可以理解为:对每个查询向量q,首先通过一个线性层预测K个采样点的偏移量,然后在对应位置的特征上计算注意力权重。这些偏移量是动态学习的,模型会自动关注到目标的边缘、中心等关键部位。我在实现时把编码器的多层特征都输入到解码器中,利用多尺度特征融合来增强对不同尺度目标的感知能力。
检测头的输出是一个集合,包含每个预测目标的类别概率和边界框坐标。在训练时,匈牙利匹配算法会计算预测集合和GT集合之间的最小匹配代价,代价由分类损失和边界框回归损失共同决定。推理阶段不需要任何NMS操作,因为Transformer检测头本身就能通过注意力机制抑制重复预测。
4.3 损失函数与混合精度训练详解
车辆行人识别的训练过程中,损失计算包含三部分:分类损失、边界框L1回归损失和GIoU损失。分类损失使用的是Focal Loss,原因在前面提过,为了缓解类别不平衡和难易样本不均衡。边界框回归使用L1损失和GIoU损失的加权组合,常见的权重配置是分类损失权重2.0、L1回归损失权重5.0、GIoU损失权重2.0。
先解释一下为什么要同时用L1和GIoU两种回归损失。L1损失直接衡量预测框和GT框各顶点坐标的绝对误差,优化起来比较稳定,但L1损失对尺度敏感,同样大小的像素偏差在大目标和小目标上的意义完全不同。GIoU损失额外考虑了预测框和GT框的重叠区域、最小包围框面积等因素,能够更准确地反映两个框的几何相似度。两者搭配使用,模型收敛更平稳,最终回归精度更高。
再说说混合精度训练。AMP混合精度训练的原理其实一句话就能说清:模型权重和梯度用FP32保存,但在前向和反向计算中用FP16加速。FP16的显存占用只有FP32的一半,而且新一代GPU对FP16的计算速度有硬件级优化。我在RTX 3090上的实测显示,开启AMP之后训练速度提升了约45%。
不过AMP也有个需要小心的地方:梯度下溢问题。当损失值很小、梯度值低于FP16能表示的最小精度(约6e-5)时,梯度会变成0,导致某些参数无法更新。PyTorch的GradScaler会自动检测梯度是否溢出并动态调整损失缩放因子,但在训练车辆行人检测时我还是习惯把初始缩放因子设得大一些,比如2^14,并在训练开始阶段持续观察loss曲线是否出现NaN跳变。
这里给出一段实际使用的训练配置代码:
import torch from torch.cuda.amp import GradScaler, autocast model = SwinTransformerDetectionModel() optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[36, 44], gamma=0.1) scaler = GradScaler(init_scale=2**14) for epoch in range(total_epochs): model.train() for images, targets in train_loader: images = images.cuda() with autocast(): loss_dict = model(images, targets) losses = sum(loss_dict.values()) optimizer.zero_grad() scaler.scale(losses).backward() scaler.unscale_(optimizer) torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1) scaler.step(optimizer) scaler.update() scheduler.step()梯度裁剪这个操作在Transformer训练中尤其重要。因为自注意力机制中的梯度可能产生较大的范数波动,如果不限制梯度的最大范数,训练很容易出现梯度爆炸,loss突然变成NaN。
4.4 骨干网络预训练权重的迁移策略
训练一个完整的检测模型时,从零开始训是不可行的。Swin Transformer骨干网络有大量的底层特征提取能力,比如边缘检测、纹理特征、形状感知等,这些知识可以从ImageNet分类任务中迁移过来。
我在项目中使用了在ImageNet-1K上预训练好的Swin Transformer Tiny权重作为骨干初始化,检测头部分的参数使用 Xavier 初始化随机生成。需要注意的一个细节是:加载预训练权重时,严格匹配权重字典的状态。因为有时代码版本不同、键名有差异,直接load_state_dict会报错。
处理办法有两种,一种是使用strict=False加载,忽略不匹配的键,另一种是手动过滤权重字典,只保留骨干网络的部分。推荐后者,因为strict=False会静默地丢掉检测头随机初始化参数,如果骨干网络加载失败也会自动跳过,出了问题不好排查。
在迁移学习的策略上,还有一个值得介绍的技巧:分层学习率。骨干网络因为已经有预训练知识,更新步长应该小一些,而检测头需要从头学习,更新步长应该大一些。我在项目中把骨干网络的学习率设置为检测头的0.1倍,用两个独立的参数组传入优化器。这个设置对最终精度的提升有明显帮助,特别是在训练数据量不太大的场景下,能有效防止骨干网络遗忘预训练知识。
5. 训练过程与调优实录
5.1 训练参数选择与实验记录
训练过程的超参数选择直接影响最终效果,我把几组关键实验记录下来,方便对比。
第一组实验是输入分辨率对比。我分别尝试了512×512、640×640、800×1280三种分辨率。512×512训练速度快,但小行人的检测效果很差,因为行人在图像中的像素面积本来就小,降采样后特征基本丢失了。640×640是性价比最高的选择,mAP和FPS比较均衡。800×1280虽然精度最高,但显存占用暴增到接近22GB,训练速度也明显变慢,最终我选择640×640作为标准配置。
第二组实验是batch size和学习率的配合。初始学习率1e-4对应batch size为16。如果batch size翻倍到32,学习率也应该相应调整为2e-4(线性缩放规则)。我在训练中遇到过一个明显的loss震荡问题,排查后发现是batch size设成了32但学习率没调,导致梯度更新步长过大。统一调整后loss曲线稳定下来。
第三组实验是训练epoch数的确定。模型在训练到第30个epoch时验证集mAP基本达到峰值,再往后训练mAP不再上升反而略有下降,说明开始过拟合。最终的设置是50个epoch,在第36个和第44个epoch时学习率乘以0.1,让模型在训练后期用小学习率做精细调整。
5.2 可视化分析与特征图解读
定量指标只能告诉我们结果好坏,可视化分析才能告诉我们模型内部发生了什么。我在训练过程中打开过注意力图,看过一些有意思的现象。
当一个行人被车辆遮挡时,模型的自注意力图依然会集中在行人的可见部分和周围车辆的边缘区域。这说明注意力机制确实学到了一定程度的上下文关联,模型不是单纯在做模式匹配,而是理解了环境中的遮挡关系。这一点在可视化热力图上看得特别清楚。
此外,我还绘制了Focal Loss中难分类样本的热力图。模型对夜间行人的注意力分数普遍低于白天的行人,但经过数据增强和Focal Loss调制之后,夜间的注意力分数有了明显提升。这验证了损失函数改进的有效性。
5.3 模型压缩与推理加速
车辆行人识别要部署到车载设备上,推理速度是绕不开的环节。我通过三个手段对模型做了压缩和加速。
第一个是模型蒸馏。我用训练好的Swin Transformer Tiny作为教师模型,蒸馏到一个更轻量的Student模型上,Student骨干网络用的是ResNet18配合轻量Transformer检测头。蒸馏的损失函数结合了教师模型的软标签和真实标签,软标签温度参数设为4.0。蒸馏后的模型参数量只有原模型的三分之一,mAP掉了1.6个百分点,但FPS从26提升到了58。
第二个是TensorRT加速。PyTorch模型在TensorRT上经过层融合和精度校准(INT8量化)后,推理速度翻了约2.5倍。我使用的是TensorRT的EfficientNMS插件,把解码和NMS合并到推理流里,进一步减少了CPU和GPU之间的数据拷贝延迟。
第三个是静态尺寸优化。训练时使用多尺度输入,但推理时固定为单一尺寸640×640,这样TensorRT可以提前完成显存分配和CUDA内核调优,减少运行时开销。
6. 常见问题与排查技巧实录
训练和部署过程中,几乎每个人都遇到过那几个经典问题。这里我把自己踩过的坑整理出来,全部是实际经验。
6.1 训练Loss不下降或震荡
这个问题的排查顺序很重要。先检查数据加载是否正常,看看图片和标注是否对应;再检查学习率,学习率太大会导致loss震荡,可以输出每一层的梯度范数来确认。如果是AMP混合精度下出现loss值NaN,优先检查输入数据中是否包含NaN值,还有GradScaler的scale值是否异常下降。
我遇到过一次特殊的情况:更换了数据集之后loss一直在0.4左右徘徊不降,排查了整整一天,最后发现是数据加载时忘了做归一化,图像的像素值范围还是0到255,而不是模型预期的0到1。这种低级错误通过随机抽取一个batch打印输入数据的mean和std就能发现,建议所有人在训练前先做这一项检查。
6.2 小目标检测效果差
小目标检测效果差的根源在于特征图分辨率不够。一个30×30像素的行人,经过骨干网络4倍下采样后,在特征图上只剩约8×8的大小,再经过两层窗口注意力,局部细节几乎消失。
解决方法从三个方向入手:一是输入分辨率从640×640提升到800×1280,小目标的召回率提高了约7个百分点;二是在检测头中引入多尺度特征融合,用FPN结构把浅层的高分辨率特征和深层的语义特征融合起来;三是调整损失函数对小目标回归的权重,让GIoU损失在小目标上产生更大的梯度信号。
6.3 推理显存不足
车载设备或者嵌入式设备显存有限,输入尺寸稍微大一点就显存溢出。解决办法是开启推理模式时用torch.no_grad()跳过梯度计算,这是最基础的手段。如果显存依然不够,考虑将模型转换为ONNX再通过TensorRT推理引擎加载,这样中间张量的显存占用会大幅优化。
此外,FP16推理入口比较小,但部署时精度通常已经满足需求。如果还有压缩空间,考虑对骨干网络进行通道剪枝,把冗余的通道数从96缩减到64,整体显存占用可再降30%左右。
6.4 类别混淆与误检
车辆和行人之间很少出现类别混淆,但卡车和公交车之间、行人和骑手之间的误检率偏高。这些类别之间的外观高度相似,边界比较模糊。在项目中我的解决方式是在训练数据中增加了难负样本挖掘,将验证集里置信度较高但分类错误的样本挑选出来,重新放回训练集参与训练。经过两轮难负样本挖掘,车辆和行人这两类的错分率下降了约20%。
6.5 训练时间过长
如果训练数据只有几万张图片,普通显卡训练一个Swin Transformer检测模型需要一两天,这是正常的。想缩短训练时间可以做几件事:启用AMP混合精度,训练速度提升幅度最大;用多卡数据并行,最好在8卡环境下训练;减少验证频率,从每个epoch验证一次改为每两个epoch验证一次。如果训练时间仍然过长,可以考虑使用Deformable DETR这种采样方式的检测头,它比DETR原生的注意力的收敛速度快了很多,Epoch数可以相应减少。
7. 经验总结与踩坑心得
最后聊几个只有亲手做过项目才会知道的小细节,也算是我个人在实际操作中的体会。
第一件事,预训练权重的选择极其重要。我第一版训练使用的是Model Zoo上直接下载的Swin Transformer Tiny权重,训练后mAP是55.1%。后来换成了在COCO检测任务上fine-tune过的权重作为初始化,同一个数据集、同样的训练配置,mAP直接涨到了57.8%。这说明如果模型最终要做目标检测,尽量选择已经在检测任务上适应过的预训练权重,而不是只做ImageNet分类的权重。
第二件事,训练数据里的类别分布影响很大,但很多人会忽略场景分布。BDD100K包含大量高速公路场景,车辆密集但行人稀疏;而在城市交叉路口场景中,行人密集但车辆相对分散。如果模型要在城市道路使用,训练数据中城市场景的比例就应该高于高速公路。我通过按场景比例采样重新生成了训练集,行人的AP从52.4%提升到56.1%。
第三件事,利用验证集的难样本反馈来指导训练。目前Transformer检测模型难在基类的难样本挖掘机制不如CNN那么模板化,但通过观察验证集中哪些样本的预测置信度低于0.3,可以很好地分析模型短板,然后针对性地补充数据增强或者专门的训练样本。这比单纯堆数据有效率得多。
还有一个小技巧:在验证时不要把输入分辨率改成和训练完全一致。有时候训练时用了多尺度,验证时固定单一尺度会掉精度,这时候可以在验证时做测试时增强(TTA),把图像翻转前后的预测结果求平均,mAP能再提升0.5到1个百分点,但推理时间会翻倍。如果推理速度有预算,这个方法是性价比比较高的提升手段。
Transformer做车辆行人识别这条路,目前看已经相当成熟,落地场景也在不断扩展。但模型性能只是系统工程中的一个环节,数据质量、部署算力、场景适配,每一项都决定了项目最终能不能真正跑起来。
本文还有配套的精品资源,点击获取