三万字硬核详解:卷积神经网络CNN(原理详解 + 项目实战 + 经验分享)

三万字硬核详解:卷积神经网络CNN(原理详解 + 项目实战 + 经验分享)深入解析卷积神经网络 CNN 的实际应用 全面整理并梳理了 CNN 的关键知识点

大家好,欢迎来到IT知识分享网。

文章目录

一、项目实战

卷积神经网络的发展历史(超详细介绍)

卷积神经网络(Convolutional Neural Network,CNN):是一种前馈神经网络,最早可追溯到1986年的BP算法。

  • 三大网络结构:卷积层(Conv2d)、池化层(MaxPool2d)、全连接层(Linear)
  • 两大特点:稀疏连接、权值共享(使用更少的参数,获得更高的性能)

1.1、PyTorch:搭建 CNN 模型

在这里插入图片描述

# 胖墩会武术 -  import torch import torch.nn as nn import torch.nn.functional as F class SimpleCNN(nn.Module): def __init__(self): """__init__:定义网络结构""" super(SimpleCNN, self).__init__() # 作用: 在 SimpleCNN 类中,使用 self 来调用父类的 nn.Module 的初始化方法。 # SimpleCNN :当前子类的名字。 # self :当前实例,传入 super() 以告诉它要操作的是当前对象。 # __init__ :表示调用的是 nn.Module 中的初始化方法。 # 定义模型的各个层 self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=5, stride=1, padding=0) # 卷积层1:输入通道 3,输出通道16,卷积核大小3x3 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, padding=0) # 卷积层2:输入通道16,输出通道32,卷积核大小3x3 self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2) # 最大池化层2 self.fc3 = nn.Linear(6 * 6 * 36, 128) # 全连接层3:将多维特征图展平为一维向量(6 * 6 * 36 = 1296) self.fc4 = nn.Linear(128, 10) # 全连接层4(输出层) def forward(self, x): """forward:前向传播过程""" 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, 6 * 6 * 36) # 展平操作(参数-1表示自动调整size) x = F.relu(self.fc3(x)) # 全连接层3 -> 激活函数(relu) x = self.fc4(x) # 输出层(最后一层通常不使用激活函数) return x if __name__ == "__main__": model = SimpleCNN() # 模型实例化  input_data = torch.randn(1, 3, 32, 32) # batch_size=1, 通道数=3, 图像尺寸=32x32 output = model(input_data) # 前向传播 output_forward = model.forward(input_data) # (显示)前向传播  print("模型结构:\n", model) print("模型输出(十分类):\n", output) print(output_forward) """ 模型结构: SimpleCNN( (conv1): Conv2d(3, 16, kernel_size=(5, 5), stride=(1, 1)) (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (conv2): Conv2d(16, 36, kernel_size=(3, 3), stride=(1, 1)) (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (fc3): Linear(in_features=1296, out_features=128, bias=True) (fc4): Linear(in_features=128, out_features=10, bias=True) ) # 模型输出(十分类): tensor([[ 0.1202, 0.0287, -0.0160, 0.0384, 0.0442, 0.0127, -0.0626, -0.1158, -0.0499, 0.1266]], grad_fn=<AddmmBackward0>) tensor([[ 0.1202, 0.0287, -0.0160, 0.0384, 0.0442, 0.0127, -0.0626, -0.1158, -0.0499, 0.1266]], grad_fn=<AddmmBackward0>) """ 

1.1.1、模型的类函数:__ init __ () 、 forward() 、 __ call __ () 、 super()

通过分布教程,展示了 call() 函数是如何自动调用 init() 函数,然后调用 forword() 函数。

  • __init__() 构造函数每当创建类的实例时,都会自动调用它。
    • 用于定义和初始化模型的层结构,如(卷积层、池化层、全连接层等),以及设置层的超参数(比如卷积核的大小、卷积层的输入输出通道数等)。
  • forward() 前向传播函数每次模型接收到输入数据时,PyTorch 会自动调用 forward() 函数。
    • 用于定义 ” 数据流动 ” 过程:每次输入一个或 batch 数据,都会依次经过各个层(卷积、池化、ReLU 激活等),最后输出结果。
  • __call__() 特殊函数可以像调用函数一样调用对象,使得模型的调用变得非常直观。
    • 在 PyTorch 中:(1)__ call __ 函数已经由 nn.Module 类隐式定义和实现了,因此我们不需要显式地在子类中重新定义它。(2)根据当前模型是处于训练模式(model.train())还是评估模式(model.eval()),自动切换模型的行为。如 Dropout 和 Batch Normalization 在训练和评估时会有所不同。
    • 当你调用 model(input_data) 时,实际上是调用了模型的 __ call __ 方法,其会自动调用 forward() 方法,完成前向传播的计算。而不需要(显示地)手动调用 model.forward(input_data)。这也是为什么 PyTorch 模型的使用是 model(input_data) 而不是 model.forward(input_data),因为 call 自动完成了这一过程。
  • super() 常用函数用于调用父类(基类)的初始化方法
    • 在 PyTorch 中,nn.Module 是所有神经网络模型的基类。nn.Module 实现了大量管理模型、参数和行为的基础设施(例如自动求导、参数初始化等)。当我们创建一个新的模型类时,继承 nn.Module 可以使我们自动获得这些功能。
    • 但是,仅仅继承 nn.Module 并不足够,我们还需要在子类中显式地调用父类的 __ init __ 方法。如:nn.Module 提供的功能之一是自动注册模型的所有参数。当我们在模型中定义层(如 nn.Conv2d、nn.Linear 等)时,PyTorch 会将这些层的参数加入模型的参数列表中。如果不调用父类的 init,这些参数不会被注册,模型也不会知道这些参数的存在,从而导致无法进行参数更新和梯度计算。
    • 若不显式调用,则模型初始化不完整,导致训练和评估时出现各种问题。

