目录
1.理论
(Dropout 引入的噪音与常规的数据噪声不同。因为是随机加入,这种“强制学习”让模型更加鲁棒,更能够关注整体特征而非局部模式,数据自带的噪音可能存在偏好,加入随机噪音后,能把这种偏好抵消一些。)

假设x是一层到下一层的输出的话,我们希望虽然加入了噪音,但不要改变期望。丢弃法做了:给定一个概率,在p的概率里面把输入,真实的原始的输入变成0,在剩下的地方,把输入除以1-p(p是0到1,则1-p小于1,输出就变大了)。

丢弃法作用在层与层之间


2.代码实现
2.1从0开始实现
#我们实现 dropout_layer 函数, 该函数以dropout的概率丢弃张量输入X中的元素
import torch
from torch import nn
from d2l import torch as d2l
def dropout_layer(X, dropout):
#dropout是指概率
assert 0 <= dropout <= 1
# 在本情况中,所有元素都被丢弃
if dropout == 1:
return torch.zeros_like(X)
# 在本情况中,所有元素都被保留
if dropout == 0:
return X
mask = (torch.rand(X.shape) > dropout).float()
#torch.rand是用于生成均匀随机分布张量的函数,从区间[0,1)的均匀分布中随机抽取一个
#随机数生成一个张量,输出张量的形状和X相同,这个mask是为了判断该保留什么和去掉什么,
#mask是0和1的矩阵,根据dropout rate得出来的,然后原始的X乘mask就知道那些是保留的,
#哪些是归零的
return mask * X / (1.0 - dropout)
#测试dropout_layer函数
X= torch.arange(16, dtype = torch.float32).reshape((2, 8))
print(X)
print(dropout_layer(X, 0.))
#dropout=0就返回自己,所有都不丢弃
print(dropout_layer(X, 0.5))
print(dropout_layer(X, 1.))
#丢弃所有
"""结果输出:
tensor([[ 0., 1., 2., 3., 4., 5., 6., 7.],
[ 8., 9., 10., 11., 12., 13., 14., 15.]])
tensor([[ 0., 1., 2., 3., 4., 5., 6., 7.],
[ 8., 9., 10., 11., 12., 13., 14., 15.]])
tensor([[ 0., 2., 0., 6., 0., 0., 0., 14.],
[16., 18., 0., 22., 0., 26., 28., 30.]])
#变成两倍是因为随机生成的数>0.5,返回mask是1.0,之后乘以X再除以(1-p)就相当于是乘以2
tensor([[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.]])"""
"""定义模型参数"""
#定义具有两个隐藏层的多层感知机,每个隐藏层包含256个单元]
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256
"""定义模型"""
dropout1, dropout2 = 0.2, 0.5
class Net(nn.Module):
def __init__(self, num_inputs, num_outputs, num_hiddens1, num_hiddens2,
is_training = True):
#注意要区别是不是在训练
super(Net, self).__init__()
self.num_inputs = num_inputs
self.training = is_training
self.lin1 = nn.Linear(num_inputs, num_hiddens1)
self.lin2 = nn.Linear(num_hiddens1, num_hiddens2)
self.lin3 = nn.Linear(num_hiddens2, num_outputs)
#有两个隐藏层就是有3个线性层因为还包含一个输出层
self.relu = nn.ReLU()
def forward(self, X):
H1 = self.relu(self.lin1(X.reshape((-1, self.num_inputs))))
#这是第一个隐藏层的输出
#过程就是把X reshape一下输入到第一个隐藏层然后做一下relu
# 只有在训练模型时才使用dropout
if self.training == True:
# 在第一个全连接层之后添加一个dropout层
H1 = dropout_layer(H1, dropout1)
H2 = self.relu(self.lin2(H1))
if self.training == True:
# 在第二个全连接层之后添加一个dropout层
H2 = dropout_layer(H2, dropout2)
out = self.lin3(H2)
#输出层不用激活函数不用dropout
return out
net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)
"""训练和测试"""
num_epochs, lr, batch_size = 10, 0.5, 256
loss = nn.CrossEntropyLoss(reduction='none')
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
结果输出:

和之前的区别就是精度稍微高了一点,之前用的是单隐藏层,现在用的是两层隐藏层,而且隐藏层大小是256,对于这个小数据集,算是比较大的模型了。这里dropout是防止过拟合,因为数据不变,但是模型变复杂了,容易过拟合。
2.2简洁实现
net = nn.Sequential(nn.Flatten(),
#先把输入拉平拉成一个2维的
nn.Linear(784, 256),
#输入大小为784,批量大小为256
nn.ReLU(),
#经过非线性激活函数
# 在第一个全连接层之后添加一个dropout层
nn.Dropout(dropout1),
#其实Dropout放在relu前面或者后面都是一样的
nn.Linear(256, 256),
nn.ReLU(),
# 在第二个全连接层之后添加一个dropout层
nn.Dropout(dropout2),
nn.Linear(256, 10))
def init_weights(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight, std=0.01)
net.apply(init_weights);
"""对模型进行训练和测试"""
trainer = torch.optim.SGD(net.parameters(), lr=lr)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)
输出结果:

思考:把dropout概率改为0会怎么样?

dropout是正则用来防止过拟合的,这里不用dropout之后损失更小,说明过拟合程度加深了。对深度学习来讲,通常可以把模型弄的复杂一点点,然后通过正则化来控制模型复杂度意味着在有dropout情况下,可以稍微把隐藏层设大一点点,再把dropout率也设大一点点。可能会比不用dropout隐藏层设的小效果要好一点点。

3913

被折叠的 条评论
为什么被折叠?



