Dropout的rescale

dropout 有两种实现方式,Vanilla Dropout 和inverted Dropout。前者是 原始论文 中的朴素版后者在 Andrew Ng 的 cs231 课程中(https://cs231n.github.io/neural-networks-2/#init)有介绍。其实就两个要点:

  1. rescale。输入序列为 X = [ x 0 , x 1 , x 2 , x 3 ] X=[x_0,x_1,x_2,x_3] X=[x0,x1,x2,x3] ,正常的梯度序列为 D = [ d 0 , d 1 , d 2 , d 3 ] D=[d_0,d_1,d_2,d_3] D=[d0,d1,d2,d3] ,假设以丢弃概率 p=0.2 进行伯努利采样Q得到的 mask 序列M =1011 。如果是 vanilla 版本,在 forward 时直接对输入进行遮罩 X ′ = [ x 0 , 0 , x 2 , x 3 ] X^{'}=[x_0,0,x_2,x_3] X=[x0,0,x2,x3] ,在 backward 时梯度和输入保持一致 D ′ = [ d 0 , 0 , d 2 , d 3 ] D^{'}=[d_0,0,d_2,d_3] D=[d0,0,d2,d3];在infer 时进行 1-p 倍的缩小I = 0.8X 。你看,训练时候因为随机扔掉了一些节点Q,总期望变小,那么预测时候就全体缩小一点来保持一致。这样处理的一个最大问题,就是预测过程需要跟着 dropout 策略做调整,哪些层取消了、加重了或者减轻了,都需要改。一不小心就会出错,inverted 版本顺势而生,把所有的修改都放在训练阶段,保持预测阶段不变。在 forward 时先遮罩再进行 1/(1-p) 的放大 X ′ = 1.25 ∗ [ x 0 , 0 , x 2 , x 3 ] X^{'}=1.25*[x_0,0,x_2,x_3] X=1.25[x0,0,x2,x3],在 backward 时需要同步修改梯度 D ′ = [ d 0 , 0 , d 2 , d 3 ] D^{'}=[d_0,0,d_2,d_3] D=[d0,0,d2,d3],在 infer 时不做额外的处理I = X。训练时虽然也随机扔掉了一些节点,但是做了 rescale 之后总期望被拉回了原来的水平,训练过程和预测过程仍然是一致的。
  2. forward和backward的时候扩大1-p倍,Infer的时候啥也不用干了

Pytorch对dropout的实现早就被放到cpp和cuda里去了,在古早版本里还有python版本的dropout,在https://github.com/colesbury/pytorch-old/blob/master/torch/nn/functions/dropout.py 这里可以看到源代码,input.new()就是随机了一个矩阵,下面摘录一些要点:

import torch
import torch.nn as nn
import torch.autograd.functional as Function

class MyDropoutFunc(Function):
    def __init__(self, p, train):
        self.p = p
        self.train = train
    def forward(self, input):
        if self.p > 0 and self.train:
            self.noise = self._make_noise(input).bernoulli_(1 - self.p).div_(1 - self.p)
            input.mul_(self.noise)
            return input
        return input
    def backward(self, grad_output):
        if self.p > 0 and self.train:
            return grad_output.mul(self.noise)
        else:
            return grad_output

部分转述自: https://www.zhihu.com/question/60874090/answer/181672076?from=groupmessage

你可能感兴趣的:(深度学习)