RetinaNet

论文:https://openaccess.thecvf.com/content_ICCV_2017/papers/Lin_Focal_Loss_for_ICCV_2017_paper.pdf

讲解(含代码讲解):https://zhuanlan.zhihu.com/p/410436667

论文解读:https://zhuanlan.zhihu.com/p/466853103

交叉熵损失函数:https://blog.csdn.net/weixin_45665708/article/details/111299919

从网络结构的角度来说,一阶段检测器的精度会小于二阶段方法,因此主要的重点在于损失函数的创新。

单阶段目标检测主要存在的问题是:正样本太多,负样本太少。因此本文的重点在于改变正负样本不平均的局面。

1. 损失函数

1.1 交叉熵损失函数

交叉熵目标函数是深度学习中常用的目标函数。它是从最早的信息量逐渐演变而来的。

信息量

\[I(x) = -log(P(x)) \]

信息量的性质:

  1. 事件发生概率越低,信息量越大;反之,事件发生概率越高,信息量越小;
  2. 多个时间同时发生的概率表现为概率相乘,而总信息量表现为信息量相加(这也是信息量表现为对数形式的原因)。

信息熵

表示可能产生的信息量的期望

\[H(X) = -\sum^n_{i=1}p(x_i)log(p(x_i)) \]

信息熵越大,不确定性越强。

相对熵

又被称为KL散度,用于反映两个概率分布之间的非对称性。设\(p(x)\)为样本真实分布,\(q(x)\)为预测分布,则他们的信息熵可以表示为:

\[p(x) : H_{pp}(X) = -\sum^n_{i=1}p(x_i)log(p(x_i))\\ q(x) : H_{pq}(X) = -\sum^n_{i=1}p(x_i)log(q(x_i))\\ KL\ Divergence = H_{pq}(X) - H_{pp}(X)\\ = \sum^n_{i=1}p(x_i)log(\frac{p(x_i)}{q(x_i)}) \]

交叉熵

\[H(p,q) = -\sum^n_{i=1}p(x_i)log(q(x_i)) \]

因为在有监督训练中,样本标签是确定的,相当于知识的概率密度分布已知,KL散度的第一项可以看做是常数。因此KL散度和交叉熵的关系可以表示为:

\[KL\ Divergence = constant + H(p,q) \]

定义:交叉熵损失函数

在二分类问题中,针对单样本的交叉熵损失函数定义为如下形式:

\[CELoss = \begin{cases} -log(p),\ if\ \ y=1\\ -log(1-p),\ if\ \ y=0 \end{cases} \]

其中,p表示预测样本等于1的概率。针对所有样本的交叉熵损失函数定义如下:

\[L = \frac{1}{N}(\sum^m_{y_i=1}-log(p)-\sum^n_{y_i=0}-log(1-p)) \]

扩展到多类:

\[L = -\frac{1}{N}(\sum_i\sum^M_{c=1}y_{ic}log(p_{ic})) \]

如二分类问题中的所有样本的交叉熵函数所示,m为正样本数量,n为负样本数量。当样本类别不平均例如\(m<<n\)时,负样本的损失会在总损失中占主导,导致模型训练过程中倾斜于样本多的类别,从而对少样本类别分类性能较差

1.2 Balance Cross Entropy

为了解决这个问题,平衡CELoss的方法被提出,即引入一个平衡参数\(\alpha \in (0,1)\)

\[BCELoss = \begin{cases} -\alpha log(p),\ if\ \ y=1\\ -(1-\alpha) log(1-p),\ if\ \ y=0 \end{cases} \]

其中,\(\alpha\)的值由正负样本的比例来确定,即\(\frac{\alpha}{1-\alpha} = \frac{n}{m}\)

二分类所有样本的损失函数如下:

\[L = \frac{1}{N}(\sum^m_{y_i=1}-\alpha log(p)-\sum^n_{y_i=0}-(1-\alpha)log(1-p)) \]

1.3 RetinaNet中的Focal Loss Definition

虽然平衡交叉熵损失函数对样本中的正负样本进行了平衡,目标检测中还存在了一个问题:易分样本(置信度高的样本)和难分样本数量不平衡。因此,作者利用置信度进一步改造了损失函数,添加了调制因子:

\[FLoss = -(1-p_t)^\gamma log(p_t) = \begin{cases} -(1-p)^\gamma log(p),\ if\ \ y=1\\ -p^\gamma log(1-p),\ if\ \ y=0 \end{cases} \]

其中:

\[p_t = \begin{cases} p,\ if\ \ y=1\\ 1-p,\ if\ \ y=0 \end{cases} \]

\(p_t\)越高,说明该样本越容易被分类。因此,当\(p_t \rightarrow 0\)时,说明该样本是难分样本,调制因子也接近于1,Loss整体不变;相反,当\(p_t \rightarrow 1\)时,说明是易分样本,调制因子也接近于0,==降低了易分类样本的权重。

2. 网络结构

2.1 Backbone

选择resnet50作为backbone,用于提取图片特征。

backbone=dict(
        type='ResNet',
        depth=50, # resnet 50
        num_stages=4, # res layer的层数,restnet50中该值为4
        out_indices=(0, 1, 2, 3), # 按out_indices指定顺序输出,其中int表示第几个res layer层的输出,从0开始计数。
        frozen_stages=1, # 冻结梯度以及设置eval模式的前n个stages。为0表示conv1、norm1(或者stem);为1表示res layer1,以此类推;-1 表示不冻结任何。
        norm_cfg=dict(type='BN', requires_grad=True), # norm层的配置,默认bacthnorm
        norm_eval=True, # 默认为True,这样在训练时就不会更新norm层中的均值和方差,
        # 当然norm层中的可学习参数beta等还是会更新的。这是一个迁移训练技巧,加载的预训练模型,并把norm设置为eval。
        style='pytorch', # resnet的历史遗留问题,默认pytorch就行了
        init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')), # 加载预训练模型的地址

2.2 Neck

neck即FPN网络,接受C2,C3,C4三个特征图,输出P2-P7五个特征。

 neck=dict(
        type='FPN',
        in_channels=[256, 512, 1024, 2048], # backbone输出的每层特征层的channel
        out_channels=256, # FPN输出的每层特征层的channel
        start_level=1, # FPN利用的backbone特征层的起始层序号
        add_extra_convs='on_input', # 表示在输入的backbone的最后一层特征层进行3*3的卷积
        num_outs=5),  # FPN输出的特征层数量

2.3 Head

头部网络接收Neck输出的五个特征图,五个特征图的head模块权重共享;后面分为两个分支,分别进行分类和bbox回归,这两个分支的权重不共享。RetinaNet是anchor based模型也体现在head

训练

  • focal loss:平衡了正负样本,所以头部网络在训练时不需要进行bbox sampler;

  • bbox_coder:将bbox编码成bbox和ground truth的delta形式;

  • bbox_assigner:将ground truth与anchor对应,根据IoU确定正样本、负样本和忽略样本。

posted @ 2024-01-31 17:09  PaB式乌龙茶  阅读(24)  评论(0编辑  收藏  举报