卷积神经网络CNN的实战知识

文章目录


经典神经网络超详细介绍

一、构建神经网络

卷积神经网络(Convolutional Neural Network,CNN)是一种前馈神经网络,最早可追溯到1986年的BP算法。其主要的三大网络结构有卷积层(Conv2d)、池化层(MaxPool2d)、全连接层(Linear)。相比其他深度,具有稀疏连接和权值共享两大特点可以用更少的参数,获得更高的性能。

下面是一个比较简单的卷积神经网络模型,对手写输入数据(MNIST)实现10分类。
在这里插入图片描述

代码实现上幅图的卷积神经网络:

import torch
import torch.nn as nn
import torch.nn.functional as F


class CNNNet(nn.Module):
    """类对象:网络模型"""
    def __init__(self):
        """函数功能:网络层定义"""
        super(CNNNet, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1)         # (二维)卷积层1
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)                                      # (二维)最大池化层1
        self.conv2 = nn.Conv2d(in_channels=16, out_channels=36, kernel_size=3, stride=1)        # (二维)卷积层2
        self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)                                      # (二维)最大池化层2
        self.fc1 = nn.Linear(1296, 128)                                                         # 全连接层1
        self.fc2 = nn.Linear(128, 10)                                                           # 全连接层2

    def forward(self, x):
        """函数功能:前向传播"""
        x = self.pool1(F.relu(self.conv1(x)))       # 卷积层1 -> 激活函数(relu) -> 池化层1
        x = self.pool2(F.relu(self.conv2(x)))       # 卷积层2 -> 激活函数(relu) -> 池化层2
        x = x.view(-1, 36*6*6)                      # 改变数组形状(参数-1表示自动调整size)
        x = F.relu(self.fc2(F.relu(self.fc1(x))))   # 全连接层1 -> 激活函数(relu) -> 全连接层2 -> 激活函数(relu)
        return x


device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')     # 检测是否有可用的GPU,有则使用,否则使用CPU。
net = CNNNet()              # 网络模型实例化
net = net.to(device)        # 将构建的张量或者模型分配到相应的设备上。

Pytorch之(__ call__)、( __ init__)、(forward)


二、前向传播(Forward propagation)

函数定义:def forward(self, x):
函数调用:outputs = net(inputs)
作用:实现信息的前向传导。
过程:将数据从输入层,依次经过多个隐藏层(如果有),最终到达输出层。其中,数据每经过一个网络层,其节点输出的值所代表的信息层次就越高阶和概括。

例如:人脸识别。

  • (1)输入数据中的每一个元素只表示图像的灰度值信息。
  • (2)经过一个网络层处理后,可能第二层中的每个节点输出的值代表的是各种不同的边缘信息。
  • (3)然后第三层输出值代表的是边缘信息组合成的更高阶部位信息。比如鼻子、眼睛等。
  • (4)最后输出层代表的就是系统对输入数据的判断结果,即是否是一张人脸。
    备注:节点中输出的值是通过与其相连的前一层中所有的节点输出值的加权求和处理后的结果。

2.1 卷积层(torch.nn.Conv2d)

torch.nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1, padding=0)

2.1.1 卷积运算 —— 提取特征

在这里插入图片描述
输入和卷积核都是张量,卷积运算就是用卷积分别乘以输入张量中的每个元素,然后输出一个代表每个输入信息的张量。其中,卷积核(kernel)又叫权重过滤器,简称为过滤器(filter)。


卷积运算公式
假设输入图像大小是 input x input,卷积核大小是 kernel,补0的圈数为 padding,步长为 stride,卷积后输出特征图大小为 output x output。
在这里插入图片描述
另外,附上分别计算图像的高和宽公式:
在这里插入图片描述
备注:如果输入图像大小为:mxn,则可以通过【裁剪】、【拼接】or【填充】的方法,转换成 mxm 或 nxn 的图像。

2.1.2 稀疏连接 —— 降低了模型的复杂度,防止过拟合

【输入条件】
(1)输入单通道图像大小为(8 * 8);(2)卷积核大小为(3 * 3)。
【分析说明】

  • 11、【高维空间 - 卷积运算】取输入图像的(3 x 3)矩阵与卷积核进行卷积运算,得到计算结果: 0 * 0 + 1 * 1 + 3 * 2 + 4 * 3 = 19。然后通过滑窗的方式,循环计算得到输出特征图(2 * 2)。
  • 22、特征图中的每个元素(也就是神经元)只与上一层的所有神经元中的9个进行连接。
  • 结论:相比于全连接,稀疏连接很大程度上缩减了参数个数,同时也一定程度上避免了模型的过拟合。
    在这里插入图片描述

2.1.3 权值共享 —— 减少权值数量,使得网络易于优化

