Dropout丢弃法
ZOU

方法

除了 L2 范数正则化之外,Dropout 也是一种应对过拟合的方法。它只用于模型训练时期。

假设一个单隐藏层的多层感知机。其中输入个数为 4,隐藏单元数为 5,隐藏单元 hi 的表达式为:

其中是激活函数。当对该隐藏层使用 Dropout 时,某一些隐藏单元有概率会被丢弃掉。设丢弃概率为 p,则有 p 的概率 hi 会被清零,有 1-p 的概率 hi 会除以 1-p 做拉伸。丢弃概率是超参数,我们采用一个随机变量来表示这个概率,设定其值为 1 的概率是 1-p。我们人为规定一个新的隐藏单元 hi’

这么规定的好处是使得输入的期望值不会变:

如果一个隐藏单元被清零,那么在反向传播过程中,该隐藏单元相关权重梯度均为 0,即对参数更新无法产生影响。由于训练中隐藏层神经元的丢弃是随机的,因此,输出层的计算无法过渡依赖任何一个隐藏单元,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。

实现

1
2
3
optim = torch.optim.SGD(net.parameters(), lr=0.5)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs,
batch_size, None, None, optim)

测试一下 Dropout 的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
X = torch.arange(16).view(2, 8)
dropout(X, 0)
# tensor([[ 0., 1., 2., 0., 4., 5., 6., 0.],
# [ 8., 0., 10., 11., 12., 0., 14., 15.]])

dropout(X, 0.5)
# tensor([[ 0., 0., 4., 0., 8., 0., 12., 14.],
# [ 0., 0., 20., 0., 24., 0., 28., 0.]])


dropout(X, 0.5)
# tensor([[ 0., 0., 4., 0., 8., 0., 12., 14.],
# [ 0., 0., 20., 0., 24., 0., 28., 0.]])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 定义模型参数
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256
W1 = torch.tensor(np.random.normal(0, 0.01, size=(num_inputs, num_hiddens1)),
dtype=torch.float, requires_grad=True)
b1 = torch.zeros(num_hiddens1, requires_grad=True)
W2 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens1, num_hiddens2)),
dtype=torch.float, requires_grad=True)
b2 = torch.zeros(num_hiddens2, requires_grad=True)
W3 = torch.tensor(np.random.normal(0, 0.01, size=(num_hiddens2, num_outputs)),
dtype=torch.float, requires_grad=True)
b3 = torch.zeros(num_outputs, requires_grad=True)
params = [W1, b1, W2, b2, W3, b3]

# 模型将全连接层和激活函数ReLU串起来。我们可以分别设置各个层的丢弃概率
# 通常的建议是把靠近输入层的丢弃概率设得小一点
drop_prob1, drop_prob2 = 0.2, 0.5

def net(X, is_training=True):
X = X.view(-1, num_inputs)
H1 = (torch.matmul(X, W1) + b1).relu()
if is_training: # 只在训练时采用dropout
H1 = dropout(H1, drop_prob1) # 全连接后接Dropout
H2 = (torch.matmul(H1, W2) + b2).relu()
if is_training: # 只在训练时采用dropout
H2 = dropout(H2, drop_prob2)
return torch.matmul(H2, W3) + b3

# 在对模型评估时不应该进行丢弃
def evaluate_accuracy(data_iter, net):
acc_sum, n = 0.0, 0
for X, y in data_iter:
if isinstance(net, torch.nn.Module):
net.eval() # 评估模型,关闭Dropout
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
net.train() # 改回训练模式
else:
if ("is_training" in net.__code__.co_varnames):
acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
else:
acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
n += y.shape[0]
return acc_sum / n

测试一下模型:

1
2
3
4
5
num_epochs, lr, batch_size = 5, 100.0, 256
loss = torch.nn.CrossEntropyLoss()
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs,
batch_size, params, lr)
1
2
3
4
5
epoch 1, loss 0.0043, train acc 0.568, test acc 0.785
epoch 2, loss 0.0021, train acc 0.796, test acc 0.783
epoch 3, loss 0.0018, train acc 0.827, test acc 0.833
epoch 4, loss 0.0017, train acc 0.841, test acc 0.789
epoch 5, loss 0.0016, train acc 0.851, test acc 0.826

使用nn.Sequential()简洁实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 简洁实现
net = nn.Sequential(
d2l.FlattenLayer(),
nn.Linear(num_inputs, num_hiddens1),
nn.ReLU(),
nn.Dropout(drop_prob1),
nn.Linear(num_hiddens1, num_hiddens2),
nn.ReLU(),
nn.Dropout(drop_prob2),
nn.Linear(num_hiddens2, 10)
)

for param in net.parameters():
nn.init.normal_(param, mean=0, std=0.01)
  • 本文标题:Dropout丢弃法
  • 本文作者:ZOU
  • 创建时间:2022-03-18 17:16:42
  • 本文链接:https://yipeng.xyz/2022/03/18/Dropout丢弃法/
  • 版权声明:可随意使用,但是转载请联系我!
 评论