自学内容网 自学内容网

深度学习--神经网络基础

神经网络基础

1、简介

神经网络:人工神经网络,简称为神经网络,是一种模仿生物神经网络结构和功能的计算模型,由神经元构成(加权和+激活函数)。

神经网络的信息只向一个方向移动,即从输入节点向前移动,通过隐藏节点,再向输出节点移动。其中的基本部分是:

  • 输入层:即输入x的那一层
  • 输出层:即输出y的那一层
  • 隐藏层:输入层和输出层之间都是隐藏层

特点是:

  • 同一层的神经元之间没有连接
  • 第N层每个神经元和第N-1层的所有神经元相连(这就是full connected的含义),这就是全连接神经网络
  • 第N-1层神经元的输出就是第N层神经元的输入
  • 每个连接都有一个权重值(w系数和b系数)

2、激活函数

(一)、简介

激活函数用于对每层的输出数据进行变换,进而为整个网络注入了非线性因素,此时神经网络就可以拟合各种曲线。

  • 没有引入非线性因素的网络等价于使用一个线性模型来拟合
  • 通过给网络输出增加激活函数,实现引入非线性因素,使得网络模型可以逼近任何函数,提升网络对复杂问题的拟合能力

如果不使用激活函数,整个网络虽然看起来复杂,其本质还相当于一种线性模型。

(二)、sigmoid激活函数

s i g m o i d 激活函数: f ( x ) = 1 1 + e − x 激活函数求导公式: f ′ ( x ) = ( 1 1 + e − x ) ′ = 1 1 + e − x ( 1 − 1 1 + e − x ) = f ( x ) ( 1 − f ( x ) ) sigmoid激活函数 :f(x)=\frac{1}{1+e^{-x}}\\ 激活函数求导公式:f'(x)=(\frac{1}{1+e^{-x}})'=\frac{1}{1+e^{-x}}(1-\frac{1}{1+e^{-x}})=f(x)(1-f(x)) sigmoid激活函数:f(x)=1+ex1激活函数求导公式:f(x)=(1+ex1)=1+ex1(11+ex1)=f(x)(1f(x))

sigmoid函数图像:

在这里插入图片描述

  • sigmoid函数可以将任意的输入映射到(0,1)之间,当输入的值大致再<-6或者>6时,意味着输入任何值得到的激活值都是差不多的,这样会丢失部分信息。比如输入100和输出10000结果sigmoid的激活值几乎都是等于1的,但是输入的数据之间相差100倍的信息就丢失了。
  • 对于sigmoid函数而言,输入值在[-6,6]之间输出值才会有明显差异,输入值在[-3,3]之间才会有比较好的效果。
  • 通过上述导数图像,我们发现导数数值范围是(0,0.25),当输入<-6或者>6时,sigmoid激活函数图像的导数接近为0,此时网络参数将更新极其缓慢,或者无法更新。
  • 一般来叔,sigmoid网络在五层以内就会产生梯度消失的现象,而且该激活函数并不是以0为中心,所以在实践中这种激活函数使用的很少,sigmoid函数一般只用于二分类的输出层。
def sigmoid():
    # 创建画布和坐标轴
    _, axes = plt.subplots(1, 2)
    # sigmoid函数图像
    x = torch.linspace(-12, 12, 1000)
    # 输入值x通过sigmoid函数转换成激活值y
    y = torch.sigmoid(x)
    axes[0].plot(x, y)
    axes[0].grid()
    axes[0].set_title('sigmoid原函数')

    # sigmoid导数图像
    x = torch.linspace(-12, 12, 1000, requires_grad=True)
    # 对梯度求和,再根据该标量求导
    torch.sigmoid(x).sum().backward()
    # x.detach():输入值x的数值,x.grad:计算梯度,求导
    axes[1].plot(x.detach(), x.grad)
    axes[1].grid()
    axes[1].set_title('sigmoid导数图像')
    plt.show()

(三)、Tanh激活函数

T a n h 激活函数: f ( x ) = 1 − e − 2 x 1 + e − 2 x 激活函数求导公式: f ′ ( x ) = ( 1 − e − 2 x 1 + e − 2 x ) ′ = 1 − f 2 ( x ) Tanh激活函数:f(x)=\frac{1-e^{-2x}}{1+e^{-2x}}\\ 激活函数求导公式:f'(x)=(\frac{1-e^{-2x}}{1+e^{-2x}})'=1-f^2(x) Tanh激活函数:f(x)=1+e2x1e2x激活函数求导公式:f(x)=(1+e2x1e2x)=1f2(x)

Tanh的函数图像:

在这里插入图片描述

  • Tanh函数将输入映射到(-1,1)之间,图像以0为中心,在0点对称,当输入大概<-3或者>3时将被映射为-1或者1。其导数范围为(0,1),当输入的值大概<-3或者>3时,其导数近似0。
  • 与sigmoid相比,它是以0为中心的,且梯度相对于sigmoid大,使得其收敛速度要比sigmoid快,减少迭代次数。然而,从图中可以看出,Tanh两侧的导数也为0,同样会造成梯度消失。
  • 若使用时可在隐藏层使用tanh函数,在输出层使用sigmoid函数。