在这里插入图片描述
输入条件

  • (1)输入彩色图像 Input = 8 * 8 * 3;其中,3表示RGB三个通道
  • (2)卷积层的卷积核个数为 Kernels = 5;每一个 Kernel 都有3个channel(3 * 3 * 3),即共有(3 * 3 * 3 )* 5个特征图。

卷积运算(多通道)

  • 11、每个 Kernel 的 (3 个channle)RGB 分别和 Input 对应的(3 个channle) RGB 进行卷积运算,进而得到3个(6 * 6)的feature map。
  • 22、将 3 个(6 * 6)的feature map逐元素相加(即通道融合)得到 1 个feature map,然后该 feature map 的每个元素都加上其对应的偏置 b(同一个 Kernel 对应的 3 个channle共享一个偏置),最后得到该 Kernel 的 Feature Map。同理,其余(M-1)个 Kernel 也进行上述操作,进而得到(6 * 6 * 5) 个Feature Maps。
  • 33、最终该卷积层的输出 Feature Maps = 5个(即最终输出的channel数等于 Kernel 的个数)。
    结论
  • (1)输出的同一张特征图 Feature Map 上的所有元素共享同一个卷积核 Kernel ,即共享一个权重。不同特征图对应不同的卷积核。
  • (2)权重共享大大减少了模型的训练参数。权重共享意味着当前隐藏层中的所有神经元都在检测图像不同位置处的同一个特征,即检测特征相同。因此也将输入层到隐藏层的这种映射称为特征映射。
  • 备注:多个卷积核Kernel,是为了识别更多的边缘特征。最后通过全连接层,把该输入图像的所有特征整合到一起,输出一个值(猫 / 狗)。
    在这里插入图片描述

2.1.4 权值初始化 —— 决定算法是否收敛

深度学习为何要初始化?
   —— 深度学习算法一般采用迭代方法,而且参数多,层数多,所以初始化会影响算法结果。而机器学习很少有采用迭代的方法进行优化,影响小。
 
初始化对训练的影响?
   —— 初始化能决定算法是否收敛。(1)若参数太大,将导致在前向传播或反向传播中产生爆炸的值;(2)若参数太小,将导致信息丢失;(3)而适当的参数初始化,能加快收敛速度。

偏置初始化:采用启发式的方法挑选常数,进而设置每个单元的偏置。
权重初始化:包括零值初始化、随机初始化、均匀分布初始化、正态分布初始化和正交分布初始化等。实验表明,正态分布初始化(高斯分布)、正交分布初始化、均匀分布初始化的初始值能带来更好地效果。

2.1.5 感受野 —— 在原始图像上映射的区域大小

感受野(Receptive Field,RF):每一层输出特征图上的像素点在原始图像上映射的区域大小。
在这里插入图片描述
注意1:感受野的计算不考虑" 边界填充 " 。
注意2:最后一层(卷积层或池化层)输出特征图的感受野大小等于卷积核的大小。
注意3:第(i)层卷积层的感受野大小和卷积核大小,与步长、第(i+1)层的感受野大小都有关。

详细计算过程:根据卷积得到的最后一层感受野,反算前一层感受野,然后循环且逐层的传递到第一层。 公式如下:
在这里插入图片描述

  • 【举例1】:两层3 * 3的卷积核,卷积操作之后的感受野是5 * 5。其中,卷积核的步长为1、padding为0。
    计算如下:(1-1)* 1+3 = 3 => (3-1)*1+3 = 5
    在这里插入图片描述
  • 【举例2】:三层3 * 3卷积核,卷积操作之后的感受野是7 * 7。其中,卷积核的步长为1,padding为0。

计算如下:(1-1)* 1+3 = 3 => (3-1)*1+3 = 5 => (5-1)*1+3 = 7
在这里插入图片描述

  • 【举例3】:神经网络
    在这里插入图片描述

❤️ 感受野与卷积核的关系,是否卷积核越大越好?

  • 如果堆叠3个3 * 3的卷积核,并且保持步长为1,其感受野等于7 * 7;那么这和直接使用7 * 7卷积核的结果是一样的,为什么要如此麻烦呢?
    在这里插入图片描述

2.1.6 边界填充Padding —— 图像尺寸对齐,保持边界信息

torch.nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1, padding=1)
padding=1:表示在输入图像的边界填充一圈全为0的元素。
作用:

  • (1)图像尺寸对齐。当输入图像与卷积核不匹配时,或卷积核超过图像边界时,可以采用边界填充(Padding)的方法。即对图像进行扩展,扩展区域补零。
  • (2)保持边界信息,防止信息丢失。如果没有加padding的话,输入图片最边缘的像素点信息只会被卷积核操作一次,但是图像中间的像素点会被扫描很多次,那么就会在一定程度上降低边界信息的参考程度。
    在这里插入图片描述