1.1.2、类函数: __ init __ 、 __ len __ 、 __ getitem __

import torch from torch.utils.data import Dataset, DataLoader class CustomDataset(Dataset): def __init__(self, data, labels): self.data = data self.labels = labels def __len__(self): return len(self.data) def __getitem__(self, idx): return self.data[idx], self.labels[idx] if __name__ == '__main__': data = torch.randn(100, 3) # 100个样本,每个样本有3个特征 labels = torch.randint(0, 2, (100,)) # 100个样本,每个样本的标签为0或1 dataset = CustomDataset(data, labels) dataloader = DataLoader(dataset, batch_size=10, shuffle=True) for batch_data, batch_labels in dataloader: print("批数据:", batch_data) print("批标签:", batch_labels) break """ 批数据: tensor([[ 0.4106, -0.8040, 0.1003], [ 0.5072, 0.3028, -0.5967], [ 0.8407, -0.6981, 2.1016], [ 0.3916, 0.9360, 0.6978], [-0.4918, 1.6023, -0.0661], [-1.1485, -1.0621, 1.0461], [ 0.3963, -0.4333, -0.7826], [ 0.0556, 1.5501, -0.5498], [ 3.7245, -0.0163, -0.9922], [-0.8662, -0.9823, -0.8796]]) 批标签: tensor([1, 0, 1, 1, 1, 0, 1, 1, 1, 1]) """ 

1.2、PyTorch:完成 CNN 模型的训练 + 验证 + 测试(保存与加载模型)

【Pytorch项目实战】手写数字识别(MNIST)、普适物体识别(CIFAR-10)

二、前向传播(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、卷积运算 —— 提取特征

卷积运算就是互相关( cross-correlation )运算。
在这里插入图片描述
卷积运算卷积核中的 2 分别乘以输入中的每个值,然后输出卷积结果。
其中,输入、输出和卷积核都是张量,卷积核(kernel)又叫权重过滤器,简称过滤器(Filter)。

卷积运算公式
(1)假设输入图像大小是input x input,卷积核大小是kernel,补0的圈数为padding,步长为stride,卷积后输出特征图大小为output x output
在这里插入图片描述
(2)图像高度和宽度的计算公式:
在这里插入图片描述
备注:若输入图像为 m x n,想要转换为 m x m 或 n x n 的图像,则可以采用裁剪、拼接、填充的方法。

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

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

  • 卷积运算的详细过程:(1)取(3 x 3)输入图像中的(2 x 2)矩阵,然后与(2 x 2)卷积核进行卷积运算,得到一个计算结果0 * 0 + 1 * 1 + 3 * 2 + 4 * 3 = 19);(2)通过滑窗,循环计算得到输出特征图(2 x 2)。
  • 特征图中的每个元素(即:神经元)只与上一层的所有神经元中的4个进行连接。

【结论】:稀疏连接相比于全连接,大大缩减了权重参数数量,也极大避免了过拟合。
在这里插入图片描述

2.1.3、权值共享 —— 减少权值数量,模型易优化

在这里插入图片描述
输入图像

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

卷积运算(多通道)

  • 1、Kernels卷积核计算
    • 每个 Kernel 的 3 个channle(R/G/B)分别与 Input 对应的 3 个channle(R/G/B)进行卷积运算,最后每个 Kernel 得到 3 个(6 * 6) feature_map_0
  • 2、Feature Maps特征图计算
    • 将 3 个(6 * 6)的 feature_map0 逐元素相加(即通道融合)得到 1 个 feature_map_1
    • 然后将 feature_map1 的每个元素都加上其对应的偏置 b1,得到该 Kernel 的 FeatureMap1
      • 同一个 Kernel1 对应的 3 个 channle 共享一个偏置 b1(权值共享1)
      • 同一个 FeatureMap1 的所有元素共享一个卷积核 Kernel1(权值共享2)
    • 同理其余Kernel,最后得到(6 * 6 * 5) 个 Feature Maps(即最终输出的 channel 数等于 Kernel 的个数)。
  • 3、Feature Vector向量计算
    • 采用全局最大池化,分别计算每一个Feature Map,最终得到一个(1 * 1 * 5)Feature Vector。

结论
(0)权值共享:大大减少了模型的权重参数。
(1)权值共享:当前隐藏层中的所有神经元都在检测图像不同位置的同一个特征,即检测特征相同。因此也将输入层到隐藏层的这种映射称为特征映射。
(2)多个卷积核分别用于识别图像不同位置的特征。通过全连接层将所有特征整合,输出一个值(猫 / 狗)。

在这里插入图片描述

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

(1)深度学习为何要初始化? —— 影响算法结果

  • 深度学习算法:由于迭代训练 + 网络层数多 + 权重参数多
  • 机器学习算法:影响小。

