中山网站运营/上海百度推广优化公司

中山网站运营,上海百度推广优化公司,网站蜘蛛池怎么做的,网站目录做外链摘要 本文分类使用的是resNet34,什么不用yolo v8,yolo v10系列,虽然他们也可以分类,因为yolo系列模型不纯粹,里面包含了目标检测的架构,所以分类使用的是resNet 本文详细介绍了三种不同的方法来训练卷积神经网络进行 CIFAR-10 图…

摘要

本文分类使用的是resNet34,什么不用yolo v8,yolo v10系列,虽然他们也可以分类,因为yolo系列模型不纯粹,里面包含了目标检测的架构,所以分类使用的是resNet

本文详细介绍了三种不同的方法来训练卷积神经网络进行 CIFAR-10 图像分类任务,帮助读者从零开始学习如何设计和优化深度学习模型。首先,我们通过手工设计一个卷积神经网络并进行训练,讲解每个层次的作用和设计思想;接着,我们使用一个开源模型 ResNet34,并对其进行微调以适应我们的数据集;最后,我们通过一系列高效的训练技巧,显著提高训练效率,减少训练时间。最终,您将不仅能实现这一目标,而且能够举一反三,设计自己的模型,提升模型效率。每个批次由原来的111秒,变成12秒,准确率最终达到了90.24 %。


引言

在深度学习的世界中,图像分类任务无疑是最经典且最具挑战性的任务之一。CIFAR-10 数据集作为经典的图像分类数据集,包含了10个类别的32x32像素彩色图像,广泛用于模型训练和测试。今天,我们将介绍如何使用三种不同的方法来训练卷积神经网络(CNN)以完成 CIFAR-10 的分类任务。

本文的目标不仅是教会你如何实现这些方法,更重要的是让你理解背后的设计思想,以及如何将这些思想转化为你自己的模型。通过阅读本文,你将掌握设计卷积神经网络、使用开源模型以及进行高效训练的能力。

目录

  • 第一部分:手工设计卷积神经网络
    • 1.1 网络结构设计思路
    • 1.2 训练与验证模型
    • 1.3 训练效果与分析
  • 第二部分:使用开源模型——ResNet34
    • 2.1 为什么选择 ResNet34
    • 2.2 如何微调 ResNet34 适应 CIFAR-10
    • 2.3 训练与验证模型
    • 2.4 训练效果与分析
  • 第三部分:高效训练技巧
    • 3.1 为什么需要优化训练效率
    • 3.2 高效训练技巧与代码优化
    • 3.3 训练效果与分析
  • 总结与展望

第一部分:手工设计卷积神经网络

在本部分,我们将手工设计一个卷积神经网络,用于进行 CIFAR-10 图像分类。我们将会从设计网络结构开始,逐步增加网络层,并讲解每一层的作用。

1.1 网络结构设计思路

卷积神经网络(CNN)通常由以下几部分组成:

  1. 卷积层:用于提取图像的特征。通过卷积核(或滤波器)对输入图像进行卷积运算,得到特征图。
  2. 激活层:使用非线性激活函数,如 ReLU,来增加网络的表达能力。
  3. 池化层:对卷积后的特征图进行下采样,减小数据维度,保留重要特征。
  4. 全连接层:将卷积和池化操作提取的特征映射到最终的分类输出。
  5. Dropout:用于防止过拟合。

根据这些部分,我们设计了一个简单的卷积神经网络,包含两层卷积层、两层全连接层、以及 Dropout。

1.2 训练与验证模型

使用 PyTorch 框架,我们构建了卷积神经网络,并加载了 CIFAR-10 数据集。下面是训练过程的代码:(完整代码在最后面,此处只展示相关代码)

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10
from torchvision.transforms import ToTensor, Compose
import timeclass ImageClassification(nn.Module):def __init__(self):super(ImageClassification, self).__init__()# --------------------# 卷积层1:输入通道=3(RGB),输出通道=6,卷积核=3x3# 主要用来提取图像初步特征# --------------------self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3)# --------------------# BatchNorm1:对6个通道进行批归一化# 可以加速收敛、稳定训练# --------------------self.bn1 = nn.BatchNorm2d(num_features=6)# --------------------# MaxPool层:核大小=2x2,步幅=2# 进行下采样,减小特征图空间尺寸# --------------------self.pool = nn.MaxPool2d(kernel_size=2, stride=2)# --------------------# 卷积层2:输入通道=6,输出通道=16,卷积核=3x3# 继续提取更深层次特征# --------------------self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=3)# --------------------# BatchNorm2:对16个通道进行批归一化# --------------------self.bn2 = nn.BatchNorm2d(num_features=16)# --------------------# 全连接层1:输入576(16通道 * 6 * 6) => 输出120# 提取卷积层输出的全局特征# --------------------self.fc1 = nn.Linear(in_features=576, out_features=120)# --------------------# 全连接层2:输入120 => 输出84# 进一步映射到更低维空间# --------------------self.fc2 = nn.Linear(in_features=120, out_features=84)# --------------------# 全连接层3:输入84 => 输出10# 最终分类层,对应CIFAR10的10个类别# --------------------self.fc3 = nn.Linear(in_features=84, out_features=10)# --------------------# Dropout:在全连接层后,随机失活一些神经元# 防止过拟合# --------------------self.dropout = nn.Dropout(p=0.5)def forward(self, x):# x shape: (batch_size, 3, 32, 32)# 第一次卷积 => 批归一化 => ReLU => MaxPool# 结果 shape: (batch_size, 6, 14, 14)x = self.pool(torch.relu(self.bn1(self.conv1(x))))# 第二次卷积 => 批归一化 => ReLU => MaxPool# 结果 shape: (batch_size, 16, 6, 6)x = self.pool(torch.relu(self.bn2(self.conv2(x))))# 展平 => (batch_size, 16*6*6=576)x = x.view(-1, 576)# 全连接层1 => ReLU => Dropoutx = torch.relu(self.fc1(x))x = self.dropout(x)# 全连接层2 => ReLU => Dropoutx = torch.relu(self.fc2(x))x = self.dropout(x)# 最后全连接层 => 输出10维(对应CIFAR10的10个类别)x = self.fc3(x)return x

主要流程:

1. 两次卷积 + 批归一化 + ReLU + 池化

提取特征并减小特征图空间尺寸。

2. Flatten

将卷积层输出展平为一维向量,方便进入全连接层。

3. 全连接层 + Dropout

将特征映射到分类空间。Dropout 随机失活部分神经元,减少过拟合。

4. 输出 10 类

对应 CIFAR-10 的 10 个类别(0~9)。

1.3 训练效果与分析

经过100个epoch的训练,我们的网络达到了约64.85%的准确率。尽管这是一个不错的起点,但显然可以进一步提高网络的表现。因此,我们决定使用开源模型 ResNet34 来尝试提升准确率。


第二部分:使用开源模型——ResNet34

在这一部分,我们将展示如何使用 ResNet34 这一经过验证的模型,并通过微调使其适应我们的 CIFAR-10 数据集。

2.1 为什么选择 ResNet34

ResNet34 是一个深度神经网络,由多个残差模块(Residual Blocks)组成。与传统的 CNN 相比,ResNet34 能够通过跳跃连接(skip connections)解决深度网络中的梯度消失问题,从而使模型能够在非常深的层数中仍然保持有效的学习能力。

2.2 如何微调 ResNet34 适应 CIFAR-10

ResNet34 在默认情况下是为 ImageNet 数据集设计的,因此我们需要对其进行一些修改,以适应 CIFAR-10 数据集。具体来说,我们将调整第一层卷积层和最后的全连接层。

import torchvision.models as models  # 导入 torchvision 模型模块,便于加载 ResNet34 模型
import torch.nn as nn             # 导入 torch.nn 模块,用于构建和修改神经网络层def get_resnet34_cifar10():# 加载 ResNet34 模型,注意这里不使用预训练权重(pretrained=False)# 因为我们可能希望在 CIFAR-10 数据集上从头微调模型,或者你想使用自己的预训练权重model = models.resnet34(pretrained=False)# 修改第一层卷积层(conv1):# 原始 ResNet34 中的 conv1 是一个 7x7 卷积,stride=2,这会使 32x32 的 CIFAR-10 图像尺寸迅速减小# 因此我们将其替换为 3x3 卷积,stride=1,padding=1,以保持图像尺寸更稳定,便于后续的特征提取model.conv1 = nn.Conv2d(in_channels=3,       # 输入通道数为3(RGB图像)out_channels=64,     # 输出通道数为64,与原始网络保持一致kernel_size=3,       # 使用 3x3 卷积核stride=1,            # 步幅设为1,不进行下采样padding=1,           # 填充1,保证卷积后输出尺寸不变bias=False           # 不使用偏置项(后续的 BatchNorm 会处理偏置问题))# 修改最后一层全连接层(fc):# 原始 ResNet34 的 fc 层用于 ImageNet 分类,其输出类别数为1000# 这里我们将其替换为适用于 CIFAR-10 的全连接层,输出类别数设置为10model.fc = nn.Linear(in_features=model.fc.in_features,  # 保持原 fc 层输入特征数不变out_features=10                    # 输出特征数设为10,匹配 CIFAR-10 的类别数量)# 返回修改后的模型return model

代码说明

1. 导入模块

• torchvision.models:用于加载各种预定义的模型架构,如 ResNet34。

• torch.nn:提供构建和修改神经网络层所需的类和函数。

2. 加载 ResNet34 模型,但没有使用模型的网络权重

使用 models.resnet34(pretrained=False) 加载 ResNet34 模型,不使用预训练权重,因为 CIFAR-10 图像尺寸较小或你可能希望从头微调模型。

3. 修改第一层卷积

• 原始 ResNet34 的第一层卷积层使用的是 7x7 卷积核和 stride=2,会使输入图像(CIFAR-10 的 32x32 图像)尺寸大幅度缩小。

• 为了适应 CIFAR-10 的图像尺寸,我们使用 3x3 卷积核、stride=1 和 padding=1,这样可以保持特征图尺寸相对稳定,有利于后续特征提取。