假设输入图像大小是 input x input,卷积核大小是 kernel,步长为stride,补0的圈数为padding。
在这里插入图片描述
Python深度学习pytorch神经网络填充和步幅的理解

2.2 激活函数(torch.nn.Sigmoid / ReLU)—— 增加非线性能力

激活函数常用于神经网络的隐含层与输出层节点之间(如:卷积层+激活函数+池化层)。
作用:为神经网络提供非线性建模能力**。而如果没有激励函数,神经网络只能处理线性可分问题。这种情况与最原始的感知机相当,网络的逼近能力将相当有限。

如图A:是线性可分问题。通过一条直线就可以将图像A完全分类,当然这也是最理想、最简单的二分类问题。
如图B:是非常复杂的线性不可分问题。找不到任何一条直线可以将图像B中蓝点和绿点完全分开,而必须圈出一个封闭曲线。
在这里插入图片描述

深度学习的四种激活函数:PyTorch中激活函数的方法示例在这里插入图片描述
在这里插入图片描述
如何选择激活函数
(1)任选其一:如果搭建的神经网络层数不多。
(2)ReLU:如果搭建的网络层较多,就不宜选择sigmoid、tanh激活函数,因为它们的导数都小于1,尤其是sigmoid的导数在[0, 1/4]之间。根据微积分链式法则,随着层数增加,导数或偏导将指数级变小。所以层数较多的激活函数其导数不宜小于1也不能大于1,大于1将导致梯度爆炸,导数为1最好,而激活函数relu正好满足这个要求。
(3)Softmax:常用于多分类神经网络输出层。一分钟理解softmax函数(超简单)

2.3 池化层(torch.nn.MaxPool2d / AvgPool2d) —— 降采样,增大感受野,防止过拟合。

池化(Pooling),又叫下采样、降采样。

为什么要进行池化操作?:通过卷积层获得的图像特征,理论上可以直接训练分类器(如:Softmax)。但是,这样做很容易导致过拟合现象。

  • 作用: 降采样(减少参数),增大感受野,提高运算速度及减小噪声影响,防止过拟合。
  • 分类: 局部池化(最大池化、均值池化)全局池化(全局最大池化、全局均值池化)
    其中:全局池化通过Pytorch中的自适应池化层来实现。全局池化相比局部池化能减少更多参数,且泛化能力比较好,但唯一的不足之处是收敛速度比较慢。
  • 最大池化(Max Pooling):取Pooling窗口内的最大值作为采样值。
  • 均值池化(Average Pooling):取Pooling窗口内的所有值相加取均值作为采样值。
  • 全局最大池化(Global Max Pooling):取以整个特征图为单位的最大值作为采样值。
  • 全局均值池化(Global Average Pooling):取以整个特征图为单位的所有值相加取均值作为采样值。
  • torch.nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
  • torch.nn.AvgPool2d(kernel_size=2, stride=1, padding=0)
  • torch.nn.AdaptiveMaxPool2d(output_size=(2, 2))
  • torch.nn.AdaptiveAvgPool2d(output_size=(2, 2))
    Python 池化的用法及代碼示例
    在这里插入图片描述

2.4 全连接层(torch.nn.Linear)—— 分类器

torch.nn.Linear(n_hidden, classification_num)

  • 卷积层、池化层和激活函数作用: 将原始数据映射到隐层特征空间来提取特征;
  • 全连接层(Fully Connected Layers,FC)作用: 将学到的 " 分布式特征表示 " 映射到样本标记空间。在整个卷积神经网络中起到 " 分类器 " 的作用。

如图:神经网络在经过卷积、ReLU激活后得到3x3x5的输出,那它是怎么转成1x4096的形式?在这里插入图片描述
很简单,可以理解为在中间做了一个卷积。
在这里插入图片描述
如上图,(1)用【一个3x3x5的 filter】去卷积【激活函数的输出】,得到fully connected layer中【一个神经元的输出 1x1 】 。(2)因为我们有4096个神经元,所以实际上就是用去卷积激活函数的输出【不带偏置】,得到1x4096。因此,全连接层中的每个神经元都可以看成一个不带偏置加权平均的多项式。可以简单写成:y = W * x。

