一. 神经网络搭建和参数计算
一个继承(nn.model), 两个方法(init, forward)
简介
在pytorch中定义深度神经网络其实就是层堆叠的过程,继承自nn.Module,实现两个方法:
-
init方法中定义网络中的层结构,主要是全连接层,并进行初始化,
定义层
-
forward方法,在实例化模型的时候,底层会自动调用该函数。该函数中可以定义学习率,为初始化定义的layer传入数据等。
将各层连接
案例
需求
编码设计如下:
-
第1个隐藏层:权重初始化采用标准化的xavier初始化 激活函数使用sigmoid
-
第2个隐藏层:权重初始化采用标准化的He初始化 激活函数采用relu
-
out输出层线性层 假若二分类,采用softmax做数据归一化
代码演示
import torch import torch.nn as nn from torchsummary import summary class NnModel(nn.Module): def __init__(self): super(NnModel, self).__init__() # Hidden1(3, 3) self.linear1 = nn.Linear(3, 3) nn.init.xavier_normal_(self.linear1.weight) # Hidden2(3, 2) self.linear2 = nn.Linear(3, 2) nn.init.kaiming_uniform_(self.linear2.weight) # Output(2, 2) self.out = nn.Linear(2, 2) def forward(self, x): # 数据经第一层线性层处理 x = self.linear1(x) # 激活函数 x = torch.sigmoid(x) # 数据经第二层线性层处理 x = self.linear2(x) # 激活函数 x = torch.relu(x) # 数据经输出层 out = self.out(x) # dim=-1: 输出每个样本预测不同类别的概率, 每个样本类别预测和为1 out = torch.softmax(out, dim=-1) return out if __name__ == '__main__': # 'cuda': 使用GPU, 数据模型等都需要放到一起 model = NnModel().to('cuda') my_data = torch.randn(5, 3).to('cuda') output = model(my_data) print(output) # 查看网络结构 summary(model, (3,), 1, device='cuda') # 查看网络参数 for name, param in model.named_parameters(): print(name, param)
模型参数的计算
-
以第一个隐层为例:该隐层有3个神经元,每个神经元的参数为:4个(w1,w2,w3,b1),所以一共用3x4=12个参数。
-
输入数据和网络权重是两个不同的事儿!对于初学者理解这一点十分重要,要分得清。
总结
神经网络的搭建方
-
定义继承自nn.Module的模型类
-
在init方法中定义网络中的层结构
-
在forward方法中定义数据传输方式
网络参数量的统计方法
-
统计每一层中的权重w和偏置b的数量
优缺点
1. 优点
-
精度高,性能优于其他的机器学习算法,甚至在某些领域超过了人类
-
可以近似任意的非线性函数随之计算机硬件的发展,
-
近年来在学界和业界受到了热捧,有大量的框架和库可供调。
2. 缺点
-
黑箱,很难解释模型是怎么工作的
-
训练时间长,需要大量的计算资源
-
网络结构复杂,需要调整超参数
-
部分数据集上表现不佳,容易发生过拟合
二. 损失函数
概念
在深度学习中, 损失函数是用来衡量模型参数的质量的函数, 衡量的方式是比较网络输出和真实输出的差异:
损失函数在不同的文献中名称是不一样的,主要有以下几种命名方式:
多分类损失函数-softmax
loss = nn.CrossEntropyLoss()
概念
在多分类任务通常使用softmax将logits转换为概率的形式,所以多分类的交叉熵损失
也叫做softmax损失
,它的计算方法是:
公式
其中:
1.y 是样本 x 属于某一个类别的真实概率
2.而 f(x) 是样本属于某一类别的预测分数
3.S 是 softmax 激活函数,将属于某一类别的预测分数转换成概率
4.L 用来衡量真实值 y 和预测值 f(x) 之间差异性的损失结果
举例
import torch from torch import nn # 多分类交叉熵损失,使用nn.CrossEntropyLoss()实现。nn.CrossEntropyLoss()=softmax + 损失计算 def test1(): # 设置真实值: 可以是热编码后的结果也可以不进行热编码 # y_true = torch.tensor([[0, 1, 0], [0, 0, 1]], dtype=torch.float32) # 注意的类型必须是64位整型数据 y_true = torch.tensor([1, 2], dtype=torch.int64) y_pred = torch.tensor([[0.2, 0.6, 0.2], [0.1, 0.8, 0.1]], dtype=torch.float32) # 实例化交叉熵损失 loss = nn.CrossEntropyLoss() # 计算损失结果 my_loss = loss(y_pred, y_true).numpy() print('loss:', my_loss)
二分类损失函数-sigmoid
criterion = nn.BCELoss()
概念
在处理二分类任务时,我们不再使用softmax激活函数,而是使用sigmoid激活函数
,那损失函数也相应的进行调整,使用二分类的交叉熵损失函数
:
公式
其中:
-
y 是样本x属于某一个类别的真实概率
-
而 y^ 是样本属于某一类别的预测概率
-
L 用来衡量真实值y与预测值y^之间差异性的损失结果。
举例
import torch from torch import nn def test2(): # 1 设置真实值和预测值 # 预测值是sigmoid输出的结果 y_pred = torch.tensor([0.6901, 0.5459, 0.2469], requires_grad=True) y_true = torch.tensor([0, 1, 0], dtype=torch.float32) # 2 实例化二分类交叉熵损失 criterion = nn.BCELoss() # 3 计算损失 my_loss = criterion(y_pred, y_true).detach().numpy() print('loss:', my_loss)
回归-MAE损失函数
loss = nn.L1Loss()
概念
Mean absolute loss(MAE)也被称为L1 Loss
,是以绝对误差作为距离。损失函数公式:
特点
-
由于L1 loss具有稀疏性,为了惩罚较大的值,因此常常将其作为正则项添加到其他loss中作为约束。
-
L1 loss的最大问题是梯度在零点不平滑,导致会跳过极小值。
举例
import torch from torch import nn def test3(): # 1 设置真实值和预测值 y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True) y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32) # 2 实例MAE损失对象 loss = nn.L1Loss() # 3 计算损失 my_loss = loss(y_pred, y_true).detach().numpy() print('loss:', my_loss)
回归-MSE损失函数
loss = nn.MSELoss()
概念
Mean Squared Loss/ Quadratic Loss(MSE loss)也被称为L2 loss
,或欧氏距离,它以误差的平方和的均值作为距离
特点
-
L2 loss也常常作为正则项。
-
当预测值与目标值相差很大时, 梯度容易爆炸。
举例
import torch from torch import nn def test4(): # 1 设置真实值和预测值 y_pred = torch.tensor([1.0, 1.0, 1.9], requires_grad=True) y_true = torch.tensor([2.0, 2.0, 2.0], dtype=torch.float32) # 2 实例MSE损失对象 loss = nn.MSELoss() # 3 计算损失 my_loss = loss(y_pred, y_true).detach().numpy() print('myloss:', my_loss)
回归-SmoothL1损失
loss = nn.SmoothL1Loss()
概念
smooth L1说的是光滑之后的L1。损失函数公式:
解释
其中:𝑥 = f(x) − y 为真实值和预测值的差值。
从下图中可以看出,该函数实际上就是一个分段函数
-
在[-1,1]之间实际上就是L2损失,这样解决了L1的不光滑问题
-
在[-1,1]区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题
举例
import torch from torch import nn def test5(): # 1 设置真实值和预测值 y_true = torch.tensor([0, 3]) y_pred = torch.tensor([0.6, 0.4], requires_grad=True) # 2 实例化smoothL1损失对象 loss = nn.SmoothL1Loss() # 3 计算损失 my_loss = loss(y_pred, y_true).detach().numpy() print('loss:', my_loss)
三. 网络优化方法
网格优化, 使得损失函数最小, 快速找到预测值和真实值更接近的方法
梯度下降算法
寻找使损失函数最小化
原理
梯度下降法是一种寻找使损失函数最小化的方法。从数学上的角度来看,梯度的方向是函数增长速度最快的方向,那么梯度的反方向就是函数减少最快的方向,所以有:
其中,η是学习率,如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本。如果,学习率太大,那就有可能直接跳过最优解,进入无限的训练中。解决的方法就是,学习率也需要随着训练的进行而变化。
相关概念
在进行模型训练时,有三个基础的概念:
-
Epoch: 使用全部数据对模型进行以此完整训练,训练轮次
-
Batch_size: 使用训练集中的小部分样本对模型权重进行以此反向传播的参数更新,每次训练每批次样本数量
-
Iteration: 使用一个 Batch 数据对模型进行一次参数更新的过程, 等于Batch
假设数据集有 50000 个训练样本,现在选择 Batch Size = 256 对模型进行训练。
-
每个 Epoch 要训练的图片数量:50000
-
训练集具有的 Batch 个数:50000/256+1=196
-
每个 Epoch 具有的 Iteration 个数:196
-
10个 Epoch 具有的 Iteration 个数:1960
分类
在深度学习中,梯度下降的几种方式的根本区别就在于 Batch Size不同,如下表所示:
注:
上表中 Mini-Batch 的 Batch 个数为 N / B + 1 是针对未整除的情况。整除则是 N / B。
反向传播算法
从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新
原理
前向传播:指的是数据输入的神经网络中,逐层向前传输,一直到运算到输出层为止。
反向传播(Back Propagation):利用损失函数 ERROR,从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新
举例
反向传播对神经网络中的各个节点的权重进行更新。一个简单的神经网络用来举例:激活函数为sigmoid
网络
前向传播
反向传播
接下来是反向传播,我们先来求最简单的,求误差E对w5的导数。要求误差E对w5的导数,需要先求误差E对out o1的导数,再求out o1对net o1的导数,最后再求net o1对w5的导数,经过这个处理,我们就可以求出误差E对w5的导数(偏导),如下图所示:
参数更新
导数(梯度)已经计算出来了,下面就是反向传播与参数更新过程:
如果要想求误差E对w1的导数,误差E对w1的求导路径不止一条,这会稍微复杂一点,但换汤不换药,计算过程如下所示:
代码演示
import torch from torch import nn from torch import optim # 创建神经网络类 class Model(nn.Module): # 初始化参数 def __init__(self): # 调用父类方法 super(Model, self).__init__() # 创建网络层 self.linear1 = nn.Linear(2, 2) self.linear2 = nn.Linear(2, 2) # 初始化神经网络参数 self.linear1.weight.data = torch.tensor([[0.15, 0.20], [0.25, 0.30]]) self.linear2.weight.data = torch.tensor([[0.40, 0.45], [0.50, 0.55]]) self.linear1.bias.data = torch.tensor([0.35, 0.35]) self.linear2.bias.data = torch.tensor([0.60, 0.60]) # 前向传播方法 def forward(self, x): # 数据经过第一层隐藏层 x = self.linear1(x) # 计算第一层激活值 x = torch.sigmoid(x) # 数据经过第二层隐藏层 x = self.linear2(x) # 计算第二层激活值 x = torch.sigmoid(x) return x if __name__ == '__main__': # 定义网络输入值和目标值 inputs = torch.tensor([[0.05, 0.10]]) target = torch.tensor([[0.01, 0.99]]) # 实例化神经网络对象 model = Model() output = model(inputs) print("output-->", output) loss = torch.sum((output - target) ** 2) / 2 # 计算误差 print("loss-->", loss) # 优化方法和反向传播算法 optimizer = optim.SGD(model.parameters(), lr=0.5) optimizer.zero_grad() loss.backward() print("w1,w2,w3,w4-->", model.linear1.weight.grad.data) print("w5,w6,w7,w8-->", model.linear2.weight.grad.data) optimizer.step() # 打印神经网络参数 print(model.state_dict()) >>> output--> tensor([[0.7514, 0.7729]], grad_fn=<SigmoidBackward0>) loss--> tensor(0.2984, grad_fn=<DivBackward0>) w1,w2,w3,w4--> tensor([[0.0004, 0.0009], [0.0005, 0.0010]]) w5,w6,w7,w8--> tensor([[ 0.0822, 0.0827], [-0.0226, -0.0227]]) OrderedDict([('linear1.weight', tensor([[0.1498, 0.1996], [0.2498, 0.2995]])), ('linear1.bias', tensor([0.3456, 0.3450])), ('linear2.weight', tensor([[0.3589, 0.4087], [0.5113, 0.5614]])), ('linear2.bias', tensor([0.5308, 0.6190]))])
总结
前向传播:指的是数据输入的神经网络中,逐层向前传输,一直到运算到输出层为止。
反向传播(Back Propagation):利用损失函数 ERROR,从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新
梯度下降优化方法
碰到平缓区域,梯度值较小,参数优化变慢
碰到 “鞍点” ,梯度为 0,参数无法优化
碰到局部最小值,参数不是最优
对梯度下降算法的优化方法,例如:Momentum、AdaGrad、RMSprop、Adam 等
指数加权平均
用于动量算法和RMSProp算法的更新步骤
指数移动加权平均则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。
-
St 表示指数加权平均值;
-
Yt 表示 t 时刻的值;
-
β 调节权重系数,该值越大平均数越平缓。
momentum-梯度
动量算法调整梯度
公式
梯度计算公式:Dt = β * St-1 + (1- β) * Wt
-
St-1 表示历史梯度移动加权平均值
-
Wt 表示当前时刻的梯度值
-
Dt 为当前时刻的指数加权平均梯度值
-
β 为权重系数
举例
假设:权重 β 为 0.9,例如:
-
第一次梯度值:D1 = S1 = W1
-
第二次梯度值:D2=S2 = 0.9 * S1 + W2 * 0.1
-
第三次梯度值:D3=S3 = 0.9 * S2 + W3 * 0.1
-
第四次梯度值:D4=S4 = 0.9 * S3 + W4 * 0.1
梯度下降公式中梯度的计算,就不再是当前时刻 t 的梯度值,而是历史梯度值的指数移动加权平均值。公式修改为:grad2 = grad1 * A + grad2 * B
优缺点
-
当处于鞍点位置时,由于当前的梯度为 0,参数无法更新。但是 Momentum 动量梯度下降算法已经在先前积累了一些梯度值,很有可能使得跨过鞍点。
克服鞍点问题
-
由于 mini-batch 普通的梯度下降算法,每次选取少数的样本梯度确定前进方向,可能会出现震荡,使得训练时间变长。Momentum 使用移动加权平均,平滑了梯度的变化,
使得前进方向更加平缓
,有利于加快训练过程。
代码演示
import torch import matplotlib.pyplot as plt def test03(): # 1 初始化权重参数 w = torch.tensor([1.0], requires_grad=True, dtype=torch.float32) y = ((w ** 2) / 2.0).sum() # 2 实例化优化方法,SGD指定参数beta=0.9 optimizer = torch.optim.SGD([w], lr=0.01, momentum=0.9) # 3 第一次更新计算梯度,并对参数进行更新 optimizer.zero_grad() y.backward() optimizer.step() print('第1次: 梯度w.grad: %f,更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) # 4 第2次更新 计算梯度,并对参数进行更新 # 使用更新后的参数机选输出结果 y = ((w ** 2) / 2.0).sum() optimizer.zero_grad() y.backward() optimizer.step() print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) >>> 第1次: 梯度w.grad: 1.0000000000, 更新后的权重:0.9900000095 第2次: 梯度w.grad: 0.9900000095, 更新后的权重:0.9711000323
AdaGrad-学习率
累计平方梯度优化学习率
步骤
AdaGrad 通过对不同的参数分量使用不同的学习率,AdaGrad 的学习率总体会逐渐减小。
其计算步骤如下:
1.初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
2.初始化梯度累积变量 s = 0
3.从训练集中采样 m 个样本的小批量,计算梯度 g
4.累积平方梯度 s = s + g ⊙ g,⊙ 表示各个分量相乘
公式
学习率 α 的计算公式如下:
参数更新公式如下:
累计平方梯度:
累积平方梯度 s = s + g ⊙ g,⊙ 表示各个分量相乘
用梯度平方惩罚学习率
优缺点
AdaGrad 缺点是可能会使得学习率过早、过量的降低,导致模型训练后期学习率太小,较难找到最优解。
导致学习率过低, 容易陷入局部极小值点
代码演示
import torch import matplotlib.pyplot as plt def test04(): # 1 初始化权重参数 w = torch.tensor([1.0], requires_grad=True, dtype=torch.float32) y = ((w ** 2) / 2.0).sum() # 2 实例化优化方法:adagrad优化方法 optimizer = torch.optim.Adagrad([w], lr=0.01) # 3 第1次更新 计算梯度,并对参数进行更新 optimizer.zero_grad() y.backward() optimizer.step() print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) # 4 第2次更新 计算梯度,并对参数进行更新 # 使用更新后的参数机选输出结果 y = ((w ** 2) / 2.0).sum() optimizer.zero_grad() y.backward() optimizer.step() print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) >>> 第1次: 梯度w.grad: 1.000000, 更新后的权重:0.990000 第2次: 梯度w.grad: 0.990000, 更新后的权重:0.982965
RMSProp-学习率
使用指数移动加权平均累计历史梯度优化AdaGrad
步骤
RMSProp 优化算法是对 AdaGrad 的优化. 最主要的不同是,其使用指数移动加权平均梯度替换历史梯度的平方和。其计算过程如下:
-
初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
-
初始化参数 θ
-
初始化梯度累计变量 s
-
从训练集中采样 m 个样本的小批量,计算梯度 g
-
使用指数移动平均累积历史梯度,公式如下:
公式
学习率 α 的计算公式如下:
参数更新公式如下:
指数移动加权平均累积历史梯度,公式如下:
代码演示
import torch import matplotlib.pyplot as plt def test05(): # 1 初始化权重参数 w = torch.tensor([1.0], requires_grad=True, dtype=torch.float32) y = ((w ** 2) / 2.0).sum() # 2 实例化优化方法:RMSprop算法,其中alpha对应β optimizer = torch.optim.RMSprop([w], lr=0.01, alpha=0.9) # 3 第1次更新 计算梯度,并对参数进行更新 optimizer.zero_grad() y.backward() optimizer.step() print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) # 4 第2次更新 计算梯度,并对参数进行更新 # 使用更新后的参数机选输出结果 y = ((w ** 2) / 2.0).sum() optimizer.zero_grad() y.backward() optimizer.step() print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) >>> 第1次: 梯度w.grad: 1.000000, 更新后的权重:0.968377 第2次: 梯度w.grad: 0.968377, 更新后的权重:0.945788
Adam-学习率+梯度
-
Momentum 使用指数加权平均计算当前的梯度值
-
AdaGrad、RMSProp 使用自适应的学习率
-
Adam优化算法(Adaptive Moment Estimation,自适应矩估计)将 Momentum 和 RMSProp 算法结合在一起。
-
修正梯度: 使⽤梯度的指数加权平均
-
修正学习率: 使⽤梯度平⽅的指数加权平均。
import torch import matplotlib.pyplot as plt def test06(): # 1 初始化权重参数 w = torch.tensor([1.0], requires_grad=True) y = ((w ** 2) / 2.0).sum() # 2 实例化优化方法:Adam算法,其中betas是指数加权的系数, 一个梯度, 一个学习率 optimizer = torch.optim.Adam([w], lr=0.01, betas=[0.9, 0.99]) # 3 第1次更新 计算梯度,并对参数进行更新 optimizer.zero_grad() y.backward() optimizer.step() print('第1次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) # 4 第2次更新 计算梯度,并对参数进行更新 # 使用更新后的参数机选输出结果 y = ((w ** 2) / 2.0).sum() optimizer.zero_grad() y.backward() optimizer.step() print('第2次: 梯度w.grad: %f, 更新后的权重:%f' % (w.grad.numpy(), w.detach().numpy())) >>> 第1次: 梯度w.grad: 1.000000, 更新后的权重:0.990000 第2次: 梯度w.grad: 0.990000, 更新后的权重:0.980003
总结
学习率衰减方法
等间隔学习率衰减
设置调整学习率的间隔, 训练指定间隔数后调整学习率
def test_StepLR(): # 0.参数初始化 LR = 0.1 # 设置学习率初始化值为0.1 iteration = 10 max_epoch = 200 # 1 初始化参数 y_true = torch.tensor([0]) x = torch.tensor([1.0]) w = torch.tensor([1.0], requires_grad=True) # 2.优化器 optimizer = optim.SGD([w], lr=LR, momentum=0.9) # 3.设置学习率下降策略 scheduler_lr = optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.5) # 4.获取学习率的值和当前的epoch lr_list, epoch_list = list(), list() for epoch in range(max_epoch): lr_list.append(scheduler_lr.get_last_lr()) # 获取当前lr epoch_list.append(epoch) # 获取当前的epoch for i in range(iteration): # 遍历每一个batch数据 loss = ((w*x-y_true)**2)/2.0 # 目标函数 optimizer.zero_grad() # 反向传播 loss.backward() optimizer.step() # 更新下一个epoch的学习率 scheduler_lr.step() # 5.绘制学习率变化的曲线 plt.plot(epoch_list, lr_list, label="Step LR Scheduler") plt.xlabel("Epoch") plt.ylabel("Learning rate") plt.legend() plt.show()
指定间隔学习率衰减
def test_MultiStepLR(): torch.manual_seed(1) LR = 0.1 iteration = 10 max_epoch = 200 weights = torch.randn((1), requires_grad=True) target = torch.zeros((1)) print('weights--->', weights, 'target--->', target) optimizer = optim.SGD([weights], lr=LR, momentum=0.9) # 设定调整时刻数 milestones = [50, 125, 160] # 设置学习率下降策略 scheduler_lr = optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.5) lr_list, epoch_list = list(), list() for epoch in range(max_epoch): lr_list.append(scheduler_lr.get_last_lr()) epoch_list.append(epoch) for i in range(iteration): loss = torch.pow((weights - target), 2) optimizer.zero_grad() # 反向传播 loss.backward() # 参数更新 optimizer.step() # 更新下一个epoch的学习率 scheduler_lr.step() plt.plot(epoch_list, lr_list, label="Multi Step LR Scheduler\nmilestones:{}".format(milestones)) plt.xlabel("Epoch") plt.ylabel("Learning rate") plt.legend() plt.show()
指数学习率衰减
def test_ExponentialLR(): # 0.参数初始化 LR = 0.1 # 设置学习率初始化值为0.1 iteration = 10 max_epoch = 200 # 1 初始化参数 y_true = torch.tensor([0]) x = torch.tensor([1.0]) w = torch.tensor([1.0], requires_grad=True) # 2.优化器 optimizer = optim.SGD([w], lr=LR, momentum=0.9) # 3.设置学习率下降策略 gamma = 0.95 scheduler_lr = optim.lr_scheduler.ExponentialLR(optimizer, gamma=gamma) # 4.获取学习率的值和当前的epoch lr_list, epoch_list = list(), list() for epoch in range(max_epoch): lr_list.append(scheduler_lr.get_last_lr()) epoch_list.append(epoch) for i in range(iteration): # 遍历每一个batch数据 loss = ((w * x - y_true) ** 2) / 2.0 optimizer.zero_grad() # 反向传播 loss.backward() optimizer.step() # 更新下一个epoch的学习率 scheduler_lr.step() # 5.绘制学习率变化的曲线 plt.plot(epoch_list, lr_list, label="Multi Step LR Scheduler") plt.xlabel("Epoch") plt.ylabel("Learning rate") plt.legend() plt.show()
四. 正则化方法
缓解过拟合, 减小测试集误差
Dropout正则化
神经元随机失活
发生在训练阶段, 不在测试阶段, 网络结构不变, 测试阶段还会用到失活的神经元
策略
让神经元以超参数p的概率停止工作或者被激活置为为0, 未被置为0的按比例1/(1-p)缩放
代码演示
import torch import torch.nn as nn def test(): # 初始化随机失活层 dropout = nn.Dropout(p=0.4) # 初始化输入数据:表示某一层的weight信息 inputs = torch.randint(0, 10, size=[1, 4]).float() layer = nn.Linear(4,5) y = layer(inputs) print("未失活FC层的输出结果:\n", y) y = dropout(y) print("失活后FC层的输出结果:\n", y) >>> 未失活FC层的输出结果: tensor([[-0.8369, -5.5720, 0.2258, 3.4256, 2.1919]], grad_fn=<AddmmBackward0>) 失活后FC层的输出结果: tensor([[-1.3949, -9.2866, 0.3763, 0.0000, 3.6531]], grad_fn=<MulBackward0>)
批量归一化-(BN层)
批量归一化层在计算机视觉领域使用较多
BN层: 对数据标准化,再对数据重构(缩放+平移)
先对数据标准化,再对数据重构(缩放+平移),如下所示:
-
λ 和 β 是可学习的参数,它相当于对标准化后的值做了一个线性变换,λ 为系数,β 为偏置;
-
eps 通常指为 1e-5,避免分母为 0;
-
E(x) 表示变量的均值;
-
Var(x) 表示变量的方差;