4. 修改全连接层

• 原模型的全连接层输出 1000 个类别,这对应于 ImageNet 数据集。

• 将其修改为输出 10 个类别,适应 CIFAR-10 数据集的分类任务。

通过这些修改,这个函数返回一个经过定制化调整后的 ResNet34 模型,适合用来处理 CIFAR-10 图像分类任务。


2.3 训练与验证模型

在微调完成后,我们就可以使用 ResNet34 来进行训练。训练过程类似于代码1中的流程,但我们直接使用开源模型并做出适应性调整。

2.4 训练效果与分析

使用 ResNet34 后,我们发现训练时间显著提高,每个批次需要112秒,这么算100批次就要训练3小时。虽然准确率有所提升,但我们希望通过进一步优化训练过程来提高训练效率。因此直接手动kill  掉了训练过程,下面是我们的优化过程


第三部分:高效训练技巧

现在,我们已经有了一个效果较好的模型(ResNet34)。但是,训练一个大型网络,如 ResNet34,可能需要很长时间。为了提高训练效率,我们在这一部分介绍了几种高效训练的技巧。

3.1 为什么需要优化训练效率

在使用 GPU 训练时,尽管我们的 GPU(V100)具有强大的计算能力,但如果训练过程中的数据加载、内存传输等环节没有得到优化,GPU 的计算能力将得不到充分发挥。

3.2 高效训练技巧与代码优化

为了优化训练,我们采用了以下几种方法:

  1. 增加 num_workerspin_memory:通过多线程加速数据加载,同时使用 pin_memory 将数据从主机内存直接传输到 GPU,减少数据传输的时间。
  2. 混合精度训练:通过 torch.cuda.amp 进行混合精度训练,这可以在不损失精度的情况下提高计算效率。
  3. 调整批量大小(Batch Size):通过增加批量大小来提高 GPU 的利用率。
# 创建训练集和验证集的 DataLoader
# DataLoader 用于按批次加载数据,加快训练过程并支持多线程数据预处理
train_loader = DataLoader(train_dataset,           # 训练数据集batch_size=128,          # 每个批次加载128张图像shuffle=True,            # 每个epoch开始前随机打乱数据,增强模型泛化能力num_workers=8,           # 使用8个子进程并行加载数据(可根据CPU核心数调整)pin_memory=True          # 将数据加载到锁页内存中,方便快速转移到GPU
)valid_loader = DataLoader(valid_dataset,           # 验证数据集batch_size=128,          # 每个批次加载128张图像shuffle=False,           # 验证时不需要打乱数据,保持顺序便于评估num_workers=8,           # 同样使用8个子进程加速加载pin_memory=True          # 使用锁页内存,提高数据传输速度
)# 初始化混合精度训练的梯度缩放器
# 混合精度训练(Automatic Mixed Precision, AMP)可以使用float16加速训练并降低显存占用
scaler = torch.cuda.amp.GradScaler()# 开始训练过程,遍历指定的训练轮数(EPOCHS)
for epoch in range(EPOCHS):model.train()  # 切换模型到训练模式,启用Dropout和BatchNorm的训练行为for x, y in train_loader:# 将优化器中的梯度清零,防止梯度累加optimizer.zero_grad()# 自动混合精度上下文,启用 float16 运算以提高效率with torch.cuda.amp.autocast(device_type=device.type):output = model(x)           # 前向传播:计算模型输出loss = criterion(output, y) # 计算损失:比较模型输出和真实标签# 通过梯度缩放器将损失值进行缩放后反向传播,防止梯度溢出scaler.scale(loss).backward()# 使用梯度缩放器更新参数(结合自动混合精度运算的结果)scaler.step(optimizer)# 更新梯度缩放器状态,为下一个迭代做准备scaler.update()

详细说明

1. DataLoader 配置

batch_size=128:每个批次包含128张图片,有助于充分利用GPU的并行计算能力。

shuffle=True (训练集):在每个epoch开始前打乱数据,防止模型记住数据顺序,提高泛化能力。验证集通常不打乱。

num_workers=8:开启8个工作进程来加载数据,加快数据预处理和加载速度。

pin_memory=True:将加载的数据存放在页锁定内存中,以便更快地传输到GPU中。

2. 混合精度训练(AMP)

torch.cuda.amp.GradScaler():初始化梯度缩放器,用于在混合精度训练时动态调整梯度缩放因子,防止数值不稳定。

with torch.cuda.amp.autocast():在此上下文中,部分计算会自动采用半精度(float16)运算,既加快了计算速度,又降低显存消耗。

3. 训练循环

optimizer.zero_grad():清零梯度,防止前一次梯度累加。

模型前向传播:将输入数据传入模型,得到输出。

计算损失:使用损失函数(如交叉熵损失)比较模型输出与真实标签。

梯度反向传播和更新:通过梯度缩放器缩放损失后反向传播,再通过 scaler.step(optimizer) 更新模型参数,最后更新 scaler 的状态以适应下一次计算。

这样设置可以充分利用混合精度训练的优势,提高训练速度并降低显存占用,同时保证模型的数值稳定性。

3.3 训练效果与分析

经过这些优化,我们的训练时间从每个批次 111 秒缩短至 12 秒,由原来的3小时,变成了20分钟,显著提高了训练效率。从而且,准确率保持在了 87.5%。


第四部分:使用预训练权重

4.1 为什么需要ImageNet的原始权重

对于 CIFAR-10 这种数据集(相对较小且与 ImageNet 有一定相似性),大多数情况下建议使用预训练权重。使用预训练权重的优势主要有:

加速收敛:预训练模型已经学到了一些通用的低级特征(如边缘、纹理等),在微调时可以更快达到较好的效果。

更高的准确率:特别是在数据量不足以从头训练深层网络时,预训练权重可以帮助避免过拟合,并提高最终的分类准确率。

稳定性更好:预训练模型在大规模数据集(如 ImageNet)上经过充分训练,其特征具有较好的泛化性,迁移到 CIFAR-10 上通常能获得较稳定的性能。

当然,如果你有足够的数据或你希望针对特定领域做更深入的调整,也可以考虑从头训练,但这往往需要更多的计算资源和更长的训练时间。

综上所述,我的建议是:对于 CIFAR-10 等常见数据集,建议使用原始的 ImageNet 预训练权重,然后进行微调。这通常能带来更好的效果和更快的训练收敛。

4.2 使用ImageNet的原始权重代码

import torchvision.models as models  # 导入 torchvision 中的预定义模型
import torch.nn as nn               # 导入 torch.nn 模块,用于构建和修改网络层def get_resnet34_cifar10():"""构建适用于 CIFAR-10 图像分类任务的 ResNet34 模型。该函数主要做了以下三处修改:1) 修改第一个卷积层(conv1):将原来的 7x7 卷积替换成 3x3 卷积,并设置 stride=1 和 padding=1,以防止 CIFAR-10 图像(32×32)的尺寸被过度缩小。2) 修改最大池化层(maxpool):将其替换成 Identity(),也就是不做任何池化操作。3) 修改最后全连接层(fc):将输出类别数从原来的 1000 改为 10(因为 CIFAR-10 有 10 类)。"""# 使用 torchvision 官方提供的 ResNet34 模型,并加载在 ImageNet 1K 数据集上预训练的权重# 这里使用的是 weights=models.ResNet34_Weights.IMAGENET1K_V1net = models.resnet34(weights=models.ResNet34_Weights.IMAGENET1K_V1)# weights=models.ResNet34_Weights.IMAGENET1K_V1 的解释:# - models.ResNet34_Weights 是一个枚举类,其中定义了 ResNet34 模型可用的预训练权重选项。# - IMAGENET1K_V1 是该枚举中的一个具体成员,表示在 ImageNet 1K 数据集上训练得到的第一个版本的预训练权重。#   使用这个权重可以让模型初始状态已经学到大量通用的视觉特征,#   这对于迁移学习和微调任务(例如 CIFAR-10 分类)非常有帮助。# 修改第一层卷积层:# 原始 ResNet34 的 conv1 层默认使用的是 7x7 卷积核,stride=2,# 这对于尺寸只有32x32的 CIFAR-10 图像来说会过于激进,导致特征图尺寸急剧减小,# 所以这里我们使用一个 3x3 的卷积核,并设置 stride=1 和 padding=1,# 这样可以更好地保留图像的细节信息。net.conv1 = nn.Conv2d(in_channels=3,    # 输入通道数为3(RGB图像)out_channels=64,  # 输出通道数为64,保持与原模型一致kernel_size=3,    # 使用 3x3 的卷积核stride=1,         # 步幅设为1,不进行下采样padding=1,        # 填充1,保证卷积后输出尺寸与输入相同bias=False        # 不使用偏置项(通常和 BatchNorm 搭配时可省略偏置))# 修改 maxpool 层:# 原始模型中 conv1 后接有一个 3x3 的最大池化层(stride=2),# 用于快速减小特征图尺寸,但对于小尺寸图像(如32x32)来说可能导致信息丢失,# 因此这里将 maxpool 替换为 Identity() 层,即直接将输入原样传递,不做池化操作。net.maxpool = nn.Identity()# 修改全连接层:# 原始 ResNet34 的 fc 层输出 1000 维向量(对应 ImageNet 1000 个类别),# 这里我们将其替换为一个新的全连接层,使得输出类别数变为 10,# 以适应 CIFAR-10 数据集的分类任务。net.fc = nn.Linear(in_features=net.fc.in_features,  # 保持输入特征数不变(通常为512)out_features=10,                 # 输出10个类别的概率bias=True                        # 使用偏置项)# 返回经过修改的模型return net

详细讲解

1. 预训练权重的选择:

• 使用 weights=models.ResNet34_Weights.IMAGENET1K_V1 可以加载在 ImageNet 1K 数据集上预训练好的权重。

为什么选择这个?

预训练优势:ImageNet 数据集包含百万级图像和1000个类别,模型在此数据上学到了大量通用的视觉特征(如边缘、纹理等),这些特征在其他图像分类任务中(例如 CIFAR-10)也非常有效。