(2)初始化对训练的影响?—— 决定算法是否收敛

  • (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:表示在输入图像的边界填充 1 圈,全 0 元素。
作用:

  • (1)图像尺寸对齐:对图像进行扩展,扩展区域补零。
  • (2)保持边界信息:若没有padding,卷积核将对输入图像的边界像素只卷积一次,而中间像素卷积多次,导致边界信息丢失。

在这里插入图片描述

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

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

(1)用于神经网络的隐含层与输出层之间(如:卷积层+激活函数+池化层),为神经网络提供非线性建模能力
(2)若没有激励函数,则模型只能处理线性可分问题。与最原始的感知机相当,拟合能力有限。

左图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)又叫下采样、降采样。
作用: 降采样(减少参数),增大感受野,提高运算速度及减小噪声影响,防止过拟合。
分类:

  • (1)局部池化
    • 最大池化(Max Pooling):取Pooling窗口内的最大值作为采样值。
    • 均值池化(Average Pooling):取Pooling窗口内的所有值相加取均值作为采样值。
      • torch.nn.MaxPool2d(kernel_size=2, stride=1, padding=0)
      • torch.nn.AvgPool2d(kernel_size=2, stride=1, padding=0)
  • (2)全局池化
    • 全局最大池化(Global Max Pooling):取以整个特征图为单位的最大值作为采样值。
    • 全局均值池化(Global Average Pooling):取以整个特征图为单位的所有值相加取均值作为采样值。
      • torch.nn.AdaptiveMaxPool2d(output_size=(2, 2))
      • torch.nn.AdaptiveAvgPool2d(output_size=(2, 2))

备注:全局池化相比局部池化能减少更多参数,且泛化能力比较好,但唯一的不足之处是收敛速度比较慢。
在这里插入图片描述
Python:池化的用法及代码(示例)

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

全连接层(Fully Connected Layers,FC)torch.nn.Linear(n_hidden, classification_num)

  • 卷积层、池化层、激活函数 —— 将原始数据映射到隐层特征空间,提取特征
  • 全连接层 —— 将学到的 “分布式特征” 映射到样本标记空间,进行分类

全连接层FC的缺点

  • (1)由于忽略了空间结构,故不适用于目标检测。如:分割任务中的FCN采用卷积层替换全连接层。
  • (2)全连接层的参数过多,导致模型复杂度提升,容易过拟合。如:ResNet、GoogleNet采用全局平均池化替换全连接层。
  • 详解1:神经网络在经过卷积+激活后,得到 3 x 3 x 5 的输出结果,它是怎么转换为1 x 4096?在这里插入图片描述
  • 详解2:可以理解为在中间做了一个卷积操作,详细步骤如下:
    • (1)将【3 x 3 x 5特征图】分别与对应的【3 x 3 x 5卷积核】进行【卷积convolution】【不带偏置】,然后通过全连接FC得到一个预测结果【即一个神经元的输出 1 x 1 】
    • (2)当我们有4096个神经元,将得到【4092个神经元的输出 1 x 4096】。

全连接层中的每个神经元都可以看成是一个加权平均且不带偏置的多项式。可以简单写成:y = W * x。

在这里插入图片描述

全连接层FC的作用:这一步卷积还有一个非常重要的作用,就是将分布式特征representation映射到样本标记空间。简单来说,就是将输入图像的所有特征整合到一起,输出一个值(猫 / 狗)
效果:大大减少目标位置的不同,而对分类结果带来的影响。
在这里插入图片描述
下张图是上面任意一张图的图解:
在这里插入图片描述
由图可得:

  • (1)猫虽然在不同的位置输出的特征值相同,但可能分类结果不同。
  • (2)全连接层FC:忽略了空间结构特性。即目标不管在什么位置,只要图像中有这个猫,那么就能判断它是猫。

三、反向传播(Backward propagation)

反向传播:loss.backward()

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

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

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

损失函数:用来衡量模型预测值与真实值之间的差异。

  • 损失函数越小说明模型和参数越符合训练样本。
  • 任何能够衡量模型预测值与真实值之间差异的函数都可以叫做损失函数。
  • 为了避免过拟合,一般在损失函数的后面添加正则化项(F = 损失函数 + 正则化项)

常用的损失函数:

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

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

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

思路在损失函数的后面添加一个惩罚项。
作用:正则化系数 λ :控制模型的可学习参数,防止过拟合。
计算过程(求解线性模型):找到一个 θ 值,然后对 J(θ) 进行求导,使得代价函数 J(x) 取得最小值(导数为0)。
在这里插入图片描述

常用的惩罚项:L1正则化、L2正则化、Dropout正则化

  • 在机器学习的线性回归模型中,使用L1正则化得到的模型叫Lasso模型,使用L2正则化得到的模型叫岭回归(Ridge regression)

3.2.1、L1正则化(特征选择) —— 冗余参数置零

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

3.2.2、L2正则化(权重衰减) —— 过拟合参数趋近于0

  • L2正则化(L2范数),通常表示为:||W||2指权值向量 W 中各个元素的平方和,然后求平方根。
  • 特点:又叫权重衰减。即抑制模型中产生过拟合的参数,使其趋近于0(而不等于0),影响变小。
  • 特点:倾向于让所有参数的权值尽可能小。

若不关心显式特征(哪些特征重要或不重要),L2正则化的性能往往优于L1正则化。

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

torch.nn.Dropout(p=0.5) # 表示随机删除 50% 的神经元

  • Dropout正则化在训练过程中,随机删除一定比例的神经元(比例参数可设置)。
  • 一般只在训练阶段使用,测试阶段不使用。
  • 一般控制在20% ~ 50%。太低没有效果,太高则会导致模型欠学习。

应用场景
(1)在大型网络模型上效果显著
(2)在输入层和隐藏层都使用Dropout
(3)当学习率和冲量值较大时:如学习率扩大10 ~ 100倍,冲量值调高到0.9 ~ 0.99。
(4)用于限制网络模型的权重时:学习率越大,权重值越大。
在这里插入图片描述

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

optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

优化器(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(), lr=0.001, momentum=0.9)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

