深度学习中丢弃法原理及实现(基于pytorch)

608 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

1.环境配置及要求

  1. pytorch1.12.1
  2. numpy 1.23.3
  3. python 3.8
  4. pandas 1.2.4
  5. matplotlib 3.5.1
  6. jupyter note运行

2.要解决的问题

丢弃法的出现还是为了解决过拟合的问题,过拟合是指:模型的训练误差远小于它在测试数据集上的误差,我们称该现象为过拟合。

3.丢弃法原理及方法

原理即是当对隐藏层使用丢弃法时,该层的隐藏单元将有一定概率被丢弃掉,即通过概率筛选的方式对数据进行处理。丢弃概率是丢弃法的超参数,且丢弃法不改变其输入的期望值。 image.png 由于在训练中隐藏层神经元的丢弃是随机的,即h1,....h5都有可能被清零,输出层的计算无法过度依赖h1,。。。h5中的任意一个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。当然我们在测试的时候,为了结果的确定性,一般不适用丢弃法,仅是在测试的时候!

4.丢弃法的实现

根据丢弃法的定义,我们可以进一步实现它,其中dropout函数将以drop_prob的概率丢弃x中的元素。

4.1 导入环境

image.png

import matplotlib_inline
import torch
import torch.nn as nn
import numpy as np
import sys
sys.path.append(r"E:\anaconda\envs\pytorch\Lib\d2lzh_pytorch")
import d2lzh_pytorch as d2l

这里还是使用之前的库包版本,一般情况下更高的版本可以向下兼容。注意导入d2l包,路径的放置前如果报错了要注意在路径前添加r。

4.2设置丢弃函数即dropout函数

image.png

def dropout(X,drop_prob):
    X=X.float()
    assert 0<=drop_prob<=1
    keep_prob=1-drop_prob
    #这种情况下把全部元素都丢弃
    if keep_prob==0:
        return torch.zeros_like(X)#输出以Xshape相同的形状
    mask=(torch.randn(X.shape)<keep_prob).float()
    return mask*X/keep_prob

这里我们将丢弃函数设置好后可以对其进行测试:

X=torch.arange(16).view(2,8)
dropout(X,0)#丢弃率为0
dropout(X,0.5)#丢弃率为0.5
dropout(X,1)#丢弃率为1

可以很明显的看出,当概率设置为0时,X中的元素全部保留,当概率设置为0.5时,X中的元素有一半设置为0,当概率为1时,X中的元素全部为0。综上,丢弃函数的功能已经达到我们的预期要求。

4.3定义模型参数

参数,即我们的模型需要使用的数据,这里我们使用Fashion-MNIST数据集,定义一个包含两个隐藏层的多层感知机,其中两个隐藏层的输出个数都是256.

image.png

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]

这里跟之前的区别不会很大,唯一的区别在于这是个两层隐藏层的方式,即第一层传来的数字进入第二层的时候,第二层再进行计算的一个过程。

4.4 定义模型

这里就用到我们之前了解到的一个激活函数,ReLU激活函数,并对每个激活函数的输出使用丢弃法。我们分别设置各个层的丢弃概率,我们通常会将输入层的丢弃概率设的小一点,在这里我们把第一个隐藏层的丢弃概率设为0.2,把第二个隐藏层的丢弃概率设为0.5.

image.png

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:  #只在训练模型时使用丢弃法
        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)+b3

这里我们应该注意一点,对模型评估的时候不应该进行丢弃。

image.png

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):#如果有is_training这个参数
                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

4.5 训练和测试模型

这一部分与之前多层感知机的训练和测试类似。

image.png

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)