YOLOV4知识点分析(二)

YOLOV4知识点分析(二)

6. 数据增强相关-mixup

 论文名称:mixup: BEYOND EMPIRICAL RISK MINIMIZATION

论文地址:https://arxiv.org/abs/1710.09412

    mixup由于非常有名,大家都应该知道,而且网上各种解答非常多,故这里就不重点说了。

    其核心操作是:两张图片采用比例混合,label也需要混合。

 

 

 论文中提到的一些关键的Insight:

    1 也考虑过三个或者三个以上的标签做混合,但是效果几乎和两个一样,而且增加了mixup过程的时间。
    2 当前的mixup使用了一个单一的loader获取minibatch,对其随机打乱后,mixup对同一个minibatch内的数据做混合。这样的策略和在整个数据集随机打乱效果是一样的,而且还减少了IO的开销。
    3 在同种标签的数据中使用mixup不会造成结果的显著增强

7. 数据增强相关-cutmix和Mosaic

论文名称:CutMix: Regularization Strategy to Train Strong Classifiers with Localizable Features
论文地址:https://arxiv.org/abs/1905.04899
开源地址:https://github.com/clovaai/CutMix-PyTorch

 

 

 mixup相当于是全图融合,cutout仅仅对图片进行增强,不改变label,而cutmix则是采用了cutout的局部融合思想,并且采用了mixup的混合label策略,看起来比较make sense。

    cutmix和mixup的区别是,其混合位置是采用hard 0-1掩码,而不是soft操作,相当于新合成的两张图是来自两张图片的hard结合,而不是Mixup的线性组合。但是其label还是和mixup一样是线性组合。作者认为mixup的缺点是:Mixup samples suffer from the fact that they are locally ambiguous and unnatural, and therefore confuses the model, especially for localization。

 

 M是和原图大小一样的矩阵,只有0-1值,用于控制线性混合度,通过参数可以控制裁剪矩形大小,

 

 

 伪代码如下

 

 Mosaic增强是本文提出的,属于cutmix的扩展,cutmix是两张图混合,而马赛克增强是4张图混合,好处非常明显是一张图相当于4张图,等价于batch增加了,可以显著减少训练需要的batch size大小。

 

 

 8. 数据增强相关-Stylized-ImageNet