学习率(learning rate,lr)用于控制权重参数的学习速度一般情况下,学习率随着epoch的增大而逐渐减小,可以达到更好的训练效果。

学习率 学习速度 适用时期 适用范围 缺点
模型刚训练时 [0.01,0.001] 易震荡;模型不收敛;
模型训练后期 [0.001,0.00001] 局部收敛,找不到最优解;

为什么加入学习率?

  • (1)神经网络通过梯度下降+反向传播进行参数更新
    • 主要流程:将可学习参数(权值W和偏置bias)反向传播给网络层,使得损失函数Loss值逐步降低,输出的模型更接近真实标签。
  • (2)神经网络在参数学习过程中,需要多轮训练,而每一论训练得到的可学习参数,需要通过学习率来控制学习速度,使得下一轮训练时的损失更小,最终达到最优模型

3.4.1、自定义更新学习率(根据epoch)

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

3.4.2、动态调整学习率:torch.optim.lr_scheduler

  • optimizer.step()用于在每 1 个 batch_size 中,更新一次参数;
  • scheduler_StepLR.step()用于在每 1 / N个 epoch 中,更新一次参数
  • Pytorch官网:torch.optim.lr_scheduler 动态调整学习率
  • torch.optim.lr_scheduler:调整学习率(详解)
""" torch.optim.lr_scheduler 动态调整学习率 """ torch.optim.lr_scheduler.LambdaLR() :通过自定义 Lambda 函数来设置每个参数组的学习率。 torch.optim.lr_scheduler.MultiplicativeLR() :通过指定乘法因子来调整每个参数组的学习率。 torch.optim.lr_scheduler.StepLR() :在每个给定的步骤上降低学习率,步骤由用户定义。 torch.optim.lr_scheduler.MultiStepLR() :在每个给定的步骤上降低学习率,步骤由用户定义。 torch.optim.lr_scheduler.ExponentialLR() :每个周期按指数衰减学习率。 torch.optim.lr_scheduler.CosineAnnealingLR() :每个周期按余弦函数衰减。 # scheduler.step() # 上述方法不需要输入参数 torch.optim.lr_scheduler.ReduceLROnPlateau() :根据验证集的性能指标来调整学习率。 # scheduler.step(metrics) # metrics参数:表示验证集的性能指标 # 若验证集的损失值在连续 patience 个 epoch 中都没有减少,则学习率会乘以 factor 参数(通常是一个小于 1 的值)以降低学习率。 """ # 函数功能:在每个指定的步骤(epoch)之后,将学习率降低一个给定的因子(factor)。 # 函数说明: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): 学习率调整的乘法因子; # (4)last_epoch (int): 最后一个epoch的index。 # 当训练N个epoch后中断,然后继续训练时,该值等于加载模型的epoch。 # 默认为-1:表示从头开始训练,即从epoch=1开始。 """ """ # 函数功能:根据验证集的性能指标(如损失值或准确率)来自动调整学习率(下降)。 # 函数说明:scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=5, # verbose=True, threshold=0.0001, threshold_mode='rel', cooldown=0, min_lr=0, eps=1e-08) # 参数说明: # optimizer: 需要调整学习率的优化器。 # mode: 监控指标的模式,可以是 "min" 或 "max",分别表示监控指标越小越好,还是越大越好。 # factor: 学习率调整的缩放因子,即在性能不再改善时将学习率乘以该因子。 # patience: 指标不再改善时等待的周期数,等待 patience 个周期后学习率才会被调整。 # threshold: 控制学习率是否被调整的阈值,例如如果 mode='min',当指标监控的值下降小于 threshold 时,学习率会被调整。 # threshold_mode: 阈值模式,可以是 'rel' 或 'abs',分别表示阈值是相对值还是绝对值。 # cooldown: 调整学习率后等待的周期数,防止在连续多个周期内反复调整学习率。 # min_lr: 学习率的下限,学习率不会被调整到低于这个值。 # eps: 防止除零错误的小值。 # # 备注:若验证集的损失值在连续 patience 个 epoch 中都没有减少,则学习率会乘以 factor 参数(通常是一个小于 1 的值)以降低学习率。 """ 
import torch.nn as nn import torch.optim.lr_scheduler 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 if __name__ == '__main__': # (1)超参数初始化 lr = 0.1 epochs = 5 batch_size = 64 # (2)模型实例化 model = Model() optimizer = torch.optim.Adam(model.parameters(), lr=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, gamma=0.1) # scheduler_ReduceLROnPlateau = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer_1, mode='min', factor=0.1, patience=2) print("初始化的学习率:", optimizer.defaults['lr']) # (3)迭代训练 for epoch in range(epochs): for batch in range(batch_size): model.train() # train模型 optimizer.zero_grad() # 提取清零 optimizer.step() # 参数更新 # scheduler_StepLR.step() # 学习率更新 # scheduler_LambdaLR.step() # scheduler_MultiStepLR.step() scheduler_ExponentialLR.step() # scheduler_ReduceLROnPlateau.step() print("第%d个epoch的学习率:%f" % (epoch, optimizer.param_groups[0]['lr'])) """ 初始化的学习率: 0.1 第0个epoch的学习率:0.010000 第1个epoch的学习率:0.001000 第2个epoch的学习率:0.000100 第3个epoch的学习率:0.000010 第4个epoch的学习率:0.000001 """ 

四、模型训练

4.1、网络层的定义(具有参数计算)

  • 具有参数计算的网络层卷积层、全连接层
    • 可学习参数:是模型通过训练学习到的权重和偏置
  • 不具有参数计算的操作:激活函数、池化层

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