这一步卷积还有一个非常重要的作用,就是把分布式特征representation映射到样本标记空间。简单来说,就是把该输入图像的所有特征整合到一起,输出一个值(猫 / 狗),进而可以大大减少目标位置的不同,而对分类结果带来的影响。
在这里插入图片描述
下面这张图是上面任意一张图的图解。
在这里插入图片描述
由图可得:(1)猫在不同的位置,虽然输出的特征值相同,但是目标在特征图的位置不同。对于电脑来说,可能就是不一样的分类结果。(2)此时全连接层的作用就是,在展平后忽略其空间结构特性。即目标不管在什么位置,只要图像中有这个猫,那么就能判断它是猫。

  • 额外话题:(1)全连接层的结构不适合用于在方位上找patter的任务,例如分割任务(后面的FCN就是将全连接层改成了卷积层)。(2)全连接层有一个很大的缺点:参数过多。所以ResNet、GoogLeNet网络都已经采用全局平均池化取代全连接层来融合学到的特征。(3)参数多导致了模型复杂度提升,容易造成过拟合。
    全连接层详解
    一文带你了解CNN(卷积神经网络)

三、反向传播(Backward propagation)

反向传播:loss.backward()
作用:迭代训练降低损失(loss)。
介绍:是一种与最优化方法(如:梯度下降法)结合使用的,用来训练神经网络的常见方法。

计算过程:通过损失函数计算网络中所有权重参数的梯度,并反馈给优化算法进行梯度(权值)更新。迭代训练 N 次,获得最小损失。
单次反向传播的过程:从离输出层最近的网络层E开始,调整完后网络层D再调整。按照EDCBA依次反向传播,直到所有层都调整一次。

3.1 损失函数(torch.nn.CrossEntropyLoss / MSELoss)—— 衡量模型预测值与真实值之间的差异

损失函数用来衡量模型预测值与真实值之间的差异,损失函数越小说明模型和参数越符合训练样本。任何能够衡量模型预测值与真实值之间差异的函数都可以叫做损失函数。
备注:防止过拟合时添加的正则化项,就是添加在损失函数公式的后面(F = 损失函数 + 正则化项)

常用的损失函数:交叉熵(Cross Entropy)和均方误差(Mean Squared error,MSE),分别对应机器学习中的分类问题和回归问题。

  • (1)分类问题:【交叉熵】 反应两个概率分布的距离(不是欧式距离)。
    交叉熵又称为对数似然损失、对数损失;二分类时还可称为逻辑回归损失。在这里插入图片描述
    其中:c表示损失值;
        n表示样本数量,也就是batchsize;
        x表示预测向量维度,主要是因为需要在输出的特征向量维度上一个个计算并求和;
        y表示真实值,对应x维度上的标签(1 或 0);
        a表示输出的预测概率值(0~1之间,总和为1)。
    torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
  • (2)回归问题:【均方误差】 反应预测值与实际值之间的欧氏距离。
    在这里插入图片描述
    torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')

3.2 正则化(torch.nn.Dropout)—— 提升模型的泛化能力,防止过拟合。

正则化(Regularization):即对减少测试误差的方法的统称。
作用: 降低模型的复杂度,减少模型对训练样本的依赖度,提升模型的泛化能力,防止过拟合。
在这里插入图片描述

思路:在损失函数的后面添加一个惩罚项。
在这里插入图片描述
计算过程:求线性模型的解:即找到一个 θ 值,然后对J(θ)进行求导,使得代价函数J(x)取得最小值(导数为0)。而正则化系数 λ 能控制模型的可学习参数,防止过拟合。
   备注1:常用的惩罚项有L1正则化、L2正则化。
   备注2:在机器学习的线性回归模型中,使用L1正则化得到的模型叫Lasso模型,使用L2正则化得到的模型叫岭回归(Ridge regression)

3.2.1 L1正则化 —— 特征选择

  • L1正则化(L1范数):是指权值向量 W 中各个元素的绝对值之和,通常表示为 ||W||1
  • 特点:产生稀疏权值矩阵,即提取权重值最大的前N个值,常用于特征选择。主要通过将冗余特征的权重值置0,达到直接删除的效果。

3.2.2 L2正则化(权重衰减) —— 抑制损失函数中产生过拟合的参数

  • L2正则化(L2范数):是指权值向量 W 中各个元素的平方和然后再求平方根,通常表示为 ||W||2
  • 特点1:又叫权重衰减;其中,权重即可学习参数;衰减表示抑制模型中产生过拟合的参数,使其趋近于0(而不等于0),影响变小。
  • 特点2:倾向于让所有参数的权值尽可能小。一般认为权值小的模型相对简单,可以避免过拟合。在实践中,如果不关心显式特征选择(哪些特征重要或不重要),L2正则化的性能往往优于L1正则化。

