FA_CNN卷积神经网络七---优化器PyTorch

FA :Formulas and Algorithm,CNN:Convolutional Neural Network

1. 优化器

在神经网络训练过程中,优化器(Optimizer)扮演着至关重要的角色。它的核心作用是根据损失函数计算出的梯度,对网络参数进行调整,从而逐步降低整体误差。

1.1 损失函数与反向传播

当我们调用损失函数的 backward() 方法时,就会触发反向传播机制,自动计算出我们需要调节的梯度。随后,优化器便可以利用这些梯度对参数进行调整,达到整体误差降低的目的。

1.2 梯度清零

在每次参数更新之前,必须将梯度清零。如果梯度不清零,会导致梯度累加,从而影响参数更新的准确性。这是训练过程中最容易忽略却又极其重要的细节。

2. 神经网络优化一轮

下面我们通过一个完整的示例,演示如何对神经网络进行一轮优化。这里我们使用 CIFAR10 数据集,构建一个简单的卷积神经网络,并采用随机梯度下降(SGD)优化器。

python 复制代码
import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)       
dataloader = DataLoader(dataset, batch_size=64, drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )
    def forward(self, x):
        x = self.model1(x)
        return x

loss = nn.CrossEntropyLoss()  # 交叉熵损失函数
tudui = Tudui()
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)  # 随机梯度下降优化器

for data in dataloader:
    imgs, targets = data
    outputs = tudui(imgs)
    result_loss = loss(outputs, targets)  # 计算实际输出与目标输出的差距
    optim.zero_grad()  # 梯度清零
    result_loss.backward()  # 反向传播,计算损失函数的梯度
    optim.step()  # 根据梯度,对网络的参数进行调优
    print(result_loss)  # 对数据只看了一遍,只看了一轮,所以loss下降不大

2.1 运行结果分析

运行上述代码,可以看到每一批数据的损失值都在 2.3 左右波动,下降幅度并不明显。这是因为我们只对数据看了一遍(即只训练了一轮),模型尚未充分学习到数据的特征。

复制代码
tensor(2.2978, grad_fn=<NllLossBackward0>)
tensor(2.2988, grad_fn=<NllLossBackward0>)
tensor(2.3163, grad_fn=<NllLossBackward0>)
tensor(2.3253, grad_fn=<NllLossBackward0>)
...

3. 神经网络优化多轮

单轮训练显然不够,我们需要让模型多次遍历整个数据集。下面我们引入 epoch 的概念,对数据集进行多轮训练,并统计每一轮的累计损失。

python 复制代码
import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)       
dataloader = DataLoader(dataset, batch_size=64, drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )
    def forward(self, x):
        x = self.model1(x)
        return x

loss = nn.CrossEntropyLoss()  # 交叉熵损失函数
tudui = Tudui()
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)  # 随机梯度下降优化器

for epoch in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = tudui(imgs)
        result_loss = loss(outputs, targets)  # 计算实际输出与目标输出的差距
        optim.zero_grad()  # 梯度清零
        result_loss.backward()  # 反向传播,计算损失函数的梯度
        optim.step()  # 根据梯度,对网络的参数进行调优
        running_loss = running_loss + result_loss
    print(running_loss)  # 对这一轮所有误差的总和

3.1 运行结果分析

从输出结果可以清晰地看到,随着训练轮次的增加,每一轮的累计损失在稳步下降:

复制代码
tensor(358.1069, grad_fn=<AddBackward0>)
tensor(353.8411, grad_fn=<AddBackward0>)
tensor(337.3790, grad_fn=<AddBackward0>)
tensor(317.3237, grad_fn=<AddBackward0>)
tensor(307.6762, grad_fn=<AddBackward0>)
tensor(298.2425, grad_fn=<AddBackward0>)
tensor(289.7010, grad_fn=<AddBackward0>)
tensor(282.7116, grad_fn=<AddBackward0>)
tensor(275.8972, grad_fn=<AddBackward0>)
tensor(269.5961, grad_fn=<AddBackward0>)
tensor(263.8480, grad_fn=<AddBackward0>)
tensor(258.5006, grad_fn=<AddBackward0>)
tensor(253.4671, grad_fn=<AddBackward0>)
tensor(248.7994, grad_fn=<AddBackward0>)
tensor(244.4917, grad_fn=<AddBackward0>)
tensor(240.5728, grad_fn=<AddBackward0>)
tensor(236.9719, grad_fn=<AddBackward0>)
tensor(233.6264, grad_fn=<AddBackward0>)
tensor(230.4298, grad_fn=<AddBackward0>)
tensor(227.3427, grad_fn=<AddBackward0>)

这说明多轮训练能够有效帮助模型学习数据特征,逐步降低损失。

4. 神经网络学习率优化

在训练过程中,学习率的大小直接影响模型的收敛速度和最终效果。PyTorch 提供了 torch.optim.lr_scheduler 模块,可以在训练过程中动态调整学习率。