超参数:影响网络模型的性能。通过参数调整(调参),以寻找模型的全局最优解。

  • 调参是一门技术活。
  • 超参数是模型训练前设置的模型参数,而不是训练后得到的参数。
超参数 名称 经验范围 介绍
Epoch 周期 50、100 一个Epoch:将整个训练集完成一次训练的过程
Batch Size 批大小 8、16、32、64 将整个训练集分成 N 个 Batch Size 大小的子数据集,然后迭代训练。
Learing Rate 学习率 0.01,0.001 在训练过程中,学习率(先)取较大值以加快训练;其后需逐渐衰减,使模型收敛。
weight decay 权重衰减 [0, 0.0001] 用于抑制损失函数的优化参数,避免过拟合;通常使用建议值,不必过多尝试。
Dropout 正则化 [0,0.5] 随机删除一定比例的神经元,避免过拟合。
momentum 优化器的动量 [0, 1] 控制了梯度更新的速度和方向,即当前方向考虑之前更新方向的程度比。常用值=0.9
kernel_size 卷积核大小 1×1、3×3、5×5 (1)在相同感受野下,卷积核与参数越小,则计算量越小;(2)一般取卷积核=3;(3)【=1】不改变感受野;(4)通常取奇数,【=偶数】无法保证输入与输出的特征图尺寸不变;
kernel_channels 卷积层的通道数 1、3、5 (1)数量越多,提取特征越多;(2)网络性能先上升后下降
超参数 类型 介绍
优化器 SGD、Adam (1)逐步降低Loss,更接近真实标签;(2)例如:Adam优化器中的参数β1,β2,ϵ(通常取默认值);
池化层 最大池化层、均值池化层 (1)降采样,避免过拟合(2)卷积核大小:取一半
激活函数 Sigmoid、ReLu (1)增加非线性能力;(2)依据网络层的深度,选择类型
网络层数 \ (1)网络层数越多,更” 接近现实 “,但也更容易过拟合。(2)网络层数越多,训练难度越大,模型越难以收敛。

4.2.1、超参数 —— Epoch

一个Epoch(周期)将整个训练集在模型上完成一次训练的过程,包括前向传播和反向传播。

多个 Epoch 训练(目的):实现模型的迭代学习,充分学习数据集中的特征并提高性能,直到收敛到最佳性能或达到停止条件。

  • 在训练过程中,可以根据模型的性能和训练效果来决定训练的 Epoch 数量。
  • 通常情况下,Epoch 数量越多,模型的性能可能会越好,但也可能会导致过拟合的问题。因此,在选择 Epoch 数量时需要进行适当的调整和权衡。

4.2.2、超参数 —— Batch Size

Batch Size(批大小)在训练过程中,每次输入模型的样本数量。

  • batch_size 经验值 = 8、16、32、64、128 等等 —— (GPU对 2 的幂次的 batch 可以发挥更佳的性能)
  • 影响因素:整个训练集大小、硬件资源、训练速度、收敛速度和模型稳定性等
  • 如何选择最优的 Batch Size ? (具体问题具体分析)测试不同的 batch_size,并根据测试结果不同调整,从而找到适合当前任务的最佳设置。

Batch Size:决定了梯度下降的方向。

  • (1)Batch Size = 1 —— 导致梯度震荡,模型不收敛。
  • (2)Batch Size 逐渐增大 —— 噪声影响逐渐减小 —— 减小batch换来的收敛速度提升,远不如引入大量噪声导致的性能下降。
  • 训练速度提升:使用GPU多线程并行处理,加速参数更新过程,直到达到时间最优。
    • 前提:显卡显存支持,否则明显减慢。 —— 若超过显存(专用 GPU 内存),则超出部分转移到(共享 GPU 内存),此时速度会明显减慢。
    • 若(创建和销毁线程的时间开销)小于(将Batch_size数据从CPU上传到GPU),则加速,反之减速。
  • 收敛速度变慢:导致梯度估计的不准确性和方差较大,使得模型需要更多的 Epoch 来收敛到较优的解。
  • 泛化性能增强:提供更多的样本信息来估计梯度,使得模型更容易学习数据集的特征,并提高泛化性能。而较小的 batch-size 下更易受到噪声影响。
  • 最终收敛精度提升:可以减少梯度的方差,使得参数更新的方向更加稳定,从而更容易收敛到较优的解。
  • (3)较小的 Batch Size(适用于中 / 小样本) —— 如:几百到几千个样本
    • batch_size 经验范围:小几十以内
    • 优点:降低内存消耗、模型收敛速度更快
    • 缺点:梯度的方差较大,使得参数更新的方向不稳定(梯度震荡大),从而增加训练的不确定性。
  • (4)较大的 Batch Size(适用于大样本) —— 如:几万到几百万个样本
    • batch_size 经验范围:大几十到几百,但一般不会超过几千。
    • 减少梯度的方差,使得参数更新的方向更加稳定(梯度震荡小),但也可能陷入局部最优。
    • 充分利用硬件资源:使用多线程并行计算,提高训练效率。
    • 缺点:内存占用增加,特别是在处理大型数据集或单个数据较大的情况下。
  • (5)Batch Size = all:全部数据集的梯度方向,能够更好的代表样本总体,确定其极值所在。只适用于小样本,且大样本有内存限制。

4.2.3、超参数 —— Epoch、Batch Size、Patch的关系