3.2.3 Dropout正则化 —— 随机删除一定比例的神经元

  • Dropout正则化: 即在训练过程中,随机删除一定比例的神经元(比例参数可设置)。
    备注1:一般只在训练阶段使用,测试阶段不使用。
    备注2:一般控制在20% ~ 50%。太低没有效果,太高则会导致模型欠学习。
     
    应用场景:(1)在大型网络模型上效果显著(2)在输入层和隐藏层都使用Dropout(3)当学习率和冲量值较大时:如学习率扩大10 ~ 100倍,冲量值调高到0.9 ~ 0.99。(4)用于限制网络模型的权重时:学习率越大,权重值越大。
  • torch.nn.Dropout(p=0.5) # p=0.5表示随机删除50%的神经元
    在这里插入图片描述

3.3 优化器(torch.optim.SGD / Adam)

optimizer = torch.optim.SGD(model.parameters(), Learning_Rate=lr, momentum=momentum)
优化器(optimizer)作用:通过优化策略(梯度下降)来更新可学习参数(权值W和偏置bias),使得损失函数Loss值逐步降低,输出的模型更接近真实标签。
影响因素:(1)梯度方向(2)学习率
 
常用优化器

  • 00、经典的梯度下降法。
  • 11、梯度下降优化算法:SGD、SGDM、NAG
       缺点:缓解了参数空间的方向问题,但需要新增参数,且对学习率的控制也不太理想。
  • 22、自适应优化算法:AdaGrad(累积梯度平方)、RMSProp(累积梯度平方的滑动平均)、Adam(带动量项的RMSProp)
       而自适应优化算法,学习率不再是一个固定不贬值,它会根据实际情况自动调整以适应环境。

3.3.1 梯度下降 —— 使得loss值逐步降低

梯度清零:optimizer.zero_grad()
梯度更新:optimizer.step()
梯度下降的公式如下图:
在这里插入图片描述
其中:
   θ(t+1)是参数θ在t+1次迭代时需要更新的参数;
   θ(t)是参数θ在第t次迭代时更新的参数值;
   J(θt)是目标函数(损失函数)在θ(t)该点上的梯度;
   lr是学习率,控制参数更新速度以及模型学习速度。

梯度下降:沿着目标函数梯度的反方向搜索极小值。在这里插入图片描述
梯度下降算法原理讲解

  • 经典的梯度下降法:先假设一个学习率,参数沿梯度的反方向移动
  • 计算:每次迭代过程中,采用所有的训练数据的平均损失来近似目标函数。
  • 特点:(1)靠近极值点时收敛速度减慢;(2)可能会" 之字形 "的下降。(3)对学习率非常敏感,难以驾驭;(4)对参数空间的方向没有解决方法。
    在这里插入图片描述
  • 左图一 学习率过小,将导致收敛速度慢,耗时长;
  • 中图二 当学习率取值恰当时,可以收敛到全面最优点(凸函数)或局部最优点(非凸函数)。
  • 右图三 学习率过大,将导致越过极值点(如:在平坦的区域,因梯度接近于0,可能导致训练提前终止)
    备注: 可能出现在迭代过程中保持不变,容易造成算法被卡在鞍点的位置。

3.3.2 SGD、SGDM、NAG

  • SGD(随机梯度下降法)
    计算:每次只使用一个数据样本,去计算损失函数,求梯度,更新参数。
    特点:(1)计算速度快,但是梯度下降慢(2)可能会在最低处两边震荡,停留在局部最优。
  • SGDM(SGM with Momentum:动量梯度下降)
    计算:对已有的梯度进行指数加权平均,然后加上(1 - beta)。即在现有梯度信息的基础上,加入一个惯性。而梯度方向,由之前与现在的梯度方向共同决定。
    优缺点:(1)与SGD相比,训练过程的震荡幅度会变小,速度变快。(2)SGDM速度没Adam快,但泛化能力好。
  • NAG(Nesterov Accelerated Gradient)
    计算:先使用累积的动量计算一次,得到下一次的梯度方向,再把下一个点的梯度方向,与历史累积动量相结合,计算现在这个时刻的累计动量。
    在这里插入图片描述

3.3.3 AdaGrad、RMSProp、Adam

  • AdaGrad(Adaptive Gradient,自适应步长) —— 累积梯度平方
    计算:在现有的(梯度 * 步长)上,添加了一个系数:1 /(历史梯度的平方和,再开根号)。
    优缺点:(1)适合处理稀疏数据,可以对稀疏特征进行大幅更新。(2)学习率单调递减,最终会趋于0,学习提前终止。
  • RMSProp(root mean square prop,梯度平方根)—— 累积梯度平方的滑动平均
    计算:该方法和Adagrad的区别就是分母不一样,使得系数不会因为前几步的梯度太大而导致分母太大,从而导致系数变得太小而走不动。
  • Adam(Adaptive Moment Estimation,自适应估计)—— 利用梯度的一阶、二阶矩估计
    优缺点:Adam是一种在深度学习模型中用来替代随机梯度下降的优化算法。它是SGDM和RMSProp算法的结合,训练速度快,泛化能力不太行。
    多种优化算法(理论)