论文名称:ImageNet-trained cnns are biased towards texture; increasing shape bias improves accuracy and robustness

     本文非常有意思,得到的结论非常有意义,可以指导我们对于某些场景测试失败的分析。本质上本文属于数据增强论文,做的唯一一件事就是:对ImageNet数据集进行风格化。

    本文结论是:CNN训练学习到的实际是纹理特征(texture bias)而不是形状特征,这和人类的认知方式有所区别,如论文题目所言,存在纹理偏置。而本文引入风格化imagenet数据集,平衡纹理和形状偏置,提高泛化能力。

    本文指出在ImageNet上训练的CNN强烈的偏向于识别纹理而不是形状,这和人的行为是极为不同的,存在纹理偏差,所以提出了Stylized-ImageNet数据,混合原始数据训练就可以实现既关注纹理,也关注形状(也就是论文标题提到的减少纹理偏向,增加形状偏向)。从而不仅更适合人类的行为,更惊讶的是提升了目标检测的精度,以及鲁棒性,更加体现了基于形状表示的优势。

    文章从一只披着象皮的猫究竟会被识别为大象还是猫这个问题入手,揭示了神经网络根据物体的texture进行识别而非我们以为的根据物体的形状。

    作者准备了6份数据,分别是正常的图片,灰色图,只包含轮廓的,只包含边缘的,只有纹理没有形状,纹理和形状相互矛盾(大象的纹理,猫的形状),对于第六份数据(纹理和形状冲突的数据),作者采用Stylized-ImageNet随机地将物体的纹理替换掉(也就是本文创新点),如下(c)所示:

 

 

 采用了4个主流网络,加上人类直观评估。原图其实是作者除了物体外,其余都是白色背景的数据集,目的是去除干扰。

    对于前面5份数据,采用原图和灰度图,神经网络都可以取得非常高的准确率,而对于只包含轮廓和只包含边缘的图片,神经网络的预测准确率则显著降低。更有意思的是,对于只包含纹理的图片,神经网络取得特别高的准确率。因而不难推断出,神经网络在识别中,主要是参考纹理信息而不是形状信息。

    作者先构造数据集,然后再进行后面的深入实验,IN就是指的ImageNet,SIN是指的风格化的ImageNet,如下所示

 

 

 SIN的特点是保留shape,但是故意混淆掉纹理信息。

 

 

 从上表的第一行可以看出,在原始图片IN上训练的模型不能适应去除纹理SIN的图片(IN-SIN),而使用去除纹理的图片进行训练和测试效果会差于使用原始图片进行训练和测试(SIN-SIN),这说明纹理信息在图像识别中确实起到了一定的作用,去除了纹理信息会提高模型识别的难度。最后,当我们使用去除纹理的图片进行训练而在原图进行测试的时候(SIN-IN),效果比在去除纹理的图片上面效果好(SIN-SIN)。

    后面三行的实验采用的是第一行resnet的网络结构,其主要特征是限制模型的感受野,从而让模型无法学习到空间的信息,其对应的感受野分别是33*33,17*17,9*9,对于训练原始的图片,其结果测试误差跟没有加上感受野限制的误差差别不大,从而说明纹理信息起到主导作用(IN-IN),而对应去除掉纹理信息的图片,其测试结果下降十分明显(SIN-SIN),说明形状信息起到主要的作用,证明了SIN的模型确实在学习形状的信息而不是纹理的信息。这个实验是要说明提出的SIN数据集由于强制抹掉了固定纹理,网络训练难度增大,在没有限制感受野情况下可以学的蛮好,但是一旦限制了感受野就不行了,说明SIN模型学习到的不仅仅是纹理(因为纹理是局部的,如果依靠纹理来分类,那么准确率应该下降不了这么多),更多的是依靠shape分类,因为感受野外限制了,导致无法看到整个shape,并且通过更加限制感受野,SIN-SIN准确率下降更多可以发现。也就是说SIN数据集由于替换掉了纹理,迫使网络学习shape和纹理,达到了本文目的。SIN上训练的ResNet50展示出更强的形状偏向,符合人类常理。

    增强形状偏向也改变了表示,那么影响了CNN的性能和鲁棒性了吗?我们设置了两个训练方案:

    1 同时在SIN和IN上训练
    2 同时在SIN和IN上训练,在IN上微调。称为Shape-ResNet。

 

 

 作者把去掉纹理的数据和原图一起放进去模型中进行训练,最后用原图进行finetune,发现这种方法可以提高模型的性能。Shape-ResNet超过了原始ResNet的准确率,说明SIN是有用的图像增强。

    总结:CNN识别强烈依赖于纹理,而不是全局的形状,但是这是不好的,为了突出形状bias,可以采用本文的SIN做法进行数据增强,SIN混合原始数据训练就可以实现既关注纹理,也关注形状,不仅符合人类直观,也可以提高各种任务的准确率和鲁邦性。所以本文其实是提出了一种新的数据增强策略。是不是很有意思的结论?

9. 数据增强相关-label smooth

论文题目:Rethinking the inception architecture for computer vision

    label smooth是一个非常有名的正则化手段,防止过拟合,基本上没有人不知道,故不详说了,核心就是对label进行soft操作,不要给0或者1的标签,而是有一个偏移,相当于在原label上增加噪声,让模型的预测值不要过度集中于概率较高的类别,把一些概率放在概率较低的类别。

10. 特征增强相关-DropBlock

论文题目:DropBlock: A regularization method for convolutional networks
论文地址:https://arxiv.org/abs/1810.12890
开源代码:https://github.com/miguelvr/dropblock

    由于dropBlock其实是dropout在卷积层上的推广,故很有必须先说明下dropout操作。

    dropout,训练阶段在每个mini-batch中,依概率P随机屏蔽掉一部分神经元,只训练保留下来的神经元对应的参数,屏蔽掉的神经元梯度为0,参数不参数与更新。而测试阶段则又让所有神经元都参与计算。

    dropout操作流程:参数是丢弃率p
    1)在训练阶段,每个mini-batch中,按照伯努利概率分布(采样得到0或者1的向量,0表示丢弃)随机的丢弃一部分神经元(即神经元置零)。用一个mask向量与该层神经元对应元素相乘,mask向量维度与输入神经一致,元素为0或1。
    2)然后对神经元rescale操作,即每个神经元除以保留概率1-P,也即乘上1/(1-P)。
    3)反向传播只对保留下来的神经元对应参数进行更新。
    4)测试阶段,Dropout层不对神经元进行丢弃,保留所有神经元直接进行前向过程。