def tanh():
    # 创建画布和坐标轴
    _, axes = plt.subplots(1, 2)
    # tanh函数图像
    x = torch.linspace(-12, 12, 1000)
    y = torch.tanh(x)
    axes[0].plot(x, y)
    axes[0].grid()
    axes[0].set_title('tanh原函数')

    # tanh导数图像
    x = torch.linspace(-12, 12, 1000, requires_grad=True)
    torch.tanh(x).sum().backward()
    axes[1].plot(x.detach(), x.grad)
    axes[1].grid()
    axes[1].set_title('tanh导数图像')
    plt.show()

(四)、ReLU激活函数

R e L U 激活函数: f ( x ) = m a x ( 0 , x ) 激活函数求导公式: f ′ ( x ) = { 1 , x ≥ 0 0 , f < 0 ReLU激活函数:f(x)=max(0,x)\\ 激活函数求导公式:f'(x)=\left\{\begin{matrix}1,x\ge0\\0,f<0 \end{matrix}\right. ReLU激活函数:f(x)=max(0,x)激活函数求导公式:f(x)={1x00f<0

ReLU的函数图像:

在这里插入图片描述

  • ReLU激活函数将小于0的值映射为0,而大于0的值则保持不变,它更加重视正信号,而忽略负信号,这种激活函数运算更为简单,能够提高模型的训练效果。
  • 当x<0时,ReLU导数为0,而当x>0时,则不存在饱和问题,所以ReLU能够在x>0时保持梯度不衰减,从而缓解梯度消失问题。然而随着训练的推进,部分输入会落入小于0的区域,导致对应权重无法更新,这种现象被称为“神经元死亡”。
  • ReLU是目前最常用的激活函数,与sigmoid相比,RELU的优势是:
    • 采用sigmoid函数,计算量大(指数计算),反向传播求误差梯度时,计算量相对大,而采用ReLU激活函数,整个过程的计算量节省很多。
    • sigmoid函数反向传播时,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。ReLU会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。
def relu():
    # 创建画布和坐标轴
    _, axes = plt.subplots(1, 2)
    # ReLU函数图像
    x = torch.linspace(-12, 12, 1000)
    y = torch.relu(x)
    axes[0].plot(x, y)
    axes[0].grid()
    axes[0].set_title('relu原函数')

    # relu导数图像
    x = torch.linspace(-12, 12, 1000, requires_grad=True)
    torch.relu(x).sum().backward()
    axes[1].plot(x.detach(), x.grad)
    axes[1].grid()
    axes[1].set_title('relu导数图像')
    plt.show()

(五)、SoftMax激活函数

S o f t M a x 激活函数: s o f t m a x ( z i ) = e z i ∑ j e z j SoftMax激活函数:softmax(z_i)=\frac{e^{z_i}}{\sum_je^{z_j}}\\ SoftMax激活函数:softmax(zi)=jezjezi

softmax就是将网络输出的logits通过softmax函数,就映射成为(0,1)的值,而这些值的累和为1(满足概率的性质),那么我们将它理解成概率,选取概率最大(也就是值对应最大的)节点,作为我们的预测目标类别。

import torch

scores = torch.tensor([0.2, 0.02, 0.15, 0.15, 1.3, 0.5, 0.06, 1.1, 0.05, 3.75])
# dim = 0,按行计算
probabilities = torch.softmax(scores, dim=0)
print(probabilities)

(六)、其他常见的激活函数

在这里插入图片描述

(七)、激活函数选择方法

(1)、对于隐藏层
  • 优先选择ReLU激活函数
  • 如果ReLU效果不好,那么可以尝试其他激活,如Leaky ReLU等
  • 如果你使用了ReLU,需要注意一下Dead ReLU问题,避免出现大的梯度从而导致过多的神经元死亡
  • 少用使用sigmoid激活函数,可以尝试使用tanh激活函数
(2)、对于输出层
  • 二分类问题选择sigmoid激活函数
  • 多分类问题选择softmax激活函数
  • 回归问题选择identity激活函数

3、初始化方式

(一)、均匀分布初始化

权重参数初始化从区间均匀随机取值。即在(-1/√d,1/√d)均匀分布中生成当前神经元的权重,其中d为每个神经元的输入数量

# 均匀分布随机初始化
def my_uniform():
    linear = nn.Linear(5, 3)
    # 从0-1均匀分布产生参数
    nn.init.uniform_(linear.weight)
    print(linear.weight.data)

(二)、正态分布初始化

随机初始化从均值为0,标准差是1的高斯分布中取样,使用一些很小的值对参数W进行初始化

# 正态分布随机初始化
def my_normal():
    linear = nn.Linear(5, 3)
    nn.init.normal_(linear.weight, mean=0, std=1)
    print(linear.weight.data)

(三)、固定值初始化

(1)、全0初始化

将神经网络中的所有权重参数初始化为 0

# 全0初始化
def my_zero():
    linear = nn.Linear(5, 3)
    nn.init.zeros_(linear.weight)
    print(linear.weight.data)
(2)、全1初始化

将神经网络中的所有权重参数初始化为 1

# 全1初始化
def my_ones():
    linear = nn.Linear(5, 3)
    nn.init.ones_(linear.weight)
    print(linear.weight.data)
(3)、固定值初始化

将神经网络中的所有权重参数初始化为某个固定值

# 固定初始化
def my_constant():
    linear = nn.Linear(5, 3)
    nn.init.constant_(linear.weight, 5)
    print(linear.weight.data)

(四)、kaiming初始化/HE初始化

HE 初始化分为正态分布的 HE 初始化、均匀分布的 HE 初始化

fan_in 输入神经元的个数

(1)、正态化的he初始化

stddev = sqrt(2 / fan_in)

def my_kaiming_normal():
    # kaiming 正态分布初始化
    linear = nn.Linear(5, 3)
    nn.init.kaiming_normal_(linear.weight)
    print(linear.weight.data)
(2)、均匀分布的he初始化

它从 [-limit,limit] 中的均匀分布中抽取样本, limit是 sqrt(6 / fan_in)

def my_kaiming_uniform():
    # kaiming 均匀分布初始化
    linear = nn.Linear(5, 3)
    nn.init.kaiming_uniform_(linear.weight)
    print(linear.weight.data)

(五)、xavier初始化

Xavier 初始化,也叫做 Glorot初始化,该方法也有两种,一种是正态分布的 xavier 初始化、一种是均匀分布的 xavier 初始化

fan_in 是输入神经元的个数, fan_out 是输出的神经元个数

(1)、正态化的Xavier初始化

stddev = sqrt(2 / (fan_in + fan_out))

# xavier 初始化
def my_xavier_nomal():
    # xavier 正态分布初始化
    linear = nn.Linear(5, 3)
    nn.init.xavier_normal_(linear.weight)
    print(linear.weight.data)
(2)、均匀分布的Xavier初始化

[-limit,limit] 中的均匀分布中抽取样本, limit 是 sqrt(6 / (fan_in + fan_out))

def my_xavier_uniform():
    # xavier 均匀分布初始化
    linear = nn.Linear(5, 3)
    nn.init.xavier_uniform_(linear.weight)
    print(linear.weight.data)

(六)、总结

  • 常见的初始化方式

均匀分布初始化,正态分布初始化,全0全1初始化,固定值初始化,kaming的初始化,xavier初始化

  • 初始化方法的选择

一般我们在使用 PyTorch 构建网络模型时,每个网络层的参数都有默认的初始化方法,优先选择kaming的初始化,xavier初始化方式

4、神经网络搭建以及参数计算

(一)、搭建神经网络

在pytorch中定义深度神经网络其实就是层堆叠的过程,继承自nn.Module,实现两个方法:

  • __init__方法中定义网络中的层结构,主要是全连接层,并进行初始化
  • forward方法,在实例化模型的时候,底层会自动调用该函数。该函数中可以定义学习率,为初始化定义的layer传入数据等

编码设计如下:

  1. 第1个隐藏层:权重初始化采用标准化的kaiming初始化 激活函数使用sigmoid
  2. 第2个隐藏层:权重初始化采用标准化的xavier初始化 激活函数采用relu
  3. out输出层线性层 假若二分类,采用softmax做数据归一化
import torch
import torch.nn as nn
from torchsummary import summary

class My_Model(nn.Module):
    def __init__(self):
        # 继承父类初始化属性
        super(My_Model, self).__init__()
        # 创建第一个隐藏层模型,3个输入特征,3个输出特征
        self.linear1 = nn.Linear(3, 3)
        # kaiming初始化参数
        nn.init.kaiming_normal_(self.linear1.weight)

        # 创建第二个隐藏层模型,3个输入特征,2个输出特征
        self.linear2 = nn.Linear(3, 2)
        # xavier初始化参数
        nn.init.xavier_normal_(self.linear2.weight)
        
        # 创建输出层模型,2个输入特征,2个输出特征
        self.out = nn.Linear(2, 2)

    def forward(self, x):
        # 数据经过第一个线性层
        x = self.linear1(x)
        # 使用sigmoid激活函数
        x = torch.sigmoid(x)
        # 数据经过第二个线性层
        x = self.linear2(x)
        # 使用relu激活函数
        x = torch.relu(x)
        # 数据经过输出层
        x = self.out(x)
        # 使用softmax激活函数
        x = torch.softmax(x, dim=1)
        return x

if __name__ == "__main__":
    cuda = torch.device("cuda:0")
    # 实例化model对象
    my_model = My_Model().to(cuda)
    # 随机产生数据
    my_data = torch.randn(5, 3).to(cuda)
    print("mydata shape", my_data.shape)
    # 数据经过神经网络模型训练
    output = my_model.forward(my_data)
    print("output shape", output.shape)
    # 计算模型参数
    # 计算每层每个神经元的w和b个数总和
    summary(my_model, input_size=(3,), batch_size=5)
    # 查看模型参数
    print("模型参数")
    for name, parameter in my_model.named_parameters():
        print(name, parameter)

结果:

mydata shape torch.Size([5, 3])
output shape torch.Size([5, 2])
----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Linear-1                     [5, 3]              12
            Linear-2                     [5, 2]               8
            Linear-3                     [5, 2]               6
================================================================
Total params: 26
Trainable params: 26
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.00
Forward/backward pass size (MB): 0.00
Params size (MB): 0.00
Estimated Total Size (MB): 0.00
----------------------------------------------------------------
模型参数
linear1.weight Parameter containing:
tensor([[-0.5810,  0.5505,  0.1791],
        [ 0.7054,  0.1916,  0.1438],
        [-0.2873, -0.6783,  0.1165]], device='cuda:0', requires_grad=True)
linear1.bias Parameter containing:
tensor([-0.2477,  0.3324,  0.3270], device='cuda:0', requires_grad=True)
linear2.weight Parameter containing:
tensor([[ 0.1969, -0.9622,  0.1967],
        [-0.2553,  1.1646,  1.0402]], device='cuda:0', requires_grad=True)
linear2.bias Parameter containing:
tensor([-0.3502,  0.3354], device='cuda:0', requires_grad=True)
out.weight Parameter containing:
tensor([[-0.0446,  0.2801],
        [-0.2462,  0.0301]], device='cuda:0', requires_grad=True)
out.bias Parameter containing:
tensor([ 0.4709, -0.3597], device='cuda:0', requires_grad=True)

(二)、总结

(1)、模型参数计算

1.以第一个隐层为例:该隐层有3个神经元,每个神经元的参数为:4个(w1,w2,w3,b1),所以一共用3x4=12个参数。

2.输入数据和网络权重是两个不同的事儿!对于初学者理解这一点十分重要,要分得清。

在这里插入图片描述

(2)、模型搭建
<一>、神经网络的搭建方法
  • 定义继承自nn.Module的模型类
  • 在__init__方法中定义网络中的层结构
  • 在forward方法中定义数据传输方式
<二>、网络参数量的统计方法

统计每一层种权重w和偏置b的数量

(3)、优缺点
  • 优点
    • 精度高,性能优于其他的机器学习算法,甚至在某些领域超过了人类
    • 可以近似任意的非线性函数随之计算机硬件的发展
    • 近年来在学界和业界受到了热捧,有大量的框架和库可供调
  • 缺点
    • 黑箱,很难解释模型是怎么工作的
    • 训练时间长,需要大量的计算资源
    • 网络结构复杂,需要调整超参数
    • 部分数据集上表现不佳,容易发生过拟合

5、损失函数

(一)、简介

在深度学习中, 损失函数是用来衡量模型参数的质量的函数, 衡量的方式是比较网络输出和真实输出的差异。

在不同文献中名称不同:损失函数(loss function)、代价函数(cost function)、目标函数(objective function)、误差函数(error function)

(二)、分类任务损失函数–多分类损失

在多分类任务通常使用softmax将logits转换为概率的形式,所以多分类的交叉熵损失也叫做softmax损失,它的计算方法是:
L = − ∑ i = 1 n y i l o g ( S ( f θ ( X i ) ) ) y 是样本 x 属于某一个类别的真实概率 f ( x ) 是样本属于某一类别的预测分数 S 是 s o f t m a x 激活函数,将属于某一个类别的预测分数转化成概率 L 用来衡量真实值 y 和预测值 f ( x ) 之间差异性的损失结果 L=-\sum^n_{i=1}y_ilog(S(f_\theta(X_i)))\\ y是样本x属于某一个类别的真实概率\\ f(x)是样本属于某一类别的预测分数\\ S是softmax激活函数,将属于某一个类别的预测分数转化成概率\\ L用来衡量真实值y和预测值f(x)之间差异性的损失结果 L=i=1nyilog(S(fθ(Xi)))y是样本x属于某一个类别的真实概率f(x)是样本属于某一类别的预测分数Ssoftmax激活函数,将属于某一个类别的预测分数转化成概率L用来衡量真实值y和预测值f(x)之间差异性的损失结果

API:

nn.CrossEntropyLoss()

例如:

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)

