Dropout本质探讨

NTU李宏毅老师的《深度学习》课程讲dropout的本质是模型融合ensemble(bagging性质)。

Train阶段
在train的时候,每一次update参数之前,对network里面的每个neural(包括input),做sampling(抽样)。 每个neural会有p%会被丢掉,跟着的weight也会被丢掉。

接着使用新的改变了的网络结构进行训练。那么显然由于某些neural不见了,你在training 时,performance会变的有一点差,加上dropout,你会看到在testing set会变得有点差,但是dropout真正做的事就是让你testing 越做越好

test阶段

  • test阶段是不做dropout处理的
  • 假设training时dropout rate是p%,在testing rate时weights都要乘以(1-p)%
    fully-connected neural layer 用drop-out
  • 为什么dropout会有效?为什么在训练的时候要dropout,但是测试的时候不dropout?

    直观的理解

    那么在testing的时候,按照ensemble方法,我们需要把之前的每个不同dropout的network拿出来,然后把train data丢到network里面去,每一个network都会给你一个结果,这些结果的平均值就是最终的结果。但是实际上没有办法这样做,因为network太多了。(每个神经元有2种可能,一共N个神经元,则有 2^M 个网络)。dropout最神奇的是:当你把一个完整的network不进行dropout,但是将它的weights乘以(1-p)%,然后将train data输入,得到的output y:之前做average的结果跟output y是approximated。

    为什么会近似?

    image.png

    通过以上例子可以发现,针对只有2个神经元的全连接网络,dropout后一个产生四种可能的网络结构,对他们的输出做average,等效于不做dropout时对每个w*(1-p)%,所以dropout的本质就是模型融合。同时只有是linear network,ensemble才会等于weights multiply一个值。所以一般都是在全连接网络后面加入dropout。

    实践中比较常用的不是直接dropout,而是inveted dropout。原理是在训练的时候dropout之后做一个比例缩放,将所有w乘1/(1-p),则测试的时候不需要做缩放。无论做不做dropout,可保持inference代码不变。

    """ 普通版随机失活: 不推荐实现 (看下面笔记) """
    p = 0.5 # 激活神经元的概率. p值更高 = 随机失活更弱
    def train_step(X):
      """ X中是输入数据 """
      # 3层neural network的前向传播
      H1 = np.maximum(0, np.dot(W1, X) + b1)
      U1 = np.random.rand(*H1.shape) < p # 第一个随机失活遮罩,rand() [0,1)的随机数
      H1 *= U1 # drop!
      H2 = np.maximum(0, np.dot(W2, H1) + b2)
      U2 = np.random.rand(*H2.shape) < p # 第二个随机失活遮罩
      H2 *= U2 # drop!
      out = np.dot(W3, H2) + b3
      # 反向传播:计算梯度... (略)
      # 进行参数更新... (略)
    def predict(X):
      # 前向传播时模型集成
      H1 = np.maximum(0, np.dot(W1, X) + b1) * p # 注意:激活数据要乘以p
      H2 = np.maximum(0, np.dot(W2, H1) + b2) * p # 注意:激活数据要乘以p
      out = np.dot(W3, H2) + b3
    反向随机失活: 推荐实现方式.
    在训练的时候drop和调整数值范围,测试时不做任何事.
    p = 0.5 # 激活神经元的概率. p值更高 = 随机失活更弱
    def train_step(X):
      # 3层neural network的前向传播
      H1 = np.maximum(0, np.dot(W1, X) + b1)
      U1 = (np.random.rand(*H1.shape) < p) / p # 第一个随机失活遮罩. 注意/p!
      H1 *= U1 # drop!
      H2 = np.maximum(0, np.dot(W2, H1) + b2)
      U2 = (np.random.rand(*H2.shape) < p) / p # 第二个随机失活遮罩. 注意/p!
      H2 *= U2 # drop!
      out = np.dot(W3, H2) + b3
      # 反向传播:计算梯度... (略)
      # 进行参数更新... (略)
    def predict(X):