深度学习记录例子篇————droupout正则化和BN层

Droupout是什么

Dropout是Srivastava等人在2014年的一篇论文中,提出的一种针对神经网络模型的正则化方法 Dropout: A Simple Way to Prevent Neural Networks from Overfitting。
Dropout在训练模型中是如何实现的呢?Dropout的做法是在训练过程中按一定比例(比例参数可设置)随机忽略或屏蔽一些神经元。这些神经元被随机“抛弃”,也就是说它们在正向传播过程中对于下游神经元的贡献效果暂时消失了,反向传播时该神经元也不会有任何权重的更新。所以,通过传播过程,dropout将产生和L2范数相同的收缩权重的效果。
随着神经网络模型的不断学习,神经元的权值会与整个网络的上下文相匹配。神经元的权重针对某些特征进行调优,会产生一些特殊化。周围的神经元则会依赖于这种特殊化,如果过于特殊化,模型会因为对训练数据过拟合而变得脆弱不堪。神经元在训练过程中的这种依赖于上下文的现象被称为复杂的协同适应(complex co-adaptations)。
加入了Dropout以后,输入的特征都是有可能会被随机清除的,所以该神经元不会再特别依赖于任何一个输入特征,也就是说不会给任何一个输入设置太大的权重。网络模型对神经元特定的权重不那么敏感。这反过来又提升了模型的泛化能力,不容易对训练数据过拟合。
Dropout训练的集成包括所有从基础网络除去非输出单元形成子网络,如下图所示
在这里插入图片描述
Dropout训练所有子网络组成的集合,其中子网络是从基本网络中删除非输出单元构建。我们从具有两个可见单元和两个隐藏单元的基本网络开始,这四个单元有十六个可能的子集。右图展示了从原始网络中丢弃不同的单元子集而形成的所有十六个子网络。在这个例子中,所得到的大部分网络没有输入单元或没有从输入连接到输出的路径。当层较宽时,丢弃所有从输入到输出的可能路径的概率变小,所以,这个问题对于层较宽的网络不是很重要。
较先进的神经网络基于一系列仿射变换和非线性变换,我们可以将一些单元的输出乘零,就能有效地删除一些单元。这个过程需要对模型进行一些修改,如径向基函数网络,单元的状态和参考值之间存在一定区别。为简单起见, 在这里提出乘零的简单Dropout算法,被简单地修改后,可以与其他操作一起工作。
dropout在训练阶段和测试阶段是不同的,一般在训练中使用,测试不使用。不过测试时,为平衡(因训练时舍弃了部分节点或输出),一般将输出按dropout rate比例缩小。

如何或何时使用Dropout呢?以下是一般原则:

(1)通常丢弃率控制在20%~50%比较好,可以从20%开始尝试。如果比例太低则起不到效果,比例太高则会导致模型的欠学习。
(2)在大的网络模型上应用。
当dropout用在较大的网络模型时,更有可能得到效果的提升,模型有更多的机会学习到多种独立的表征。
(3)在输入层和隐藏层都使用dropout。
对于不同的层,设置的keep_prob也不同,一般来说神经元较少的层,会设keep_prob
为1.0或接近于1.0的数;神经元多的层,则会将keep_prob设置的较小,如0.5或更小。
(4)增加学习速率和冲量。
把学习速率扩大10100倍,冲量值调高到0.90.99。
(5)限制网络模型的权重。
大的学习速率往往导致大的权重值。对网络的权重值做最大范数的正则化,被证明能提升模型性能。
以下我们通过房价预测为例来比较使用dropout和不使用dropout对训练损失或测试损失的影响。

使用dropout正则化解决拟合问题

(1)导入需要的包和数据集

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
import numpy  as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn

(2)获得数据对其标准化