加速收敛:预训练模型提供了一个较好的初始化状态,微调时可以更快达到较优性能,避免从头训练带来的长时间收敛问题。

稳定性:预训练权重通常经过大量数据训练,具有良好的泛化能力,在数据量较少的 CIFAR-10 上使用可以减少过拟合风险。

2. 为什么修改第一层卷积层:

• 原始 ResNet34 的第一层是 7x7 卷积,stride=2,这对 ImageNet(224×224)这样的高分辨率图像效果很好,但对于 32×32 的 CIFAR-10 图像来说,会使得特征图尺寸过小,从而丢失很多细节信息。

• 将卷积核修改为 3x3、stride=1 和 padding=1 后,可以保持较多原始信息,有助于在后续层中提取更有区分度的特征。

3. 为什么去掉 MaxPool 层:

• 在原始 ResNet 中,conv1 后的池化层用于进一步减小特征图尺寸。但对于 CIFAR-10 这种低分辨率图像,过早下采样会使得信息量严重减少。

• 用 nn.Identity() 替换 maxpool 层,即让数据原样传递,不做任何池化,这样可以保留更多细节,有助于分类任务。

4. 修改全连接层的原因:

• 原始 ResNet34 的全连接层输出 1000 个类别,用于 ImageNet 分类。

• CIFAR-10 数据集只有 10 个类别,所以需要将 fc 层的输出维度调整为 10,以适应具体任务。


4.3 训练效果与分析

 经过使用原始权重这些优化,我们的训练时间依旧 12 秒,但是显著提高了准确率。准确率保持在了 90.24 %。

因此90.24 %准确率的分类,该分类模型已经完成了产品的需求是可以直接应用市场的了~


 4.3 关于应用自己训练或他人训练的模型权重

使用自己训练的模型权重

• 假设你已经使用类似的网络结构训练了一个模型,并将权重保存到了一个文件(例如 my_weights.pth),你可以这样加载并应用权重:

# 加载自己训练好的权重文件
state_dict = torch.load('my_weights.pth', map_location=device)
model = get_resnet34_cifar10()  # 构建模型架构
model.load_state_dict(state_dict)  # 将保存的权重加载到模型中

• 这里需要注意:如果你保存的是整个模型(包括结构和权重),使用 torch.load 直接加载可能需要调用 torch.load('my_model.pth');但通常建议只保存权重(state_dict),这样更灵活。

使用他人训练的模型权重也是一样的

• 如果你下载了他人训练好的权重文件,流程与上面类似:

state_dict = torch.load('downloaded_weights.pth', map_location=device)
model = get_resnet34_cifar10()  # 构建模型架构
model.load_state_dict(state_dict)  # 加载他人训练好的权重

• 确保你下载的权重文件与模型结构完全一致,否则可能出现加载错误。如果权重来自不同的版本或结构,需要先做适配。

通过这种方式,无论是使用自己训练的权重还是下载的权重,你都可以将预训练好的参数加载到模型中,进行微调或直接推理。


完整代码全过程:


代码1:手工设计卷积神经网络分类模型
 

