李沐之丢弃法

目录

1.理论

2.代码实现

2.1从0开始实现

2.2简洁实现


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隐藏层设的小效果要好一点点。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值