# 载入数据
boston = load_boston()
X,y   = (boston.data, boston.target)
dim = X.shape[1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
num_train = X_train.shape[0]
#对训练数据进行标准化
mean=X_train.mean(axis=0)
std=X_train.std(axis=0)
X_train-=mean
X_train/=std

X_test-=mean
X_test/=std
# 转成tensor
train_data=torch.from_numpy(X_train)
# 查看数据
dtype = torch.FloatTensor
train_data.type(dtype)
tensor([[-0.3726, -0.4996, -0.7049,  ..., -0.4846,  0.3717, -0.4110],
        [-0.3971, -0.4996, -0.0449,  ...,  0.3365,  0.2050, -0.3877],
        [-0.4027,  0.7712, -0.8868,  ..., -0.8496,  0.3666, -0.1819],
        ...,
        [-0.3981, -0.4996, -0.1594,  ..., -0.3022,  0.4034, -0.3301],
        [-0.3884, -0.4996, -0.6033,  ..., -0.2565,  0.3834,  0.8359],
        [-0.3995, -0.4996, -1.0128,  ..., -0.8496,  0.4304,  0.2721]])
#分别获得训练集和测试集
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
train_data=torch.from_numpy(X_train).float()
train_target=torch.from_numpy(y_train).float()
test_data=torch.from_numpy(X_test).float()
test_target=torch.from_numpy(y_test).float()

(3)构建模型

# 构建模型
# 普通
net1_overfitting = torch.nn.Sequential(
    torch.nn.Linear(13, 16),
    torch.nn.ReLU(),
    torch.nn.Linear(16, 32),
    torch.nn.ReLU(),
    torch.nn.Linear(32, 1),
)

# 正则化
net1_dropped = torch.nn.Sequential(
    torch.nn.Linear(13, 16),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(16, 32),
    torch.nn.Dropout(0.5),  # drop 50% of the neuron
    torch.nn.ReLU(),
    torch.nn.Linear(32, 1),
)

(4)定义loss值选择迭代器

loss_func = torch.nn.MSELoss()
optimizer_ofit = torch.optim.Adam(net1_overfitting.parameters(), lr=0.01)
optimizer_drop = torch.optim.Adam(net1_dropped.parameters(), lr=0.01)

(5)训练可视化数据

from tensorboardX import SummaryWriter
writer = SummaryWriter(log_dir='logs')
for epoch in range(100):
    # 调用训练
    net1_overfitting.train()
    net1_dropped.train()
    # 获得预测值
    pred_ofit=  net1_overfitting(train_data)
    pred_drop = net1_dropped(train_data)    
    # 计算loss
    loss_ofit = loss_func(pred_ofit, train_target)
    loss_drop = loss_func(pred_drop, train_target)
    #计算梯度
    optimizer_ofit.zero_grad()
    optimizer_drop.zero_grad()
    loss_ofit.backward()
    loss_drop.backward()
    optimizer_ofit.step()
    optimizer_drop.step()
    # 保存loss的数据与epoch数值
    writer.add_scalars('train_group_loss',{'origloss':loss_ofit.item(),'droploss':loss_drop.item()}, epoch)

    #进行预测
    net1_overfitting.eval()
    net1_dropped.eval() 
    test_pred_orig = net1_overfitting(test_data)
    test_pred_drop = net1_dropped(test_data)
    orig_loss=loss_func(test_pred_orig, test_target)
    drop_loss=loss_func(test_pred_drop, test_target)
    writer.add_scalars('test_group_loss',{'origloss':orig_loss.item(),'droploss':drop_loss.item()}, epoch)

在这里插入图片描述

批量正则化

什么是批量正则化

我们知道数据归一化,这个一般是针对输入数据而言。但在实际训练过程中,经常出现隐含层因数据分布不均,导致梯度消失或不起作用的情况。如采用sigmoid函数或tanh函数为激活函数时,如果数据分布在两侧,这些激活函数的导数就接近于0,这样一来,BP算法得到的梯度也就消失了。如何解决这个问题?
Sergey Ioffe和Christian Szegedy两位学者提出了批标准化(Batch Normalization)方法。Batch Normalization不仅可以有效解决梯度消失问题,而且还可以让调试超参数更加简单,在提高训练模型效率的同时,还可让神经网络模型更加“健壮”。Batch Normalization是如何做到这些的呢? 首先,我们介绍一下BN的算法流程:
输入:微批次(mini-batch) 数据:B={x_1,x_2⋯x_m}
学习参数:γ,β 类似于权重参数,可以通过梯度下降等算法求得。
其中x_i 并不是网络的训练样本,而是指原网络中任意一个隐藏层激活函数的输入,这些输入是训练样本在网络中前向传播得来的。
输出:{y_i=NB_(γ,β) (x_i)}
#求微批次样本均值:
μ_B ← 1/m 〖∑┴m〗┬(i=1) x_i (5.4)
#求微批次样本方差:
σ_B^2 ← 1/m 〖∑┴m〗┬(i=1) 〖(x〗_i-〖μ_B)〗^2 (5.5)
#对x_i进行标准化处理:
(x_i ) ̂ ← (x_i-μ_B)/√(σ_B^2+ϵ) (5.6)
#反标准化操作:
y_i=γ(x_i ) ̂+β≡NB_(γ,β) (x_i) (5.7)
BN是对隐藏层的标准化处理,它与输入的标准化处理Normalizing inputs是有区别的。 Normalizing inputs使所有输入的均值为0,方差为1。而Batch Normalization可使各隐藏层输入的均值和方差为任意值。实际上,从激活函数的角度来说,如果各隐藏层的输入均值在靠近0的区域,即处于激活函数的线性区域,这样不利于训练好的非线性神经网络,而且得到的模型效果也不会太好。式(5.6)就起这个作用,当然它还有将归一化后的 x 还原的功能。BN一般用在哪里呢?BN应作用在非线性映射前,即对x=Wu+b做规范化时,在每一个全连接和激励函数之间。
何时使用BN呢?一般在神经网络训练时遇到收敛速度很慢,或梯度爆炸等无法训练的状况时,可以尝试用BN来解决。另外,在一般情况下,也可以加入BN来加快训练速度,提高模型精度,还可以大大提高训练模型的效率。BN具体功能有:
(1)可以选择比较大的初始学习率,让训练速度飙涨。以前还需要慢慢调整学习率,甚至在网络训练到一半的时候,还需要想着学习率进一步调小的比例选择多少比较合适,现在我们可以采用初始很大的学习率,然后学习率的衰减速度也很大,因为这个算法收敛很快。当然,这个算法即使你选择了较小的学习率,也比以前的收敛速度快,因为它具有快速训练收敛的特性。
(2)不用再去理会过拟合中drop out、L2正则项参数的选择问题,采用BN算法后,你可以移除这两项参数,或者可以选择更小的L2正则约束参数了,因为BN具有提高网络泛化能力的特性。
(3)再也不需要使用局部响应归一化层。
(4)可以把训练数据彻底打乱。
下面还是以房价预测为例,比较添加BN层与不添加BN层,两者在测试集上的损失值比较。下例为两者网络结构代码。
如通上面步骤

添加BN层训练

(1)导入需要的包

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
import numpy  as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn

(2)获得数据进行标准化

# 载入数据
boston = load_boston()
X,y   = (boston.data, boston.target)
dim = X.shape[1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
num_train = X_train.shape[0]
#对训练数据进行标准化
mean=X_train.mean(axis=0)
std=X_train.std(axis=0)

X_train-=mean
X_train/=std

X_test-=mean
X_test/=std

(3)得到训练集和标签

#分别获得训练集和测试集
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
train_data=torch.from_numpy(X_train).float()
train_target=torch.from_numpy(y_train).float()
test_data=torch.from_numpy(X_test).float()
test_target=torch.from_numpy(y_test).float()

(4)定义模型和loss

# 普通
net1_overfitting = torch.nn.Sequential(
    torch.nn.Linear(13, 16),
    torch.nn.ReLU(),
    torch.nn.Linear(16, 32),
    torch.nn.ReLU(),
    torch.nn.Linear(32, 1),
)
# 标准化
net1_nb = torch.nn.Sequential(
    torch.nn.Linear(13, 8),
    nn.BatchNorm1d(num_features=8),
    torch.nn.ReLU(),
    torch.nn.Linear(8, 4),
    nn.BatchNorm1d(num_features=4),  
    torch.nn.ReLU(),
    torch.nn.Linear(4, 1),
)
loss_func = torch.nn.MSELoss()
optimizer_ofit = torch.optim.Adam(net1_overfitting.parameters(), lr=0.01)
optimizer_nb = torch.optim.Adam(net1_nb.parameters(), lr=0.01)

(5)训练并可视化结果

from tensorboardX import SummaryWriter
writer = SummaryWriter(log_dir='log')
for epoch in range(200):
    net1_overfitting.train()
    net1_nb.train()
    
    pred_ofit = net1_overfitting(train_data)
    pred_nb = net1_nb(train_data)
    
    loss_ofit = loss_func(pred_ofit, train_target)
    loss_nb = loss_func(pred_nb, train_target)
    
    optimizer_ofit.zero_grad()
    optimizer_nb.zero_grad()
    
    loss_ofit.backward()
    loss_nb.backward()
    
    optimizer_ofit.step()
    optimizer_nb.step()
    net1_overfitting.eval()
    net1_nb.eval() 
    test_pred_orig = net1_overfitting(test_data)
    test_pred_nb = net1_nb(test_data)
    orig_loss=loss_func(test_pred_orig, test_target)
    nb_loss=loss_func(test_pred_nb, test_target)
    # 保存loss的数据与epoch数值
    #writer.add_scalar('train_loss', loss_ofit, t)
    writer.add_scalars('train_group_loss',{'orig_loss':loss_ofit.item(),'nb_loss':loss_nb.item(),}, epoch)
    writer.add_scalars('test_nb_loss',{'orig_loss':orig_loss.item(),'nb_loss':nb_loss.item()}, epoch)

在这里插入图片描述
GitHub代码: https://github.com/yunlong-G/tensorflow_learn/blob/master/%E5%9F%BA%E6%9C%AC%E7%9A%84/%E6%AD%A3%E5%88%99%E5%8C%96%E7%9A%84%E4%BD%BF%E7%94%A8.ipynb

  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值