import torch
import torch.nn as nn
from torchvision.datasets import CIFAR10
from torchvision.transforms import ToTensor, Compose
import torch.optim as optim
from torch.utils.data import DataLoader
import time
import matplotlib.pyplot as plt
from torchsummary import summary# 配置字体参数
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False# 额外导入,用于混淆矩阵可视化
import numpy as np
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# 全局超参数
BATCH_SIZE = 8           # 批量大小
EPOCHS = 100              # 训练轮数(演示时设置10,实际可设置更多)
LEARNING_RATE = 1e-3     # 学习率def create_dataset():"""加载 CIFAR10 数据集,并返回训练集和测试集。数据集下载路径: root='data'这里使用 ToTensor() 将图片转为 PyTorch 张量。Returns:train (Dataset): 训练集valid (Dataset): 测试集"""train = CIFAR10(root='data',train=True,transform=Compose([ToTensor()]),download=True)valid = CIFAR10(root='data',train=False,transform=Compose([ToTensor()]),download=True)return train, validclass ImageClassification(nn.Module):"""该卷积神经网络用于对 CIFAR10 数据集进行分类。输入: 3 通道图像 (3, 32, 32)输出: 10 类别网络结构(加入 BatchNorm 后):- Conv1: 输入通道=3, 输出通道=6, 卷积核=3x3- BatchNorm1: 对6个通道进行归一化- ReLU- MaxPool1: 核大小=2x2, 步长=2- Conv2: 输入通道=6, 输出通道=16, 卷积核=3x3- BatchNorm2: 对16个通道进行归一化- ReLU- MaxPool2: 核大小=2x2, 步长=2- Flatten => 全连接层- Linear1: 输入576 -> 输出120- ReLU- Dropout (防止过拟合)- Linear2: 输入120 -> 输出84- ReLU- Dropout- Output: 输入84 -> 输出10"""def __init__(self):super(ImageClassification, self).__init__()# 第一个卷积层self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1)# 第一个批归一化层self.bn1 = nn.BatchNorm2d(num_features=6)# 第一个池化层self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)# 第二个卷积层self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=3, stride=1)# 第二个批归一化层self.bn2 = nn.BatchNorm2d(num_features=16)# 第二个池化层self.pool2 = nn.MaxPool2d(kernel_size=2, stride=2)# 全连接层前,特征图大小 = 16通道 * 6 * 6 = 576self.linear1 = nn.Linear(in_features=576, out_features=120)self.dropout1 = nn.Dropout(p=0.5)  # 添加 Dropoutself.linear2 = nn.Linear(in_features=120, out_features=84)self.dropout2 = nn.Dropout(p=0.5)  # 添加 Dropoutself.out = nn.Linear(in_features=84, out_features=10)def forward(self, x):"""前向传播流程:1) Conv1 -> BatchNorm1 -> ReLU -> Pool12) Conv2 -> BatchNorm2 -> ReLU -> Pool23) Flatten4) Linear1 -> ReLU -> Dropout5) Linear2 -> ReLU -> Dropout6) 输出层"""# 第一次卷积x = self.conv1(x)# 加入 BatchNorm1 归一化x = self.bn1(x)# 激活函数x = torch.relu(x)# 第一次池化x = self.pool1(x)# 第二次卷积x = self.conv2(x)# 加入 BatchNorm2 归一化x = self.bn2(x)# 激活函数x = torch.relu(x)# 第二次池化x = self.pool2(x)# 将特征图展平为向量x = x.view(x.size(0), -1)# 全连接层 + 激活 + Dropoutx = torch.relu(self.linear1(x))x = self.dropout1(x)x = torch.relu(self.linear2(x))x = self.dropout2(x)# 输出层x = self.out(x)return xdef validate_model(model, valid_loader, criterion, device):"""在验证集上评估模型的损失和准确率。参数:model (nn.Module): 当前待评估的神经网络模型valid_loader (DataLoader): 验证集数据加载器,用于逐批次提供验证数据criterion: 损失函数,这里通常使用交叉熵损失函数 CrossEntropyLoss返回:avg_loss (float): 验证集上的平均损失值accuracy (float): 验证集上的整体准确率"""model.eval()  # 将模型设置为评估模式。在评估模式下,Dropout、BatchNorm等层会表现为推理模式,# 例如 Dropout 会关闭随机失活,BatchNorm会使用固定的均值和方差进行归一化,# 这样可以确保在验证或测试时模型行为稳定,不受训练时随机因素的影响。total_loss = 0.0    # 初始化累计损失,用于累加每个批次计算得到的损失值total_correct = 0   # 初始化累计正确预测的样本数,用于计算准确率total_samples = 0   # 初始化累计样本总数,用于后续计算准确率# 使用 torch.no_grad() 上下文管理器,表示在该代码块内不计算梯度,降低内存消耗、提高运行速度。with torch.no_grad():# 遍历验证集 DataLoader 中的每个批次数据for x, y in valid_loader:x, y = x.to(device), y.to(device)# x: 当前批次输入数据,形状通常为 (batch_size, channels, height, width)# y: 当前批次对应的真实标签,形状通常为 (batch_size,)output = model(x)  # 将输入 x 传入模型进行前向传播,得到输出结果 output,# output 通常是一个形状为 (batch_size, num_classes) 的张量,# 每一行表示每个样本各类别的预测分数或概率(在未经过 softmax 的情况下为logits)loss = criterion(output, y)# 计算当前批次的损失值:# 将模型输出 output 与真实标签 y 输入到损失函数中,# 例如对于多分类问题常用的 CrossEntropyLoss,该损失函数内部会对 output 进行 softmax 计算,# 并根据真实标签 y 计算交叉熵损失。total_loss += loss.item()# loss.item() 将当前批次的损失值从张量中取出为 Python 数值,# 并累加到 total_loss 中,目的是统计整个验证集上的累计损失。preds = torch.argmax(output, dim=-1)# torch.argmax(output, dim=-1) 沿着最后一个维度(类别维度)取最大值的索引,# 得到模型对每个样本预测的类别标签。即预测类别为得分最高的那一项。total_correct += (preds == y).sum().item()# (preds == y) 会返回一个布尔型张量,表示每个预测是否与真实标签相等,# .sum() 对布尔值进行求和(True 计为1,False计为0),得到本批次预测正确的数量,# .item() 将结果转换为 Python 数值,并累加到 total_correct 中。total_samples += len(y)# 累加本批次样本数。 len(y) 返回当前批次的样本数量,# 累加到 total_samples 中,用于后续计算整体准确率。# 计算整个验证集的平均损失avg_loss = total_loss / len(valid_loader)# len(valid_loader) 表示验证集中批次数目,所以 avg_loss 是每个批次的平均损失值。# 计算验证集的准确率accuracy = total_correct / total_samples# 用累计正确预测数除以总样本数,得到准确率(一个介于0到1之间的浮点数)return avg_loss, accuracydef train_model(model, train_dataset, valid_dataset, device):"""使用给定的模型和训练数据集进行训练,并在每个Epoch结束后在验证集上评估。- 损失函数: CrossEntropyLoss(适用于多分类问题)- 优化器: AdamW(自带权重衰减,改进版Adam)- 保存最优模型(基于验证集准确率)- 记录训练过程中每个Epoch的Loss和Accuracy,用于后续可视化"""# 创建交叉熵损失函数对象,用于计算模型输出和真实标签之间的误差criterion = nn.CrossEntropyLoss()# 创建AdamW优化器,传入模型所有参数和设定学习率optimizer = optim.AdamW(model.parameters(), lr=LEARNING_RATE)# 利用 DataLoader 将训练数据集按照指定批量大小随机打乱后加载train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)# 验证集 DataLoader,不需要打乱顺序valid_loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False)# 初始化两个列表,用于保存每个Epoch的训练损失和验证损失train_losses, valid_losses = [], []# 初始化两个列表,用于保存每个Epoch的训练准确率和验证准确率train_accuracies, valid_accuracies = [], []# 初始化一个变量记录历史最高验证准确率,用于保存最佳模型best_acc = 0.0# 指定保存最佳模型的文件路径best_model_path = './深度学习实战/model/image_classification/image_classification_best.pth'# 开始遍历每个训练周期(Epoch)for epoch_idx in range(EPOCHS):# 将模型设置为训练模式,这会启用Dropout、BatchNorm等训练时的特殊行为model.train()# 记录当前Epoch开始的时间,用于计算训练耗时start_time = time.time()# 初始化本Epoch内累计的训练损失、正确预测数和样本总数total_loss = 0.0total_correct = 0total_samples = 0# 遍历训练数据加载器中的每个批次数据for x, y in train_loader:# 将当前批次数据迁移到指定设备x, y = x.to(device), y.to(device)# 前向传播:将当前批次输入数据 x 传入模型,得到预测输出 outputoutput = model(x)# 计算当前批次的损失值:将模型输出和真实标签 y 输入到损失函数中计算交叉熵损失loss = criterion(output, y)# 优化器梯度归零,防止梯度累加(PyTorch中梯度默认累加)optimizer.zero_grad()# 反向传播:根据当前损失计算梯度loss.backward()# 更新模型参数:根据计算得到的梯度和优化器规则更新参数optimizer.step()# 累加当前批次的损失值(loss.item()转换为Python数值)total_loss += loss.item()# 使用torch.argmax在最后一个维度上取最大值的索引,得到每个样本预测的类别preds = torch.argmax(output, dim=-1)# 比较预测类别与真实标签,统计当前批次中预测正确的样本数,并累加到total_correcttotal_correct += (preds == y).sum().item()# 累加当前批次的样本数total_samples += len(y)# 计算本Epoch的平均训练损失:总损失除以批次数(即train_loader中批次数量)avg_train_loss = total_loss / len(train_loader)# 计算本Epoch的训练准确率:正确预测样本数除以总样本数train_accuracy = total_correct / total_samples# 在验证集上评估模型性能,调用validate_model函数返回验证集平均损失和准确率avg_valid_loss, valid_accuracy = validate_model(model, valid_loader, criterion, device)# 将本Epoch的训练和验证损失、准确率分别记录到对应的列表中,便于后续绘图和分析train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accuracies.append(train_accuracy)valid_accuracies.append(valid_accuracy)# 如果当前Epoch的验证准确率优于历史最佳验证准确率,则更新best_acc并保存当前模型参数if valid_accuracy > best_acc:best_acc = valid_accuracytorch.save(model.state_dict(), best_model_path, _use_new_zipfile_serialization=True)# 计算本Epoch耗时:当前时间减去开始时间elapsed = time.time() - start_time# 打印本Epoch的训练信息,包含训练损失、训练准确率、验证损失、验证准确率和耗时print(f"Epoch [{epoch_idx+1}/{EPOCHS}] "f"训练损失: {avg_train_loss:.4f}, 训练准确率: {train_accuracy:.2f}, "f"验证损失: {avg_valid_loss:.4f}, 验证准确率: {valid_accuracy:.2f}, "f"耗时: {elapsed:.2f}s")# 训练结束后,将最终模型(最后一次训练得到的参数)保存到指定文件中final_model_path = './深度学习实战/model/image_classification/image_classification_last.pth'torch.save(model.state_dict(), final_model_path, _use_new_zipfile_serialization=True)print(f"训练完成!最优验证准确率: {best_acc:.2f}, 模型已保存至: {best_model_path} 和 {final_model_path}")# 返回训练过程中每个Epoch的训练/验证损失和训练/验证准确率,方便后续进行曲线可视化return train_losses, valid_losses, train_accuracies, valid_accuraciesdef plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):"""可视化训练过程中每个Epoch的损失曲线和准确率曲线。参数:train_losses (list): 一个列表,保存了每个Epoch的训练损失值valid_losses (list): 一个列表,保存了每个Epoch的验证损失值train_accs (list): 一个列表,保存了每个Epoch的训练准确率valid_accs (list): 一个列表,保存了每个Epoch的验证准确率"""# 创建一个迭代器,从1开始到训练轮数(Epoch)+1# epochs_range 中的每个值代表一个Epoch的编号epochs_range = range(1, len(train_losses) + 1)# 创建一个包含1行2列子图的画布,画布大小设定为宽12英寸,高5英寸# 这样可以在同一画布上并排显示两个图,左边显示Loss曲线,右边显示Accuracy曲线fig, axs = plt.subplots(1, 2, figsize=(12, 5))# 绘制左侧子图:Loss曲线# 在左侧子图上绘制训练Loss曲线,使用圆形标记('o')axs[0].plot(epochs_range, train_losses, label='训练Loss', marker='o')# 同样在左侧子图上绘制验证Loss曲线,使用叉形标记('x')axs[0].plot(epochs_range, valid_losses, label='验证Loss', marker='x')# 设置X轴的标签为"训练轮数"axs[0].set_xlabel('训练轮数')# 设置Y轴的标签为"Loss"axs[0].set_ylabel('Loss')# 设置子图的标题为"训练 & 验证 Loss"axs[0].set_title('训练 & 验证 Loss')# 在左侧子图中显示图例,图例会自动显示各曲线对应的label名称axs[0].legend()# 绘制右侧子图:Accuracy曲线# 在右侧子图上绘制训练准确率曲线,使用圆形标记('o')axs[1].plot(epochs_range, train_accs, label='训练Accuracy', marker='o')# 在右侧子图上绘制验证准确率曲线,使用叉形标记('x')axs[1].plot(epochs_range, valid_accs, label='验证Accuracy', marker='x')# 设置X轴的标签为"训练轮数"axs[1].set_xlabel('训练轮数')# 设置Y轴的标签为"Accuracy"axs[1].set_ylabel('Accuracy')# 设置子图的标题为"训练 & 验证 Accuracy"axs[1].set_title('训练 & 验证 Accuracy')# 在右侧子图中显示图例axs[1].legend()# 调整子图布局,使其不会重叠,并使整个画布看起来更紧凑plt.tight_layout()# 显示图像plt.show()def test_model_and_confusion(valid_dataset, device):"""使用验证集上表现最优的模型,在测试集上评估准确率,并生成混淆矩阵。参数:valid_dataset (Dataset): 验证集或测试集数据集device (torch.device): 指定的设备(如 CPU、GPU 或 MPS),用于模型和数据的迁移"""# 利用 DataLoader 构建验证集的批次数据加载器# batch_size 为全局变量 BATCH_SIZE,shuffle 设置为 False 保持顺序(便于统计和混淆矩阵计算)valid_loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False)# 指定保存最优模型的路径best_model_path = './深度学习实战/model/image_classification/image_classification_best.pth'# 实例化 ImageClassification 模型对象(必须保证模型结构与训练时一致)model = ImageClassification()# 加载保存的最佳模型参数# torch.load(best_model_path) 从指定路径加载保存的模型参数(state_dict)# model.load_state_dict(...) 将加载的参数应用到当前模型中model.load_state_dict(torch.load(best_model_path))model = model.to(device)# 设置模型为评估模式# eval() 会使模型进入推理状态,关闭 Dropout、使 BatchNorm 使用训练时计算的均值和方差等model.eval()# 初始化统计变量,用于累加预测正确的样本数和总样本数total_correct = 0total_samples = 0# 初始化两个列表,分别用于记录所有预测的标签和真实标签all_preds = []all_labels = []# 使用 torch.no_grad() 表示在验证和测试阶段不计算梯度,降低内存占用、提高推理速度with torch.no_grad():# 遍历验证数据加载器中的每个批次for x, y in valid_loader:# 将当前批次的输入数据 x 和标签 y 迁移到指定设备上(例如 GPU 或 MPS)x, y = x.to(device), y.to(device)# 将输入数据传入模型进行前向传播,得到输出结果 output# output 的形状通常为 (batch_size, num_classes)output = model(x)# 通过 torch.argmax 从 output 中选取得分最大的索引作为预测的类别# 这里 dim=-1 表示沿着最后一个维度进行比较,即每个样本在所有类别上的得分preds = torch.argmax(output, dim=-1)# 将当前批次的预测结果转换为 CPU 上的 NumPy 数组,并追加到 all_preds 列表中# 这样做便于后续使用 scikit-learn 计算混淆矩阵all_preds.extend(preds.cpu().numpy())# 同理,将当前批次的真实标签转换为 NumPy 数组并追加到 all_labels 列表中all_labels.extend(y.cpu().numpy())# 比较预测结果与真实标签,统计预测正确的样本数# (preds == y) 会生成一个布尔张量,.sum() 求和(True 计为1),再用 .item() 转为 Python 数值total_correct += (preds == y).sum().item()# 累计本批次的样本数(len(y) 返回当前批次中样本的数量)total_samples += len(y)# 计算整体准确率:正确预测的样本数除以总样本数accuracy = total_correct / total_samples# 打印在测试集上的准确率,格式化为百分比,保留两位小数print(f"使用最优模型在测试集上的准确率: {accuracy * 100:.2f}%")# 利用 scikit-learn 的 confusion_matrix 函数计算混淆矩阵# 参数 all_labels 为真实标签列表,all_preds 为模型预测的标签列表cm = confusion_matrix(all_labels, all_preds)# 利用 ConfusionMatrixDisplay 将混淆矩阵可视化# display_labels=range(10) 表示显示 0~9 共10个类别的标签disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=range(10))# 绘制混淆矩阵图像,cmap=plt.cm.Blues 设置配色方案为蓝色渐变disp.plot(cmap=plt.cm.Blues)# 为混淆矩阵图添加标题plt.title("CIFAR10 混淆矩阵")# 显示混淆矩阵图plt.show()if __name__ == '__main__':import os# 判断是否在远程服务器环境if os.path.exists("/home/ubuntu/data/pycharm_project_377"):# 远程服务器环境os.chdir("/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject")print(os.getcwd())else:# 本地环境os.chdir("/Users/coyi/PycharmProjects/GPU_32_pythonProject")print(os.getcwd())# 从这里开始,你原有的代码无需修改# 假设后续代码中有 ./model/image_classification 之类的相对路径# 它们都会基于你上面 chdir 到的目录进行解析# 检查 GPU 是否可用;如果没有,则检查苹果 MPS 是否可用;否则使用 CPUif torch.cuda.is_available():device = torch.device("cuda")elif torch.backends.mps.is_available():device = torch.device("mps")else:device = torch.device("cpu")print("使用的设备:", device)# 1. 加载数据集并查看基本信息train_dataset, valid_dataset = create_dataset()print("训练集类别映射:", train_dataset.class_to_idx)print("训练集数据形状:", train_dataset.data.shape)print("测试集数据形状:", valid_dataset.data.shape)# 2. 可视化一张训练集图片plt.figure(figsize=(2, 2))plt.imshow(train_dataset.data[1])plt.title(f"类别索引: {train_dataset.targets[1]}")plt.show()# 3. 实例化网络并查看结构model = ImageClassification().to(device)summary(model, input_size=(3, 32, 32), batch_size=1)# model = model# 4. 训练模型并记录曲线数据train_losses, valid_losses, train_accs, valid_accs = train_model(model, train_dataset, valid_dataset, device)# 5. 绘制训练过程曲线plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# 6. 使用最佳模型进行测试并展示混淆矩阵test_model_and_confusion(valid_dataset, device)