(三)、分类任务损失函数–二分类损失

在处理二分类任务时,我们不再使用softmax激活函数,而是使用sigmoid激活函数,那损失函数也相应的进行调整,使用二分类的交叉熵损失函数:
L = − y l o g y ^ − ( 1 − y ) l o g ( 1 − y ^ ) y 是样本 x 属于某个类别的真实概率 y ^ 是样本属于某一类别的预测概率 L 用来衡量真实值 y 与预测值 y ^ 之间差异性的损失结果 L=-ylog\hat y-(1-y)log(1-\hat y)\\ y是样本x属于某个类别的真实概率\\ \hat y是样本属于某一类别 的 预测概率\\ L用来衡量真实值y与预测值\hat y之间差异性的损失结果 L=ylogy^(1y)log(1y^)y是样本x属于某个类别的真实概率y^是样本属于某一类别的预测概率L用来衡量真实值y与预测值y^之间差异性的损失结果

API:

nn.BCELoss()

例如:

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损失

**Mean absolute loss(MAE)**也被称为L1 Loss,是以绝对误差作为距离。损失函数公式:
L = 1 n ∑ i = 1 n ∣ y i − f θ ( x i ) ∣ y 是样本 x 属于某一个类别的真实概率 f ( x ) 是样本属于某一类别的预测分数 L 用来衡量真实值 y 和预测值 f ( x ) 之间差异性的损失结果 L=\frac{1}{n}\sum^n_{i=1}|y_i-f_\theta(x_i)|\\ y是样本x属于某一个类别的真实概率\\ f(x)是样本属于某一类别的预测分数\\ L用来衡量真实值y和预测值f(x)之间差异性的损失结果 L=n1i=1nyifθ(xi)y是样本x属于某一个类别的真实概率f(x)是样本属于某一类别的预测分数L用来衡量真实值y和预测值f(x)之间差异性的损失结果
特点:

  • 由于L1 loss具有稀疏性,为了惩罚较大的值,因此常常将其作为正则项添加到其他loss中作为约束
  • L1 loss的最大问题是梯度在零点不平滑,导致会跳过极小值