(1)在模型训练过程中,通常将整个训练集完成多次迭代训练;
(2)每一次训练通常将整个数据集划分为若干个批次(每个批次包含多个样本),然后每次输入一个批次数据到模型中。
(3)每个批次数据在输入模型训练之前,通常采用图像预处理(灰度化 + 归一化 + 去噪 + 裁剪等等)。

  • Epoch(周期):表示将整个训练集在模型中的完成一次训练的过程;
    • 在每个Epoch中,通常都会采用 ” 数据集随机打乱 ” 的策略,提高模型的泛化能力。
  • Batch Size(批大小):表示每次输入到模型的样本数量;
    • 如:训练集样本数 = 500,Batch Size = 32,共得到 batches = (500/64) = 7.8125。
    • 在Pytorch数据加载器中,可以选择是否丢弃最后一个不足Batch Size的样本,若不丢弃则最后一个训练集样本数量 = 0.8125 * 64 = 52。
    • 用于提高训练效率。
  • Patch(图像块):表示一幅图像中的一个子区域;
    • 如:图像尺寸 = 256 x 256,patch = 64 x 64,重叠率overlap = 0,共得到 patches = (256/64)x(256/64)= 16。
    • (数据增强)用于局部特征的学习,从而更好地理解整体图像。

4.2.4、影响因素:时耗

  • 模型的网络层数:网络层越多,耗时约长。如:Unet与轻量级Unet
  • DataLoader数据加载器的耗时问题
    • (1)在每个epoch开始时,数据加载器会重新创建并启动。因此,前几个batch_size的耗时总是很高。
    • (2)参数 – batch_size:对于小样本数据,批大小越大,时耗越高。对于大样本则相反。
    • (3)参数 – num_workers:多进程加载数据
      • 缺点:增加内存占用量(较大,根据加载数据的数量而定)
    • (4)参数 – pin_memory:加快数据从主机内存到GPU内存的传输速度。
      • 缺点:增加内存占用量(较少)
  • image.to(device)将图像(或batch_image)从CPU上传GPU的过程,会产生一定的耗时。与数据传输带宽、图像大小和 GPU 设备性能等有关。
    • (1)若上传图所需内存超过了专用 GPU 内存(显存),则超出部分将转移到共享 GPU 内存中,以防止程序奔溃,但由于共享GPU内存是主存划分出来与GPU共享的内存,因此速度会明显减慢。
    • (2)显存性能测试(batch=8):3060TI – 耗时=0.9秒,3090TI=0.3秒
    • (3)显存性能测试(batch=8、16、32、64):
      • (1)batch=8 / 16:所需内存在专用 GPU 内存范围内;
      • (2)batch=32及以上:所需内存超过专用 GPU 内存,则超出部分转移到共享 GPU 内存中。

4.3、数据预处理

4.3.1、数据分配:训练集 + 验证集 + 测试集

  • 数据随机分配:
  • (1)训练集、验证集、测试集(比例为8 : 1 : 1),用于防止模型过拟合。
  • (2)若没有验证集,则训练集与验证集的划分比例为7 : 3
  • 图像前处理(预处理):
    • (1)若训练集的全部数据处理,则训练集 + 验证集 + 测试集必须保持一致。
      • 如:图像归一化,用于降低不同图像的灰度强度对模型的影响力。
    • (2)若训练集的部分数据处理,则训练集 + 验证集一致,而测试集不应用。
      • 如:随机高斯噪声、明亮度调整。用于提高模型的泛化性。
  • 不同数据集的作用:
    • 训练集(Training set):用于迭代以得到最优化模型,使模型更接近真实数据。
    • 验证集(Validation set)用于测试训练集效果并通过多次调参(超参数),使模型泛化性能更好。
    • 测试集(Test set):用来测试最终模型的效果。此时,既不参与权值的学习(权值更新),也不参与超参数的选择(调参)。

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

4.3.2、数据增强(Data Augmentation)

在现有数据集下,通过数据增强扩增 N 倍的训练集,而不增加额外数据集,从而实现大样本训练。

  • 作用一:扩大训练集,降低模型对某些属性的依赖,提高模型的泛化能力。
  • 作用二:减少模型对物体位置的依赖性,并降低模型对色彩的敏感度等。
    • 如:CNN物体分类,不会因为其位置、角度、大小、照明等(或以上的组合)而发生改变,因此CNN具有不变性属性。

torchvision.transforms:图像变换

  • 单样本数据增强
    • (1)常用的几何变换方法:翻转,旋转,裁剪,缩放,平移,抖动
    • (2)常用的像素变换方法:椒盐噪声,高斯噪声,高斯模糊,亮度 / 饱和度,直方图均衡化,白平衡等。
  • 多样本数据增强
    • (1)mixup(分类任务):将随机的两张样本按比例混合(分类的结果按比例分配)
    • (2)cutmix(分类任务)将一部分区域cut减掉,但不填充0像素,而是随机填充训练集中的其他数据的区域像素值(分类结果按一定的比例分配)
    • (3)cutout(分类、检测、识别):随机的将样本中的部分区域cut减掉,并填充0像素值(分类的结果不变)

4.3.3、归一化 + 标准化 + 正态分布

标准化:既是归一化也是正态分布。

❤️ 归一化(Normaliation)将数据范围归整到(0,1)或(-1,1)之间的小数。

  • Min-Max 归一化
    • ( 0,1):X' = ( X - Min ) / ( Max - Min )
    • (-1,1):X' = ( X - Mean ) / ( Max - Min ) * 2 - 1

❤️ 标准化(Standardization)具有均值为0,标准差为1的标准正态分布,取值范围 (+∞,−∞)。

  • 均值标准化(Z-score standardization):X' = ( X - Mean ) / ( Standard deviation )