代码1输出:

 


代码2: 使用ResNet34 自己从0训练权重,未优化,未使用原始权重

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10
from torchvision.transforms import ToTensor, Composeimport torchvision.models as models  # 用于加载官方resnet34
import time
import matplotlib.pyplot as plt
from torchsummary import summary# 方便中文显示的 matplotlib 配置
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = Falseimport numpy as np
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# ---------------------------
# 1. 全局超参数
# ---------------------------
BATCH_SIZE = 8            # 每批处理多少张图
EPOCHS = 100              # 训练轮数
LEARNING_RATE = 1e-3      # 学习率# ---------------------------
# 2. 数据集加载函数
# ---------------------------
def create_dataset():"""加载 CIFAR-10 数据集。CIFAR-10:训练集50000张,测试集10000张,图像32x32,RGB三通道,分成10类。ToTensor() 会把 [0,255] 的像素值归一化到 [0,1] 的 float32 张量。download=True 会自动下载到根目录 data/ 下。"""train = CIFAR10(root='data',        # 指定下载/存储路径train=True,         # True表示训练集transform=Compose([ToTensor()]),  # 将图像转为张量download=True       # 若本地无数据则自动下载)valid = CIFAR10(root='data',train=False,        # False表示测试集transform=Compose([ToTensor()]),download=True)return train, valid# ---------------------------
# 3. 构建 resnet34 模型并适配 CIFAR-10
# ---------------------------
def get_resnet34_cifar10():"""使用官方 torchvision.models.resnet34(pretrained=False)。1) 将 conv1 改成 3x3, stride=1, padding=12) 将 maxpool 改成 Identity() => 不执行池化3) 将 fc 改成输出通道数=10(CIFAR10有10类)"""net = models.resnet34(pretrained=False)  # 不使用ImageNet预训练权重# 修改第一层卷积 => 原本7x7, stride=2,容易把32x32图像缩得太小net.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)# 去掉 maxpool => 不进行额外下采样net.maxpool = nn.Identity()# 最后全连接层 => 改为输出10类net.fc = nn.Linear(in_features=512, out_features=10, bias=True)return net# ---------------------------
# 4. 验证过程
# ---------------------------
def validate_model(model, valid_loader, criterion, device):"""在验证集/测试集上计算Loss和Accuracy,用于评估模型性能。model: 当前的神经网络valid_loader: 验证集DataLoadercriterion: 损失函数device: 'cuda' 或 'cpu'返回: (平均loss, 准确率)"""model.eval()  # 切换到评估模式,关闭Dropout等随机操作total_loss = 0.0total_correct = 0total_samples = 0with torch.no_grad():for x, y in valid_loader:x, y = x.to(device), y.to(device)  # 将数据放到指定设备上output = model(x)                 # 前向传播loss = criterion(output, y)       # 计算损失total_loss += loss.item()# argmax => 找到输出张量中得分最大的类别preds = torch.argmax(output, dim=-1)total_correct += (preds == y).sum().item()total_samples += len(y)avg_loss = total_loss / len(valid_loader)accuracy = total_correct / total_samplesreturn avg_loss, accuracy# ---------------------------
# 5. 训练过程
# ---------------------------
def train_model(model, train_dataset, valid_dataset, device):"""训练并验证模型。若发现更高验证集准确率则保存最优权重。返回各Epoch的Loss和Accuracy,供绘图使用。"""criterion = nn.CrossEntropyLoss()                     # 交叉熵损失optimizer = optim.AdamW(model.parameters(), lr=LEARNING_RATE)  # AdamW优化器train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)valid_loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False)train_losses, valid_losses = [], []train_accs, valid_accs = [], []best_acc = 0.0# 当验证集准确率超过之前最优时,保存到 best_model_pathbest_model_path = './深度学习实战/model/resnet34_cifar10_best.pth'for epoch_idx in range(EPOCHS):model.train()  # 切换到训练模式start_time = time.time()total_loss = 0.0total_correct = 0total_samples = 0for x, y in train_loader:x, y = x.to(device), y.to(device)output = model(x)         # 前向loss = criterion(output, y)optimizer.zero_grad()     # 梯度清零loss.backward()           # 反向传播optimizer.step()          # 更新参数total_loss += loss.item()preds = torch.argmax(output, dim=-1)total_correct += (preds == y).sum().item()total_samples += len(y)avg_train_loss = total_loss / len(train_loader)train_acc = total_correct / total_samples# 在验证集上评估avg_valid_loss, valid_acc = validate_model(model, valid_loader, criterion, device)train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accs.append(train_acc)valid_accs.append(valid_acc)# 如果当前验证准确率超过历史最优 => 保存if valid_acc > best_acc:best_acc = valid_acctorch.save(model.state_dict(), best_model_path)elapsed = time.time() - start_timeprint(f"Epoch [{epoch_idx+1}/{EPOCHS}] "f"Train Loss: {avg_train_loss:.4f}, Train Acc: {train_acc:.2f}, "f"Valid Loss: {avg_valid_loss:.4f}, Valid Acc: {valid_acc:.2f}, "f"Time: {elapsed:.2f}s")# 最后再保存一次final_path = './深度学习实战/model/resnet34_cifar10_last.pth'torch.save(model.state_dict(), final_path)print(f"训练完成!最优验证准确率: {best_acc:.2f}. 模型已保存到 {best_model_path} 和 {final_path}")return train_losses, valid_losses, train_accs, valid_accs# ---------------------------
# 6. 绘制训练过程曲线
# ---------------------------
def plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):"""传入训练/验证Loss和Acc的列表,画出随Epoch变化的曲线。"""epochs_range = range(1, len(train_losses)+1)fig, axs = plt.subplots(1, 2, figsize=(12,5))# 左边画 Lossaxs[0].plot(epochs_range, train_losses, label='Train Loss', marker='o')axs[0].plot(epochs_range, valid_losses, label='Valid Loss', marker='x')axs[0].set_xlabel('Epochs')axs[0].set_ylabel('Loss')axs[0].set_title('Train & Valid Loss')axs[0].legend()# 右边画 Accuracyaxs[1].plot(epochs_range, train_accs, label='Train Acc', marker='o')axs[1].plot(epochs_range, valid_accs, label='Valid Acc', marker='x')axs[1].set_xlabel('Epochs')axs[1].set_ylabel('Accuracy')axs[1].set_title('Train & Valid Accuracy')axs[1].legend()plt.tight_layout()plt.show()# ---------------------------
# 7. 使用最佳模型 + 混淆矩阵
# ---------------------------
def test_model_and_confusion(valid_dataset, device):"""加载最优模型,在测试集上计算准确率并绘制混淆矩阵。"""loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False)# 初始化网络 => 需要跟训练时的结构一致model = get_resnet34_cifar10()best_model_path = './深度学习实战/model/resnet34_cifar10_best.pth'model.load_state_dict(torch.load(best_model_path))  # 加载最佳权重model = model.to(device)model.eval()total_correct = 0total_samples = 0all_preds = []all_labels = []with torch.no_grad():for x, y in loader:x, y = x.to(device), y.to(device)output = model(x)preds = torch.argmax(output, dim=-1)# 用于混淆矩阵all_preds.extend(preds.cpu().numpy())all_labels.extend(y.cpu().numpy())total_correct += (preds == y).sum().item()total_samples += len(y)acc = total_correct / total_samplesprint(f"使用最优模型在测试集上的准确率: {acc*100:.2f}%")# 混淆矩阵cm = confusion_matrix(all_labels, all_preds)disp = ConfusionMatrixDisplay(cm, display_labels=range(10))disp.plot(cmap=plt.cm.Blues)plt.title("resnet34 on CIFAR10 Confusion Matrix")plt.show()# ---------------------------
# 8. 主函数入口
# ---------------------------
if __name__ == '__main__':import os# 检查是远程路径还是本地路径if os.path.exists("/home/ubuntu/data/pycharm_project_377"):os.chdir("/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject")print("当前工作目录(远程):", os.getcwd())else:os.chdir("/Users/coyi/PycharmProjects/GPU_32_pythonProject")print("当前工作目录(本地):", os.getcwd())# 检查设备 => 优先用 GPUif torch.cuda.is_available():device = torch.device("cuda:0")else:device = torch.device("cpu")print("使用的设备:", device)# (1) 加载CIFAR10train_dataset, valid_dataset = create_dataset()print("训练集类别映射:", train_dataset.class_to_idx)print("训练集数据形状:", train_dataset.data.shape)print("测试集数据形状:", valid_dataset.data.shape)# (2) 可视化一张图plt.figure()plt.imshow(train_dataset.data[0])plt.title(f"标签: {train_dataset.targets[0]}")plt.show()# (3) 实例化 resnet34 并查看结构model = get_resnet34_cifar10().to(device)summary(model, input_size=(3, 32, 32), batch_size=1)# (4) 训练train_losses, valid_losses, train_accs, valid_accs = train_model(model, train_dataset, valid_dataset, device)# (5) 可视化训练plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# (6) 测试集上做预测 + 混淆矩阵test_model_and_confusion(valid_dataset, device)

 输出:

然后被我KIll了,因为太慢了


代码3: 使用ResNet34 自己从0训练权重,优化训练过程,未使用原始权重

代码是在代码2的基础上加了优化过程

import torchtorch.backends.cudnn.benchmark = True #torch.backends.cudnn.benchmark = True 会自动搜索最佳的卷积算法,当输入尺寸固定时可以大幅加速卷积计算。
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10
from torchvision.transforms import ToTensor, Composeimport torchvision.models as models  # 用于加载官方resnet34
import time
import matplotlib.pyplot as plt
from torchsummary import summary# 方便中文显示的 matplotlib 配置
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = Falseimport numpy as np
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# ---------------------------
# 1. 全局超参数
# ---------------------------
BATCH_SIZE = 256            # 每批处理多少张图
EPOCHS = 100              # 训练轮数
LEARNING_RATE = 1e-3      # 学习率# ---------------------------
# 2. 数据集加载函数
# ---------------------------
def create_dataset():"""加载 CIFAR-10 数据集。CIFAR-10:训练集50000张,测试集10000张,图像32x32,RGB三通道,分成10类。ToTensor() 会把 [0,255] 的像素值归一化到 [0,1] 的 float32 张量。download=True 会自动下载到根目录 data/ 下。"""train = CIFAR10(root='data',        # 指定下载/存储路径train=True,         # True表示训练集transform=Compose([ToTensor()]),  # 将图像转为张量download=True       # 若本地无数据则自动下载)valid = CIFAR10(root='data',train=False,        # False表示测试集transform=Compose([ToTensor()]),download=True)return train, valid# ---------------------------
# 3. 构建 resnet34 模型并适配 CIFAR-10
# ---------------------------
def get_resnet34_cifar10():"""使用官方 torchvision.models.resnet34(pretrained=False)。1) 将 conv1 改成 3x3, stride=1, padding=12) 将 maxpool 改成 Identity() => 不执行池化3) 将 fc 改成输出通道数=10(CIFAR10有10类)"""net = models.resnet34(pretrained=False)  # 不使用ImageNet预训练权重# 修改第一层卷积 => 原本7x7, stride=2,容易把32x32图像缩得太小net.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)# 去掉 maxpool => 不进行额外下采样net.maxpool = nn.Identity()# 最后全连接层 => 改为输出10类net.fc = nn.Linear(in_features=512, out_features=10, bias=True)return net# ---------------------------
# 4. 验证过程
# ---------------------------
def validate_model(model, valid_loader, criterion, device):"""在验证集/测试集上计算Loss和Accuracy,用于评估模型性能。model: 当前的神经网络valid_loader: 验证集DataLoadercriterion: 损失函数device: 'cuda' 或 'cpu'返回: (平均loss, 准确率)"""model.eval()  # 切换到评估模式,关闭Dropout等随机操作total_loss = 0.0total_correct = 0total_samples = 0with torch.no_grad():for x, y in valid_loader:x, y = x.to(device), y.to(device)  # 将数据放到指定设备上output = model(x)                 # 前向传播loss = criterion(output, y)       # 计算损失total_loss += loss.item()# argmax => 找到输出张量中得分最大的类别preds = torch.argmax(output, dim=-1)total_correct += (preds == y).sum().item()total_samples += len(y)avg_loss = total_loss / len(valid_loader)accuracy = total_correct / total_samplesreturn avg_loss, accuracy# ---------------------------
# 5. 训练过程
# ---------------------------
def train_model(model, train_dataset, valid_dataset, device):"""训练并验证模型。若发现更高验证集准确率则保存最优权重。返回各Epoch的Loss和Accuracy,供绘图使用。"""criterion = nn.CrossEntropyLoss()                     # 交叉熵损失optimizer = optim.AdamW(model.parameters(), lr=LEARNING_RATE)  # AdamW优化器# 优化 DataLoader,数值 num_workers 可根据 CPU 核心数进行调整。# •	增加 num_workers:DataLoader 的 num_workers 参数决定了数据预处理的并行程度。增大 num_workers 可以加快数据加载速度,避免因数据加载速度过慢而使 GPU 等待数据。# •	设置 pin_memory=True:对于 GPU 训练,设置 pin_memory=True 可以加速数据从主机内存传输到 GPU。train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=8, pin_memory=True)valid_loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=8, pin_memory=True)train_losses, valid_losses = [], []train_accs, valid_accs = [], []best_acc = 0.0# 当验证集准确率超过之前最优时,保存到 best_model_pathbest_model_path = './深度学习实战/model/resnet34_cifar10_best.pth'scaler = torch.amp.GradScaler()  # 混合精度梯度缩放器for epoch_idx in range(EPOCHS):model.train()  # 切换到训练模式start_time = time.time()total_loss = 0.0total_correct = 0total_samples = 0for x, y in train_loader:x, y = x.to(device), y.to(device)# output = model(x)         # 前向# loss = criterion(output, y)# optimizer.zero_grad()     # 梯度清零# loss.backward()           # 反向传播# optimizer.step()          # 更新参数optimizer.zero_grad()# 自动混合精度上下文with torch.amp.autocast(device_type=device.type):output = model(x)loss = criterion(output, y)scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()total_loss += loss.item()preds = torch.argmax(output, dim=-1)total_correct += (preds == y).sum().item()total_samples += len(y)avg_train_loss = total_loss / len(train_loader)train_acc = total_correct / total_samples# 在验证集上评估avg_valid_loss, valid_acc = validate_model(model, valid_loader, criterion, device)train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accs.append(train_acc)valid_accs.append(valid_acc)# 如果当前验证准确率超过历史最优 => 保存if valid_acc > best_acc:best_acc = valid_acctorch.save(model.state_dict(), best_model_path)elapsed = time.time() - start_timeprint(f"Epoch [{epoch_idx+1}/{EPOCHS}] "f"Train Loss: {avg_train_loss:.4f}, Train Acc: {train_acc:.2f}, "f"Valid Loss: {avg_valid_loss:.4f}, Valid Acc: {valid_acc:.2f}, "f"Time: {elapsed:.2f}s")# 最后再保存一次final_path = './深度学习实战/model/resnet34_cifar10_last.pth'torch.save(model.state_dict(), final_path)print(f"训练完成!最优验证准确率: {best_acc:.2f}. 模型已保存到 {best_model_path} 和 {final_path}")return train_losses, valid_losses, train_accs, valid_accs# ---------------------------
# 6. 绘制训练过程曲线
# ---------------------------
def plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):"""传入训练/验证Loss和Acc的列表,画出随Epoch变化的曲线。"""epochs_range = range(1, len(train_losses)+1)fig, axs = plt.subplots(1, 2, figsize=(12,5))# 左边画 Lossaxs[0].plot(epochs_range, train_losses, label='Train Loss', marker='o')axs[0].plot(epochs_range, valid_losses, label='Valid Loss', marker='x')axs[0].set_xlabel('Epochs')axs[0].set_ylabel('Loss')axs[0].set_title('Train & Valid Loss')axs[0].legend()# 右边画 Accuracyaxs[1].plot(epochs_range, train_accs, label='Train Acc', marker='o')axs[1].plot(epochs_range, valid_accs, label='Valid Acc', marker='x')axs[1].set_xlabel('Epochs')axs[1].set_ylabel('Accuracy')axs[1].set_title('Train & Valid Accuracy')axs[1].legend()plt.tight_layout()plt.show()# ---------------------------
# 7. 使用最佳模型 + 混淆矩阵
# ---------------------------
def test_model_and_confusion(valid_dataset, device):"""加载最优模型,在测试集上计算准确率并绘制混淆矩阵。"""loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=8, pin_memory=True)# 初始化网络 => 需要跟训练时的结构一致model = get_resnet34_cifar10()best_model_path = './深度学习实战/model/resnet34_cifar10_best.pth'model.load_state_dict(torch.load(best_model_path, weights_only=True))  # 加载最佳权重model = model.to(device)model.eval()total_correct = 0total_samples = 0all_preds = []all_labels = []with torch.no_grad():for x, y in loader:x, y = x.to(device), y.to(device)output = model(x)preds = torch.argmax(output, dim=-1)# 用于混淆矩阵all_preds.extend(preds.cpu().numpy())all_labels.extend(y.cpu().numpy())total_correct += (preds == y).sum().item()total_samples += len(y)acc = total_correct / total_samplesprint(f"使用最优模型在测试集上的准确率: {acc*100:.2f}%")# 混淆矩阵cm = confusion_matrix(all_labels, all_preds)disp = ConfusionMatrixDisplay(cm, display_labels=range(10))disp.plot(cmap=plt.cm.Blues)plt.title("resnet34 on CIFAR10 Confusion Matrix")plt.show()# ---------------------------
# 8. 主函数入口
# ---------------------------
if __name__ == '__main__':import os# 检查是远程路径还是本地路径if os.path.exists("/home/ubuntu/data/pycharm_project_377"):os.chdir("/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject")print("当前工作目录(远程):", os.getcwd())else:os.chdir("/Users/coyi/PycharmProjects/GPU_32_pythonProject")print("当前工作目录(本地):", os.getcwd())# 检查设备 => 优先用 GPUif torch.cuda.is_available():device = torch.device("cuda:0")else:device = torch.device("cpu")print("使用的设备:", device)# (1) 加载CIFAR10train_dataset, valid_dataset = create_dataset()print("训练集类别映射:", train_dataset.class_to_idx)print("训练集数据形状:", train_dataset.data.shape)print("测试集数据形状:", valid_dataset.data.shape)# (2) 可视化一张图plt.figure()plt.imshow(train_dataset.data[0])plt.title(f"标签: {train_dataset.targets[0]}")plt.show()# (3) 实例化 resnet34 并查看结构model = get_resnet34_cifar10().to(device)summary(model, input_size=(3, 32, 32), batch_size=1)# (4) 训练train_losses, valid_losses, train_accs, valid_accs = train_model(model, train_dataset, valid_dataset, device)# (5) 可视化训练plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# (6) 测试集上做预测 + 混淆矩阵test_model_and_confusion(valid_dataset, device)

