为什么使用Dropout
Dropout的本质是每个batch训练一个完整网络的子网,然后测试的时候使用这些网络的集成,所以必须去掉
Dropout会产生收缩权重平方范数的效果,即压缩权重。并完成一些预防过拟合的外层正则化。Dropout被正式作为一种正则化的替代形式,L2对不同权重的衰减是不同的,它取决于倍增的激活函数的大小。Dropout的功能类似于L2正则化,与L2正则化不同的是,被应用的方式不同dropout也会有所不同,甚至更适用于不同的输入范围。
不同层的keep-prob可以不一样,参数多的层,keep-prob可以设置小一些,少的可以设置大一些,或者为1。
如果你觉得某一层比其它层更容易过拟合,你可以把该层的keep-drop设得比其它层低一些,但是你需要使用交叉验证来搜索更多的超参数。你也可以有的层用dropout,有的不用,这样在应用dropout的层只含有一个超级参数。
Dropout主要用在计算机视觉领域,因为数据量不够,容易过拟合,需要dropout。
怎样使用dropout
tensorflow
def add_layer(inputs, in_size, out_size, layer_name, activation_function=None, ):
# add one more layer and return the output of this layer
Weights = tf.Variable(tf.random_normal([in_size, out_size]))
biases = tf.Variable(tf.zeros([1, out_size]) + 0.1, )
Wx_plus_b = tf.matmul(inputs, Weights) + biases
# here to dropout
Wx_plus_b = tf.nn.dropout(Wx_plus_b, keep_prob)
if activation_function is None:
outputs = Wx_plus_b
else:
outputs = activation_function(Wx_plus_b, )
tf.histogram_summary(layer_name + '/outputs', outputs)
return outputs
pytorch
net_dropout = torch.nn.Sequential(
torch.nn.Linear(1, N_HIDDEN),
torch.nn.Dropout(0.5), # drop 50% neurons
torch.nn.ReLU(),
torch.nn.Linear(N_HIDDEN, N_HIDDEN),
torch.nn.Dropout(0.5),
torch.nn.ReLU(),
torch.nn.Linear(N_HIDDEN, 1)
)
怎样在测试时去掉dropout
tensorflow
(1) 通过命令行参数的训练状态来控制
is_training = (mode == tf.estimator.ModeKeys.TRAIN)
with tf.variable_scope("loss"):
if is_training:
# I.e., 0.1 dropout
output_layer = tf.nn.dropout(output_layer, keep_prob=0.9)
(2) 通过feed_dict来控制
prob = tf.placeholder_with_default(1.0, shape=())
layer = tf.nn.dropout(layer, prob)
---------------------------
sess.run(train_step, feed_dict={prob: 1})
pytorch
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import torchvision
import torchvision.datasets as dsets
from torchvision.transforms import transforms
import cv2
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.fc = nn.Linear(5, 5)
self.dp = nn.Dropout(0.5)
def forward(self, x):
x = self.fc(x)
x = self.dp(x)
return x
if __name__ == '__main__':
x = torch.FloatTensor([1]*5)
z = torch.FloatTensor([1]*5)
print(x)
net = Net()
criterion = nn.MSELoss()
optim = torch.optim.SGD(net.parameters(), 0.1)
optim.zero_grad()
net.train()
y = net(x)
loss = criterion(y, z)
loss.backward()
optim.step()
print(y)
print(net(x))
# net(x) is not the same as y -> dropout() changes result every time
net.eval()
optim.zero_grad()
y = net(x)
loss = criterion(y, z)
loss.backward()
optim.step()
print(y)
print(net(x))
# eval() or mode(train=False) only changes the state of some modules, e.g., dropout, but do not disable loss back-propogation.
# By setting train=False, dropout() does not work and is temporarily removed from the chain of update.
with torch.no_grad():
optim.zero_grad()
y = net(x)
loss = criterion(y, z)
# loss.backward() -> torch.no_grad sets torch.parameters() to be an empty set, and conducting loss.backward() will raise error.
optim.step()
print(y)
print(net(x))
# net(x) == y -> with no loss.backward(), params of network are fixed.
使用 tensorflow 中的 DropoutWrapper给RNN的每个时间步增加dropout
class DropoutWrapper(RNNCell):
"""Operator adding dropout to inputs and outputs of the given cell."""
def __init__(self, cell, input_keep_prob=1.0, output_keep_prob=1.0,
state_keep_prob=1.0, variational_recurrent=False,
input_size=None, dtype=None, seed=None,
dropout_state_filter_visitor=None):
其中三个 keep_prob 分别决定 RNN 的输入、输出、状态向量的 dropout 概率
RNN dropout 方式是对于一个序列的所有时间步,输入向量、输出向量、状态向量分别采用相同的 dropout mask,如图所示,相同颜色表示相同的 dropout mask
将GRUCell与DropoutWraper结合时产生梯度爆炸问题
下图是 GRU 的计算结构:
在对 GRU 的状态向量引入 dropout 后结构如下图,图中 h* 表示 dropout 后的状态向量。我们知道,dropout 根据 1-keep_rate 的概率生成一个 mask 向量,根据 mask 对状态向量中某些值置0,然后为了保证向量在后续与权值矩阵 W 相乘后得到的结果向量的 scale 与不 dropout 时一致,会对向量 h 中的对应 mask 非 0 的值除以 keep_rate。在我的实验中 dropout 对h中每个对应 mask 非0值除以0.8即乘以1.25。如前所述,由于变分 RNN dropout 中所有时间步的 dropout mask 都是相同的,所以对于一个长 n 的序列,状态向量 h* 中有些值在这个序列中永远是0,而另外一些值每经过一个时间步就要乘以 1/keep_rate(我的实验中是1.25),一个序列计算完后状态向量的值要乘以 (1/keep_rate)n,这些值在长序列情况下会变得非常大甚至溢出。这样就解释了为什么将 GRU 和 variational RNN dropout 结合使用的时候 loss 会变成 nan。
然后看看 LSTM,LSTM 的结构如下图所示。由图中结构可以看到,LSTM 的状态向量 h 每次都与矩阵相乘后再使用,这样可以保证即使每个时间步 h 的某些值会乘以 1/keep_rate,在与矩阵相乘后不会造成像 GRU 那样 h 的值呈指数上升的情况。
总结
- 由于 GRU 与 LSTM 结构差异,variantional RNN dropout 可以很好地在 LSTM 中使用,但是不能在 GRU 中使用,variantional RNN dropout 有这样的局限性。
Comments