API:

nn.L1Loss()

例如:

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损失

**Mean Squared Loss/ Quadratic Loss(MSE loss)**也被称为L2 loss,或欧氏距离,它以误差的平方和的均值作为距离损失函数公式:
L = 1 n ∑ i = 1 n ( y i − f θ ( x i ) ) 2 y 是样本 x 属于某一个类别的真实概率 f ( x ) 是样本属于某一类别的预测分数 L 用来衡量真实值 y 和预测值 f ( x ) 之间差异性的损失结果 L=\frac{1}{n}\sum^n_{i=1}(y_i-f_\theta(x_i))²\\ y是样本x属于某一个类别的真实概率\\ f(x)是样本属于某一类别的预测分数\\ L用来衡量真实值y和预测值f(x)之间差异性的损失结果 L=n1i=1n(yifθ(xi))2y是样本x属于某一个类别的真实概率f(x)是样本属于某一类别的预测分数L用来衡量真实值y和预测值f(x)之间差异性的损失结果
特点:

  • L2 loss也常常作为正则项
  • 当预测值与目标值相差很大时,梯度容易爆炸4

API:

nn.MSELoss()

例如:

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)

(六)、回归任务损失函数–Smooth L1损失

smooth L1说的是光滑之后的L1。损失函数公式:
s m o o t h L 1 ( x ) = { 0.5 x 2 , i f    ∣ x ∣ < 1 ∣ x ∣ − 0.5 , o t h e r w i s e x = y − f ( x ) 为真实值和预测值的差值 y 是样本 x 属于某一个类别的真实概率 f ( x ) 是样本属于某一类别的预测分数 smooth_{L1}(x)=\left\{\begin{matrix}0.5x^2,if~~|x|<1\\|x|-0.5,otherwise \end{matrix}\right.\\ x=y - f(x)为真实值和预测值的差值\\ y是样本x属于某一个类别的真实概率\\ f(x)是样本属于某一类别的预测分数\\ smoothL1(x)={0.5x2if  x<1x0.5otherwisex=yf(x)为真实值和预测值的差值y是样本x属于某一个类别的真实概率f(x)是样本属于某一类别的预测分数
该函数实际上就是一个分段函数

  • 在[-1,1]之间实际上就是L2损失,这样解决了L1的不光滑问题

  • 在[-1,1]区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题

API:

nn.SmoothL1Loss()

例如:

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)

6、网络优化方法

(一)、梯度下降算法

梯度下降法是一种寻找使损失函数最小化的方法。从数学上的角度来看,梯度的方向是函数增长速度最快的方向,那么梯度的反方向就是函数减少最快的方向
w i j n e w = w i j o l d − α ∂ L ∂ w i j α 是学习率 如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本 如果学习率太大,那就有可能直接跳过最优解,进入无限的训练中 解决的方法就是,学习率也需要随着训练的进行而变化 w^{new}_{ij}=w^{old}_{ij}-\alpha \frac{\partial L}{\partial w_{ij}}\\ \alpha是学习率\\ 如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本\\ 如果学习率太大,那就有可能直接跳过最优解,进入无限的训练中\\ 解决的方法就是,学习率也需要随着训练的进行而变化 wijnew=wijoldαwijLα是学习率如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本如果学习率太大,那就有可能直接跳过最优解,进入无限的训练中解决的方法就是,学习率也需要随着训练的进行而变化
在进行模型训练时,有三个基础的概念:

1.Epoch: 使用全部数据对模型进行以此完整训练,训练轮次

2.Batch_size: 使用训练集中的小部分样本对模型权重进行以此反向传播的参数更新,每次训练每批次样本数量

3.Iteration: 使用一个 Batch 数据对模型进行一次参数更新的过程

假设数据集有 50000 个训练样本,现在选择 Batch Size = 256 对模型进行训练。

  • 每个 Epoch 要训练的图片数量:50000
  • 训练集具有的 Batch 个数:50000/256+1=196
  • 每个 Epoch 具有的 Iteration 个数:196
  • 10个 Epoch 具有的 Iteration 个数:1960

在深度学习中,梯度下降的几种方式的根本区别就在于 Batch Size不同.

梯度下降方式Training Set SizeBatch SizeNumber of Batches
BGDNN1
SGDN1N
Mini-BatchNBN/B + 1

注意:

  • B:Batch,在1~N之间的数字,不包括1和N

  • 上表中 Mini-Batch 的 Batch 个数为 N / B + 1 是针对未整除的情况。整除则是 N / B

(二)、反向传播算法(BP算法)

我再看看

(三)、梯度下降优化方法

(1)、指数加权平均

指数移动加权平均则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。

计算公式:
S t = {                                   Y 1 , t = 0 β ∗ S t − 1 + ( 1 − β ) ∗ Y t , t > 0 S t 表示指数加权平均值 Y t 表示 t 时刻的值 β 调节权重系数,该值越大平均数越平缓 S_t=\left\{\begin{matrix}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Y_1,t=0\\\beta * S_{t-1}+(1-\beta)*Y_t,t>0 \end{matrix}\right.\\ S_t表示指数加权平均值\\ Y_t表示t时刻的值\\ \beta调节权重系数,该值越大平均数越平缓 St={                                 Y1t=0βSt1+(1β)Ytt>0St表示指数加权平均值Yt表示t时刻的值β调节权重系数,该值越大平均数越平缓
温度

import torch
import matplotlib.pyplot as plt

ELEMENT_NUMBER = 30
# 1. 实际平均温度
def test01():
    # 固定随机数种子
    torch.manual_seed(0)
    # 产生30天的随机温度
    temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10
    print(temperature)
    # 绘制平均温度
    days = torch.arange(1, ELEMENT_NUMBER + 1, 1)
    plt.plot(days, temperature, color='r')
    plt.scatter(days, temperature)
    plt.show()

在这里插入图片描述

加权平均温度

# 2. 指数加权平均温度
def test02(beta=0.9):
    torch.manual_seed(0) # 固定随机数种子
    temperature = torch.randn(size=[ELEMENT_NUMBER,]) * 10 # 产生30天的随机温度
    exp_weight_avg = []
    for idx, temp in enumerate(temperature, 1):  # 从下标1开始
        # 第一个元素的的 EWA 值等于自身
        if idx == 1:
            exp_weight_avg.append(temp)
            continue
        # 第二个元素的 EWA 值等于上一个 EWA 乘以 β + 当前气温乘以 (1-β)
        new_temp = exp_weight_avg[idx - 2] * beta + (1 - beta) * temp
        exp_weight_avg.append(new_temp)
    days = torch.arange(1, ELEMENT_NUMBER + 1, 1)
    plt.plot(days, exp_weight_avg, color='r')
    plt.scatter(days, temperature)
    plt.show()

在这里插入图片描述

  • 指数加权平均绘制出的气氛变化曲线更加平缓。
  • β 的值越大,则绘制出的折线越加平缓,波动越小。
(2)、动量算法–Momentum*

梯度计算公式: D t = β ∗ S t − 1 + ( 1 − β ) ∗ W t S t − 1 表示历史梯度移动加权平均值 W t 表示当前时刻的梯度值 D t 为当前时刻的指数加权平均梯度值 β 为权重系数 公式修改为: w i j n e w = w i j o l d − α D t α 是学习率 梯度计算公式:D_t=\beta*S_{t-1}+(1-\beta)*W_t\\ S_{t-1}表示历史梯度移动加权平均值\\ W_t表示当前时刻的梯度值\\ D_t为当前时刻的指数加权平均梯度值\\ \beta为权重系数\\ 公式修改为:w^{new}_{ij}=w^{old}_{ij}-\alpha D_t\\ \alpha是学习率 梯度计算公式:Dt=βSt1+(1β)WtSt1表示历史梯度移动加权平均值Wt表示当前时刻的梯度值Dt为当前时刻的指数加权平均梯度值β为权重系数公式修改为:wijnew=wijoldα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

Monmentum 优化方法是如何一定程度上克服 “平缓”、”鞍点” 的问题呢?

  • 当处于鞍点位置时,由于当前的梯度为 0,参数无法更新。但是 Momentum 动量梯度下降算法已经在先前积累了一些梯度值,很有可能使得跨过鞍点。
  • 由于 mini-batch 普通的梯度下降算法,每次选取少数的样本梯度确定前进方向,可能会出现震荡,使得训练时间变长。Momentum 使用移动加权平均,平滑了梯度的变化,使得前进方向更加平缓,有利于加快训练过程。

API:

torch.optim.SGD([w], lr=0.01, momentum=0.9)

'''
Params (iterable):要优化或定义字典的参数的可迭代对象参数组
lr (float):学习率
momentum(float,可选):动量因子(默认:0)
weight_decay (float,可选):权重衰减(L2惩罚)(默认值:0)
dampening (float,可选):对动量的阻尼(默认:0)
nesterov (bool,可选):启用nesterov动量(默认:False)
'''

例如:

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.99000000952: 梯度w.grad: 0.9900000095, 更新后的权重:0.9711000323
(3)、AdaGrad

AdaGrad 通过对不同的参数分量使用不同的学习率,AdaGrad 的学习率总体会逐渐减小

计算步骤如下:

  • 初始化学习率α、初始化参数θ、小常数 σ = 1e-6
  • 初始化梯度累计变量s=0
  • 从训练集中采样m个样本的小批量,计算梯度
  • 累积平方梯度s_i=s_i-1 + g ⊙ g,⊙表示各个分量相乘

学习率 α 计算方式如下: α = α s + σ s 更新方式: s i = s i − 1 + g ⊙ g 梯度: g = ∂ L ∂ w i j 参数更新公式为: w i j n e w = w i j o l d − α s + σ ∗ g 学习率\alpha计算方式如下:\alpha=\frac{\alpha}{\sqrt{s}+\sigma}\\ s更新方式:s_i=s_{i-1}+g⊙g\\ 梯度:g=\frac{\partial L}{\partial w_{ij}}\\ 参数更新公式为:w^{new}_{ij}=w^{old}_{ij}-\frac{\alpha}{\sqrt{s}+\sigma}*g 学习率α计算方式如下:α=s +σαs更新方式:si=si1+gg梯度:g=wijL参数更新公式为:wijnew=wijolds +σαg

重复计算,就可以完成网络训练。

AdaGrad 缺点是可能会使得学习率过早、过量的降低,导致模型训练后期学习率太小,较难找到最优解。

API:

torch.optim.Adagrad([w], lr=0.01)

'''
params (iterable):要优化或定义字典的参数的可迭代对象参数组
lr(float,可选):学习率(默认为1e-2)
lr_decay (float,可选):学习率衰减(默认为0)
weight_decay (float,可选):权重衰减(L2惩罚)(默认值:0)
eps(float,可选):将项添加到分母以改进,数值稳定性(默认:1e-10)
'''

例如:

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.9900002: 梯度w.grad: 0.990000, 更新后的权重:0.982965
(4)、RMSProp

RMSProp 优化算法是对 AdaGrad 的优化. 最主要的不同是,其使用指数移动加权平均梯度替换历史梯度的平方和。其计算过程如下:

  • 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
  • 初始化参数 θ
  • 初始化梯度累计变量 s
  • 从训练集中采样 m 个样本的小批量,计算梯度 g
  • 使用指数移动平均累积历史梯度

历史梯度计算: s i = β ∗ s i − 1 + ( 1 − β ) g ⊙ g 学习率 α 计算公式: α = α s + σ 参数更新公式: w i j n e w = w i j o l d − α s + σ ∗ g β 调节权重系数,该值越大平均数越平缓 历史梯度计算:s_i=\beta*s_{i-1}+(1-\beta)g⊙g\\ 学习率\alpha计算公式:\alpha=\frac{\alpha}{\sqrt{s}+\sigma}\\ 参数更新公式:w^{new}_{ij}=w^{old}_{ij}-\frac{\alpha}{\sqrt{s}+\sigma}*g\\ \beta调节权重系数,该值越大平均数越平缓 历史梯度计算:si=βsi1+(1β)gg学习率α计算公式:α=s +σα参数更新公式:wijnew=wijolds +σαgβ调节权重系数,该值越大平均数越平缓

API:

torch.optim.RMSprop([w], lr=0.01, alpha=0.9)

'''
params (iterable):要优化或定义字典的参数的可迭代对象参数组
lr(float,可选):学习率(默认为1e-2)
momentum(float,可选):动量因子(默认:0)
alpha(float,可选):平滑常数(默认:0.99)
eps(float,可选):将项添加到分母以改进,数值稳定性(默认:1e-8)
centered(bool,可选):如果"True",计算居中的RMSProp;梯度通过方差的估计归一化
weight_decay (float,可选):权重衰减(L2惩罚)(默认值:0)
'''

例如:

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对应这beta
    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.9683772: 梯度w.grad: 0.968377, 更新后的权重:0.945788
  • RMSProp 是一种自适应学习率优化算法。
  • RMSProp 抑制振荡,从而更快地达到最小值。
  • RMSprop 充当移动平均筛选器,在更新学习率时考虑以前的梯度
  • RMSprop 解决了 RProp 无法处理小批量的问题
(5)、Adam*
  • Adam优化算法(Adaptive Moment Estimation,自适应矩估计)将 Momentum 和 RMSProp 算法结合在一起。
  • 修正梯度: 使⽤梯度的指数加权平均
  • 修正学习率: 使⽤梯度平⽅的指数加权平均

历史梯度计算: s i = β 1 ∗ s i − 1 + ( 1 − β 1 ) g ⊙ g 学习率 α 计算公式: α = α s + σ 梯度计算公式: D i = β 2 ∗ S i − 1 + ( 1 − β 2 ) ∗ W i S i − 1 表示历史梯度移动加权平均值 W i 表示当前时刻的梯度值 D i 为当前时刻的指数加权平均梯度值 β 为权重系数 参数更新公式: w i j n e w = w i j o l d − α s + σ ∗ D i 历史梯度计算:s_i=\beta_1*s_{i-1}+(1-\beta_1)g⊙g\\ 学习率\alpha计算公式:\alpha=\frac{\alpha}{\sqrt{s}+\sigma}\\ 梯度计算公式:D_i=\beta_2*S_{i-1}+(1-\beta_2)*W_i\\ S_{i-1}表示历史梯度移动加权平均值\\ W_i表示当前时刻的梯度值\\ D_i为当前时刻的指数加权平均梯度值\\ \beta为权重系数\\ 参数更新公式:w^{new}_{ij}=w^{old}_{ij}-\frac{\alpha}{\sqrt{s}+\sigma}*D_i\\ 历史梯度计算:si=β1si1+(1β1)gg学习率α计算公式:α=s +σα梯度计算公式:Di=β2Si1+(1β2)WiSi1表示历史梯度移动加权平均值Wi表示当前时刻的梯度值Di为当前时刻的指数加权平均梯度值β为权重系数参数更新公式:wijnew=wijolds +σαDi

API:

torch.optim.Adam([w], lr=0.01, betas=(0.9, 0.99))

'''
params (iterable):要优化或定义字典的参数的可迭代对象参数组
lr(float,可选):学习率(默认为1e-3)
beta (Tuple[float, float],可选):用于计算的系数梯度及其平方的运行平均值(默认值:(0.9,0.999))
eps(float,可选):将项添加到分母以改进数值稳定性(默认:1e-8)
weight_decay (float,可选):权重衰减(L2惩罚)(默认值:0)
amsgrad (bool,可选):是否使用它的amsgrad变体,(默认值:False)
'''

例如:

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.9900002: 梯度w.grad: 0.990000, 更新后的权重:0.980003

(四)、学习率衰减方法

(1)、等间隔学习率衰减

在这里插入图片描述

API:

optim.lr_scheduler.StepLR(optimizer, step_size=50, gamma=0.5)
# 等间隔调整学习率
# 调整方式:lr = lr * gamma

'''
optimizer(优化器):包装优化器。
step_size (int):学习率衰减的周期。
gamma (float):学习率衰减的乘法因子。默认值:0.1。
last_epoch (int):最后一个epoch的索引。默认值:1。
verbose (bool):如果是"True",则打印一条消息到标准输出每一个更新。默认值:"False"。
'''

例如:

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()
(2)、指定间隔学习率衰减

在这里插入图片描述

API:

optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones, gamma=0.5)
# 指定间隔,调整学习率
# 调整方式:lr = lr * gamma

'''
optimizer(优化器):包装优化器。
milestone (list): epoch索引的列表。肯定在增加。
gamma (float):学习率衰减的乘法因子。默认值:0.1。
last_epoch (int):最后一个epoch的索引。默认值:1。
verbose (bool):如果"True",则打印一条消息到标准输出,每一个更新。默认值:"False"。
'''

例如:

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()
(3)、按指数学习率衰减

在这里插入图片描述

API:

optim.lr_scheduler.ExponentialLR(optimizer, gamma=gamma)
# 按指数衰减,调整学习率
# 调整方式:lr = lr * gamma

'''
optimizer(优化器):包装优化器。
gamma (float):学习率衰减的乘法因子。
last_epoch (int):最后一个epoch的索引。默认值:1。
verbose (bool):如果"True",则打印一条消息到标准输出,每一个更新。默认值:"False"。
'''

例如:

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()

7、正则化方法

在设计机器学习算法时希望在新样本上的泛化能力强,许多机器学习算法都采用的策略来减小测试误差,这些策略都被统称为正则化。神经网络的强大能力经常会遇到过拟合,所以需要使用不同形式的正则化策略。目前在深度学习中使用较多的策略有范数惩罚,DropOut,特殊的网络层等

(一)、Dropout正则化

训练神经网络中模型参数较多,在数据量不足的请客下,很容易过拟合。Dropout(随机失活)是一个简单有效的正则化方法。

  • 在训练过程中,Dropout的实现是让神经元以超参数p的概率停止工作或者激活被置为0,未被置为0的进行缩放,缩放比例为1/(1-p)。训练过程可以认为是对完整 的神经网络的一些子集进行训练,每次基于输入数据只更新子网络的参数。
  • 在测试过程中,随机失活不起作用。

API:

nn.Dropout(p=0.4)
# 让神经元以p概率停止工作

'''
P:元素归零的概率。默认值:0.5
inplace:如果设置为“True”,将就地执行此操作。默认值:"False"
'''

例如:

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)
    
'''
上述代码将 Dropout 层的概率 p 设置为 0.4,此时经过 Dropout 层计算的张量中就出现了很多 0 , 未变为0的按照(1/(1-0.4))进行处理。
'''

结果:

未失活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层)

f ( x ) = λ x − E ( x ) V a r ( x ) + ϵ + β λ 和 β 是可学习的参数,它相当于标准化后的值做了一个线性变化, λ 为系数, β 为偏置 ϵ 一般是 1 e − 5 ,避免分母为 0 E ( x ) 表示变量的均值 V a r ( x ) 表示变量的方差, σ 2 f(x)=\lambda\frac{x-E(x)}{\sqrt{Var(x)+\epsilon}}+\beta\\ \lambda和\beta是可学习的参数,它相当于标准化后的值做了一个线性变化,\lambda为系数,\beta为偏置\\ \epsilon一般是1e-5,避免分母为0\\ E(x)表示变量的均值\\ Var(x)表示变量的方差,\sigma^2 f(x)=λVar(x)+ϵ xE(x)+βλβ是可学习的参数,它相当于标准化后的值做了一个线性变化,λ为系数,β为偏置ϵ一般是1e5,避免分母为0E(x)表示变量的均值Var(x)表示变量的方差,σ2

批量归一化层在计算机视觉领域使用较多

(三)、小结

  • 正则化的作用:

缓解过拟合

  • 随机失活Dropout策略

让神经元以超参数p的概率停止工作或者激活被置为0

  • BN层是指什么?

对数据标准化,再对数据重构(缩放+平移)


原文地址:https://blog.csdn.net/egglikeFe/article/details/143723064

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!