下面我们使用 StepLR 调度器,每经过 step_size 个 epoch,将学习率乘以 gamma 进行衰减。

python 复制代码
import torch
import torchvision
from torch import nn 
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)       
dataloader = DataLoader(dataset, batch_size=64, drop_last=True)

class Tudui(nn.Module):
    def __init__(self):
        super(Tudui, self).__init__()        
        self.model1 = Sequential(
            Conv2d(3, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 32, 5, padding=2),
            MaxPool2d(2),
            Conv2d(32, 64, 5, padding=2),
            MaxPool2d(2),
            Flatten(),
            Linear(1024, 64),
            Linear(64, 10)
        )
    def forward(self, x):
        x = self.model1(x)
        return x

loss = nn.CrossEntropyLoss()  # 交叉熵损失函数
tudui = Tudui()
optim = torch.optim.SGD(tudui.parameters(), lr=0.01)  # 随机梯度下降优化器
scheduler = torch.optim.lr_scheduler.StepLR(optim, step_size=5, gamma=0.1)  # 每过 step_size 更新一次优化器,学习率变为原来的 0.1 倍

for epoch in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = tudui(imgs)
        result_loss = loss(outputs, targets)  # 计算实际输出与目标输出的差距
        optim.zero_grad()  # 梯度清零
        result_loss.backward()  # 反向传播,计算损失函数的梯度
        optim.step()  # 根据梯度,对网络的参数进行调优
        scheduler.step()  # 学习率太小了,所以20个轮次后,相当于没走多少
        running_loss = running_loss + result_loss
    print(running_loss)  # 对这一轮所有误差的总和

4.1 运行结果分析

观察输出可以发现,损失值几乎没有下降,始终维持在 359 左右:

复制代码
tensor(359.4722, grad_fn=<AddBackward0>)
tensor(359.4630, grad_fn=<AddBackward0>)
tensor(359.4630, grad_fn=<AddBackward0>)
...

这是因为我们在每个 batch 之后 都调用了 scheduler.step(),导致学习率被过早、过快地衰减到极小值。学习率太小,模型几乎无法更新参数,20 个轮次后相当于没走多少步。

4.2 正确用法

StepLR 的 step() 方法应该在每个 epoch 结束后 调用一次,而不是在每个 batch 之后。正确的写法是将 scheduler.step() 放在内层循环之外:

python 复制代码
for epoch in range(20):
    running_loss = 0.0
    for data in dataloader:
        imgs, targets = data
        outputs = tudui(imgs)
        result_loss = loss(outputs, targets)
        optim.zero_grad()
        result_loss.backward()
        optim.step()
        running_loss = running_loss + result_loss
    scheduler.step()  # 每个 epoch 结束后更新一次学习率
    print(running_loss)

5. 总结

通过本文的实战演练,我们掌握了以下核心要点:

  • 优化器:利用反向传播计算出的梯度对参数进行调整,逐步降低损失。
  • 梯度清零 :每次参数更新前必须调用 optim.zero_grad(),防止梯度累加。
  • 多轮训练 :通过 epoch 循环让模型多次遍历数据集,损失会稳步下降。
  • 学习率调度 :lr_scheduler 可以动态调整学习率,但要注意 step() 的调用时机------应在每个 epoch 结束后调用,而非每个 batch 之后。

希望本文能帮助你更好地理解 PyTorch 中神经网络优化的完整流程。欢迎在评论区交流你的训练心得!

相关推荐
段一凡-华北理工大学2 小时前
工业智能体:从Codex/Claude到项目实战~系列文章06:一张蓝图看懂“工业智能体×高炉炼铁“
人工智能·claude·codex·工业智能体·高炉炼铁智能化·高炉智能体技术栈
怕浪猫2 小时前
十个人,630 万订阅:一张 AI 学习清单里的五种老师
人工智能
JonaHu2 小时前
5分钟搭建帮你赚钱的 AI API 网站:从品牌门户到客户 Key 交付
人工智能·api·开发者·ai创业·acedatacloud
Csvn3 小时前
模型挂了全站 502?降级链让它先保住“有响应”(R04)
人工智能·aigc·agent
isNotNullX3 小时前
仓储管理最全框架:6大流程、8项指标、10个管理动作、12个常见问题
大数据·人工智能
小白菜004 小时前
AI的理解
人工智能·机器学习
科技苑4 小时前
C++ CAS与ABA问题详解,无锁编程的经典陷阱怎么解决?
c++·人工智能
DFT计算杂谈4 小时前
可DIY布里渊区图像绘制编辑器
人工智能·机器学习·语言模型·矩阵·编辑器
国科安芯4 小时前
把接口变成观测量:CANFD收发器状态可观测性驱动的星上故障检测隔离与恢复方法
大数据·人工智能·物联网·卫星·抗辐射·通信芯片·载荷