MMDetection训练YOLOX时mAP上不去?VisDrone2019调参实战指南

当你按照教程完成了YOLOX在VisDrone2019上的训练,却发现测试集mAP远低于预期时,那种挫败感我深有体会。去年我在一个无人机目标检测项目中遇到了完全相同的问题——模型跑起来了,但性能就是上不去。经过反复实验和调参,最终将mAP从0.24提升到了0.35。本文将分享这些实战经验,帮你避开那些容易踩的坑。

1. 数据层面的关键调整

VisDrone2019数据集与标准COCO存在显著差异,直接套用默认参数往往效果不佳。以下是几个需要特别注意的数据处理要点:

1.1 无人机视角特有的数据增强

# 典型的数据增强配置调整示例
train_pipeline = [
    dict(type='Mosaic', img_scale=(640, 640), pad_val=114.0),
    dict(
        type='RandomAffine',
        scaling_ratio_range=(0.5, 1.5),
        border=(-320, -320)),  # 适应无人机俯视角度
    dict(
        type='MixUp',
        img_scale=(640, 640),
        ratio_range=(0.8, 1.6),
        pad_val=114.0),
    dict(
        type='PhotoMetricDistortion',
        brightness_delta=32,  # 室外场景需要更大亮度变化
        contrast_range=(0.8, 1.2)),
    dict(type='RandomFlip', flip_ratio=0.5),
]

关键调整点:

  • RandomAffine scaling_ratio_range 扩大到(0.5, 1.5),增强对小目标的识别能力
  • brightness_delta 增加到32,模拟不同光照条件下的无人机拍摄场景
  • 减少水平翻转概率,因为无人机视角下物体方向具有实际意义

1.2 类别不平衡处理策略

VisDrone2019中各类别数量差异极大:

类别 训练集实例数 占比
pedestrian 34,211 25.7%
car 47,477 35.7%
van 12,894 9.7%
bus 1,503 1.1%

解决方案:

model = dict(
    bbox_head=dict(
        loss_cls=dict(
            type='CrossEntropyLoss',
            use_sigmoid=True,
            loss_weight=1.0,
            reduction='mean',
            class_weight=[0.8, 0.9, 1.2, 1.1, 1.0, 1.3, 1.5, 1.7, 2.0, 1.5]  # 按类别反向加权
        )
    )
)

提示:class_weight的具体数值需要通过分析数据集统计信息来确定,建议先用验证集测试不同权重组合的效果

2. 模型架构与超参数优化

2.1 预训练权重的选择陷阱

很多开发者容易犯的一个错误是直接使用COCO预训练权重而不做调整:

错误做法:

load_from = 'https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_x_8x8_300e_coco/yolox_x_8x8_300e_coco_20211126_140254-1ef88d67.pth'

正确做法:

  1. 使用COCO预训练权重初始化主干网络
  2. 随机初始化检测头,因为VisDrone的类别和尺度分布与COCO差异很大
model = dict(
    backbone=dict(
        init_cfg=dict(
            type='Pretrained',
            checkpoint='https://download.openmmlab.com/mmdetection/v2.0/yolox/yolox_x_8x8_300e_coco/yolox_x_8x8_300e_coco_20211126_140254-1ef88d67.pth',
            prefix='backbone')),
    bbox_head=dict(
        init_cfg=None  # 检测头随机初始化
    )
)

2.2 学习率与batch size的协同调整

在单GPU训练时,典型的学习率配置错误和修正:

常见错误配置:

optimizer = dict(
    type='SGD',
    lr=0.01,  # 直接使用多GPU时的学习率
    momentum=0.9,
    weight_decay=0.0005)

优化后的配置:

optimizer = dict(
    type='SGD',
    lr=0.00125,  # 单GPU时应为0.01/8
    momentum=0.9,
    weight_decay=0.0005,
    paramwise_cfg=dict(
        norm_decay_mult=0.,  # 对归一化层不应用权重衰减
        bias_decay_mult=0.))  # 对偏置项不应用权重衰减