以下是MNIST数据集在多种优化器下的收敛效果对比图。
在这里插入图片描述
多种优化算法的代码实现与收敛效果对比

3.4 学习率(Learning Rate)—— 控制可学习参数的更新速度

optimizer = torch.optim.SGD(model.parameters(), Learning_Rate=lr, momentum=momentum)
学习率是什么?

  • (1)神经网络通过梯度下降+反向传播进行参数更新。主要流程:将可学习参数(权值W和偏置bias)反向传播给网络层,使得损失函数Loss值逐步降低,输出的模型更接近真实标签。
  • (2)神经网络在参数最优化过程中,往往需要训练多轮数据,而每论训练得到的可学习参数,需要通过学习率进行控制。即当前得到的可学习参数,其可信度应以多少为准,参数更新后进行下一轮训练时,才可以得到更小的损失loss?
  • 学习率(learning rate,lr),也称为学习步长。主要用于控制可学习参数的更新速度(模型学习速度)。
  • 一般情况下,学习率随着epoch的增大而逐渐减小,可以达到更好的训练效果。
    (1)刚开始训练时:学习率以 0.01 ~ 0.001 为宜。
    (2)学习率太大:【学习速度】快;【适用范围】模型刚开始训练时;【缺点】易震荡;模型不收敛;
    (3)学习率太小:【学习速度】慢;【适用范围】模型刚开始训练时;【缺点】局部收敛,找不到最优解;

Pytorch官网:How to adjust learning rate
torch.optim.lr_scheduler :基于epoch训练次数来调整学习率(learning rate)的方法。
torch.optim.lr_scheduler.ReduceLROnPlateau :基于训练中某些测量值使学习率动态下降的方法(不依赖epoch更新)。
 
注意1:在 PyTorch 1.1.0 之前的版本,学习率的调整应该放在optimizer更新之前(即 scheduler.step()放在optimizer.step()之前),其 learning rate schedule 的第一个值将会被跳过。
注意2:如果 PyTorch 1.1.0 之前的版本,想要移植到 1.1.0及之后的版本运行,需要检查是否将scheduler.step()放在了optimizer.step()之前。

3.4.1 自定义更新学习率

for epoch in range(num_epoch):
    if epoch % 5 == 0:      
        optimizer.param_groups[0]['lr'] *= 0.1       

3.4.2 torch.optim.lr_scheduler

(1)torch.optim.lr_scheduler.LambdaLR 每个epoch,都更新一次:new_lr = λ × initial_lr
(2)torch.optim.lr_scheduler.ExponentialLR 每个epoch,都更新一次:new_lr = initial_lr × γ^(epoch)
(3)torch.optim.lr_scheduler.StepLR 每训练step_size个epoch,更新一次。
(4)torch.optim.lr_scheduler.MultiStepLR 每次遇到milestones中的epoch,做一次更新。milestones(list):递增的list,存放要更新lr的epoch;
torch.optim.lr_scheduler:调整学习率(详细讲解所有方法)

#########################################################################
# torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1)
# 		(1)optimizer :				要更改学习率的优化器;
# 		(2)step_size(int):			每训练step_size个epoch,更新一次参数;
# 		(3)gamma(float):			    更新lr的乘法因子;
# 		(4)last_epoch (int):		    最后一个epoch的index。当训练N个epoch后中断,然后继续训练时,该值等于加载模型的epoch。默认为-1:表示从头开始训练,即从epoch=1开始。
#########################################################################
import torch
import torch.nn as nn
import torch.optim.lr_scheduler
# 超参数初始化
initial_lr = 0.1
epoch = 20


# 构建网络模型
class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=3, kernel_size=3)

    def forward(self, x):
        pass


net_1 = Model()
optimizer_1 = torch.optim.Adam(net_1.parameters(), lr=initial_lr)       # 优化器
scheduler_StepLR = torch.optim.lr_scheduler.StepLR(optimizer_1, step_size=3, gamma=0.1)
# scheduler_LambdaLR = torch.optim.lr_scheduler.LambdaLR(optimizer_1, lr_lambda=lambda epoch: 1/(epoch+1))
# scheduler_MultiStepLR = torch.optim.lr_scheduler.MultiStepLR(optimizer_1, milestones=[3, 7], gamma=0.1)
# scheduler_ExponentialLR = torch.optim.lr_scheduler.ExponentialLR(optimizer_1, gamma=0.1)
# scheduler_ReduceLROnPlateau = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer_1, mode='min', factor=0.1, patience=2)
print("初始化的学习率:", optimizer_1.defaults['lr'])
for epoch in range(1, 11):
    # train模型
    optimizer_1.zero_grad()		# 提取清零
    optimizer_1.step()			# 参数更新
    print("第%d个epoch的学习率:%f" % (epoch, optimizer_1.param_groups[0]['lr']))
    scheduler_StepLR.step()		# 学习率更新
    # scheduler_LambdaLR.step()
    # scheduler_MultiStepLR.step()
    # scheduler_ExponentialLR.step()
    # scheduler_ReduceLROnPlateau.step()