为啥要rescale呢?是为了保证训练和测试分布尽量一致,或者输出能量一致。可以试想,如果训练阶段随机丢弃,那么其实dropout输出的向量,有部分被屏蔽掉了,可以认为输出变了,如果dropout大量应用,那么其实可以等价为进行模拟遮挡的数据增强,如果增强过度,导致训练分布都改变了,那么测试时候肯定不好,引入rescale可以有效的缓解,保证训练和测试时候,经过dropout后数据分布能量相似

 

 

 上面的截图来自:https://www.pianshen.com/article/2769164511/

    dropout方法多是作用在全连接层上,在卷积层应用dropout方法意义不大。文章认为是因为每个feature map的位置都有一个感受野范围,仅仅对单个像素位置进行dropout并不能降低feature map学习的特征范围,也就是说网络仍可以通过该位置的相邻位置元素去学习对应的语义信息,也就不会促使网络去学习更加鲁邦的特征。

    既然单独的对每个位置进行dropout并不能提高网络的泛化能力,那么很自然的,如果我们按照一块一块的去dropout,就自然可以促使网络去学习更加鲁邦的特征。思路很简单,就是在feature map上去一块一块的找,进行归零操作,类似于dropout,叫做dropblock。

 

 

 绿色阴影区域是语义特征,b图是模拟dropout的做法,随机丢弃一些位置的特征,但是作者指出这做法没啥用,因为网络还是可以推断出来,(c)是本文做法。

 

 

 dropblock有三个比较重要的参数,一个是block_size,用来控制进行归零的block大小;一个是γ,用来控制每个卷积结果中,到底有多少个channel要进行dropblock;最后一个是keep_prob,作用和dropout里的参数一样。

 

 

 M大小和输出特征图大小一致,非0即1,为了保证训练和测试能量一致,需要和dropout一样,进行rescale。

    上述是理论分析,在做实验时候发现,block_size控制为7*7效果最好,对于所有的feature map都一样,γ通过一个公式来控制,keep_prob则是一个线性衰减过程,从最初的1到设定的阈值(具体实现是dropout率从0增加到指定值为止),论文通过实验表明这种方法效果最好。如果固定prob效果好像不好。实践中,并没有显式的设置的值,而是根据keep_prob(具体实现是反的,是丢弃概率)来调整。

    DropBlock in ResNet-50 DropBlock加在哪?最佳的DropBlock配置是block_size=7,在group3和group4上都用。将DropBlock用在skip connection比直接用在卷积层后要好,具体咋用,可以看代码。

class DropBlock2D(nn.Module):    r"""Randomly zeroes 2D spatial blocks of the input tensor.
    As described in the paper    `DropBlock: A regularization method for convolutional networks`_ ,    dropping whole blocks of feature map allows to remove semantic    information as compared to regular dropout.
    Args:        drop_prob (float): probability of an element to be dropped.        block_size (int): size of the block to drop
    Shape:        - Input: `(N, C, H, W)`        - Output: `(N, C, H, W)`
    .. _DropBlock: A regularization method for convolutional networks:       https://arxiv.org/abs/1810.12890
    """
    def __init__(self, drop_prob, block_size):        super(DropBlock2D, self).__init__()
        self.drop_prob = drop_prob        self.block_size = block_size
    def forward(self, x):        # shape: (bsize, channels, height, width)
        assert x.dim() == 4, \            "Expected input with 4 dimensions (bsize, channels, height, width)"
        if not self.training or self.drop_prob == 0.:            return x        else:            # get gamma value            gamma = self._compute_gamma(x)
            # sample mask            mask = (torch.rand(x.shape[0], *x.shape[2:]) < gamma).float()
            # place mask on input device            mask = mask.to(x.device)
            # compute block mask            block_mask = self._compute_block_mask(mask)
            # apply block mask            out = x * block_mask[:, None, :, :]
            # scale output            out = out * block_mask.numel() / block_mask.sum()
            return out
    def _compute_block_mask(self, mask):        # 比较巧妙的实现,用max pool来实现基于一点来得到全0区域        block_mask = F.max_pool2d(input=mask[:, None, :, :],                                  kernel_size=(self.block_size, self.block_size),                                  stride=(1, 1),                                  padding=self.block_size // 2)
        if self.block_size % 2 == 0:            block_mask = block_mask[:, :, :-1, :-1]
        block_mask = 1 - block_mask.squeeze(1)
        return block_mask
    def _compute_gamma(self, x):        return self.drop_prob / (self.block_size ** 2)

联合线性调度一起使用,如下所示:

 

 

posted @ 2020-05-10 07:10  吴建明wujianming  阅读(1188)  评论(0编辑  收藏  举报