输出:

 


代码4: 使用ResNet34 的预训练权重,优化训练过程

在代码3的基础上,使用了resnet34的原始权重的基础上进行训练

import torchtorch.backends.cudnn.benchmark = True #torch.backends.cudnn.benchmark = True 会自动搜索最佳的卷积算法,当输入尺寸固定时可以大幅加速卷积计算。
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision.datasets import CIFAR10
from torchvision.transforms import ToTensor, Composeimport torchvision.models as models  # 用于加载官方resnet34
import time
import matplotlib.pyplot as plt
from torchsummary import summary# 方便中文显示的 matplotlib 配置
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = Falseimport numpy as np
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay# ---------------------------
# 1. 全局超参数
# ---------------------------
BATCH_SIZE = 256            # 每批处理多少张图
EPOCHS = 100              # 训练轮数
LEARNING_RATE = 1e-3      # 学习率# ---------------------------
# 2. 数据集加载函数
# ---------------------------
def create_dataset():"""加载 CIFAR-10 数据集。CIFAR-10:训练集50000张,测试集10000张,图像32x32,RGB三通道,分成10类。ToTensor() 会把 [0,255] 的像素值归一化到 [0,1] 的 float32 张量。download=True 会自动下载到根目录 data/ 下。"""train = CIFAR10(root='data',        # 指定下载/存储路径train=True,         # True表示训练集transform=Compose([ToTensor()]),  # 将图像转为张量download=True       # 若本地无数据则自动下载)valid = CIFAR10(root='data',train=False,        # False表示测试集transform=Compose([ToTensor()]),download=True)return train, valid# ---------------------------
# 3. 构建 resnet34 模型并适配 CIFAR-10
# ---------------------------
def get_resnet34_cifar10():"""使用官方 torchvision.models.resnet34(pretrained=False)。1) 将 conv1 改成 3x3, stride=1, padding=12) 将 maxpool 改成 Identity() => 不执行池化3) 将 fc 改成输出通道数=10(CIFAR10有10类)"""net = models.resnet34(weights=models.ResNet34_Weights.IMAGENET1K_V1)  # 使用ImageNet预训练权重# 修改第一层卷积 => 原本7x7, stride=2,容易把32x32图像缩得太小net.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)# 去掉 maxpool => 不进行额外下采样net.maxpool = nn.Identity()# 最后全连接层 => 改为输出10类net.fc = nn.Linear(in_features=512, out_features=10, bias=True)return net# ---------------------------
# 4. 验证过程
# ---------------------------
def validate_model(model, valid_loader, criterion, device):"""在验证集/测试集上计算Loss和Accuracy,用于评估模型性能。model: 当前的神经网络valid_loader: 验证集DataLoadercriterion: 损失函数device: 'cuda' 或 'cpu'返回: (平均loss, 准确率)"""model.eval()  # 切换到评估模式,关闭Dropout等随机操作total_loss = 0.0total_correct = 0total_samples = 0with torch.no_grad():for x, y in valid_loader:x, y = x.to(device), y.to(device)  # 将数据放到指定设备上output = model(x)                 # 前向传播loss = criterion(output, y)       # 计算损失total_loss += loss.item()# argmax => 找到输出张量中得分最大的类别preds = torch.argmax(output, dim=-1)total_correct += (preds == y).sum().item()total_samples += len(y)avg_loss = total_loss / len(valid_loader)accuracy = total_correct / total_samplesreturn avg_loss, accuracy# ---------------------------
# 5. 训练过程
# ---------------------------
def train_model(model, train_dataset, valid_dataset, device):"""训练并验证模型。若发现更高验证集准确率则保存最优权重。返回各Epoch的Loss和Accuracy,供绘图使用。"""criterion = nn.CrossEntropyLoss()                     # 交叉熵损失optimizer = optim.AdamW(model.parameters(), lr=LEARNING_RATE)  # AdamW优化器# 优化 DataLoader,数值 num_workers 可根据 CPU 核心数进行调整。# •	增加 num_workers:DataLoader 的 num_workers 参数决定了数据预处理的并行程度。增大 num_workers 可以加快数据加载速度,避免因数据加载速度过慢而使 GPU 等待数据。# •	设置 pin_memory=True:对于 GPU 训练,设置 pin_memory=True 可以加速数据从主机内存传输到 GPU。train_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=8, pin_memory=True)valid_loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=8, pin_memory=True)train_losses, valid_losses = [], []train_accs, valid_accs = [], []best_acc = 0.0# 当验证集准确率超过之前最优时,保存到 best_model_pathbest_model_path = './深度学习实战/model/resnet34_cifar10_best.pth'scaler = torch.amp.GradScaler()  # 混合精度梯度缩放器for epoch_idx in range(EPOCHS):model.train()  # 切换到训练模式start_time = time.time()total_loss = 0.0total_correct = 0total_samples = 0for x, y in train_loader:x, y = x.to(device), y.to(device)# output = model(x)         # 前向# loss = criterion(output, y)# optimizer.zero_grad()     # 梯度清零# loss.backward()           # 反向传播# optimizer.step()          # 更新参数optimizer.zero_grad()# 自动混合精度上下文with torch.amp.autocast(device_type=device.type):output = model(x)loss = criterion(output, y)scaler.scale(loss).backward()scaler.step(optimizer)scaler.update()total_loss += loss.item()preds = torch.argmax(output, dim=-1)total_correct += (preds == y).sum().item()total_samples += len(y)avg_train_loss = total_loss / len(train_loader)train_acc = total_correct / total_samples# 在验证集上评估avg_valid_loss, valid_acc = validate_model(model, valid_loader, criterion, device)train_losses.append(avg_train_loss)valid_losses.append(avg_valid_loss)train_accs.append(train_acc)valid_accs.append(valid_acc)# 如果当前验证准确率超过历史最优 => 保存if valid_acc > best_acc:best_acc = valid_acctorch.save(model.state_dict(), best_model_path)elapsed = time.time() - start_timeprint(f"Epoch [{epoch_idx+1}/{EPOCHS}] "f"Train Loss: {avg_train_loss:.4f}, Train Acc: {train_acc:.2f}, "f"Valid Loss: {avg_valid_loss:.4f}, Valid Acc: {valid_acc:.2f}, "f"Time: {elapsed:.2f}s")# 最后再保存一次final_path = './深度学习实战/model/resnet34_cifar10_last.pth'torch.save(model.state_dict(), final_path)print(f"训练完成!最优验证准确率: {best_acc:.2f}. 模型已保存到 {best_model_path} 和 {final_path}")return train_losses, valid_losses, train_accs, valid_accs# ---------------------------
# 6. 绘制训练过程曲线
# ---------------------------
def plot_training_curves(train_losses, valid_losses, train_accs, valid_accs):"""传入训练/验证Loss和Acc的列表,画出随Epoch变化的曲线。"""epochs_range = range(1, len(train_losses)+1)fig, axs = plt.subplots(1, 2, figsize=(12,5))# 左边画 Lossaxs[0].plot(epochs_range, train_losses, label='Train Loss', marker='o')axs[0].plot(epochs_range, valid_losses, label='Valid Loss', marker='x')axs[0].set_xlabel('Epochs')axs[0].set_ylabel('Loss')axs[0].set_title('Train & Valid Loss')axs[0].legend()# 右边画 Accuracyaxs[1].plot(epochs_range, train_accs, label='Train Acc', marker='o')axs[1].plot(epochs_range, valid_accs, label='Valid Acc', marker='x')axs[1].set_xlabel('Epochs')axs[1].set_ylabel('Accuracy')axs[1].set_title('Train & Valid Accuracy')axs[1].legend()plt.tight_layout()plt.show()# ---------------------------
# 7. 使用最佳模型 + 混淆矩阵
# ---------------------------
def test_model_and_confusion(valid_dataset, device):"""加载最优模型,在测试集上计算准确率并绘制混淆矩阵。"""loader = DataLoader(valid_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=8, pin_memory=True)# 初始化网络 => 需要跟训练时的结构一致model = get_resnet34_cifar10()best_model_path = './深度学习实战/model/resnet34_cifar10_best.pth'model.load_state_dict(torch.load(best_model_path, weights_only=True))  # 加载最佳权重model = model.to(device)model.eval()total_correct = 0total_samples = 0all_preds = []all_labels = []with torch.no_grad():for x, y in loader:x, y = x.to(device), y.to(device)output = model(x)preds = torch.argmax(output, dim=-1)# 用于混淆矩阵all_preds.extend(preds.cpu().numpy())all_labels.extend(y.cpu().numpy())total_correct += (preds == y).sum().item()total_samples += len(y)acc = total_correct / total_samplesprint(f"使用最优模型在测试集上的准确率: {acc*100:.2f}%")# 混淆矩阵cm = confusion_matrix(all_labels, all_preds)disp = ConfusionMatrixDisplay(cm, display_labels=range(10))disp.plot(cmap=plt.cm.Blues)plt.title("resnet34 on CIFAR10 Confusion Matrix")plt.show()# ---------------------------
# 8. 主函数入口
# ---------------------------
if __name__ == '__main__':import os# 检查是远程路径还是本地路径if os.path.exists("/home/ubuntu/data/pycharm_project_377"):os.chdir("/home/ubuntu/data/pycharm_project_377/GPU_32_pythonProject")print("当前工作目录(远程):", os.getcwd())else:os.chdir("/Users/coyi/PycharmProjects/GPU_32_pythonProject")print("当前工作目录(本地):", os.getcwd())# 检查设备 => 优先用 GPUif torch.cuda.is_available():device = torch.device("cuda:0")else:device = torch.device("cpu")print("使用的设备:", device)# (1) 加载CIFAR10train_dataset, valid_dataset = create_dataset()print("训练集类别映射:", train_dataset.class_to_idx)print("训练集数据形状:", train_dataset.data.shape)print("测试集数据形状:", valid_dataset.data.shape)# (2) 可视化一张图plt.figure()plt.imshow(train_dataset.data[0])plt.title(f"标签: {train_dataset.targets[0]}")plt.show()# (3) 实例化 resnet34 并查看结构model = get_resnet34_cifar10().to(device)summary(model, input_size=(3, 32, 32), batch_size=1)# (4) 训练train_losses, valid_losses, train_accs, valid_accs = train_model(model, train_dataset, valid_dataset, device)# (5) 可视化训练plot_training_curves(train_losses, valid_losses, train_accs, valid_accs)# (6) 测试集上做预测 + 混淆矩阵test_model_and_confusion(valid_dataset, device)

输出:

 


总结与展望

本文以 CIFAR-10 为例,提供了四 份完整可运行的 PyTorch 代码,分别展示了以下四个层次的图像分类实战案例:

代码一:自定义 CNN

介绍了如何手工设计一个卷积神经网络,突出基础原理与手工实现,让你真正理解卷积神经网络的构造和各层设计的作用。

代码二:使用开源模型 ResNet34

演示如何快速拿来使用经过 ImageNet 预训练的经典模型,并做少量调整(如修改第一层卷积和全连接层)以适应 CIFAR-10 的数据和分类任务,从而得到更高的准确率。

代码三:高效训练优化

针对训练效率问题,采用了多线程加载、混合精度训练(AMP)、cuDNN 加速,提高批次 等策略,大幅提升训练速度同时保证精度,最终案例准确率达到了 87.57%, 单次训练时长12秒。

代码四:使用开源模型 ResNet34 的原生权重微调

最终案例准确率达到了 90。24%, 单次训练时长12秒。完成了需求

通过这篇文章,你已经具备了独立实现或改造 CNN 分类项目的能力,并能举一反三,将这些方法拓展到更大规模或更复杂的项目中。未来可以尝试加入更多优化技巧,如模型压缩、数据增强等,进一步提高训练效率和模型准确率。


参考文献/引用来源

1. PyTorch 官方文档

PyTorch documentation — PyTorch 2.6 documentation

2. torchvision.models 源码

官方 GitHub 仓库及文档中提供了各种预训练模型的详细实现。

3. Deep Residual Learning for Image Recognition (ResNet)

He, K., Zhang, X., Ren, S., & Sun, J. (2016). Deep Residual Learning for Image Recognition. arXiv:1512.03385

4. CIFAR-10 数据集

CIFAR-10 and CIFAR-100 datasets — 官方网站


如果你觉得这篇文章对你有所帮助,欢迎你点赞、收藏、评论和转发支持一下!你的关注与互动将是我持续创作高质量内容的最大动力,也欢迎在评论区分享你训练的结果和心得,与大家一起交流进步!

希望以上整理能满足你的需求,感谢阅读,我们下篇文章再见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/71363.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OPPO Find N5折叠手机:创新与实用的完美融合,FPC应用展现科技魅力【新立电子】

OPPO Find N5作为2025年新出世的折叠手机,以其卓越的设计、强大的性能以及创新的技术,为消费者带来了全新的使用体验。FPC(柔性电路板)在其中的运用,也进一步提升了手机的整体性能和用户体验。 OPPO Find N5的最大亮点…

【AD】PCB增加相关图层——以机械层为例

问题:图中PCB仅有机械层1和机械层2,想要在加一个机械层3 解决 1.点击视图—面板—View Configuration,选中机械层右键单击增加层,其他层类似

测试用例总结

一、通用测试用例八要素   1、用例编号;    2、测试项目;   3、测试标题; 4、重要级别;    5、预置条件;    6、测试输入;    7、操作步骤;    8、预期输出 二、具体分析通…

不用写代码,批量下载今日头条文章导出excel和pdf

前几天有人问我怎么批量抓取今日头条某个号的所有文章数据,需要文章链接,标题和时间,但是不会写代码,于是我写了个简单的教程 这里以渤海小吏为例 首先用edge浏览器安装web-scraper浏览器扩展 然后打开浏览器控制台,找…

Starrocks 写入报错 primary key memory usage exceeds the limit

背景 本文基于 StarRocks 3.3.5 单个Starrocks BE配置是 16CU 32GB 在Flink Yaml CDC 任务往 Starrocks写数据的过程中,突然遇到了primary key memory usage exceeds the limit 问题,具体如下: java.lang.RuntimeException: com.starrocks.…

利用Adobe Acrobat 实现PPT中图片分辨率的提升

1. 下载适用于 Windows 的 64 位 Acrobat 注册方式参考:https://ca.whu.edu.cn/knowledge.html?type1 2. 将ppt中需要提高分辨率的图片复制粘贴到新建的pptx问价中,然后执行“文件—>导出---->创建PDF、XPS文档” 3. 我们会发现保存下来的distrib…

【Python爬虫】爬取公共交通路网数据

程序来自于Github,以下这篇博客作为完整的学习记录,也callback上一篇爬取公共交通站点的博文。 Bardbo/get_bus_lines_and_stations_data_from_gaode: 这个项目是基于高德开放平台和公交网获取公交线路及站点数据,并生成shp文件,…

VUE_使用Vite构建vue项目

创建项目 // 安装vite npm install vite// 创建名为vite-app的项目 npm create vite vite-app --template vue// 到项目目录 cd vite-app// 安装依赖 npm install// 运行项目 npm run dev// 打包 npm run build// 打包预览 npm run serve 增加路由 // 安装路由 npm add vue-r…

ctf网络安全赛题

CTF简介 CTF(Capture The Flag)中文一般译作夺旗赛,在网络安全领域中指的是网络安全技术人员之间进行技术竞技的一种比赛形式。CTF起源于1996年DEFCON全球黑客大会,以代替之前黑客们通过互相发起真实攻击进行技术比拼的方式。发展…

性能测试监控工具jmeter+grafana

1、什么是性能测试监控体系? 为什么要有监控体系? 原因: 1、项目-日益复杂(内部除了代码外,还有中间件,数据库) 2、一个系统,背后可能有多个软/硬件组合支撑,影响性能的因…

LabVIEW基于IMAQ实现直线边缘检测

本程序基于 NI Vision Development 模块,通过 IMAQ Find Straight Edges 函数,在指定 ROI(感兴趣区域) 内检测多条直线边缘。用户可 动态调整检测参数 或 自定义ROI,实时观察识别效果,适用于 高精度视觉检测…

一周学会Flask3 Python Web开发-在模板中渲染WTForms表单视图函数里获取表单数据

锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 为了能够在模板中渲染表单,我们需要把表单类实例传入模板。首先在视图函数里实例化表单类LoginForm,然…

小红书湖仓架构的跃迁之路

作者:李鹏霖(丁典),小红书-研发工程师,StarRocks Contributor & Apache Impala Committer 本文整理自小红书工程师在 StarRocks 年度峰会上的分享,介绍了小红书自助分析平台中,StarRocks 与 Iceberg 结合后&#x…

数据结构第五节:排序

1.常见的排序算法 插入排序:直接插入排序、希尔排序 选择排序:直接选择排序、堆排序 交换排序:冒泡排序、快速排序 归并排序:归并排序 排序的接口实现: // 1. 直接插入排序 void InsertSort(int* a, int n); // 2. 希…

C++发展

目录 ​编辑C 的发展总结:​编辑 1. C 的早期发展(1979-1985) 2. C 标准化过程(1985-1998) 3. C 标准演化(2003-2011) 4. C11(2011年) 5. C14(2014年&a…

LeetCode 21. 合并两个有序链表(Python)

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4] 示例 2: 输入:l1 [], l2 [] 输出:[] 示例 3: 输…

FPGA 配置原理

用户编程控制的FPGA 是通过加载比特位流配置内部的存储单元实现的。该存储单元就是所谓的配置单元,它必须在器件上电后进行配置,从而设置查找表(LUT)的属性、连线方式、IOB 电压标准和其它的用户设计。 1.配置帧 以Xilinx 公司的…

测试人员如何更好的跟踪BUG

软件测试中BUG跟踪是确保软件质量的关键环节。测试人员不仅需要发现BUG,还需有效管理其状态,从报告到修复验证的全过程。如何更好地跟踪BUG,成为测试人员提升效率的重要课题。本文将详细探讨测试人员可以采用的策略,包括使用工具、…

lamp平台介绍

一、lamp介绍 网站: 静态 动态 php语言 .php 作用:运行php语言编写动态网站应用 lamp Linux Apache MySQL PHP PHP是作为httpd的一个功能模块存在的 二、部署lamp平台 1、测试httpd是否可正常返回PHP的响应 2、测试PHP代码是否可正常连接数据…

2025年渗透测试面试题总结-字某跳动-渗透测试实习生(题目+回答)

网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 字某跳动-渗透测试实习生 渗透流程信息收集如何处理子域名爆破中的泛解析问题绕过CDN寻找真实IPPHPINFO页面关注…