Dropout-Pytorch实现

Dropout技术是Srivastava等人在2012年提出的技术,现在已然成为各种深度模型的标配。其中心思想是随机地冻结一部分模型参数,用于提高模型的泛化性能。

Dropout的洞察

关于Dropout,一个流行的解释是,通过随机行为训练网络,并平均多个随机决定的结果,实现了参数共享的Bagging。如下图,通过随机地冻结/抛弃某些隐藏单元,我们得到了新的子网络,而参数共享是说,与Bagging中子模型相互独立的参数不同,深度网络中Dropout生成的子网络是串行的,后一个子模型继承了前一个子模型的某些参数。

dropout

Dropout是模型自我破坏的一种形式,这种破坏使得存活下来的部分更加鲁棒。例如,某一隐藏单元学得了脸部鼻子的特征,而在Dropout中遭到破坏,则在之后的迭代中,要么该隐藏单元重新学习到鼻子的特征,要么学到别的特征,后者则说明,鼻子特征对该任务来说是冗余的,因而,通过Dropout,保留下来的特征更加稳定和富有信息。

Hinton曾用生物学的观点解释这一点。神经网络的训练过程可以看做是生物种群逐渐适应环境的过程,在迭代中传递的模型参数可以看做种群的基因,Dropout以随机信号的方式给环境随机的干扰,使得传递的基因不得不适应更多的情况才能存活。

另一个需要指出的地方是,Dropout给隐藏单元加入的噪声是乘性的,不像Bias那样加在隐藏单元上,这样在进行反向传播时,Dropout引入的噪声仍能够起作用。

代码实现

下面看在实践中,Dropout层是如何实现的。简单来说,就是生成一系列随机数作为mask,然后再用mask点乘原有的输入,达到引入噪声的效果。

From Scratch

# forward pass
def dropout_forward(x, dropout_param):
  p, mode = dropout_param['p'], dropout_param['mode']
  # p: dropout rate; mode: train or test
  if 'seed' in dropout_param:
    np.random_seed(dropout_param['seed'])
  # seed: random seed
  mask = None
  out = None
  if mode == 'train':
    mask = (np.random.rand(*x.shape) >= p)/(1-p)
    # 1-p as normalization multiplier: to keep the size of input
    out = x * mask
  elif mode == 'test':
    # do nothing when perform inference
    out = x
  cache = (dropout_param, mask)
  out = out.astype(x.dtype, copy=False)
  return out, cache

# backward pass
def dropout_backward(dout, cache):
  dropout_param, mask = cache
  mode = dropout_param['mode']

  dx = None
  if mode == 'train':
    dx = dout * mask
  elif mode == 'test':
    dx = dout
  return dx

Pytorch实现

file: /torch/nn/_functions/dropout.py

class Dropout(InplaceFunction):

    def __init__(self, p=0.5, train=False, inplace=False):
        super(Dropout, self).__init__()
        if p < 0 or p > 1:
            raise ValueError("dropout probability has to be between 0 and 1, "
                             "but got {}".format(p))
        self.p = p
        self.train = train
        self.inplace = inplace

    def _make_noise(self, input):
    # generate random signal
        return input.new().resize_as_(input)

    def forward(self, input):
        if self.inplace:
            self.mark_dirty(input)
            output = input
        else:
            output = input.clone()

        if self.p > 0 and self.train:
            self.noise = self._make_noise(input)
            # multiply mask to input
            self.noise.bernoulli_(1 - self.p).div_(1 - self.p)
            if self.p == 1:
                self.noise.fill_(0)
            self.noise = self.noise.expand_as(input)
            output.mul_(self.noise)

        return output

    def backward(self, grad_output):
        if self.p > 0 and self.train:
            return grad_output.mul(self.noise)
        else:
            return grad_output

Creative Commons License
本文章遵从署名-相同方式共享4.0国际协议(CC BY-SA 4.0)
这意味着您可以署名转载本文章,并附上此协议。
我每周会分享一些有趣实用的英文文章,欢迎关注ddlee每周分享
这里可以找到我推荐的服务、应用程序、书籍和电影。

本文链接:https://blog.ddlee.cn/posts/5556c57b/
分享文章:

小额捐助:

相关文章

[论文笔记]Deep Learning [论文笔记]Visualizing and Understanding Recurrent Networks

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×