❤️ 正态分布(Normal distribution),又名高斯分布(Gaussian distribution)。
在这里插入图片描述

  • μ:均数,决定了正态分布的位置。
    • 与μ越近,被取到的概率就越大,反之越小。
  • σ:标准差,表示正态分布的离散程度。
    • σ越大,数据分布越分散,曲线越扁平;
    • σ越小,数据分布越集中,曲线越陡峭。
  • 正态分布是一种概率分布。曲线与横轴之间的面积恒等于1
  • 正态分布的曲线特征:以均数为中心,左右对称,且曲线的两端无限趋近于横轴,但永不相交。

在这里插入图片描述

  • 正态分布:满足均值=μ,方差=σ^2 —— 记为N(μ,σ^2)
  • 标准正态分布:满足均值=0,方差=1 —— 记为N(0,1)

4.4、常见问题

4.4.1、项目开发 – 新手指南

在开发或复现一个开源项目时,需要注意几个核心问题:

测试模型是否符合预期效果

  • (1)通常来说,网络模型的设计框架才是深度学习的核心,而图像前处理主要用于数据的泛化性,所以不需要过于复杂。(如:UNet模型)
    • 数据的泛化性就是解决训练集的尺度不一致(如:200×300、300×300)、数据类型不一致(如:uint8、uint16)等问题;
    • 图像前处理全局归一化0~1、随机裁剪patch,图像切块,图像缩放、overlap数据增强等等。
      • 核心1:模型中任何算法,必须熟悉其内部实现逻辑,包括输入与输出,重点关注算法对数据的像素级变化。
        • 图像归一化(归一化是否有截断?数据类型?)
        • 冗余的图像处理操作???
        • 冗余的超参数设置???
      • 核心2:无论是调用开源库还是自定义算法(哪怕这个算法是开源项目已经写好的)。
        • 简单算法:可以直接调用 —— 如:cv2.size(),cv2.equalizeHist()等
        • 复杂算法:最好自定义实现 —— 如:rollingball等
    • 模型训练 + 模型测试:必须保持一致的图像前处理。
  • (2)在训练集上,若模型的训练结果具有与给定标签95%以上的匹配度,则证明当前模型的学习能力没有问题;
    • 验证方法:在迭代训练过程中,打印 / 保存每个 epoch 的预测结果,并观察结果是否越来越好(个别异常不影响)以及最后的预测结果是否符合要求。
  • (3)在测试集上,泛化性测试

标准的开源项目具有以下几个特点:清晰的架构设计、明确的执行流程、经过优化且逻辑的算法。

4.4.2、过拟合 + 欠拟合

❤️ 模型的泛化性:用于衡量训练集与测试集之间的误差。若相差无几,则模型具有泛化性,反之亦然。

欠拟合(underfitting)训练集与测试集的准确率都低

  • 原因:(1)训练样本数量少;(2)模型复杂度太低;(3)参数还未收敛就停止循环;
  • 解决办法
    • (1)增加样本数量
    • (2)增加权重参数
    • (3)提高模型复杂度
    • (4)增加训练次数
    • (5)查看是否是学习率过高导致模型无法收敛;

过拟合(overfitting)训练集的准确率高,而测试集的准确率低,泛化能力差。

  • 原因:(1)数据的噪声太大;(2)权重参数太多;(3)模型太复杂无法收敛。
  • 解决办法
    • (1)数据预处理,剔除冗余特征的样本数据;
    • (2)减少权重参数,避免学习冗余特征;
    • (3)降低模型复杂度,避免学习冗余特征;
    • (4)添加Dropout,避免对特定权重参数的依赖性;
    • (5)降低L2正则化参数,降低权重参数的影响;
    • (6)数据归一化,消除权重参数 W 的缩放影响。

4.4.3、梯度消失 + 梯度爆炸 —— 解决方法:激活函数 + BN批标准化

梯度(gradient)激活函数求导后,与权重参数 W 相乘后的结果。

  • 梯度爆炸(gradient > 1):当层数增多,梯度以指数形式增加,使得参数无法确定更新方向,导致无法优化代价函数。
  • 梯度消失(gradient < 1):当层数增多,梯度以指数形式衰减。使得参数一下子跳过最优点,导致学习过程极不稳定。

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

  • (1)合理选择激活函数:常用激活函数(简介+图解+导数):Sigmoid、Tanh、ReLU、LReLU、RReLU、ELU
    • sigmoid梯度随着x的增大或减小和消失,而ReLU不会
    • sigmoid导数的取值范围为[0, 0.25],而ReLU导数的取值范围为[0, 1]。
  • (2)BN批标准化(Batch Normalization)
    • 标准化处理每一层的输出信号,进而消除权重参数 W 的缩放影响

4.4.4、区别:model.train() + model.eval()

【Pytorch】model.train()和model.eval()用法和区别,以及model.eval()和torch.no_grad()的区别

Pytorch控制模型状态的两个方法:

  • model.train():将模型设置为训练模式,用于在训练集上参数计算。
    • 状态:
      • (1)启用 Dropout 和 Batch Normalization —— 增加模型的泛化能力和训练过程的稳定性。
      • (2)启用梯度计算 —— 在反向传播过程中计算梯度,并通过优化器更新模型的参数。
  • model.eval():将模型设置为评估模式,用于在验证集上评估性能。
    • 状态:
      • (2)关闭 Dropout 和 Batch Normalization —— 确保模型的输出稳定性和可重复性
      • (2)关闭梯度计算 —— 避免在推理或验证过程中对模型参数进行更新

备注:在训练过程中,模型需要保持两种状态的切换,以确保在不同阶段获得正确的行为。

