回顾
简单来说,就是设置一个的丢弃概率(这是一个超参数),当数据在层与层之间传递时,神经元上的数据有概率被我们丢弃不适用,即对应的权重参数为 0,这样就可以使我们的模型变得稀疏,防止其过度依赖某些神经元以起到正则化的作用。同时,通过拉伸操作,保证输入数据的期望值不变。
丢弃法只在训练模型时使用。
从零开始实现
python 断言
assert expression# 等价于if not expression:raise AssertionErrorassert expression [, arguments]# 等价于if not expression:raise AssertionError(arguments)
定义Drop-out函数
我们定义的dropout函数读入张量以及丢弃率两个参数。keep_prob实际上就是1减去drop_prob值。assert断言用于确保传入的drop_prob值大于0小于1。如何来实现drop-out呢?其实很简单,先初始化一个与x相同形状的张量,值在0,1区间均匀分布。检查矩阵中初始化的每个元素,若值小于keep_prob,则令其为1,否则为0。将该张量与原始的x相乘,再除以keep_prob值后(为了保持期望不变)
import torchfrom torch import nnimport d2lzhdef dropout(x, drop_prob):x = x.float()assert 0 <= drop_prob <= 1keep_prob = 1 - drop_probif keep_prob == 0:return torch.zeros_like(x)mask = (torch.rand(x.shape) < keep_prob).float()return mask * x / keep_probx = torch.arange(16).view(2, 8)print(dropout(x, 0))print(dropout(x, 0.5))print(dropout(x, 1.0))结果:tensor([[ 0., 1., 2., 3., 4., 5., 6., 7.],[ 8., 9., 10., 11., 12., 13., 14., 15.]])tensor([[ 0., 0., 4., 6., 8., 10., 12., 0.],[16., 0., 0., 22., 0., 0., 28., 0.]])tensor([[0., 0., 0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 0., 0., 0., 0.]])
定义模型参数
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256W1 = torch.normal(mean=0, std=0.01, size=[num_inputs, num_hiddens1], dtype=torch.float, requires_grad=True)b1 = torch.zeros(num_hiddens1, dtype=torch.float, requires_grad=True)W2 = torch.normal(mean=0, std=0.01, size=[num_hiddens1, num_hiddens2], dtype=torch.float, requires_grad=True)b2 = torch.zeros(num_hiddens2, dtype=torch.float, requires_grad=True)W3 = torch.normal(mean=0, std=0.01, size=[num_hiddens2, num_outputs], dtype=torch.float, requires_grad=True)b3 = torch.zeros(num_outputs, dtype=torch.float, requires_grad=True)params = [W1, b1, W2, b2, W3, b3]
定义模型
下面定义的模型将全连接层和激活函数ReLU串起来,并对每个激活函数的输出使用丢弃法。我们可以分别设置各个层的丢弃概率。通常的建议是把靠近输入层的丢弃概率设得小一点。在这个实验中,我们把第一个隐藏层的丢弃概率设为0.2,把第二个隐藏层的丢弃概率设为0.5。我们可以通过参数
is_training来判断运行模式为训练还是测试,并只需在训练模式下使用丢弃法。
我们在对模型评估的时候不应该进行丢弃,所以我们修改一下d2lzh_pytorch中的evaluate_accuracy函数:
def net(x, is_training=True):x = x.view(-1, num_inputs)h1 = (torch.matmul(x, W1) + b1).relu()if is_training: # 只在训练模型时使用丢弃法h1 = dropout(h1, drop_prob1) # 添加丢弃层h2 = (torch.matmul(h1, W2) + b2).relu() # 添加丢弃层if is_training:h2 = dropout(h2, drop_prob2)return torch.matmul(h2, W3) + b3def evaluate_accuracy(data_iter, net):acc_sum, n = 0.0, 0for X, y in data_iter:if isinstance(net, torch.nn.Module):net.eval() # 评估模式, 这会关闭dropoutacc_sum += (net(X).argmax(dim=1) == y).float().sum().item()net.train() # 改回训练模式else: # 自定义的模型if 'is_training' in net.__code__.co_varnames: # 如果有is_training这个参数# 将is_training设置成Falseacc_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 / nif __name__ == "__main__":num_epochs, lr, batch_size = 5, 100.0, 256loss = torch.nn.CrossEntropyLoss()train_iter, test_iter = d2lzh.load_data_fashion_mnist(batch_size)d2lzh.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params, lr)结果:epoch 1, loss 0.0045, train acc 0.550, test acc 0.697epoch 2, loss 0.0023, train acc 0.785, test acc 0.725epoch 3, loss 0.0019, train acc 0.822, test acc 0.802epoch 4, loss 0.0017, train acc 0.840, test acc 0.824epoch 5, loss 0.0016, train acc 0.847, test acc 0.843
简洁实现
在PyTorch中,我们只需要在全连接层后添加Dropout层并指定丢弃概率。在训练模型时,Dropout层将以指定的丢弃概率随机丢弃上一层的输出元素;在测试模型时(即model.eval()后),Dropout层并不发挥作用。
完整代码如下:
import torchfrom torch import nnimport d2lzhnum_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256drop_prob1, drop_prob2 = 0.2, 0.5num_epochs, lr, batch_size = 5, 100.0, 256train_iter, test_iter = d2lzh.load_data_fashion_mnist(batch_size)net = nn.Sequential(d2lzh.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)loss = nn.CrossEntropyLoss()optimizer = torch.optim.SGD(net.parameters(), lr=0.5)d2lzh.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None, None, optimizer)