四、训练模型

4.1 超参数 —— 调参寻找模型最优解

超参数将影响网络模型的性能。通过参数调整(调参)可以寻找到模型的全局最优解。备注:调参是一门技术活。
超参数是模型训练前设置的参数,而不是训练后得到的参数。

超参数 经验范围 介绍
Batch_size \ (1)将所有训练样本切分成 N 个 Batch 来分开训练;(2)影响梯度的学习效果,达到一定数量后,梯度学习效果达到最优且不再变化;(3)适用于大样本,避免计算机内存爆炸;(4)与Epoch_num搭配使用,可以达到最佳效果。
Epoch_num \ (1)一个Epoch就是将所有训练样本训练一次的过程;(2)随着epoch数量增加,网络权重的更新次数增加,曲线将由欠拟合变为过拟合。
Learing Rate 0.01,0.001 (1)训练刚开始,学习率取较大值以加快训练;(2)其后需逐渐衰减,使模型收敛。
weight decay [0, 0.0001] (1)权重衰减即 L2 正则化,其抑制损失函数的优化参数,避免过拟合;(2)通常会有个建议值,例如0.0005 ,使用建议值即可,不必过多尝试。
Dropout [0,0.5] 随机删除一定比例的神经元,避免过拟合
momentum [0, 1] 优化器的动量(momentum)即当前方向考虑之前更新方向的程度比。0表示没有惯性
卷积核尺寸 [1x1]、[3x3]、[5x5] (1)在达到相同感受野的情况下,卷积核越小,所需要的参数和计算量越小;(2)一般而言,取卷积核大小【=3】;(3)【=1】不改变感受野;(4)【=偶数】无法保证输入与输出的特征图尺寸不变,通常取奇数;
卷积核数量 1、3、5 (1)即卷积层的通道数;(2)数量越多,提取特征越多;(3)网络性能先上升后下降
超参数 类型 介绍
优化器 SGD、Adam (1)逐步降低Loss,更接近真实标签;(2)例如:Adam优化器中的参数β1,β2,ϵ(通常取默认值);
池化层 最大池化层、均值池化层 (1)降采样,避免过拟合(2)卷积核大小:取一半
激活函数 Sigmoid、ReLu (1)增加非线性能力;(2)依据网络层的深度,选择类型
网络层数 \ (1)网络层越多更" 接近现实 ",但也容易导致过拟合。(2)增加训练难度,模型可能难以收敛。

4.2 训练集、验证集、测试集的作用

在深度学习中,通常将数据集划分为训练集、验证集和测试集,比例为8:1:1。如果没有验证集,则划分比例为7:3。
对原始数据的划分,主要作用是为了防止模型过拟合。不同数据集通过随机抽取得到。

  • 数据预处理:为了得到更好地训练模型,训练集通常需要进行数据预处理,而验证集和测试集由于不更新参数,只计算结果,故不参与数据预处理操作。

不同数据集的作用:

  • 训练集(Training set):最优化模型。 通过训练集不断的训练来使得我们的模型能够更好的接近真实数据。
  • 验证集(Validation set):手动调参。 调节超参数使得模型泛化能力好。
  • 测试集(Test set):用来测试模型的准确性。 既不参与权值的学习(权值更新),也不参与超参数的选择(调参),仅仅用于评价模型的好坏。

备注:如果把测试集当做验证集,通过调参去拟合测试集,等于在作弊。

4.3 数据增强(Data Augmentation)

即在不增加数据的情况下,通过数据预处理将有限的数据集成倍的增加,从而实现采用大样本数据进行模型训练的效果。 例如:CNN实现对物体的分类结果,不会因为其移位、视角、大小、照明而发生改变(或以上的组合),因此CNN具有不变性的属性。

主要作用:

  • 11、扩大训练数据集的规模,降低模型对某些属性的依赖,提高模型的泛化能力。
  • 22、减少模型对物体位置的依赖性,并降低模型对色彩的敏感度等。

图像预处理方法:水平或垂直翻转、旋转、裁剪、缩放、色彩变换、增加随机噪音等。
实现方式:torchvision.transforms。包括两种操作:(1)单操作(2)组合操作。

