当前位置:   article > 正文

dropout层

dropout层

深度神经网(DNN)中经常会存在一个常见的问题:模型只学会在训练集上分类(过拟合现象),dropout就是为了减少过拟合而研究出的一种方法。

一、简介

当训练模型较大,而训练数据很少的话,很容易引起过拟合,一般情况我们会想到用正则化、或者减小网络规模。然而Hinton在2012年文献:《Improving neural networks by preventing co-adaptation of feature detectors》提出了,在每次训练的时候,随机让一定数量的卷积停止工作,这样可以提高网络的泛化能力,Hinton又把它称之为dropout
dropout是指深度学习训练过程中,对于神经网络训练单元,暂时将按照一定的概率将其从网络中移除,注意是暂时,对于随机梯度下降来说,由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。
其工作原理如下图:在这里插入图片描述
第一种理解方式是,在每次训练的时候使用dropout,每个神经元有百分之50的概率被移除,这样可以使得一个神经元的训练不依赖于另外一个神经元,同样也就使得特征之间的协同作用被减弱。Hinton认为,过拟合可以通过阻止某些特征的协同作用来缓解。
第二种理解方式是,我们可以把dropout当做一种多模型效果平均的方式。对于减少测试集中的错误,我们可以将多个不同神经网络的预测结果取平均,而因为dropout的随机性,我们每次dropout后,网络模型都可以看成是一个不同结构的神经网络,而此时要训练的参数数目却是不变的,这就解脱了训练多个独立的不同神经网络的时耗问题。在测试输出的时候,将输出权重除以二,从而达到类似平均的效果。

需要注意的是如果采用dropout,训练时间大大延长,但是对测试阶段没影响。

二、dropout数学原理

目前来说,Dropout有两种。第一种就是传统的Dropout方案。另一种,就是我们的吴恩达老师的Inverted Dropout。

1、Inverted Dropout

Inverted Dropout的实现代码,假设,我们的输入是x,p表示随机丢弃的概率, 1−p表示的是神经元保存的概率。则Inverted Dropout的实现过程如下代码所示:

import numpy as np
def dropout(x, p):
    if p < 0. or p >1.
        # 边界条件,在写代码的时候,一定要仔细!!!p为随机丢弃的概率
        raise Exception("The p must be in interval [0, 1]")
    retain_prob =1. -p
    #我们通过binomial函数,生成与x一样的维数向量。
    # binomial函数就像抛硬币一样,每个神经元扔一次,所以n=1
    # sample为生成的一个0与1构成的mask,0表示抛弃,1表示保留
    sample =np.random.binomial(n=1, p=retain_prob, size=x.shape)
    x *= sample # 与0相乘,表示将该神经元Drop掉
    x /= retain_prob
    return x
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这里解释下,为什么在后面还需要进行 x/=retain_prob 的操作?

假设该层是输入,它的期望是a,在不使用Dropout的时候,它的期望依旧是a。如果该层进行了Dropout, 相当于有p的概率被丢弃,1−p的概率被保留,则此层的期望为 ( 1 − p ) ∗ a ∗ 1 + p ∗ a ∗ 0 = ( 1 − p ) ∗ a (1−p)∗a∗1+p∗a∗0=(1−p)∗a (1p)a1+pa0=(1p)a,为了保证输入与输出的期望一致,我们需要进行代码中x/=retain_prob这一步。

二、传统Dropout

对于传统的Dropout,在训练的时候,不需要进行x/=retain_prob的这一步,直接进行神经元Drop操作。此时,假设输入x的期望是a,则此时的输出期望为(1−p)∗a。在测试的时候,整个神经元是保留的,因此输出期望为a。为了让输入与输出的期望一致,则在测试的阶段,需要乘以(1−p),使其期望值保持(1−p)∗a。
传统的dropout和Inverted-dropout虽然在具体实现步骤上有一些不同,但从数学原理上来看,其正则化功能是相同的,那么为什么现在大家都用Inverted-dropout了呢?主要是有两点原因:

