秃姐学AI系列之:丢弃法 + 代码实现 | 数值稳定性

目录

丢弃法

动机

 无偏差的加入噪音

训练中的丢弃法

推理中的丢弃法

总结

 代码实现

从0开始实现

简洁实现

QA

数值稳定性

梯度爆炸的问题

梯度消失的问题

总结 


丢弃法

动机

一个好的模型需要对输入数据的扰动鲁棒

  • 使用有噪音的数据等价于Tikhonov正则
  • 丢弃法:在层之间加入噪音

正则都可以理解为它在控制模型不要过拟合,不要太大

丢弃法不在数据中增加噪音,转而在层中增加噪音,所以丢弃法其实也是一种正则

 无偏差的加入噪音

 意味着,我以p的概率丢弃x,在剩下的概率里面我把x变大(因为p是一个0~1之间的数,所以1-p一定小于1)。

为什么要除一个1-p呢?,因为我们希望加入噪声之后我们的数据期望不变,而期望需要乘一个概率,当我们给x除了一个1-p之后可以看出,数据的期望公式并没有发生改变

训练中的丢弃法

通常将丢弃法作用在隐藏全连接层的输出上

推理中的丢弃法

dropout是一个正则项,而正则项只在训练中使用(包括L2正则等待):他们影响模型参数的更新

  • 因为正则项只会对权重造成影响

其实最早dropout被提出的时候,作者没把他当成一个正则项,他的想法是:模型每次dropout可以看作是激活了不同的很小的神经子网络,最终训练出n个很小的子神经网络去平均。这样训练一个模型可以用很多小神经网络去平均,效果肯定会好。

但是后面被大家研究研究..发现它在实践中其实就是一个正则项

在推理过程中,丢弃法直接返回输出,即输出的是他本身:h = dropout(h)

  • 这样也能保证确定性的输出

总结

  • 丢弃法将一些输出项随机置0来控制模型复杂度
  • 常作用在多层感知机的隐藏层输出上(很少用在cnn啊那些模型上面)
  • 丢弃概率是控制模型复杂度的超参数(在1不丢~0全丢之间,一般最常见的丢弃概率是0.5、0.9、0.1)

 代码实现

从0开始实现

我们实现dropout_layer函数,该函数以dropout的概率丢弃张量输入X中的元素

import torch

def dropout_layer(X, droupout):
    assert 0 <= dropout <= 1    # 一个断言,确定dropout率保持在正常区间
    if dropout == 1:
        return torch.zero_like(X)    # 直接返回全 0
    if dropout == 0:
        return X    # 不用丢,直接返回X
    # 随机生成一个0~1的向量,根据是否大于dropout生成一个布尔值(大于赋 1,小于赋 0)
    mask = (torch.randn(X.shape) > dropout).float()
    return mask * X / (1.0 - dropout)

为什么代码中挑出mask之后不直接 X[mask] = 0,来将值全部设成0?

        因为无论对于GPU还是CPU来说,做乘法远远比去选一个元素来的快 

 定义具有两个隐藏层的多层感知机,每个隐藏层包含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)
        self.relu = nn.ReLU()

def forward(self, X):
    H1 = self.relu(self,lin1(X.reshape((-1, self.num_inputs))))
    if self.training == True:
        H1 = dropout_layer(H1, dropout1)    # dropout一般作用在全连接隐藏层的输出上
    H2 = self.relu(self.lin2(H1))
    if self.training == True:
        H2 = dropout_layer(H2, dropout2)
    out = self.lin3(H2)    # 注意!!输出层是不作用dropout的!
    return out

net = Net(num_inputs, num_outputs, num_hiddens1, num_hiddens2)

简洁实现

net = nn.Sequential(
    nn.Flatten(), nn.Linear(784, 256), nn.ReLU(),
    nn.Dropout(dropout1), nn.Linear(256, 256), nn.ReLU(),
    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)

QA

  • dropout随机置 0 对求梯度和反向传播的影响是什么?

        dropout置 0 的地方梯度就是 0,但是未置 0 的地方对应乘了一个数放大了,所以dropout对于梯度是一个对称的函数,而且置 0 的那些对应的权重这一轮就不会更新。

  • dropout如何保证结果的正确性和可重复性? 

        所谓正确性,机器学习没有正确性hhh只有效果好不好。所以机器学习,特别是神经网络,你哪怕逻辑出了很大的bug,甚至可能看不出来,最终对acc的影响也就一个点不到。 

        对于dropout来说,你下次丢弃的东西可能就不是这些了,不过是dropout,对于整个神经网络来说可重复性都是一个很难的问题。或者你可以固定一个随机种子random seed,那你的drop就是可重复的,但是你整个网络的随机性还是挺重的,比如初始权值也是随机的,甚至你的cudnn每次算的都是不太一样的,加的顺序不一样出来的数就会不一样...几乎不能重复,其实没啥必要可重复,在一个范围内就行了。

        机器学习的六字真言:越随机越稳定!!

  • BN和dropout的相关性和区别 

        可以理解为BN是给CNN卷积层用的,而dropout是给全连接层用的 

  • 在同样lr下,dropout的介入会不会造成参数收敛更慢? 

        是有可能的!,但是并不太需要因为dropout的存在而调大lr的需要。因为dropout不改变期望,lr是对期望和方差敏感一点点的。但是确实有可能会导致收敛变慢

数值稳定性

因为在神经网络中,我们做了太多的矩阵乘法,所以很容易导致参数数值的不稳定

数值稳定性的常见两个问题:

  • 梯度爆炸
  • 梯度消失

梯度爆炸的问题

值超过值域(infinity)

        对于16位浮点数尤为严重(数值区间6e-5~6e4)

我们经常采用16位浮点数,因为16位浮点数可以比32位浮点数块两倍 

对学习率敏感

  • 如果学习率太大 -> 大参数值 -> 更大的梯度
  • 如果学习率太小 -> 训练无进展
  • 我们可能需要在训练过程不断调整学习率

梯度消失的问题

梯度值变成 0

        对16位浮点数尤为严重

训练没有进展

        无论如何选择学习率

对于底层尤为严重

        仅仅顶部层训练的较好

        无法让神经网络更深

因为神经网络是从顶部回传的,到底部会变得特别特别小  

总结 

  • 当数值过大或者过小时会导致数值问题
  • 常发生在深度模型中,因为其会对n个数累乘
  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值