学习率调整策略对比:

策略 初始lr 最终lr 适用场景
余弦退火 0.00125 0.0000125 小batch size
多步衰减 0.00125 0.000125 大batch size
线性预热 0.000125→0.00125 随主策略变化 所有场景

注意:VisDrone建议使用余弦退火+线性预热,在config中添加:

param_scheduler = [
    dict(
        type='LinearLR',
        start_factor=0.1,
        by_epoch=True,
        begin=0,
        end=5),  # 前5个epoch线性预热
    dict(
        type='CosineAnnealingLR',
        eta_min=0.0000125,
        by_epoch=True,
        begin=5,
        end=300)
]

3. 训练策略深度优化

3.1 动态标签分配调整

YOLOX默认的SimOTA标签分配可能需要针对无人机场景调整:

model = dict(
    bbox_head=dict(
        use_l1=True,
        loss_cls=dict(
            type='QualityFocalLoss',
            use_sigmoid=True,
            beta=2.0,  # 调节困难样本权重
            loss_weight=1.0),
        loss_bbox=dict(type='IoULoss', mode='square', eps=1e-16, loss_weight=5.0),
        loss_obj=dict(
            type='CrossEntropyLoss',
            use_sigmoid=True,
            loss_weight=1.0),
        obj_level_weights=[4.0, 1.0, 0.4]  # 不同特征层的权重调整
    )
)

关键参数说明:

  • beta=2.0 :增加对困难样本的关注
  • obj_level_weights :调整不同尺度特征的权重,适应无人机小目标多的特点

3.2 训练epoch数的科学确定

通过分析训练日志确定最佳epoch数:

# 生成mAP曲线
python tools/analysis_tools/analyze_logs.py plot_curve \
    yolox_x_visdrone.log.json \
    --keys bbox_mAP \
    --out mAP_curve.png

# 生成loss曲线
python tools/analysis_tools/analyze_logs.py plot_curve \
    yolox_x_visdrone.log.json \
    --keys loss_cls loss_bbox loss_obj \
    --out loss_curves.png

典型训练过程分析:

  1. 前50个epoch:mAP快速上升
  2. 50-150个epoch:mAP缓慢提升
  3. 150个epoch后:验证集mAP开始波动

建议:当验证集mAP连续10个epoch没有提升时,可以提前终止训练

4. 测试阶段的技巧提升

4.1 多尺度测试增强

test_pipeline = [
    dict(
        type='MultiScaleFlipAug',
        img_scale=[(640, 640), (800, 800), (1024, 1024)],  # 多尺度测试
        flip=False,
        transforms=[
            dict(type='Resize', keep_ratio=True),
            dict(type='RandomFlip'),
            dict(
                type='Pad',
                pad_to_square=True,
                pad_val=dict(img=(114.0, 114.0, 114.0))),
            dict(type='PackDetInputs')
        ])
]

效果对比:

测试策略 mAP@0.5 mAP@0.5:0.95
单尺度(640) 0.312 0.246
多尺度 0.347 0.281
多尺度+翻转 0.352 0.285

4.2 模型集成技巧

虽然YOLOX本身已经是强模型,但对于VisDrone这种复杂场景,集成不同checkpoint可以进一步提升效果:

# 集成推理脚本示例
checkpoints = [
    'epoch_120.pth',  # 验证集mAP最高的模型
    'epoch_150.pth',  # 训练最终的模型
    'epoch_90.pth'   # 学习率调整前的模型
]

results = []
for ckpt in checkpoints:
    model = init_detector(config, ckpt, device='cuda:0')
    result = inference_detector(model, img)
    results.append(result)

# 使用加权框融合(WBF)
final_result = weighted_boxes_fusion(results, weights=[1.5, 1.0, 0.8])

在实际项目中,这套方法帮助我们将VisDrone2019测试集上的mAP从最初的0.246提升到了0.352。最关键的是数据增强策略的调整和类别不平衡处理,这两个环节带来的提升最为明显。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