1、测试阶段的模型性能很重要,特别是对于上线的产品,模型已经训练好了,只要执行测试阶段的推断过程,那对于用户来说,推断越快用户体验就越好了,而Inverted-dropout把保持期望一致的关键步骤转移到了训练阶段,节省了测试阶段的步骤,提升了速度。
2、dropout方法里的p是一个可能需要调节的超参数,用Inverted-dropout的情况下,当你要改变p 的时候,只需要修改训练阶段的代码,而测试阶段的推断代码没有用到p ,就不需要修改了,降低了写错代码的概率。

三、DropConnect

DropOut的出发点是直接干掉部分神经元节点,我们能不能不干掉神经元,我们把网络权值干掉部分呢?DropConnect干掉的就是网络权重。具体细节如下:
在这里插入图片描述

针对二维卷积核进行DropConnect操作:

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn.modules.conv import _ConvNd,_pair

class DropConnectConv2D(_ConvNd):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1,
                 padding=0, dilation=1, groups=1,
                 bias=True, padding_mode='zeros', p=0.5):
        kernel_size = _pair(kernel_size)
        stride = _pair(stride)
        padding = _pair(padding)
        dilation = _pair(dilation)
        super(DropConnectConv2D, self).__init__(
            in_channels, out_channels, kernel_size, stride, padding, dilation,
            False, _pair(0), groups, bias, padding_mode)
        self.dropout = nn.Dropout(p)
        self.p = p

    def _conv_forward(self, input, weight):
        if self.padding_mode != 'zeros':
            return F.conv2d(F.pad(input, self._reversed_padding_repeated_twice, mode=self.padding_mode),
                            weight, self.bias, self.stride,
                            _pair(0), self.dilation, self.groups)
        return F.conv2d(input, weight, self.bias, self.stride,
                        self.padding, self.dilation, self.groups)

    def forward(self, input):
        return self._conv_forward(input, self.dropout(self.weight) * self.p)

if __name__=='__main__':
    conv = DropConnectConv2D(1,1,3,1,bias=False).train()
    conv.weight.data = torch.ones_like(conv.weight)

    a = torch.ones([1,1,3,3])
    print(a)
    print(conv(a))

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

四、Stochastic Depth

StochasticDepth是采取类似于Dropout的思路,在ResNet块上随机进行对模块的删除,进而提高对模型的泛化能力。
如图所示,为Stochastic Depth的具体做法。
在这里插入图片描述

五、Cutout

目前为主,丢的主要是权重,或者是丢的是神经元。这里开始,我们要丢的是是网络的输入,当然网络输入不仅仅可以丢,也可以添加噪声(Cutmix等),这个是后面要做的内容。当然,还有一些对于输入图像进行Drop的操作(如random erase)。
图像上进行随机位置和一定大小的patch进行0−mask裁剪。一开始使用裁剪上采样等变换出复杂轮廓的patch后来发现简单的固定像素patch就可以达到不错的效果,所以直接采用正方形patch。
通过patch的遮盖可以让网络学习到遮挡的特征。Cutout不仅能够让模型学习到如何辨别他们,同时还能更好地结合上下文从而关注一些局部次要的特征。

如下图:
在这里插入图片描述

六、DropBlock

首先直观的从图片中看下DropBlock的具体做法:
在这里插入图片描述
其中(b)表示的是随机Dropout的效果,©为Drop掉相邻的一整片区域,即按Spatial块随机扔。

七、Dropout与BN不和谐共处

假设我们的输入tensor的维度是(4,3,2,2),那么我们在做BN的时候,我们在channel维度中“抽”出来一个通道的数据,则其维度为(4,1,2,2)。我们需要对这16个数据求均值μ 跟方差σ,并用求得的均值与方差归一化,再缩放数据,得到BN层的输出。

Dropout在网络测试的时候神经元会产生“variance shift”,即“方差偏移”。试想若有图一中的神经响应X,当网络从训练转为测试时,Dropout 可以通过其随机失活保留率(即p)来缩放响应,并在学习中改变神经元的方差,而BN仍然维持X的统计滑动方差(varrunning_var)。这种方差不匹配可能导致数值不稳定。而随着网络越来越深,最终预测的数值偏差可能会累计,从而降低系统的性能。事实上,如果没有Dropout,那么实际前馈中的神经元方差将与BN 所累计的滑动方差非常接近,这也保证了其较高的测试准确率。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/951706
推荐阅读
相关标签
  

闽ICP备14008679号