单样本数据增强
(1)常用的几何变换方法:翻转,旋转,裁剪,缩放,平移,抖动。
(2)常用的像素变换方法:加椒盐噪声,加高斯噪声,进行高斯模糊,调整HSV对比度,调节亮度 / 饱和度,直方图均衡化,调整白平衡等。

多样本数据增强
(1)mixup:将随机的两张样本按比例混合,分类的结果按比例分配。只适合分类任务。
(2)cutmix:就是将一部分区域cut掉但不填充0像素而是随机填充训练集中的其他数据的区域像素值,分类结果按一定的比例分配。
(3)cutout:随机的将样本中的部分区域cut掉,并且填充0像素值,分类的结果不变。
其中:Mixup, Cutmix只用于分类任务,分类分数按比例分配;而 Cutout适合分类、检测、识别任务,分类结果不变 。

4.4 过拟合与欠拟合

欠拟合(underfitting):训练样本与测试样本的准确率都低。
过拟合(overfitting):训练样本准确率高,而测试样本准确率低,泛化能力差。

  • ❤️.泛化能力: 衡量训练误差与测试误差之间的差距,如果两者相差无几,则泛化能力好,反之亦然。
  • 欠拟合原因:(1)训练样本数量少;(2)模型复杂度太低;(3)参数还未收敛就停止循环;
    欠拟合的解决办法
    (1)增加样本数量
    (2)增加权重参数
    (3)提高模型复杂度
    (4)增加训练次数
    (5)查看是否是学习率过高导致模型无法收敛;
  • 过拟合原因:(1)数据的噪声太大;(2)权重参数太多;(3)模型太复杂无法收敛。
    过拟合的解决办法
    (1)数据预处理,剔除冗余特征的样本数据;
    (2)减少权重参数,避免学习冗余特征;
    (3)降低模型复杂度,避免学习冗余特征;
    (4)添加Dropout,避免对特定权重参数的依赖性;
    (5)降低L2正则化参数,降低权重参数的影响;
    (6)数据归一化,消除权重参数 W 的缩放影响。归一化的影响

4.5 梯度消失与梯度爆炸 —— 激活函数 + BN

梯度计算:激活函数求导后与权重参数 W 相乘的结果 grad。

  • 若 grad>1,那么当层数增多时,求出的梯度更新信息将以指数形式增加,即梯度爆炸;(影响:使得参数无法确定更新方向,导致无法优化代价函数)
  • 若 grad<1,那么随着层数增多,求出的梯度更新信息将以指数形式衰减,即梯度消失。(影响:使得参数一下子跳过最优点,导致学习过程极不稳定)

如何解决梯度消失和梯度爆炸?

4.6 归一化、标准化、正态化

  • ❤️归一化(Normaliation)将数据范围归整到(0,1)或(-1,1)之间的小数。常用方法:min-max归一化(Min-Max Normalization)
    (1)归整到(0,1)。转换函数:X' =(X-Min)/(Max-Min)
    (2)归整到(-1,1)。转换函数:X' =(X-Mean)/(Max-Min)

  • ❤️标准化(Standardization)将数据处理成符合均值为0,标准差为1的标准正态分布,处理后数据的取值范围是 (+∞,−∞)。 常用方法:0均值标准化(Z-score standardization)
    转换函数:X' =(X-Mean)/(Standard deviation)
    备注:标准化既是归一化也是正态化。

  • ❤️ 正态分布(Normal distribution) 又名高斯分布(Gaussian distribution),是一种概率分布,曲线与横轴间的面积恒等于1。正态分布曲线特征:以均数所在的位置为中心,左右对称,且曲线的两端无限趋近于横轴,但永不相交。

正态分布公式
在这里插入图片描述
其中:μ为均数,σ为标准差。
(1)μ决定了正态分布的位置,与μ越近,被取到的概率就越大,反之越小。
(2)σ描述的是正态分布的离散程度。σ越大,数据分布越分散曲线越扁平;σ越小,数据分布越集中曲线越陡峭。

正态分布:满足均值=μ,方差=σ ^ 2,记为N(μ,σ ^ 2)。
标准正态分布:满足均值=0,方差=1,记为N(0,1)。
在这里插入图片描述

4.7 网络层定义:只有带参数计算的才算一个网络层

  • 网络层:卷积层、全连接层
  • 非网络层:激活层、池化层

4.8 PyTorch深度学习模型的保存和加载

PyTorch深度学习模型的保存和加载

4.9 CPU与GPU加载模型的区别:torch.load()

CPU与GPU加载模型的区别:torch.load()

4.10 model.train()与model.eval()的区别

model.train():启用 Batch Normalization 和 Dropout。
model.eval():不启用 Batch Normalization 和 Dropout。

猜你喜欢

转载自blog.csdn.net/shinuone/article/details/127289512
今日推荐