4.4.5、模型实例化 + 保存模型的状态字典 + 加载模型的状态字典

  • torchvision.models:提供预训练模型下载
  • torch.load() + torch.save() + model.state_dict() + model.load_state_dict()
import torch import torchvision.models as models model = models.resnet18(pretrained=True) # 模型实例化 print("模型结构:\n", model)  # pretrained表示是否(在线)下载并加载预训练模型的权重。 # pretrained=True :下载并加载基于ImageNet的预训练权重。 # pretrained=False :不加载预训练权重,随机初始化模型权重。  torch.save(obj=model.state_dict(), f=r'data/model.pth') # 保存模型的状态字典  # model.load_state_dict(torch.load('path_to_your_model_weights.pth', weights_only=True)) # 加载模型的状态字典 # model.to(device=device) state_dict = torch.load(f=r'data/model.pth', map_location=torch.device('cpu'), weights_only=True) # 加载模型权重到状态字典中 model.load_state_dict(state_dict=state_dict, strict=False) # 将状态字典应用到模型中(strict=False,忽略最后一层的权重) print("状态字典:\n", state_dict) """ # 函数功能:用于加载模型或张量。 # 函数说明:model = torch.load(f, map_location=None, weights_only=False, pickle_module=pickle) # 参数说明: # - f: 模型文件的路径。支持的扩展名:.pt或.pth。 # - map_location(可选) : 将数据加载到指定的设备上。 # (0)map_location=None :若不指定,则加载到默认设备上。 # (1)map_location='cpu' :将数据加载到CPU上 # (2)map_location=torch.device('cuda') :将数据加载到默认的CUDA设备上。 # - weights_only(可选) : 若为Flase,表示只加载预训练模型权重。 # 若为True,表示加载预训练模型文件,包括状态字典(weights)、整个模型、训练状态等 # - pickle_module(可选): 使用Python的内置 pickle 模块来反序列化对象。用于兼容不同的Python版本或自定义的序列化格式。 # 返回参数: # 返回一个包含模型或张量的Python对象。———— 若加载的文件包含一个字典,则该字典将包含从保存的对象名称到对象本身的映射。 # 预训练模型文件通常包含以下信息: # (1)模型的权重参数: 如:各个层的权重矩阵和偏置向量。 # (2)模型的网络结构: 如:层的类型、层的连接方式等。 # (3)模型的优化器状态: 如: 学习率、动量、权重衰减等优化器相关的信息。 """ """ # 函数功能:用于加载预训练模型的状态字典并应用到模型中。 # 状态字典是一个Python字典对象,其中包含了模型的所有权重参数(如:weights和biases)。 # 函数说明:model.load_state_dict(state_dict, strict=True) # 参数说明: # - model: 加载权重参数的PyTorch预训练模型对象。 # - state_dict: 预训练模型权重参数的状态字典。 # - strict(可选): 是否严格匹配state_dict中的键与当前模型的键。 # (1)默认True,表示要求严格匹配。 # (2)若为False,则允许state_dict中的键只是当前模型键的子集,或者键的顺序不同。 # 但是,若state_dict中的键不包含在当前模型中,或者形状不匹配,则加载失败。 """ """ # 函数功能:用于保存模型 # 函数说明:torch.save(obj, f, pickle_module=pickle) # 参数说明: # - obj: 要保存的对象。 # (1)整个模型:model # (2)模型的状态字典:model.state_dict() # - f: 文件路径(字符串类型)或类文件对象(比如一个已打开的文件)。 # - pickle_module(可选): 使用Python的内置 pickle 模块来序列化对象。用于兼容不同的Python版本或自定义的序列化格式。 """ 

4.4.6、分类、分割和检测任务:损失函数选择

在深度学习的图像任务中,分类、分割、和检测各自具有不同的训练和验证损失函数需求,适合的损失函数选择能够直接影响模型的性能表现。

以下是对分类、分割和检测任务的损失函数选择和推荐的详细说明。

  • (1)分类任务
    • 训练损失函数:
      • 交叉熵损失(Cross-Entropy Loss):最常用的分类损失函数,用于多类分类问题。它能有效地衡量预测类别与真实类别的匹配程度。
      • Focal Loss:适用于类别不平衡的情况,能更关注难以分类的样本。
    • 验证指标:
      • 分类准确率:简单直观,适用于大多数平衡分类任务。
      • F1-score:适合类别不平衡任务,综合了查准率和查全率。
  • (2)分割任务
    • 训练损失函数:
      • 交叉熵损失(Cross-Entropy Loss):像素级分类损失,用于每个像素的类别判断。
      • Dice 损失(Dice Loss):重叠度量损失,对小目标和不平衡数据更加敏感。
      • 组合损失(例如交叉熵 + Dice 损失)推荐):结合了像素分类准确性和区域重叠,有助于提升整体分割质量。
    • 验证指标:
      • Dice 系数或IoU:直观衡量分割重叠度,反映模型在实际分割任务中的效果。
  • (3)检测任务
    • 训练损失函数:
      • 分类损失(如交叉熵或Focal Loss):用于目标的类别预测。
      • 回归损失(如Smooth L1 Loss、GIoU Loss、CIoU Loss):用于优化边界框回归,使预测框更接近真实框。
      • 组合损失(分类损失 + 回归损失):目标检测模型一般采用组合损失来同时优化类别预测和边界框定位。
    • 验证指标:
      • mAP(平均精度均值):在不同 IOU 阈值下的检测精度,综合考虑了分类和定位性能。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/143136.html

(0)
上一篇 2025-05-05 18:45
下一篇 2025-05-05 19:10

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信