OpenMMLab 目标检测

OpenMMLab 目标检测

  • 1. 目标检测简介
    • 1.0 常用工具
      • 1.0.0 实用工具
      • 1.0.1 [MMYOLO 自定义数据集从标注到部署](https://github.com/open-mmlab/mmyolo/blob/main/docs/zh_cn/recommended_topics/labeling_to_deployment_tutorials.md)
    • 1.1 目标检测与图像分类的区别
    • 1.2 目标检测汇总 (2022)
    • 1.3 网络结构
    • 1.4 滑窗
    • 1.5 损失的种类(分类、回归、Objectness)
    • 1.6 正负样本匹配策略
    • 1.7 单阶段、双阶段、anchor-based、anchor-free之间的关系
      • 1.7.1 划分依据
      • 1.7.2 四者的联系
    • 1.8 MMLab配置文件名的含义
    • 1.9 主干网络Backbone
      • 1.9.1 MMDetection实现的Backbone
      • 1.9.2 MMYOLO实现的Backbone
      • 1.9.3 MMClassification实现的Backbone
  • 2. 基础知识
    • 2.1 边界框(Bounding Box)
    • 2.2 交并比 Intersection Over Union
    • 2.3 置信度 Confidence Score
    • 2.4 非极大值抑制 Non-Maximum Suppression
    • 2.5 边界框回归 Bounding Box Regression
    • 2.6 边界框编码 Bbox Coding
  • 3. 两阶段目标检测算法
    • 3.1 两阶段算法概述
    • 3.2 Region-based CNN (2013)
      • 3.2.1 R-CNN的训练
      • 3.2.2 R-CNN相比于传统方法(如DPM)的提升
      • 3.2.3 R-CNN的不足(重复卷积)
    • 3.3 Fast R-CNN (2014)
      • 3.3.1 RoI Pooling
      • 3.3.2 RoI Align
      • 3.3.3 Fast R-CNN的训练
      • 3.3.4 Fast R-CNN 的速度提升
      • 3.3.5 Fast R-CNN 的精度提升
      • 3.3.6 Fast R-CNN 的速度瓶颈(产生提议框)
      • 3.3.7 降低区域提议的计算成本
      • 3.3.8 朴素方法的局限
      • 3.3.9 锚框 Anchor
    • 3.4 Faster R-CNN (2015)
      • 3.4.1 Faster R-CNN 的训练
    • 3.5 两阶段方法的发展与演进 (2013~2017)
    • 3.6 多尺度检测技术
      • 3.6.1 多尺度检测必要性
      • 3.6.2 图像金字塔 Image Pyramid
      • 3.6.3 层次化特征
      • 3.6.4 特征金字塔网络 Feature Pyramid Network (2016)
      • 3.6.5 在 Faster R-CNN 模型中使用 FPN
  • 4. 单阶段目标检测算法
    • 4.1 两阶段与单阶段的比较
    • 4.2 单阶段检测算法概述
    • 4.3 YOLO: You Only Look Once (2015)
      • 4.3.1 YOLO 的分类和回归目标
      • 4.3.2 YOLO 的损失函数
      • 4.3.3 YOLO 的优点和缺点
    • 4.4 SSD: Single Shot MultiBox Detetor (2016)
      • 4.4.1 SSD 的损失函数
    • 4.5 正负样本不均衡问题
      • 4.5.1 解决样本不均衡问题
      • 4.5.2 困难负样本 Hard Negative
      • 4.5.3 不同负样本对损失函数的贡献
      • 4.5.4 降低简单负样本的损失
      • 4.5.5 Focal Loss (焦点损失函数)
    • 4.6 RetinaNet (2017)
      • 4.6.1 RetinaNet 的性能
    • 4.7 YOLO v3 (2018)
    • 4.8 YOLO v5
      • 4.8.1 网络结构
      • 4.8.2 正负样本匹配策略
      • 4.8.3 Anchor设置
      • 4.8.4 Bbox编解码过程
      • 4.8.5 匹配策略
        • 4.8.5.1 步骤1:“比例”比较
        • 4.8.5.2 步骤2:为步骤 1 中 match 的 GT 分配对应的正样本
      • 4.8.6 回归方式
        • 4.8.6.1 中心点回归方式![在这里插入图片描述](https://img-blog.csdnimg.cn/02329d093f8f4748837d365d723a780d.png)
        • 4.8.6.1 WH 回归方式
      • 4.8.7 Loss 设计
      • 4.8.8 优化策略和训练过程
    • 4.9 RTMDet
      • 4.9.1 模型结构
      • 4.9.2 正负样本匹配策略
      • 4.9.3 Bbox 编解码过程
      • 4.9.4 匹配策略
      • 4.9.5 Loss 设计
    • 4.10 YOLOv8
      • 4.10.1 YOLOv8算法的核心特性和改动
      • 4.10.2 模型结构设计
      • 4.10.3 Loss计算
  • 5. 无锚框目标检测算法 (Anchor-free Detectors)
    • 5.1 锚框 vs 无锚框
    • 5.2 FCOS, Fully Convolutional One-Stage (2019)
      • 5.2.1 FCOS 的多尺度匹配
      • 5.2.2 FCOS 的预测目标
      • 5.2.3 中心度 Center-ness
      • 5.2.4 FCOS 的损失函数
    • 5.3 CenterNet (2019)
  • 6. Detection Transformers
    • 6.1 DETR(2020)
    • 6.2 Deformable DETR (2021)
  • 7. 目标检测模型的评估方法
    • 7.1 检测结果的正确/错误类型
    • 7.2 准确率 Precision 与 召回率 Recall
    • 7.3 准确率与召回率的平衡
    • 7.4 PR 曲线 与 AP(Average Precision) 值
    • 7.5 完整数据集上的例子
    • 7.6 PR 曲线的起伏
    • 7.7 Mean AP (mAP)
  • 8. COCO数据集及配置文件
    • 8.1 COCO 数据集的标注格式
    • 8.2 标注、类别、图像 id 的对应关系
    • 8.3 在 MMDetection 中配置 COCO 数据集
    • 8.4 数据处理流水线
    • 8.5 MMDetection 中的常用训练策略
  • 9. MMDetection实战
    • 9.1 常用命令
    • 9.2 MMDetection环境搭建
    • 9.3 OpenMMLab 项目中的重要概念——配置文件
    • 9.4 MMDetection代码库结构
    • 9.5 配置文件的运作方式
    • 9.6 两阶段检测器的构成
    • 9.7 单阶段检测器的构成
    • 9.8 RetinaNet 模型配置
      • 9.8.1 主干网络
      • 9.8.2 颈部
      • 9.8.3 bbox head 1
      • 9.8.4 bbox head 2
    • 9.9 训练自己的检测模型
  • 10、常用知识点
    • 10.1 AnchorGenerator (锚框生成器)
    • 10.2 BBox Assigner(计算每个锚框的样本属性:正、背景、忽略)
      • 10.2.1 初始化所有 anchor 为忽略样本 (1)
      • 10.2.2 计算背景样本(2)
      • 10.2.3 计算高质量正样本(3)
      • 10.2.4 适当增加更多正样本(4)
    • 10.3 BBox Encoder Decoder(平衡损失、加快收敛)
      • 10.3.1 编码过程(Encoder)
      • 10.3.2 解码过程(Decoder)
    • 10.4 Focal Loss (易学样本权重比较低,难样本权重比较高)
      • 10.4.1 核心思想
      • 10.4.2 重要性质
      • 10.4.3 具体实现
    • 10.5 交叉熵损失函数(Cross Entropy Loss Function)
      • 10.5.1 二分类
      • 10.5.2 多分类
      • 10.5.3 学习过程
        • 10.5.3.1 二分类情况
        • 10.5.3.2 多分类情况
      • 10.5.4 优点
      • 10.5.5 缺点
    • 11 常见错误排除流程

1. 目标检测简介

  • ETA: Estimate Time of Arrival (估计剩余时间)
  • 两阶段AP不如一阶段AP的原因:样本的类别不均衡导致的
    • 样本不均衡导致的问题:
      • 训练低效,因为大多数位置都是易学负样本,没有提供有用的学习信号
      • 由易学负样本主导整个训练,使得模型的优化方向并不是我们所希望的那样
  • 正负样本匹配策略或者称为标签匹配策略 Label Assignment 是目标检测模型训练中最核心的问题之一, 更好的标签匹配策略往往能够使得网络更好学习到物体的特征以提高检测能力
  • Backbone + Neck:生成多尺度特征图
  • Head:输出检测框/检测框偏移量、置信度
  • 模型配置文件名:retinanet_r50_fpn_1x_coco.py
    • retinanet:表示算法名称
    • r50:等表示骨架网络名
    • caffe: 和 PyTorch 是指 Bottleneck 模块的区别,省略情况下表示是 PyTorch。Bottleneck 是标准的 1x1-3x3-1x1 结构,考虑 stride=2 下采样的场景,caffe 模式下,stride 参数放置在第一个 1x1 卷积上,而 Pyorch 模式下,stride 放在第二个 3x3 卷积上
    • fpn:表示 Neck 模块采用了 FPN 结构
    • mstrain:表示多尺度训练,一般对应的是 pipeline 中 Resize 类
    • 1x: 表示 1 倍数的 epoch 训练即 12 个 epoch,2x 则表示 24 个 epcoh
    • coco:表示在 COCO 数据集上训练
if self.style == 'pytorch':
        self.conv1_stride = 1
        self.conv2_stride = stride
    else:
        self.conv1_stride = stride
        self.conv2_stride = 1

1.0 常用工具

1.0.0 实用工具

  • 可视化 COCO 标签
python tools/analysis_tools/browse_coco_json.py --data-root ./data/cat   --img-dir images --ann-file annotations/annotations_all.json --disp-all
# 或
python tools/analysis_tools/browse_coco_json.py --img-dir ./data/cat/images  --ann-file ./data/cat/annotations/annotations_all.json
  • 可视化数据集
python ./tools/analysis_tools/browse_dataset.py configs/yolov5/yolov5_s-v61_syncbn_fast_1xb4-300e_balloon.py --phase train --out-dir temp --mode pipeline
  • 打印完整配置文件
mim run mmdet print_config configs/yolov5/yolov5_s-v61_syncbn_fast_1xb4-300e_balloon.py --save-path ./temp/whole.py
  • 可视化数据集分析结果
python tools/analysis_tools/dataset_analysis.py  configs/yolov5/yolov5_s-v61_syncbn_fast_1xb4-300e_balloon.py
  • 优化锚框尺寸

  • 提取 COCO 子集

  • 可视化优化器参数策略

  • 数据集转换

  • 数据集下载

  • 日志分析

  • 模型转换

1.0.1 MMYOLO 自定义数据集从标注到部署

  • 数据集准备:tools/misc/download_dataset.py
  • 使用 labelme 和算法进行辅助标注:demo/image_demo.py + labelme
# 1.  推理生成labelme标注文件
python demo/image_demo.py ./data/cat/images  ./work/yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py  ./work/yolov5_s-v61_syncbn_fast_8xb16-300e_coco_20220918_084700-86e02187.pth  --out-dir ./data/cat/labels --class-name cat   --to-labelme
# 安装 labelme
conda create -n labelme python=3.8
conda activate labelme
pip install labelme==5.1.1
# 2. 启动 labelme
#labelme ${图片文件夹路径(即上一步的图片文件夹)} \
        --output ${label文件所处的文件夹路径(即上一步的 --out-dir)} \
        --autosave \
        --nodata
##--output:labelme 标注文件保存路径,如果该路径下已经存在部分图片的标注文件,则会进行加载;
##--autosave:标注文件自动保存,会略去一些繁琐的保存步骤;
##--nodata:每张图片的标注文件中不保存图片的 base64 编码,设置了这个 flag 会大大减少标注文件的大小。
labelme ./data/cat/images --output ./data/cat/labels --autosave --nodata
  • 使用脚本转换成 COCO 数据集格式:tools/dataset_converters/labelme2coco.py
# 将 labelme 的 label 转换为 COCO label
python tools/dataset_converters/labelme2coco.py --img-dir ./data/cat/images --labels-dir  ./data/cat/labels --out  ./data/cat/annotations/annotations_all.json  --class-id-txt ./data/cat/class_with_id.txt
  • 数据集划分为训练集、验证集和测试集:tools/misc/coco_split.py
python tools/misc/coco_split.py --json ./data/cat/annotations/annotations_all.json --out-dir ./data/cat/annotations --ratios 0.8 0.2  --shuffle  --seed 10
  • 构建 config 文件
_base_ = '../yolov5/yolov5_s-v61_syncbn_fast_8xb16-300e_coco.py'

max_epochs = 100  # 训练的最大 epoch
data_root = './data/cat/'  # 数据集目录的绝对路径
# data_root = '/root/workspace/mmyolo/data/cat/'  # Docker 容器里面数据集目录的绝对路径

# 结果保存的路径,可以省略,省略保存的文件名位于 work_dirs 下 config 同名的文件夹中
# 如果某个 config 只是修改了部分参数,修改这个变量就可以将新的训练文件保存到其他地方
work_dir = './work_dirs/yolov5_s-v61_syncbn_fast_1xb32-100e_cat'

# load_from 可以指定本地路径或者 URL,设置了 URL 会自动进行下载,因为上面已经下载过,我们这里设置本地路径
# 因为本教程是在 cat 数据集上微调,故这里需要使用 `load_from` 来加载 MMYOLO 中的预训练模型,这样可以在加快收敛速度的同时保证精度
load_from = './work_dirs/yolov5_s-v61_syncbn_fast_8xb16-300e_coco_20220918_084700-86e02187.pth'  # noqa

# 根据自己的 GPU 情况,修改 batch size,YOLOv5-s 默认为 8卡 x 16bs
train_batch_size_per_gpu = 32
train_num_workers = 4  # 推荐使用 train_num_workers = nGPU x 4

save_epoch_intervals = 2  # 每 interval 轮迭代进行一次保存一次权重

# 根据自己的 GPU 情况,修改 base_lr,修改的比例是 base_lr_default * (your_bs / default_bs)
base_lr = _base_.base_lr / 4

anchors = [  # 此处已经根据数据集特点更新了 anchor,关于 anchor 的生成,后面小节会讲解
    [(68, 69), (154, 91), (143, 162)],  # P3/8
    [(242, 160), (189, 287), (391, 207)],  # P4/16
    [(353, 337), (539, 341), (443, 432)]  # P5/32
]

class_name = ('cat', )  # 根据 class_with_id.txt 类别信息,设置 class_name
num_classes = len(class_name)
metainfo = dict(
    classes=class_name,
    palette=[(220, 20, 60)]  # 画图时候的颜色,随便设置即可
)

train_cfg = dict(
    max_epochs=max_epochs,
    val_begin=20,  # 第几个 epoch 后验证,这里设置 20 是因为前 20 个 epoch 精度不高,测试意义不大,故跳过
    val_interval=save_epoch_intervals  # 每 val_interval 轮迭代进行一次测试评估
)

model = dict(
    bbox_head=dict(
        head_module=dict(num_classes=num_classes),
        prior_generator=dict(base_sizes=anchors),

        # loss_cls 会根据 num_classes 动态调整,但是 num_classes = 1 的时候,loss_cls 恒为 0
        loss_cls=dict(loss_weight=0.5 *
                      (num_classes / 80 * 3 / _base_.num_det_layers))))

train_dataloader = dict(
    batch_size=train_batch_size_per_gpu,
    num_workers=train_num_workers,
    dataset=dict(
        _delete_=True,
        type='RepeatDataset',
        # 数据量太少的话,可以使用 RepeatDataset ,在每个 epoch 内重复当前数据集 n 次,这里设置 5 是重复 5 次
        times=5,
        dataset=dict(
            type=_base_.dataset_type,
            data_root=data_root,
            metainfo=metainfo,
            ann_file='annotations/trainval.json',
            data_prefix=dict(img='images/'),
            filter_cfg=dict(filter_empty_gt=False, min_size=32),
            pipeline=_base_.train_pipeline)))

val_dataloader = dict(
    dataset=dict(
        metainfo=metainfo,
        data_root=data_root,
        ann_file='annotations/trainval.json',
        data_prefix=dict(img='images/')))

test_dataloader = val_dataloader

val_evaluator = dict(ann_file=data_root + 'annotations/trainval.json')
test_evaluator = val_evaluator

optim_wrapper = dict(optimizer=dict(lr=base_lr))

default_hooks = dict(
    # 设置间隔多少个 epoch 保存模型,以及保存模型最多几个,`save_best` 是另外保存最佳模型(推荐)
    checkpoint=dict(
        type='CheckpointHook',
        interval=save_epoch_intervals,
        max_keep_ckpts=5,
        save_best='auto'),
    param_scheduler=dict(max_epochs=max_epochs),
    # logger 输出的间隔
    logger=dict(type='LoggerHook', interval=10))

  • 数据集可视化分析:tools/analysis_tools/dataset_analysis.py
# 查看训练集数据分布情况:
python tools/analysis_tools/dataset_analysis.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py    --out-dir work_dirs/dataset_analysis_cat/train_dataset

# 查看验证集数据分布情况:
python tools/analysis_tools/dataset_analysis.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py    --out-dir work_dirs/dataset_analysis_cat/val_dataset    --val-dataset
  • 优化 anchor 尺寸:tools/analysis_tools/optimize_anchors.py
python tools/analysis_tools/optimize_anchors.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py  --algorithm v5-k-means  --input-shape 640 640  --prior-match-thr 4.0 --out-dir work_dirs/dataset_analysis_cat
  • 可视化数据处理部分:tools/analysis_tools/browse_dataset.py
# 直接窗口可视化 config 配置中数据处理部分
python tools/analysis_tools/browse_dataset.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py  --show-interval 3
  • 启动训练:tools/train.py
# 1. 训练可视化
## 1.1 WandB
## WandB 可视化需要在官网注册,并在 https://wandb.ai/settings 获取到 WandB 的 API Keys
pip install wandb
wandb login 
## 在我们刚刚新建的 config 文件 configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb32-100e_cat.py 的最后添加 WandB 配置:
visualizer = dict(vis_backends=[dict(type='LocalVisBackend'), dict(type='WandbVisBackend')])
## 1.2 TensorBoard
pip install tensorboard
## 在我们刚刚新建的 config 文件 configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb32-100e_cat.py 中添加 tensorboard 配置
visualizer = dict(vis_backends=[dict(type='LocalVisBackend'),dict(type='TensorboardVisBackend')])
## 运行训练命令后,Tensorboard 文件会生成在可视化文件夹 work_dirs/yolov5_s-v61_syncbn_fast_1xb32-100e_cat/${TIMESTAMP}/vis_data 下, 运行下面的命令便可以在网页链接使用 Tensorboard 查看 loss、学习率和 coco/bbox_mAP 等可视化数据了:
  
tensorboard --logdir=work_dirs/yolov5_s-v61_syncbn_fast_1xb16-100e_cat

# 2. 执行训练
python tools/train.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py

# 使用编号为0的GPU卡
CUDA_VISIBLE_DEVICES=0 python tools/train.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py
# 使用编号为0和2的GPU卡
CUDA_VISIBLE_DEVICES=0,2 python tools/train.py configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py
  • 模型推理:demo/image_demo.py
python demo/image_demo.py ./data/cat/images  ./configs/custom_dataset/yolov5_s-v61_syncbn_fast_1xb16-100e_cat.py    ./work_dirs/yolov5_s-v61_syncbn_fast_1xb16-100e_cat/best_coco/bbox_mAP_epoch_84.pth --out-dir ./data/cat/pred_images
  • 模型部署

1.1 目标检测与图像分类的区别

  • 一个目标框至少6个参数(矩形框<4>、类别<1>、置信度<1>)
  • 两阶段目标检测的开山之作: Faster R-CNN (也是目标检测任务中的一个开山之作)
  • 单阶段目标检测算法的代表作:YOLO<推荐使用V5>、SSD
    OpenMMLab 目标检测_第1张图片

1.2 目标检测汇总 (2022)

OpenMMLab 目标检测_第2张图片

1.3 网络结构

OpenMMLab 目标检测_第3张图片

网络部件 功能 详细描述
主干网络 产生多级特征图 空间分辨率会越来越小,通道数通常会越来越大,信息会越来越集中,通常就集中到channel上,channel就会越来越多
颈部 融合不同级的特征 越靠近输入的级,其细节更好,但语义信息比较弱,输出多尺度(多种分辨率)的特征图。不同分辨率的特征图通常用于检测不同大小的物体
检测头 通常是由一个或多个卷积构成的模块,只要在这个特征图上扫一下, 在对应位置如果有物体就可以给一个输出

1.4 滑窗

  • 工作原理
    OpenMMLab 目标检测_第4张图片
    OpenMMLab 目标检测_第5张图片

  • 不足之外 (效率太代、计算量太大)
    OpenMMLab 目标检测_第6张图片

  • 改进思路 1:区域提议

    • 依赖外部算法,Selective Search不是神经网络的一部分,不可训练,不符合学习的理念。
      OpenMMLab 目标检测_第7张图片
  • 改进思路 2:分析滑窗中的重复计算
    OpenMMLab 目标检测_第8张图片

  • 消除滑窗中的重复计算
    OpenMMLab 目标检测_第9张图片

  • 在特征图上进行密集预测
    OpenMMLab 目标检测_第10张图片

  • 目标检测技术的演进
    OpenMMLab 目标检测_第11张图片OpenMMLab 目标检测_第12张图片

1.5 损失的种类(分类、回归、Objectness)

  • 一般的检测算法:
    • 回归损失只有正样本有
    • 分类损失的标签直接就非1即0,正样本的标签是1,负样本的标签是0(可以把背景作为一种类别一起算),这些标签根据anchor和GT框的比较就可以直接得到。比如标签是[0,1,0,0],代表当前样本属于背景或其他三种类别的标签。
  • YOLOV5中 :
    • 回归损失只有正样本有
    • 分类损失分为两种:
      • 一种是正样本的分类损失(不包括背景的onehot标签,比如[1,0,0])。
      • 另一种是正负样本的前景背景预测的分类损失,这里叫Objectness loss,比起以往算法前景背景损失的标签非1即0,这里采用预测框和GT框的IOU作为Objectness的标签,也就是说预测框和GT框重合度越高,当前样本属于前景的可能性就越大。一般算法对于不同的anchor接近GT框的程度不同,标签都是非1即0,统一对待。另外注意这里的Objectness loss的标签在得到预测值之前是不知道的,也就是说根据预测框的结果才实时的计算出来的,这种方法直观上来讲更合适些。

1.6 正负样本匹配策略

  • 静态标签匹配策略(基于先验)
    • FCOS
    • RetinaNet (Anchor与GT的最在IoU)
    • YOLOv5(Anchor与GT的宽高比)
  • 动态标签匹配策略
    • OTA
    • SimOTA (YOLOX)

1.7 单阶段、双阶段、anchor-based、anchor-free之间的关系

1.7.1 划分依据

  • 单阶段、双阶段、anchor-based、anchor-free 4者的联系应该分成两个并行维度来讲
  • 单阶段和双阶段的划分
    • 划分依据是是否存在显式的 ROI 特征提取过程
    • 典型的双阶段算法是 Faster R-CNN,其包括 RPN 和 R-CNN 两个模块,RPN 模块负责提取 ROI,然后通过 ROI-Pool或者 ROI-Align 进行 ROI 特征切割提取,最后在输入到 R-CNN 中进行识别和定位
    • 如果没有显式 ROI 提取过程则认为是单阶段算法,例如典型的 YOLO、RetinaNet 等等。
    • 一般而言,双阶段算法精度高但是速度慢一些,单阶段精度稍微低一点但是速度容易优化到比较快
  • Anchor-Based、Anchor-Free
    • 划分依据:是否需要显式定义先验 anchor 角度区分,如果需要定义 anchor 那么就是 anchor-base
    • 目前主流算法大部分都是这个类型,例如 Faster R-CNN、RetinaNet和YOLOv5 等等
    • 而 anchor-free 是从 2019 年开始慢慢流行,其最大优势是不需要设置麻烦且有重大影响的 anchor,输出是对每个输出特征图上点进行分类和定位建模,参数稍微少一些,更加容易理解,典型算法是FCOS、ATSS 和 SABL 等等

1.7.2 四者的联系

  • 四者的联系是Label>的编码方式。也就是一张图中若干个Label,(x0, y0, x1, y1,c)的GT检测框,是如何变为一张(1,C,H,W)的Feature Map的
  • 先从Anchor Free方法讲起。这类方法通过一些自定义的,简单的映射关系,将一个检测框的坐标直接映射到Feature Map中,比如CenterNet大致的思路是:
    • 求出GT框的中心,然后将这个中心编码成一个Heat Map对应坐标的一个点(一般这个点附近带有高斯分布)
    • 将这个GT框的宽高编码成对应wh Map对应坐标的一个点
    • 从而形成Heatmap + wh map,简单来说,检测器通过heatmap学习对目标的分类,wh map学习对目标宽高的回归(实际过程中会加上offset,但这里为了简化问题不做详细讨论,详见CenterNet论文)
    • 可以看到,在Anchor Free方法的Label编码过程中,始终是利用了目标本身的性质(图像坐标、属性),没有引入额外的信息
  • Anchor Based方法是在Anchor Free的基础上,为目标的宽高引入了若干个先验值
    • 对于Heatmap Label Feature Map中的一个点,只有当以这个点为中心,先验值的宽高所组成的“假设框"与真值很相近(比如IoU足够大),才会将检测框编码到这个点上
    • 对于wh Map中的一个点,则在编码的过程中加入anchor的宽高信息,例如Raster R-CNN在RPN中,对应的wh map值是GT框的宽高除以anchor宽高,再取对数 (编码值)
  • Two Stage方法大多使用Anchor Based这种Label 编码方式,并且将GT框的类别和坐标的编码分离。以Faster R-CNN为例,将GT框的坐标框单独编码,在RPN中做对其做回归+一个简单的正负样本的二分类;而具体的类别则在R-CNN部分对RPN中得到的bbox candidate进行分类
  • One Stage方法和Two Stage方法的不同,本质是没有分离坐标和类别属性,可以理解为就是一个加入多类别属性分类的RPN。显然,不管是One Stage还是Two Stage方法,可以用Anchor Based也可以用Anchor Free对坐标进行编码
  • 不同的编码方式,决定了模型真正学习的目标(Label Feature Map)的样本数值分布,决定了任务的难易程度,也赋予了不同检测方式的“某种天赋“。比如Anchor Based方法在不同大小的目标下具有更好的鲁棒性,Anchor Free计算量更少,等等。

1.8 MMLab配置文件名的含义

  • yolov5_n-v61_syncbn_fast_8xb16-300e_coco.py
    • yolov5:算法名
    • l/m/n/s:网络大小
    • v61:版本号,6.1版本
    • 8xb16:训练时采用的是8卡,每张卡的Batch Size是16
    • 300e:表示训练300个epoch
    • coco:表示COCO2017目标检测数据集

1.9 主干网络Backbone

1.9.1 MMDetection实现的Backbone

  • VGG (ICLR’2015)
  • ResNet (CVPR’2016)
  • ResNeXt (CVPR’2017)
  • MobileNetV2 (CVPR’ 2018)
  • HRNet (CVPR’2019)
  • Generalized Attention (ICCV’2019)
  • GCNet (ICCVW’2019)
  • Res2Net (TPAMI’2020)
  • RegNet (CVPR’2 2020)
  • ResNeSt (ArXiv’2020)
  • PVT (ICCV’2021)
  • SwinTransformer (CVPR’2021)
  • PVTv2 (ArXiv’2021)
  • ResNet Strikes back (ArXiv’2021)
  • EfficientNet (ArXiv’2021)
  • ConvNeXt (CVPR’2022)

1.9.2 MMYOLO实现的Backbone

  • YOLOv5CSPDarknet
  • PPYOLOECSPResNet
  • CSPNeXt
  • YOLOv6EfficientRep
  • YOLOv7Backbone

1.9.3 MMClassification实现的Backbone

2. 基础知识

  • Anchor(锚框):以特征图上的位置在原图上对应的位置为中心设定不同大小、不同长宽比,同时又重合的基准框。

2.1 边界框(Bounding Box)

OpenMMLab 目标检测_第13张图片
OpenMMLab 目标检测_第14张图片

2.2 交并比 Intersection Over Union

OpenMMLab 目标检测_第15张图片

2.3 置信度 Confidence Score

OpenMMLab 目标检测_第16张图片

2.4 非极大值抑制 Non-Maximum Suppression

OpenMMLab 目标检测_第17张图片

2.5 边界框回归 Bounding Box Regression

OpenMMLab 目标检测_第18张图片

2.6 边界框编码 Bbox Coding

OpenMMLab 目标检测_第19张图片
{ b x = p x + p w ∗ t x b y = p y + p h ∗ t y b w = p w ∗ e x p ( t w ) b h = p h ∗ e x p ( t h ) \left\{ \begin{array}{ll} b_x = p_x + p_w * t_x \\ b_y = p_y + p_h * t_y \\ b_w = p_w * exp(t_w) \\ b_h = p_h * exp(t_h) \end{array} \right. bx=px+pwtxby=py+phtybw=pwexp(tw)bh=phexp(th)

3. 两阶段目标检测算法

3.1 两阶段算法概述

OpenMMLab 目标检测_第20张图片

3.2 Region-based CNN (2013)

OpenMMLab 目标检测_第21张图片

3.2.1 R-CNN的训练

OpenMMLab 目标检测_第22张图片

3.2.2 R-CNN相比于传统方法(如DPM)的提升

OpenMMLab 目标检测_第23张图片

3.2.3 R-CNN的不足(重复卷积)

OpenMMLab 目标检测_第24张图片

3.3 Fast R-CNN (2014)

OpenMMLab 目标检测_第25张图片

3.3.1 RoI Pooling

OpenMMLab 目标检测_第26张图片

3.3.2 RoI Align

OpenMMLab 目标检测_第27张图片

3.3.3 Fast R-CNN的训练

OpenMMLab 目标检测_第28张图片

3.3.4 Fast R-CNN 的速度提升

OpenMMLab 目标检测_第29张图片

3.3.5 Fast R-CNN 的精度提升

OpenMMLab 目标检测_第30张图片

3.3.6 Fast R-CNN 的速度瓶颈(产生提议框)

OpenMMLab 目标检测_第31张图片

3.3.7 降低区域提议的计算成本

OpenMMLab 目标检测_第32张图片

3.3.8 朴素方法的局限

OpenMMLab 目标检测_第33张图片

3.3.9 锚框 Anchor

OpenMMLab 目标检测_第34张图片

3.4 Faster R-CNN (2015)

OpenMMLab 目标检测_第35张图片

3.4.1 Faster R-CNN 的训练

OpenMMLab 目标检测_第36张图片

3.5 两阶段方法的发展与演进 (2013~2017)

OpenMMLab 目标检测_第37张图片
OpenMMLab 目标检测_第38张图片

3.6 多尺度检测技术

3.6.1 多尺度检测必要性

OpenMMLab 目标检测_第39张图片

  • 单级特征图不擅长检测:
    • 大物体
    • 小物体

3.6.2 图像金字塔 Image Pyramid

OpenMMLab 目标检测_第40张图片

3.6.3 层次化特征

OpenMMLab 目标检测_第41张图片

3.6.4 特征金字塔网络 Feature Pyramid Network (2016)

OpenMMLab 目标检测_第42张图片

3.6.5 在 Faster R-CNN 模型中使用 FPN

OpenMMLab 目标检测_第43张图片

4. 单阶段目标检测算法

4.1 两阶段与单阶段的比较

  • 两阶段
    OpenMMLab 目标检测_第44张图片
  • 单阶段
    OpenMMLab 目标检测_第45张图片

4.2 单阶段检测算法概述

OpenMMLab 目标检测_第46张图片

4.3 YOLO: You Only Look Once (2015)

OpenMMLab 目标检测_第47张图片

4.3.1 YOLO 的分类和回归目标

OpenMMLab 目标检测_第48张图片

4.3.2 YOLO 的损失函数

OpenMMLab 目标检测_第49张图片

4.3.3 YOLO 的优点和缺点

OpenMMLab 目标检测_第50张图片

4.4 SSD: Single Shot MultiBox Detetor (2016)

OpenMMLab 目标检测_第51张图片

4.4.1 SSD 的损失函数

OpenMMLab 目标检测_第52张图片

4.5 正负样本不均衡问题

OpenMMLab 目标检测_第53张图片

OpenMMLab 目标检测_第54张图片

4.5.1 解决样本不均衡问题

OpenMMLab 目标检测_第55张图片

4.5.2 困难负样本 Hard Negative

OpenMMLab 目标检测_第56张图片

4.5.3 不同负样本对损失函数的贡献

OpenMMLab 目标检测_第57张图片

4.5.4 降低简单负样本的损失

OpenMMLab 目标检测_第58张图片

4.5.5 Focal Loss (焦点损失函数)

  • Focal Loss:几乎已经成为 one-stage 算法的标配, 因为它解决了正负样本极不平衡的问题
    OpenMMLab 目标检测_第59张图片

4.6 RetinaNet (2017)

  • 简介
    • 深入分析了极度不平衡的正负(前景背景)样本比例导致 one-stage 检测器精度低于 two-stage 检测器
    • 基于上述分析,提出了一种简单但是非常实用的 Focal Loss 焦点损失函数,并且 Loss 设计思想可以推广到其他领域
    • 同时针对目标检测领域特定问题,设计了 RetinaNet 网络,结合 Focal Loss 使得 one-stage 检测器在精度上能够达到乃至超过 two-stage 检测器
  • 创新点
    • Focal Loss (焦点损失函数)
    • RetinaNet网络
      OpenMMLab 目标检测_第60张图片

4.6.1 RetinaNet 的性能

OpenMMLab 目标检测_第61张图片

4.7 YOLO v3 (2018)

OpenMMLab 目标检测_第62张图片
OpenMMLab 目标检测_第63张图片

4.8 YOLO v5

4.8.1 网络结构

  • YOLOv5 网络结构是标准的 CSPDarknet + PAFPN + 非解耦 Head
  • YOLOv5 网络结构大小由 deepen_factor 和 widen_factor 两个参数决定。其中 deepen_factor 控制网络结构深度,即 CSPLayer 中 DarknetBottleneck 模块堆叠的数量;widen_factor 控制网络结构宽度,即模块输出特征图的通道数。以 YOLOv5-l 为例,其 deepen_factor = widen_factor = 1.0 。P5 和 P6 的模型整体结构分别如下图所示。
  • YOLOv5-l-P5 模型结构
  • YOLOv5-l-P6 模型结构
  • 图的上半部分为模型总览;下半部分为具体网络结构,其中的模块均标有序号,方便用户与 YOLOv5 官方仓库的配置文件对应;中间部分为各子模块的具体构成。
  • 特征维度(shape):都为 (B, C, H, W)

4.8.2 正负样本匹配策略

  • 正负样本匹配策略的核心是确定预测特征图的所有位置中哪些位置应该是正样本,哪些是负样本,甚至有些是忽略样本。
  • 匹配策略是目标检测算法的核心,一个好的匹配策略可以显著提升算法性能
  • YOLOV5 的匹配策略简单总结为:采用了 anchor 和 gt_bbox 的 shape 匹配度作为划分规则,同时引入跨邻域网格策略来增加正样本。 其主要包括如下两个核心步骤:
    • 对于任何一个输出层,抛弃了常用的基于 Max IoU 匹配的规则,而是直接采用 shape 规则匹配,也就是该 GT Bbox 和当前层的 Anchor 计算宽高比,如果宽高比例大于设定阈值,则说明该 GT Bbox 和 Anchor 匹配度不够,将该 GT Bbox 暂时丢掉,在该层预测中该 GT Bbox 对应的网格内的预测位置认为是负样本
    • 对于剩下的 GT Bbox(也就是匹配上的 GT Bbox),计算其落在哪个网格内,同时利用四舍五入规则,找出最近的两个网格,将这三个网格都认为是负责预测该 GT Bbox 的,可以粗略估计正样本数相比之前的 YOLO 系列,至少增加了三倍

4.8.3 Anchor设置

  • YOLOv5 是 Anchor-based 的目标检测算法,其 Anchor size 的获取方式与 YOLOv3 类似,也是使用聚类获得,其不同之处在于聚类使用的标准不再是基于 IoU 的,而是使用形状上的宽高比作为聚类准则(即 shape-match )。
  • 在用户更换了数据集后,可以使用 MMYOLO 里带有的 Anchor 分析工具,对自己的数据集进行分析,确定合适的 Anchor size。
python tools/analysis_tools/optimize_anchors.py ${CONFIG} --algorithm v5-k-means
 --input-shape ${INPUT_SHAPE [WIDTH HEIGHT]} --output-dir ${OUTPUT_DIR}
  • 然后在 config 文件 里修改默认 Anchor size:
anchors = [[(10, 13), (16, 30), (33, 23)], [(30, 61), (62, 45), (59, 119)],
           [(116, 90), (156, 198), (373, 326)]]

4.8.4 Bbox编解码过程

  • 在 Anchor-based 算法中,预测框通常会基于 Anchor 进行变换,然后预测变换量,这对应 GT Bbox 编码过程,而在预测后需要进行 Pred Bbox 解码,还原为真实尺度的 Bbox,这对应 Pred Bbox 解码过程。
  • 在 YOLOv3 中,回归公式为:
    { b x = σ ( t x ) + c x b y = σ ( t y ) + c y b w = a w ⋅ e t w b h = a h ⋅ e t h \left\{ \begin{array}{ll} b_x = \sigma(t_x) + c_x \\ b_y = \sigma(t_y) + c_y \\ b_w = a_w \cdot e^{t_w} \\ b_h = a_h \cdot e^{t_h} \end{array} \right. bx=σ(tx)+cxby=σ(ty)+cybw=awetwbh=aheth
    • a w a_w aw:Anchor的宽度
    • c x c_x cx:Grid所处的坐标
    • σ \sigma σ:Sigmoid公式
  • 而在 YOLOv5 中,回归公式为:
    { b x = ( 2 ⋅ σ ( t x ) − 0.5 ) + c x b y = ( 2 ⋅ σ ( t y ) − 0.5 ) + c y b w = a w ⋅ ( 2 ⋅ σ ( t w ) ) 2 b h = a h ⋅ ( 2 ⋅ σ ( t h ) ) 2 \left\{ \begin{array}{ll} b_x = (2 \cdot \sigma(t_x) - 0.5) + c_x \\ b_y = (2 \cdot \sigma(t_y) - 0.5) + c_y \\ b_w = a_w \cdot (2 \cdot \sigma(t_w))^2 \\ b_h = a_h \cdot (2 \cdot \sigma(t_h))^2 \end{array} \right. bx=(2σ(tx)0.5)+cxby=(2σ(ty)0.5)+cybw=aw(2σ(tw))2bh=ah(2σ(th))2
  • YOLOv5改进之处主要有以下两点:
    • 中心点坐标范围从 (0, 1) 调整至 (-0.5, 1.5)
    • 宽高范围从 ( 0 , + ∞ ) (0, + \infty) (0,+)调整至 ( 0 , 4 a w h ) (0, 4a_{wh}) (0,4awh)
  • 这个改进具有以下好处:
    • 新的中心点设置能更好的预测到 0 和 1。这有助于更精准回归出 box 坐标。
    • 宽高回归公式中 exp(x) 是无界的,这会导致梯度失去控制,造成训练不稳定。YOLOv5 中改进后的宽高回归公式优化了此问题。
      OpenMMLab 目标检测_第64张图片

4.8.5 匹配策略

  • 在 MMYOLO 设计中,无论网络是 Anchor-based 还是 Anchor-free,我们统一使用 prior 称呼 Anchor
  • 正样本匹配包含以下两步:
    • 步骤1:“比例”比较
    • 步骤2:为步骤 1 中 match 的 GT 分配对应的正样本

4.8.5.1 步骤1:“比例”比较

  • 将 GT Bbox 的 WH 与 Prior(Anchor) 的 WH 进行“比例”比较。
  • 比较流程:
    { r w = w _ g t / w _ p t r h = h _ g t / h _ p t r w m a x = m a x ( r w , 1 / r w ) r h m a x = m a x ( r h , 1 / r h ) r m a x = m a x ( r w m a x , r h m a x ) \left\{ \begin{array}{ll} r_w = w\_gt/w\_pt \\ r_h = h\_gt/h\_pt \\ r_w^{max} = max(r_w, 1/r_w) \\ r_h^{max} = max(r_h, 1/r_h) \\ r^{max} = max(r_w^{max}, r_h^{max}) \end{array} \right. rw=w_gt/w_ptrh=h_gt/h_ptrwmax=max(rw,1/rw)rhmax=max(rh,1/rh)rmax=max(rwmax,rhmax)
  • if r m a x r^{max} rmax < prior_match_thr: match!
  • 此处用一个 GT Bbox 与 P3 特征图的 Prior 进行匹配的案例进行讲解和图示:
    OpenMMLab 目标检测_第65张图片
  • prior1 匹配失败的原因:
    h _ g t / h _ p t = h _ g t / h _ p r i o r = 24 / 5 = 4.8 > p r i o r _ m a t c h _ t h r h\_gt/h\_pt = h\_gt/h\_prior=24/5=4.8 > prior\_match\_thr h_gt/h_pt=h_gt/h_prior=24/5=4.8>prior_match_thr

4.8.5.2 步骤2:为步骤 1 中 match 的 GT 分配对应的正样本

  • 依然沿用上面的例子:
    GT Bbox (cx, cy, w, h) 值为 (26, 37, 36, 24),

  • Prior WH 值为 [(15, 5), (24, 16), (16, 24)],在 P3 特征图上,stride 为 8。通过计算,prior2 和 prior3 能够 match。

  • 计算过程如下

    • 步骤1:将 GT Bbox 的中心点坐标对应到 P3 的 grid 上
      { G T x c e n t e r _ g r i d = 26 / 8 = 3.25 G T y c e n t e r _ g r i d = 37 / 8 = 4.625 \left\{ \begin{array}{ll} GT_x^{center\_grid} = 26/8 = 3.25 \\ GT_y^{center\_grid} = 37/8 = 4.625 \end{array} \right. {GTxcenter_grid=26/8=3.25GTycenter_grid=37/8=4.625
      在这里插入图片描述
    • 步骤2:将 GT Bbox 中心点所在的 grid 分成四个象限,由于中心点落在了左下角的象限当中,那么会将物体的左、下两个 grid 也认为是正样本
      OpenMMLab 目标检测_第66张图片
  • 下图展示中心点落到不同位置时的正样本分配情况:
    在这里插入图片描述

  • 那么 YOLOv5 的 Assign 方式具体带来了哪些改进?

    • 一个 GT Bbox 能够匹配多个 Prior
    • 一个 GT Bbox 和一个Prior 匹配时,能分配 1-3 个正样本
    • 以上策略能适度缓解目标检测中常见的正负样本不均衡问题

4.8.6 回归方式

  • 而 YOLOv5 中的回归方式,和 Assign 方式是相互呼应的:

4.8.6.1 中心点回归方式OpenMMLab 目标检测_第67张图片

4.8.6.1 WH 回归方式

OpenMMLab 目标检测_第68张图片

4.8.7 Loss 设计

  • YOLOv5 中总共包含 3 个 Loss,分别为:
    • Classes loss:使用的是 BCE loss
    • Objectness loss:使用的是 BCE loss
    • Location loss:使用的是 CIoU loss
  • 三个 loss 按照一定比例汇总:
    L o s s = λ 1 L c l s + λ 2 L o b j + λ 3 L l o c Loss = \lambda_1 L_{cls} + \lambda_2 L_{obj} + \lambda_3 L_{loc} Loss=λ1Lcls+λ2Lobj+λ3Lloc
  • P3、P4、P5 层对应的 Objectness loss 按照不同权重进行相加,默认的设置是:
obj_level_weights=[4., 1., 0.4]

L o b j = 4.0 ⋅ L o b j s m a l l + 1.0 ⋅ L o b j m e d i u m + 0.4 ⋅ L o b j l a r g e L_{obj} = 4.0 \cdot L_{obj}^{small} + 1.0 \cdot L_{obj}^{medium} + 0.4 \cdot L_{obj}^{large} Lobj=4.0Lobjsmall+1.0Lobjmedium+0.4Lobjlarge

4.8.8 优化策略和训练过程

  • OLOv5 对每个优化器的参数组进行非常精细的控制,简单来说包括如下部分。
  • 优化器分组
    • 将优化参数分成 Conv/Bias/BN 三组,在 WarmUp 阶段,不同组采用不同的 lr 以及 momentum 更新曲线。 同时在 WarmUp 阶段采用的是 iter-based 更新策略,而在非 WarmUp 阶段则变成 epoch-based 更新策略,可谓是 trick 十足。
    • MMYOLO 中是采用 YOLOv5OptimizerConstructor 优化器构造器实现优化器参数分组。优化器构造器的作用就是对一些特殊的参数组初始化过程进行精细化控制,因此可以很好的满足需求。
    • 而不同的参数组采用不同的调度曲线功能则是通过 YOLOv5ParamSchedulerHook 实现。而不同的参数组采用不同的调度曲线功能则是通过 YOLOv5ParamSchedulerHook 实现。
  • weight decay 参数自适应
    • 作者针对不同的 batch size 采用了不同的 weight decay 策略,具体来说为:
      • 当训练 batch size <= 64 时,weight decay 不变
      • 当训练 batch size > 64 时,weight decay 会根据总 batch size 进行线性缩放
    • MMYOLO 也是通过 YOLOv5OptimizerConstructor 实现。
  • 梯度累加
    • 为了最大化不同 batch size 情况下的性能,作者设置总 batch size 小于 64 时候会自动开启梯度累加功能。
    • 训练过程和大部分 YOLO 类似,包括如下策略:
      • 没有使用预训练权重
      • 没有采用多尺度训练策略,同时可以开启 cudnn.benchmark 进一步加速训练
      • 使用了 EMA 策略平滑模型
      • 默认采用 AMP 自动混合精度训练
    • 需要特意说明的是:YOLOv5 官方对于 small 模型是采用单卡 v100 训练,bs 为 128,而 m/l/x 等是采用不同数目的多卡实现的, 这种训练策略不太规范,为此在 MMYOLO 中全部采用了 8 卡,每卡 16 bs 的设置,同时为了避免性能差异,训练时候开启了 SyncBN。

4.9 RTMDet

  • RTMDet:Real-time Models for Object Detection (Release to Manufacture)
  • 最近一段时间,开源界涌现出了大量的高精度目标检测项目,其中最突出的就是 YOLO 系列,OpenMMLab 也在与社区的合作下推出了 MMYOLO。 在调研了当前 YOLO 系列的诸多改进模型后,MMDetection 核心开发者针对这些设计以及训练方式进行了经验性的总结,并进行了优化,推出了高精度、低延时的单阶段目标检测器 RTMDet
  • RTMDet 由 tiny/s/m/l/x 一系列不同大小的模型组成,为不同的应用场景提供了不同的选择。 其中,RTMDet-x 在 52.6 mAP 的精度下达到了 300+ FPS 的推理速度
  • 下面的推理速度和精度测试(不包含 NMS)是在 1 块 NVIDIA 3090 GPU 上的 TensorRT 8.4.3, cuDNN 8.2.0, FP16, batch size=1 条件里测试的。
  • 而最轻量的模型 RTMDet-tiny,在仅有 4M 参数量的情况下也能够达到 40.9 mAP,且推理速度 < 1 ms。
    OpenMMLab 目标检测_第69张图片
  • 上图中的精度是和 300 epoch 训练下的公平对比,为不使用蒸馏的结果。
    OpenMMLab 目标检测_第70张图片

4.9.1 模型结构

  • RTMDet 模型整体结构和 YOLOX 几乎一致,由 CSPNeXt + CSPNeXtPAFPN + 共享卷积权重但分别计算 BN 的 SepBNHead 构成。内部核心模块也是 CSPLayer,但对其中的 Basic Block 进行了改进,提出了 CSPNeXt Block。

4.9.2 正负样本匹配策略

  • 正负样本匹配策略或者称为标签匹配策略 Label Assignment 是目标检测模型训练中最核心的问题之一, 更好的标签匹配策略往往能够使得网络更好学习到物体的特征以提高检测能力。

  • 早期的样本标签匹配策略一般都是基于 空间以及尺度信息的先验 来决定样本的选取。 典型案例如下(静态匹配策略):

    • FCOS 中先限定网格中心点在 GT 内筛选后然后再通过不同特征层限制尺寸来决定正负样本
    • RetinaNet 则是通过 Anchor 与 GT 的最大 IOU 匹配来划分正负样本
    • YOLOV5 的正负样本则是通过样本的宽高比先筛选一部分, 然后通过位置信息选取 GT 中心落在的 Grid 以及临近的两个作为正样本
  • 但是上述方法都是属于基于 先验 的静态匹配策略, 就是样本的选取方式是根据人的经验规定的。 不会随着网络的优化而进行自动优化选取到更好的样本, 近些年涌现了许多优秀的动态标签匹配策略

    • OTA 提出使用 Sinkhorn 迭代求解匹配中的最优传输问题
    • YOLOX 中使用 OTA 的近似算法 SimOTA , TOOD 将分类分数以及 IOU 相乘计算 Cost 矩阵进行标签匹配等等
  • 这些算法将 预测的 Bboxes 与 GT 的 IoU 和 分类分数 或者是对应分类 Loss回归 Loss 拿来计算 Matching Cost 矩阵再通过 top-k 的方式动态决定样本选取以及样本个数。通过这种方式, 在网络优化的过程中会自动选取对分类或者回归更加敏感有效的位置的样本, 它不再只依赖先验的静态的信息, 而是使用当前的预测结果去动态寻找最优的匹配, 只要模型的预测越准确, 匹配算法求得的结果也会更优秀。但是在网络训练的初期, 网络的分类以及回归是随机初始化, 这个时候还是需要 先验 来约束, 以达到 冷启动 的效果。

  • RTMDet 作者也是采用了动态的 SimOTA 做法,不过其对动态的正负样本分配策略进行了改进。 之前的动态匹配策略( HungarianAssigner 、OTA )往往使用与 Loss 完全一致的代价函数作为匹配的依据,但我们经过实验发现这并不一定时最优的。 使用更多 Soften 的 Cost 以及先验,能够提升性能。

4.9.3 Bbox 编解码过程

  • RTMDet 的 BBox Coder 采用的是 mmdet.DistancePointBBoxCoder。
  • 该类的 docstring 为 This coder encodes gt bboxes (x1, y1, x2, y2) into (top, bottom, left, right) and decode it back to the original.
  • 编码器将 gt bboxes (x1, y1, x2, y2) 编码为 (top, bottom, left, right),并且解码至原图像上。

4.9.4 匹配策略

  • RTMDet 提出了 Dynamic Soft Label Assigner 来实现标签的动态匹配策略, 该方法主要包括使用 位置先验信息损失 , 样本回归损失 , 样本分类损失 , 同时对三个损失进行了 Soft 处理进行参数调优, 以达到最佳的动态匹配效果。

4.9.5 Loss 设计

  • 参与 Loss 计算的共有两个值:loss_cls 和 loss_bbox,其各自使用的 Loss 方法如下:
  • loss_cls:mmdet.QualityFocalLoss
  • loss_bbox:mmdet.GIoULoss
    权重比例是:loss_cls : loss_bbox = 1 : 2

4.10 YOLOv8

  • YOLOv8 是 Ultralytics 公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本,目前支持图像分类、物体检测和实例分割任务,在还没有开源时就收到了用户的广泛关注。
  • 按照官方描述,YOLOv8 是一个 SOTA 模型,它建立在以前 YOLO 版本的成功基础上,并引入了新的功能和改进,以进一步提升性能和灵活性。具体创新包括一个新的骨干网络、一个新的 Ancher-Free 检测头和一个新的损失函数,可以在从 CPU 到 GPU 的各种硬件平台上运行。 不过 Ultralytics 并没有直接将开源库命名为 YOLOv8,而是直接使用 Ultralytics 这个词,原因是 Ultralytics 将这个库定位为算法框架,而非某一个特定算法,一个主要特点是可扩展性。其希望这个库不仅仅能够用于 YOLO 系列模型,而是能够支持非 YOLO 模型以及分类分割姿态估计等各类任务。
  • 总而言之,Ultralytics 开源库的两个主要优点是
    • 融合众多当前 SOTA 技术于一体
    • 未来将支持其他 YOLO 系列以及 YOLO 之外的更多算法
      OpenMMLab 目标检测_第71张图片
  • 下表为官方在 COCO Val 2017 数据集上测试的 mAP、参数量和 FLOPs 结果。可以看出 YOLOv8 相比 YOLOv5 精度提升非常多,但是 N/S/M 模型相应的参数量和 FLOPs 都增加了不少,从上图也可以看出相比 YOLOV5 大部分模型推理速度变慢了。
    OpenMMLab 目标检测_第72张图片
  • 额外提一句,现在各个 YOLO 系列改进算法都在 COCO 上面有明显性能提升,但是在自定义数据集上面的泛化性还没有得到广泛验证,至今依然听到不少关于 YOLOv5 泛化性能较优异的说法。对各系列 YOLO 泛化性验证也是 MMYOLO 中一个特别关心和重点发力的方向。

4.10.1 YOLOv8算法的核心特性和改动

  • YOLOv8 算法的核心特性和改动可以归结为如下:

    • 提供了一个全新的 SOTA 模型,包括 P5 640 和 P6 1280 分辨率的目标检测网络和基于 YOLACT 的实例分割模型。和 YOLOv5 一样,基于缩放系数也提供了 N/S/M/L/X 尺度的不同大小模型,用于满足不同场景需求
    • 骨干网络和 Neck 部分可能参考了 YOLOv7 ELAN 设计思想,将 YOLOv5 的 C3 结构换成了梯度流更丰富的 C2f 结构,并对不同尺度模型调整了不同的通道数,属于对模型结构精心微调,不再是无脑一套参数应用所有模型,大幅提升了模型性能。不过这个 C2f 模块中存在 Split 等操作对特定硬件部署没有之前那么友好了
    • Head 部分相比 YOLOv5 改动较大,换成了目前主流的解耦头结构,将分类和检测头分离,同时也从 Anchor-Based 换成了 Anchor-Free
    • Loss 计算方面采用了 TaskAlignedAssigner 正样本分配策略,并引入了 Distribution Focal Loss
    • 训练的数据增强部分引入了 YOLOX 中的最后 10 epoch 关闭 Mosiac 增强的操作,可以有效地提升精度
  • 从上面可以看出,YOLOv8 主要参考了最近提出的诸如 YOLOX、YOLOv6、YOLOv7 和 PPYOLOE 等算法的相关设计,本身的创新点不多,偏向工程实践,主推的还是 ultralytics 这个框架本身。

4.10.2 模型结构设计

  • 在暂时不考虑 Head 情况下,对比 YOLOv5 和 YOLOv8 的 yaml 配置文件可以发现改动较小。
  • Head 部分变化最大,从原先的耦合头变成了解耦头,并且从 YOLOv5 的 Anchor-Based 变成了 Anchor-Free。其结构如下所示:
    OpenMMLab 目标检测_第73张图片
  • 可以看出,不再有之前的 objectness 分支,只有解耦的分类和回归分支,并且其回归分支使用了 Distribution Focal Loss 中提出的积分形式表示法。

4.10.3 Loss计算

  • Loss 计算过程包括 2 个部分: 正负样本分配策略和 Loss 计算。
  • 现代目标检测器大部分都会在正负样本分配策略上面做文章,典型的如 YOLOX 的 simOTA、TOOD 的 TaskAlignedAssigner 和 RTMDet 的 DynamicSoftLabelAssigner,这类 Assigner 大都是动态分配策略,而 YOLOv5 采用的依然是静态分配策略。
  • 考虑到动态分配策略的优异性,YOLOv8 算法中则直接引用了 TOOD 的 TaskAlignedAssigner。 TaskAlignedAssigner 的匹配策略简单总结为: 根据分类与回归的分数加权的分数选择正样本
    t = s α + u β t = s^{\alpha} + u^{\beta} t=sα+uβ
    • s s s:标注类别对应的预测分值
    • u u u:是预测框和 GT框的 IoU
    • 两者相加就可以衡量对齐程度
  • 对于每一个 GT,对所有的预测框基于 GT 类别对应分类,预测框与 GT 的 IoU 的加权得到一个关联分类以及回归的对齐分数 alignment_metrics
  • 对于每一个 GT,直接基于 alignment_metrics 对齐分数选取 topK 大的作为正样本
  • Loss 计算包括 2 个分支: 分类和回归分支,没有了之前的 objectness 分支:
    • 分类分支依然采用 BCE Loss
    • 回归分支需要和 Distribution Focal Loss 中提出的积分形式表示法绑定,因此使用了 Distribution Focal Loss, 同时还使用了 CIoU Loss

5. 无锚框目标检测算法 (Anchor-free Detectors)

5.1 锚框 vs 无锚框

OpenMMLab 目标检测_第74张图片
OpenMMLab 目标检测_第75张图片

5.2 FCOS, Fully Convolutional One-Stage (2019)

OpenMMLab 目标检测_第76张图片

5.2.1 FCOS 的多尺度匹配

OpenMMLab 目标检测_第77张图片

5.2.2 FCOS 的预测目标

OpenMMLab 目标检测_第78张图片

5.2.3 中心度 Center-ness

OpenMMLab 目标检测_第79张图片

5.2.4 FCOS 的损失函数

OpenMMLab 目标检测_第80张图片

5.3 CenterNet (2019)

OpenMMLab 目标检测_第81张图片

  • 主要流程
    OpenMMLab 目标检测_第82张图片

6. Detection Transformers

6.1 DETR(2020)

OpenMMLab 目标检测_第83张图片

6.2 Deformable DETR (2021)

OpenMMLab 目标检测_第84张图片

7. 目标检测模型的评估方法

7.1 检测结果的正确/错误类型

OpenMMLab 目标检测_第85张图片

7.2 准确率 Precision 与 召回率 Recall

OpenMMLab 目标检测_第86张图片

7.3 准确率与召回率的平衡

OpenMMLab 目标检测_第87张图片

7.4 PR 曲线 与 AP(Average Precision) 值

  • 计算AP的方法:AP=PR曲线下的面积
    OpenMMLab 目标检测_第88张图片

7.5 完整数据集上的例子

  • PR曲线计算公式如下:
    • AccTP:当前所有TP的总和
    • AccTP:当前所有FP的总和
    • 真值框总数:在下面的示例中,此值为15
      P r e c i s i o n = A c c T P A c c T P + A c c F P R e c a l l = A c c T P 真值框总数 Precision = \frac{AccTP}{AccTP + AccFP} \\ Recall = \frac {AccTP}{真值框总数} Precision=AccTP+AccFPAccTPRecall=真值框总数AccTP
      OpenMMLab 目标检测_第89张图片

7.6 PR 曲线的起伏

OpenMMLab 目标检测_第90张图片

7.7 Mean AP (mAP)

OpenMMLab 目标检测_第91张图片

8. COCO数据集及配置文件

8.1 COCO 数据集的标注格式

  • 图像信息
    OpenMMLab 目标检测_第92张图片
  • 类别信息
    OpenMMLab 目标检测_第93张图片
  • 标注信息
    OpenMMLab 目标检测_第94张图片

8.2 标注、类别、图像 id 的对应关系

OpenMMLab 目标检测_第95张图片

8.3 在 MMDetection 中配置 COCO 数据集

OpenMMLab 目标检测_第96张图片

8.4 数据处理流水线

OpenMMLab 目标检测_第97张图片
OpenMMLab 目标检测_第98张图片

8.5 MMDetection 中的常用训练策略

OpenMMLab 目标检测_第99张图片

9. MMDetection实战

9.1 常用命令

  • 使用mim搜索预训练模型
mim search mmdet --model "mask r-cnn"
  • 使用mim下载模型
mim download mmdet --config mask_rcnn_r50_fpn_2x_coo --dest .
  • 分割预测并显示结果
from mmdet.apis import init_detector, inference_detector, show_result_pyplot
config_file = "mask_rcnn_r50_fpn_2x_coco.py"
checkpoint_file = "mask_rcnn_r50_fpn_2x_coco_bbox_mAP-0.354_20200505_003907-3e542a40.pth"
model = init_detector(config_file, checkpoint_file)
result = inference_detector(model, "demo.jpg")
show_result_pyplot(model, "demo.jpg", result)
  • 读取完整的配置文件 (先继承后修改)
## 读基础的配置文件, 在fruit.py中加入下面一行
_base_ = ['yolov3_mibilenetv2_mstrain-416_300e_coco.py']

## 上面一行代码相当于下面的内容
from mmcv import Config
config = Config.fromfile('fruit.py')
print(config.pretty_text)
  • 模型训练
mim train mmdet fruit.py
  • 训练自己的数据集及类别
    OpenMMLab 目标检测_第100张图片
  • 修改训练epoch(轮数)、学习率(若在预训练的基础上,则改小)和日志
    OpenMMLab 目标检测_第101张图片
  • 测试在所有测试数据集上的效果
mim test mmdet fruit.py --checkpoint /home/work_dirs/fruit/latest.pth -- show-dir work_dirs/fruit/

9.2 MMDetection环境搭建

OpenMMLab 目标检测_第102张图片

9.3 OpenMMLab 项目中的重要概念——配置文件

  • 深度学习模型的训练涉及几个方面:
    • 模型结构:模型有几层、每层多少通道数等等
    • 数据集:即用什么数据训练模型,数据集划分、数据文件路径、数据增强策略等等
    • 训练策略:梯度下降算法、学习率参数、batch_size、训练总轮次、学习率变化策略等等
    • 运行时: GPU、分布式环境配置等等
    • 一些辅助功能 :如打印日志、定时保存checkpoint等等
  • 在 OpenMMLab 项目中,所有这些项目都涵盖在一个配置文件中,一个配置文件定义了一个完整的训练
    过程
    • model:字段定义模型
    • data:字段定义数据
    • optimizer、lr_config: 等字段定义训练策略
    • load_from:字段定义预训练模型的参数文件

9.4 MMDetection代码库结构

OpenMMLab 目标检测_第103张图片

9.5 配置文件的运作方式

OpenMMLab 目标检测_第104张图片

9.6 两阶段检测器的构成

OpenMMLab 目标检测_第105张图片

9.7 单阶段检测器的构成

OpenMMLab 目标检测_第106张图片

9.8 RetinaNet 模型配置

9.8.1 主干网络

OpenMMLab 目标检测_第107张图片

9.8.2 颈部

OpenMMLab 目标检测_第108张图片

9.8.3 bbox head 1

OpenMMLab 目标检测_第109张图片

9.8.4 bbox head 2

OpenMMLab 目标检测_第110张图片

9.9 训练自己的检测模型

  • 训练:寻找最小梯度,使得损失函数最小的这样一组参数的过程
    OpenMMLab 目标检测_第111张图片

10、常用知识点

10.1 AnchorGenerator (锚框生成器)

  • Anchor的生成过程
anchor_generator=dict(
    type='AnchorGenerator',
    # 特征图 anchor 的 base scale, 值越大,所有 anchor 的尺度都会变大
    octave_base_scale=4,
    # 每个特征图有3个尺度,2**0=1, 2**(1/3)=1.26, 2**(2/3)=1.59
    scales_per_octave=3,
    # 每个特征图有3个高宽比例
    ratios=[0.5, 1.0, 2.0],
    # 特征图对应的 stride,必须与特征图 stride 一致,不可以随意更改
    strides=[8, 16, 32, 64, 128]),
  • 每个特征图上有 3 种尺度和 3 种宽高比,每个位置一共 9 个 anchor,并且通过 octave_base_scale 参数来控制全局 anchor 的 base scales ,如果自定义数据集中普遍都是大物体或者小物体,则可能修改更改 octave_base_scale 参数。
  • 可视化指定特征图位置的 anchor 情况
    OpenMMLab 目标检测_第112张图片
  • 相同颜色表示在该特征图中基本尺度是相同的,只是宽高比不一样而已

10.2 BBox Assigner(计算每个锚框的样本属性:正、背景、忽略)

  • 计算得到输出特征图上每个点对应的原图 anchor 坐标后,就可以和 gt 信息计算每个 anchor 的正负样本属性,对应配置如下:
assigner=dict(
    # 最大 IoU 原则分配器
    type='MaxIoUAssigner',
    # 正样本阈值
    pos_iou_thr=0.5,
    # 负样本阈值
    neg_iou_thr=0.4,
    # 正样本阈值下限
    min_pos_iou=0,
    # 忽略 bboxs 的阈值,-1表示不忽略
    ignore_iof_thr=-1)
  • 仅从上面的描述可能比较难理解参数含义,通过下面的流程分析就比较容易理解每个参数含义了。MaxIoUAssigner 操作包括 4 个步骤:
    • 初始化所有 anchor 为忽略样本
    • 计算背景样本
    • 计算高质量正样本
    • 适当增加更多正样本

10.2.1 初始化所有 anchor 为忽略样本 (1)

  • 假设所有输出特征的所有 anchor 总数一共 n 个,对应某张图片中 gt bbox 个数为 m,首先初始化长度为 n 的 assigned_gt_inds,全部赋值为 -1,表示当前全部设置为忽略样本
# 1. assign -1 (忽略样本) by default , 0:负样本
assigned_gt_inds = overlaps.new_full((num_bboxes, ),
                                     -1,
                                     dtype=torch.long)

10.2.2 计算背景样本(2)

  • 将每个 anchor 和所有 gt bbox 计算 IoU,找出最大IoU,如果该最大IoU小于 neg_iou_thr 或者在背景样本阈值范围内,则该 anchor 对应索引位置的 assigned_gt_inds 设置为 0,表示是负样本(背景样本)
max_overlaps, argmax_overlaps = overlaps.max(dim=0)
gt_max_overlaps, gt_argmax_overlaps = overlaps.max(dim=1)

# 2. assign negative: below
# the negative inds are set to be 0
if isinstance(self.neg_iou_thr, float):
    assigned_gt_inds[(max_overlaps >= 0)
                     & (max_overlaps < self.neg_iou_thr)] = 0
elif isinstance(self.neg_iou_thr, tuple):
    assert len(self.neg_iou_thr) == 2
    # 可以设置一个范围
    assigned_gt_inds[(max_overlaps >= self.neg_iou_thr[0])
                     & (max_overlaps < self.neg_iou_thr[1])] = 0

10.2.3 计算高质量正样本(3)

  • 将每个 anchor 和所有 gt bbox 计算 IoU,找出最大IoU,如果其最大IoU大于等于 pos_iou_thr,则设置该 anchor 对应所有的 assigned_gt_inds 设置为当前匹配 gt bbox 的编号 +1(后面会减掉 1),表示该 anchor 负责预测该 gt bbox,且是高质量 anchor。之所以要加 1,是为了区分背景样本(背景样本的 assigned_gt_inds 值为 0)
# 3. assign positive: above positive IoU threshold
pos_inds = max_overlaps >= self.pos_iou_thr
assigned_gt_inds[pos_inds] = argmax_overlaps[pos_inds] + 1

10.2.4 适当增加更多正样本(4)

  • 在第三步计算高质量正样本中可能会出现某些 gt bbox 没有分配给任何一个 anchor (由于IoU低于pos_iou_thr),导致该 gt bbox 不被认为是前景物体,此时可以通过 self.match_low_quality=True 配置进行补充正样本。
  • 对于每个 gt bbox 需要找出和其最大 IoU 的 anchor 索引,如果其 IoU 大于 min_pos_iou,则将该 anchor 对应索引的 assigned_gt_inds 设置为正样本,表示该 anchor 负责预测对应的 gt bbox。通过本步骤,可以最大程度保证每个 gt bbox 都有相应的 anchor 负责预测,但是如果其最大 IoU 值还是小于 min_pos_iou,则依然不被认为是前景物体。
if self.match_low_quality:
    # Low-quality matching will overwirte the assigned_gt_inds assigned
    # in Step 3. Thus, the assigned gt might not be the best one for
    # prediction.
    # For example, if bbox A has 0.9 and 0.8 iou with GT bbox 1 & 2,
    # bbox 1 will be assigned as the best target for bbox A in step 3.
    # However, if GT bbox 2's gt_argmax_overlaps = A, bbox A's
    # assigned_gt_inds will be overwritten to be bbox B.
    # This might be the reason that it is not used in ROI Heads.
    for i in range(num_gts):
        if gt_max_overlaps[i] >= self.min_pos_iou:
            if self.gt_max_assign_all:
                #如果有多个相同最高 iou 的 anchor 和该 gt bbox 对应,则一并赋值
                max_iou_inds = overlaps[i, :] == gt_max_overlaps[i]
                # 同样需要加1
                assigned_gt_inds[max_iou_inds] = i + 1
            else:
                assigned_gt_inds[gt_argmax_overlaps[i]] = i + 1
  • 从这一步可以看出,3 和 4 有部分 anchor 重复分配了,即当某个 gt bbox 和 anchor 的最大 IoU 大于等于 pos_iou_thr,那肯定大于 min_pos_iou,此时 3 和 4 步骤分配的同一个 anchor,并且从上面注释可以看出本步骤可能会引入低质量 anchor,是否需要开启本步骤需要根据不同算法来确定。
  • 此时可以可以得到如下总结
    • 如果 anchor 和所有 gt bbox 的最大 IoU 值小于 0.4,那么该 anchor 就是背景样本
    • 如果 anchor 和所有 gt bbox 的最大 IoU 值大于等于 0.5,那么该 anchor 就是高质量正样本
    • 如果 gt bbox 和所有 anchor 的最大 IoU 值大于等于 0(可以看出每个 gt bbox 都一定有至少一个 anchor 匹配),那么该 gt bbox 所对应的 anchor 也是正样本
    • 其余样本全部为忽略样本即 anchor 和所有 gt bbox 的最大 IoU 值处于 [0.4,0.5) 区间的 anchor 为忽略样本不计算 loss

10.3 BBox Encoder Decoder(平衡损失、加快收敛)

  • 在 anchor-based 算法中,为了利用 anchor 信息进行更快更好的收敛,一般会对 head 输出的 bbox 分支 4 个值进行编解码操作,作用有两个:
    • 更好的平衡分类和回归分支 loss,以及平衡 bbox 四个预测值的 loss
    • 训练过程中引入 anchor 信息,加快收敛
  • RetinaNet 采用的编解码函数是主流的 DeltaXYWHBBoxCoder,其配置如下:
bbox_coder=dict(
    type='DeltaXYWHBBoxCoder',
    target_means=[.0, .0, .0, .0],
    target_stds=[1.0, 1.0, 1.0, 1.0]),
  • target_means 和 target_stds 相当于对 bbox 回归的 4 个 t x , t y , t w , t h t_x, t_y, t_w, t_h tx,ty,tw,th 进行变换。在不考虑 target_means 和 target_stds 情况下,其编码公式如下:
    { t x = ( x t − x a ) / w a t y = ( y t − y a ) / h a t w = l o g ( w t / w a ) t h = l o g ( h t / h a ) \left\{ \begin{array}{ll} t_x = (x_t - x_a)/w_a \\ t_y = (y_t - y_a)/h_a \\ t_w = log(w_t/w_a) \\ t_h = log(h_t/h_a) \end{array} \right. tx=(xtxa)/waty=(ytya)/hatw=log(wt/wa)th=log(ht/ha)
  • x t , y t x_t,y_t xtyt:是 gt bbox 的中心 xy 坐标
  • w t , h t w_t,h_t wtht:是 gt bbox 的 wh值
  • x a , y a x_a,y_a xaya:是 anchor 的中心 xy 坐标
  • w a , h a w_a,h_a waha:是 anchor 的 wh 值
  • t = ( t x , t y , t w , t h ) t = (t_x, t_y, t_w, t_h) t=(tx,ty,tw,th):是 bbox 分支输出的 4 个值对应 targets
  • t x , t y t_x, t_y tx,ty:预测值表示 gt bbox 中心相对于anchor 中心点的偏移,并且通过除以 anchor 的 wh 进行归一化
  • t w , t h t_w, t_h tw,th:预测值表示 gt bbox 的 wh 除以 anchor 的 wh,然后取 log 非线性变换即可。

10.3.1 编码过程(Encoder)

  • 考虑target_means 和 target_stds,其代码如下:
# gx:真值,px:预测值
dx = (gx - px) / pw
dy = (gy - py) / ph
dw = torch.log(gw / pw)
dh = torch.log(gh / ph)
deltas = torch.stack([dx, dy, dw, dh], dim=-1)
# 最后减掉均值,除以标准差
means = deltas.new_tensor(means).unsqueeze(0)
stds = deltas.new_tensor(stds).unsqueeze(0)
deltas = deltas.sub_(means).div_(stds)

10.3.2 解码过程(Decoder)

  • 解码过程是编码过程的反向,其核心代码如下:
# 先乘上 std,加上 mean
means = deltas.new_tensor(means).view(1, -1).repeat(1, deltas.size(1) // 4)
stds = deltas.new_tensor(stds).view(1, -1).repeat(1, deltas.size(1) // 4)
denorm_deltas = deltas * stds + means
dx = denorm_deltas[:, 0::4]
dy = denorm_deltas[:, 1::4]
dw = denorm_deltas[:, 2::4]
dh = denorm_deltas[:, 3::4]
# wh 解码
gw = pw * dw.exp()
gh = ph * dh.exp()
# 中心点 xy 解码
gx = px + pw * dx
gy = py + ph * dy
# 得到 x1y1x2y2 的 gt bbox 预测坐标
x1 = gx - gw * 0.5
y1 = gy - gh * 0.5
x2 = gx + gw * 0.5
y2 = gy + gh * 0.5

10.4 Focal Loss (易学样本权重比较低,难样本权重比较高)

10.4.1 核心思想

  • 出发点:希望one-stage detector(YOLO、SSD)可以达到two-stage detector(Faster R-CNN)的准确率,同时不影响原有的速度。
  • 两阶段AP不如一阶段AP的原因样本的类别不均衡导致的
  • 样本不均衡导致的问题:
    • 训练低效,因为大多数位置都是易学负样本,没有提供有用的学习信号
    • 由易学负样本主导整个训练,使得模型的优化方向并不是我们所希望的那样
  • Focal Loss的核心思想通过减少易分类样本的权重,使得模型在训练时更专注于难分类的样本
  • Focal Loss的有效性:为了证明focal loss的有效性,作者设计了一个dense detector:RetinaNet,并且在训练时采用Focal Loss训练。实验证明RetinaNet不仅可以达到one-stage detector的速度,也能有two-stage detector的准确率

10.4.2 重要性质

  • 调制系数(modulating factor):
    ( 1 − p t ) γ (1-p_t)^{\gamma} (1pt)γ
    • γ \gamma γ:称作focusing parameter ( γ ≥ 0 \gamma \ge 0 γ0
  • 性质1
    • 当一个样本被分错的时候, p t p_t pt是很小的,那么调制因子( 1 − p t 1-p_t 1pt)接近1,损失不被影响
    • p t → 1 p_t→1 pt1,因子( 1 − p t 1-p_t 1pt)接近0,那么分的比较好的(well-classified)样本的权值就被调低了
    • 因此调制系数就趋于1,也就是说相比原来的loss是没有什么大的改变的。当 p t p_t pt趋于1的时候(此时分类正确而且是易分类样本),调制系数趋于0,也就是对于总的loss的贡献很小。
  • 性质2
    • γ \gamma γ=0的时候,Focal Loss就是传统的交叉熵损失,当 γ \gamma γ增加的时候,调制系数也会增加。 专注参数 γ \gamma γ平滑地调节了易分样本调低权值的比例。
    • γ \gamma γ增大能增强调制因子的影响,实验发现 γ \gamma γ取2最好。
    • 直觉上来说,调制因子减少了易分样本的损失贡献,拓宽了样例接收到低损失的范围。当 γ \gamma γ一定的时候,比如等于2,一样easy example( p t = 0.9 p_t=0.9 pt=0.9)的loss要比标准的交叉熵loss小100+倍,当 p t p_t pt=0.968时,要小1000+倍,但是对于hard example( p t p_t pt < 0.5),loss最多小了4倍。这样的话hard example的权重相对就提升了很多。这样就增加了那些误分类的重要性

10.4.3 具体实现

  • RetinaNet 一个非常大的亮点就是提出了 Focal Loss
  • γ = 0 \gamma = 0 γ=0:表示标准的交叉熵损失(即蓝色线
    OpenMMLab 目标检测_第113张图片
  • Focal Loss 属于 CE Loss 的动态加权版本,其可以根据样本的难易程度(预测值和 label 的差距可以反映)对每个样本单独加权,易学样本权重比较低,难样本权重比较高。因为在前面的 bbox assigner 环节,大部分样本都是背景易学样本,虽然其本身 loss 比较小,但是由于数目众多最终会主导梯度,从而得到次优模型,而 Focal Loss 通过指数效应把大量易学样本的权重大大降低,从而避免上述问题。
  • 完整的Focal Loss为:
    l o s s f o c a l = − α t ( 1 − p t ) γ l o g ( p t ) loss_{focal} = - \alpha_t(1-p_t)^{\gamma} log(p_t) lossfocal=αt(1pt)γlog(pt)
  • α \alpha α:属于正负样本的加权参数,值越大,正样本的权重越大
  • γ \gamma γ:有focal效应,可以控制难易样本权重,值越大,对分类错误样本梯度越大(难样本权重大),focal 效应越大,这个参数非常关键;目的是通过减少易分类样本的权重,从而使得模型在训练时更专注于难分类的样本。
  • 计算Focal Loss的代码
pred_sigmoid = pred.sigmoid()
# one-hot 格式
target = target.type_as(pred)
pt = (1 - pred_sigmoid) * target + pred_sigmoid * (1 - target)
focal_weight = (alpha * target + (1 - alpha) *
            (1 - target)) * pt.pow(gamma)
loss = F.binary_cross_entropy_with_logits(
        pred, target, reduction='none') * focal_weight
loss = weight_reduce_loss(loss, weight, reduction, avg_factor)
return loss
  • RetinaNet 的完整 loss 配置如下
# 分类 loss
loss_cls=dict(
    type='FocalLoss',
    use_sigmoid=True,
    gamma=2.0,
    alpha=0.25,
    loss_weight=1.0),
# 回归 loss
loss_bbox=dict(type='L1Loss', loss_weight=1.0))

10.5 交叉熵损失函数(Cross Entropy Loss Function)

  • 不仅可以很好的衡量模型的效果,又可以很容易的的进行求导计算。
    OpenMMLab 目标检测_第114张图片
  • SoftMax/Sigmoid:计算对于每个预测结果的概率值
  • CrossEntropy损失函数:用于分类问题

10.5.1 二分类

  • 在二分类的情况下,模型最后需要预测的结果只有两种情况,对于每个类别我们的预测得到的概率为 p p p 1 − p 1-p 1p ,此时表达式为( l o g log log的底数为 e e e):
    L = 1 N ∑ i L i = 1 N ∑ i − [ y i ⋅ l o g ( p i ) + ( 1 − y i ) ⋅ l o g ( 1 − p i ) ] L = \frac{1}{N} \sum_{i} L_i = \frac{1}{N} \sum_i -[y_i \cdot log(p_i) + (1-y_i) \cdot log(1-p_i)] L=N1iLi=N1i[yilog(pi)+(1yi)log(1pi)]

  • y i y_i yi:表示样本 i i i 的label,正类为 1 1 1, 负类为 0 0 0

  • p i p_i pi:表示样本 i i i 预测为正类的概率

10.5.2 多分类

  • 多分类的情况实际上就是对二分类的扩展:
    L = 1 N ∑ i L i = − 1 N ∑ i ∑ c = 1 M y i c ⋅ l o g ( p i c ) L = \frac{1}{N} \sum_{i} L_i = - \frac{1}{N} \sum_i \sum_{c=1}^M y_{ic} \cdot log(p_{ic}) L=N1iLi=N1ic=1Myiclog(pic)
  • M M M:类别的数量
  • y i c y_{ic} yic:符号函数(0或1 ),如果样本 i i i的真实类别等于 c c c取1,否则取0
  • p i c p_{ic} pic:观测样本 i i i属于类别 c c c的预测概率

10.5.3 学习过程

  • 交叉熵损失函数经常用于分类问题中,特别是在神经网络做分类问题时,也经常使用交叉熵作为损失函数,此外,由于交叉熵涉及到计算每个类别的概率,所以交叉熵几乎每次都和sigmoid(或softmax)函数一起出现。
  • 我们用神经网络最后一层输出的情况,来看整个模型预测、获得损失和学习的流程:
    • 神经网络最后一层得到每个类别的得分scores(也叫logits);
    • 该得分经过sigmoid(或softmax)函数获得概率输出
    • 模型预测的类别概率输出( p i c p_{ic} pic)真实类别的one hot( y i c y_{ic} yic)形式进行交叉熵损失函数的计算
  • 学习任务分为二分类和多分类情况,我们分别讨论这两种情况的学习过程。

10.5.3.1 二分类情况

OpenMMLab 目标检测_第115张图片

  • scores是输入的线性函数作用的结果
    ∂ L i ∂ w i = ∂ L i ∂ p i ⋅ ∂ p i ∂ s i ⋅ ∂ s i ∂ w i = [ σ ( s i ) − y i ] ⋅ x i ∂ s i ∂ w i = x i \frac{\partial L_i}{\partial w_i} = \frac{\partial L_i}{\partial p_i} \cdot \frac{\partial p_i}{\partial s_i} \cdot \frac{\partial s_i}{\partial w_i} = [\sigma(s_i) - y_i] \cdot x_i \\ \frac{\partial s_i}{\partial w_i} = x_i wiLi=piLisipiwisi=[σ(si)yi]xiwisi=xi
    • p i p_i pi:表示样本 i i i预测为正类的概率
    • y i y_i yi:为符号函数,样本 i i i为正类时取1,否则取0

10.5.3.2 多分类情况

OpenMMLab 目标检测_第116张图片
∂ L i ∂ w i c = ∂ L i ∂ p i k ⋅ ∂ p i k ∂ s i c ⋅ ∂ s i c ∂ w i c = [ σ ( s i ) − y i ] ⋅ x i \frac{\partial L_i}{\partial w_{ic}} = \frac{\partial L_i}{\partial p_{ik}} \cdot \frac{\partial p_{ik}}{\partial s_{ic}} \cdot \frac{\partial s_{ic}}{\partial w_{ic}} = [\sigma(s_i) - y_i] \cdot x_i wicLi=pikLisicpikwicsic=[σ(si)yi]xi

  • y i y_i yi:为向量,若真实类别为 k k k,则有:
    y i k = 1 y i c = 0 , c ≠ k y_{ik} = 1 \\ y_{ic} = 0, c \neq k yik=1yic=0,c=k
  • 交叉熵损失函数对于二分类和多分类求导时,采用向量化的形式后,求导结果的形式是一致的。

10.5.4 优点

  • 在用梯度下降法做参数更新的时候,模型学习的速度取决于两个值:
    • 学习率;
    • 偏导值
  • 其中,学习率是我们需要设置的超参数,所以我们重点关注偏导值。从上面的式子中,我们发现,偏导值的大小取决于 x i x_i xi
    [ σ ( s ) − y ] [\sigma(s)-y] [σ(s)y] ,我们重点关注后者,后者的大小值反映了我们模型的错误程度,该值越大,说明模型效果越差,但是该值越大同时也会使得偏导值越大,从而模型学习速度更快。所以,使用逻辑函数得到概率,并结合交叉熵当损失函数时,在模型效果差的时候学习速度比较快,在模型效果好的时候学习速度变慢。

10.5.5 缺点

  • Deng [4]在2019年提出了ArcFace Loss,并在论文里说了Softmax Loss的两个缺点:
    • 随着分类数目的增大,分类层的线性变化矩阵参数也随着增大;
    • 对于封闭集分类问题,学习到的特征是可分离的,但对于开放集人脸识别问题,所学特征却没有足够的区分性。对于人脸识别问题,首先人脸数目(对应分类数目)是很多的,而且会不断有新的人脸进来,不是一个封闭集分类问题。
  • 另外,sigmoid(softmax)+cross-entropy loss 擅长于学习类间的信息,因为它采用了类间竞争机制,它只关心对于正确标签预测概率的准确性,忽略了其他非正确标签的差异,导致学习到的特征比较散。基于这个问题的优化有很多,比如对softmax进行改进,如L-Softmax、SM-Softmax、AM-Softmax等。

11 常见错误排除流程

OpenMMLab 目标检测_第117张图片

  • 参考:https://zhuanlan.zhihu.com/p/35709485

你可能感兴趣的:(训练模型,计算机视觉,人工智能)