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?
-
直观的理解
-
本质
模型融合的操作如下:将多个模型的预测结果取平均。如果model很复杂时,这一招是往往有用的,能够有效的降低模型的variance。
而dropout就相当于每个mini batch 训练一个模型,虽然每次更新只更新一个网络的参数,由于每个子网络之间参数共享,对每个子网络的训练相当于对整个网络的训练,所以最终得到了针对整个数据集的多个模型
那么在testing的时候,按照ensemble方法,我们需要把之前的每个不同dropout的network拿出来,然后把train data丢到network里面去,每一个network都会给你一个结果,这些结果的平均值就是最终的结果。但是实际上没有办法这样做,因为network太多了。(每个神经元有2种可能,一共N个神经元,则有个网络)。dropout最神奇的是:当你把一个完整的network不进行dropout,但是将它的weights乘以(1-p)%,然后将train data输入,得到的output y:之前做average的结果跟output y是approximated。
为什么会近似?
通过以上例子可以发现,针对只有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):
# 前向传播时模型集成
H1 = np.maximum(0, np.dot(W1, X) + b1) # 不用数值范围调整了
H2 = np.maximum(0, np.dot(W2, H1) + b2)
out = np.dot(W3, H2) + b3