当前位置: 首页 > news >正文

【深度学习-Day 34】CNN实战:从零构建CIFAR-10图像分类器(PyTorch)

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

深度学习系列文章目录

01-【深度学习-Day 1】为什么深度学习是未来?一探究竟AI、ML、DL关系与应用
02-【深度学习-Day 2】图解线性代数:从标量到张量,理解深度学习的数据表示与运算
03-【深度学习-Day 3】搞懂微积分关键:导数、偏导数、链式法则与梯度详解
04-【深度学习-Day 4】掌握深度学习的“概率”视角:基础概念与应用解析
05-【深度学习-Day 5】Python 快速入门:深度学习的“瑞士军刀”实战指南
06-【深度学习-Day 6】掌握 NumPy:ndarray 创建、索引、运算与性能优化指南
07-【深度学习-Day 7】精通Pandas:从Series、DataFrame入门到数据清洗实战
08-【深度学习-Day 8】让数据说话:Python 可视化双雄 Matplotlib 与 Seaborn 教程
09-【深度学习-Day 9】机器学习核心概念入门:监督、无监督与强化学习全解析
10-【深度学习-Day 10】机器学习基石:从零入门线性回归与逻辑回归
11-【深度学习-Day 11】Scikit-learn实战:手把手教你完成鸢尾花分类项目
12-【深度学习-Day 12】从零认识神经网络:感知器原理、实现与局限性深度剖析
13-【深度学习-Day 13】激活函数选型指南:一文搞懂Sigmoid、Tanh、ReLU、Softmax的核心原理与应用场景
14-【深度学习-Day 14】从零搭建你的第一个神经网络:多层感知器(MLP)详解
15-【深度学习-Day 15】告别“盲猜”:一文读懂深度学习损失函数
16-【深度学习-Day 16】梯度下降法 - 如何让模型自动变聪明?
17-【深度学习-Day 17】神经网络的心脏:反向传播算法全解析
18-【深度学习-Day 18】从SGD到Adam:深度学习优化器进阶指南与实战选择
19-【深度学习-Day 19】入门必读:全面解析 TensorFlow 与 PyTorch 的核心差异与选择指南
20-【深度学习-Day 20】PyTorch入门:核心数据结构张量(Tensor)详解与操作
21-【深度学习-Day 21】框架入门:神经网络模型构建核心指南 (Keras & PyTorch)
22-【深度学习-Day 22】框架入门:告别数据瓶颈 - 掌握PyTorch Dataset、DataLoader与TensorFlow tf.data实战
23-【深度学习-Day 23】框架实战:模型训练与评估核心环节详解 (MNIST实战)
24-【深度学习-Day 24】过拟合与欠拟合:深入解析模型泛化能力的核心挑战
25-【深度学习-Day 25】告别过拟合:深入解析 L1 与 L2 正则化(权重衰减)的原理与实战
26-【深度学习-Day 26】正则化神器 Dropout:随机失活,模型泛化的“保险丝”
27-【深度学习-Day 27】模型调优利器:掌握早停、数据增强与批量归一化
28-【深度学习-Day 28】告别玄学调参:一文搞懂网格搜索、随机搜索与自动化超参数优化
29-【深度学习-Day 29】PyTorch模型持久化指南:从保存到部署的第一步
30-【深度学习-Day 30】从MLP的瓶颈到CNN的诞生:卷积神经网络的核心思想解析
31-【深度学习-Day 31】CNN基石:彻底搞懂卷积层 (Convolutional Layer) 的工作原理
32-【深度学习-Day 32】CNN核心组件之池化层:解密最大池化与平均池化
33-【深度学习-Day 33】从零到一:亲手构建你的第一个卷积神经网络(CNN)
34-【深度学习-Day 34】CNN实战:从零构建CIFAR-10图像分类器(PyTorch)


文章目录

  • Langchain系列文章目录
  • Python系列文章目录
  • PyTorch系列文章目录
  • 机器学习系列文章目录
  • 深度学习系列文章目录
  • Java系列文章目录
  • JavaScript系列文章目录
  • 深度学习系列文章目录
  • 前言
  • 一、项目概述与环境准备
    • 1.1 任务目标:CIFAR-10 图像分类
    • 1.2 技术栈与环境确认
        • (1) 检查 GPU 支持
  • 二、数据准备与预处理
    • 2.1 使用 `torchvision` 加载数据集
    • 2.2 数据预处理详解
        • (1) `transforms.ToTensor()`
        • (2) `transforms.Normalize(mean, std)`
    • 2.3 创建数据加载器 `DataLoader`
    • 2.4 数据可视化
  • 三、构建卷积神经网络 (CNN) 模型
    • 3.1 模型架构设计
    • 3.2 代码实现:定义 CNN 类
      • 3.2.1 关键计算:展平层输入维度
    • 3.3 模型实例化与设备选择
  • 四、模型训练与评估
    • 4.1 定义损失函数和优化器
    • 4.2 编写训练循环
    • 4.3 编写评估函数
        • (1) 整体准确率
        • (2) 各类别准确率
    • 4.4 保存模型(可选但推荐)
  • 五、结果分析与改进
    • 5.1 训练结果分析
    • 5.2 可视化预测结果
    • 5.3 常见问题与改进方向
        • (1) 问题:准确率不高怎么办?
        • (2) 问题:训练速度太慢?
        • (3) 下一步:迁移学习
  • 六、总结


前言

大家好,欢迎来到我们深度学习系列的第34篇文章。在上一篇文章中,我们详细探讨了如何从零开始构建一个卷积神经网络(CNN)的基本结构,理解了卷积层、池化层等核心组件如何协同工作。理论知识固然重要,但将其付诸实践才是检验真理的唯一标准。

因此,本文的目标就是将理论转化为代码,带领大家完成一个完整的、端到端的深度学习项目:使用 PyTorch 框架构建一个 CNN 模型,对经典的 CIFAR-10 数据集进行图像分类。CIFAR-10 是一个比 MNIST 更具挑战性的彩色图像数据集,是检验模型性能的绝佳“试金石”。

通过本篇文章,你将亲手实践以下全流程:

  1. 数据加载与预处理:学习如何使用 torchvision 高效处理图像数据。
  2. CNN 模型搭建:应用之前所学,设计并实现一个针对 CIFAR-10 的 CNN 架构。
  3. 模型训练与监控:编写标准的训练循环,定义损失函数和优化器,并实时监控训练过程。
  4. 模型评估与分析:在测试集上检验模型性能,并对结果进行分析。
  5. 问题排查与优化:探讨实际项目中可能遇到的问题及相应的改进策略。

准备好了吗?让我们一起动手,用代码征服第一个真正意义上的图像分类任务!

一、项目概述与环境准备

在正式编码之前,我们首先需要明确任务目标,并确保我们的开发环境已经准备就绪。

1.1 任务目标:CIFAR-10 图像分类

CIFAR-10 是由 Geoffrey Hinton 等人整理的一个用于普适性物体识别的经典数据集。它包含了10个类别的共60,000张 32x32 像素的彩色图像。其中,50,000张用于训练,10,000张用于测试。

  • 10个类别分别为:飞机 (airplane)、汽车 (automobile)、鸟 (bird)、猫 (cat)、鹿 (deer)、狗 (dog)、青蛙 (frog)、马 (horse)、船 (ship)、卡车 (truck)。
  • 数据特点:相比于黑白手写数字的 MNIST,CIFAR-10 的图像是彩色的(3个通道),且包含的物体姿态、光照、背景更加复杂,对模型的泛化能力提出了更高的要求。

下面是数据集中的一些图像示例:

我们的任务就是训练一个CNN模型,使其能够准确识别出测试集图像所属的类别。

1.2 技术栈与环境确认

本项目主要依赖于 PyTorch 生态中的核心库。请确保你的环境中已安装以下库:

  • torch: PyTorch 深度学习框架核心。
  • torchvision: 提供了对常用数据集、模型架构和图像转换操作的支持。
  • matplotlib: 用于数据可视化,方便我们查看图像和分析结果。
  • numpy: 虽然 PyTorch 的张量操作很强大,但 numpy 仍是数据处理中不可或缺的工具。

你可以通过 pip 来安装它们:

pip install torch torchvision matplotlib numpy
(1) 检查 GPU 支持

CNN 的训练计算量巨大,使用 GPU 可以将训练速度提升数十倍。你可以使用以下代码检查 PyTorch 是否能成功调用你的 GPU:

import torch# 检查 CUDA (NVIDIA GPU) 是否可用
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print(f'Using device: {device}')# 可能的输出:
# Using device: cuda:0  (表示使用第一块GPU)
# 或
# Using device: cpu     (表示使用CPU)

如果输出为 cpu,也不必担心,本项目在 CPU 上依然可以完成,只是需要更长的等待时间。后续所有模型和数据,我们都将发送到这个 device 上执行。

二、数据准备与预处理

高质量的数据是模型成功的基石。PyTorch 的 torchvision 库为我们处理 CIFAR-10 这类标准数据集提供了极大的便利。

2.1 使用 torchvision 加载数据集

torchvision.datasets 模块允许我们仅用几行代码就下载并加载 CIFAR-10 数据集。

import torchvision
import torchvision.transforms as transforms# 定义数据预处理的步骤
# Compose a sequence of transformations
transform = transforms.Compose([# 将 PIL Image 或 ndarray 转换为张量,并将像素值从 [0, 255] 缩放到 [0.0, 1.0]transforms.ToTensor(), # 对图像进行归一化,三个通道的均值和标准差分别为 (0.5, 0.5, 0.5)transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) 
])# 下载并加载训练数据集
# download=True 会在首次运行时自动从网络下载数据集
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,download=True, transform=transform)# 下载并加载测试数据集
testset = torchvision.datasets.CIFAR10(root='./data', train=False,download=True, transform=transform)print(f'训练集大小: {len(trainset)}')
print(f'测试集大小: {len(testset)}')

首次运行时,代码会自动在当前目录下的 ./data 文件夹中下载并解压数据集。

2.2 数据预处理详解

在上面的代码中,我们定义了一个 transform 流水线,它包含了两个关键步骤:

(1) transforms.ToTensor()

这是任何图像数据处理的第一步。它负责将输入的 PIL 图像或 NumPy 数组转换成 PyTorch 张量。更重要的是,它会自动将图像的像素值范围从 [0, 255] 归一化到 [0.0, 1.0]。同时,它还会调整维度的顺序,将 [H, W, C] (高, 宽, 通道) 的图像格式转换为 PyTorch 所期望的 [C, H, W] (通道, 高, 宽) 格式。

(2) transforms.Normalize(mean, std)

数据归一化是提升模型训练速度和性能的关键技巧。它的计算公式如下:

output [ c h a n n e l ] = input [ c h a n n e l ] − mean [ c h a n n e l ] std [ c h a n n e l ] \text{output}[channel] = \frac{\text{input}[channel] - \text{mean}[channel]}{\text{std}[channel]} output[channel]=std[channel]input[channel]mean[channel]

这一操作将数据的每个通道都调整到均值为0,标准差为1的分布(近似)。我们在这里使用 (0.5, 0.5, 0.5) 作为均值和标准差,这会将数据从 [0, 1] 的范围转换到 [-1, 1] 的范围。这是一种简单且常用的归一化策略。

提示:更精确的做法是计算整个CIFAR-10训练集的均值和标准差,并使用这些精确值。但对于初学者来说,(0.5, 0.5, 0.5) 是一个完全可以接受且效果不错的选择。

2.3 创建数据加载器 DataLoader

直接遍历 Dataset 对象效率低下。我们需要 torch.utils.data.DataLoader 来帮助我们实现高效的数据加载。DataLoader 可以自动完成以下工作:

  • 批处理 (Batching): 将数据打包成一个个的小批量 (mini-batch)。
  • 数据打乱 (Shuffling): 在每个 epoch 开始时,随机打乱数据顺序,这有助于防止模型过拟合。
  • 并行加载 (Parallel Loading): 使用多个子进程来预加载数据,避免 CPU 在等待数据时空闲。
from torch.utils.data import DataLoader# 定义批处理大小
batch_size = 64# 创建训练数据加载器
# shuffle=True 表示在每个epoch开始时打乱数据
trainloader = DataLoader(trainset, batch_size=batch_size,shuffle=True, num_workers=2)# 创建测试数据加载器
# shuffle=False,因为在评估时不需要打乱顺序
testloader = DataLoader(testset, batch_size=batch_size,shuffle=False, num_workers=2)# 定义类别标签
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

这里的 num_workers 参数指定了用于数据加载的子进程数量。在 Windows 系统上,建议将其放在 if __name__ == '__main__': 块中以避免多进程错误。

2.4 数据可视化

为了确保数据加载和预处理正确无误,我们可以取出少量图像进行可视化。

import matplotlib.pyplot as plt
import numpy as np# 定义一个函数来显示图像
def imshow(img):# 我们之前对图像进行了归一化 ([-1, 1]),这里需要反归一化才能正确显示img = img / 2 + 0.5     # unnormalizenpimg = img.numpy()# 将 [C, H, W] 格式转为 [H, W, C] 格式以供 matplotlib 显示plt.imshow(np.transpose(npimg, (1, 2, 0)))plt.show()# 从训练数据加载器中获取一个批次的数据
dataiter = iter(trainloader)
images, labels = next(dataiter)# 显示图像
# torchvision.utils.make_grid 会将一个批次的图像拼接成一个网格
imshow(torchvision.utils.make_grid(images[:4])) # 只显示前4张
# 打印前4张图像的标签
print(' '.join(f'{classes[labels[j]]:5s}' for j in range(4)))

运行以上代码,你应该能看到一个 2x2 的图像网格以及它们对应的类别标签,这证明我们的数据准备工作已经成功完成。

三、构建卷积神经网络 (CNN) 模型

现在,进入最核心的部分——设计并实现我们的 CNN 模型。

3.1 模型架构设计

我们将构建一个相对简单的CNN,它包含两个卷积-池化模块和一个全连接分类器模块。这个结构足以在 CIFAR-10 上取得不错的基础性能。

  • 输入: 3x32x32 的彩色图像
  • 模块一:
    • Conv2d: 3个输入通道,6个输出通道,5x5卷积核。
    • ReLU: 激活函数。
    • MaxPool2d: 2x2窗口,步幅为2。
  • 模块二:
    • Conv2d: 6个输入通道,16个输出通道,5x5卷积核。
    • ReLU: 激活函数。
    • MaxPool2d: 2x2窗口,步幅为2。
  • 分类器:
    • Flatten: 将特征图展平。
    • Linear: 全连接层1。
    • ReLU: 激活函数。
    • Linear: 全连接层2。
    • ReLU: 激活函数。
    • Linear: 输出层,输出10个类别分数。

下面是该模型架构的流程图表示:

输入 3x32x32
Conv1: 3->6, 5x5
ReLU
MaxPool1: 2x2
Conv2: 6->16, 5x5
ReLU
MaxPool2: 2x2
Flatten
FC1: ? -> 120
ReLU
FC2: 120 -> 84
ReLU
FC3/Output: 84 -> 10

3.2 代码实现:定义 CNN 类

我们通过继承 torch.nn.Module 来定义自己的网络结构。

import torch.nn as nn
import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super().__init__()# 定义第一个卷积-池化块self.conv1 = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5)self.pool = nn.MaxPool2d(kernel_size=2, stride=2)# 定义第二个卷积-池化块self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)# 定义全连接层# 输入维度的计算见下文self.fc1 = nn.Linear(16 * 5 * 5, 120)  self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):# x 的初始维度: [batch_size, 3, 32, 32]# 通过第一个卷积-池化块x = self.pool(F.relu(self.conv1(x)))# 维度变化: Conv1 -> [b, 6, 28, 28], Pool1 -> [b, 6, 14, 14]# 通过第二个卷积-池化块x = self.pool(F.relu(self.conv2(x)))# 维度变化: Conv2 -> [b, 16, 10, 10], Pool2 -> [b, 16, 5, 5]# 展平操作,-1 表示自动计算该维度的大小# 将 [b, 16, 5, 5] 展平为 [b, 16 * 5 * 5]x = torch.flatten(x, 1) # 通过全连接层x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))# 输出层不需要激活函数,因为 nn.CrossEntropyLoss 会为我们处理x = self.fc3(x) return x

3.2.1 关键计算:展平层输入维度

初学者最容易出错的地方就是计算第一个全连接层(self.fc1)的输入维度。这个维度取决于卷积和池化层处理后输出的特征图大小。我们可以手动计算:

  1. 输入图像: 32x32
  2. conv1: kernel_size=5, padding=0 (默认), stride=1 (默认)。输出尺寸 W _ o u t = f r a c W _ i n − K + 2 P S + 1 = f r a c 32 − 5 + 0 1 + 1 = 28 W\_{out} = \\frac{W\_{in} - K + 2P}{S} + 1 = \\frac{32 - 5 + 0}{1} + 1 = 28 W_out=fracW_inK+2PS+1=frac325+01+1=28。特征图变为 28x28
  3. pool1: kernel_size=2, stride=2。输出尺寸 W _ o u t = f r a c 28 − 2 2 + 1 = 14 W\_{out} = \\frac{28 - 2}{2} + 1 = 14 W_out=frac2822+1=14。特征图变为 14x14
  4. conv2: kernel_size=5。输出尺寸 W _ o u t = f r a c 14 − 5 1 + 1 = 10 W\_{out} = \\frac{14 - 5}{1} + 1 = 10 W_out=frac1451+1=10。特征图变为 10x10
  5. pool2: kernel_size=2, stride=2。输出尺寸 W _ o u t = f r a c 10 − 2 2 + 1 = 5 W\_{out} = \\frac{10 - 2}{2} + 1 = 5 W_out=frac1022+1=5。特征图变为 5x5

经过第二个池化层后,我们得到了 165x5 的特征图。因此,展平后的向量长度为 16 t i m e s 5 t i m e s 5 = 400 16 \\times 5 \\times 5 = 400 16times5times5=400。这就是 nn.Linear(16 * 5 * 5, 120)16 * 5 * 5 的由来。

3.3 模型实例化与设备选择

现在,我们可以创建模型实例并将其移动到之前确定的 device 上。

# 实例化模型
net = Net()# 将模型移动到 GPU (如果可用)
net.to(device)print(net) # 打印网络结构

四、模型训练与评估

模型已经定义好了,接下来我们需要设定“游戏规则”(损失函数和优化器),并让模型在数据中“学习”。

4.1 定义损失函数和优化器

  • 损失函数 (Loss Function): 对于多分类问题,nn.CrossEntropyLoss 是标准选择。它内部整合了 LogSoftmaxNLLLoss,因此我们的模型输出层不需要加 Softmax 激活函数。
  • 优化器 (Optimizer): 我们选择 Adam 优化器,它是一种自适应学习率的优化算法,通常能快速收敛且效果稳健。我们也可以使用带动量的 SGD (随机梯度下降)。
import torch.optim as optim# 定义损失函数
criterion = nn.CrossEntropyLoss()# 定义优化器
# lr=0.001 是一个常用的初始学习率
optimizer = optim.Adam(net.parameters(), lr=0.001) 
# 或者使用 SGD: optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

4.2 编写训练循环

训练过程是在多个 epoch(周期)中迭代数据集。在每个 epoch 中,我们分批次地将数据喂给模型进行学习。

num_epochs = 10 # 训练10个周期print('Starting Training...')for epoch in range(num_epochs):  # 循环遍历数据集多次running_loss = 0.0for i, data in enumerate(trainloader, 0):# 1. 获取输入数据;data 是一个 [inputs, labels] 的列表inputs, labels = data[0].to(device), data[1].to(device)# 2. 将梯度缓存清零optimizer.zero_grad()# 3. 前向传播outputs = net(inputs)# 4. 计算损失loss = criterion(outputs, labels)# 5. 反向传播loss.backward()# 6. 更新权重optimizer.step()# 打印统计信息running_loss += loss.item()if i % 200 == 199:    # 每 200 个 mini-batches 打印一次print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 200:.3f}')running_loss = 0.0print('Finished Training')

4.3 编写评估函数

训练完成后,我们需要在独立的测试集上评估模型的泛化能力。

(1) 整体准确率
correct = 0
total = 0
# 在评估模式下,我们不需要计算梯度
net.eval() # 将模型设置为评估模式
with torch.no_grad():for data in testloader:images, labels = data[0].to(device), data[1].to(device)# 通过网络进行预测outputs = net(images)# 获取预测结果中分数最高的类别_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()print(f'Accuracy of the network on the 10000 test images: {100 * correct / total} %')

重要提示: net.eval()net.train() 是必须的步骤。它们会告诉模型当前处于评估还是训练状态,这对于像 DropoutBatchNorm 这样的层非常重要(尽管我们这个简单模型没用上,但这是个好习惯)。

(2) 各类别准确率

分析模型在每个类别上的表现可以给我们更多洞见。

# 准备统计每个类别的正确预测数
correct_pred = {classname: 0 for classname in classes}
total_pred = {classname: 0 for classname in classes}net.eval()
with torch.no_grad():for data in testloader:images, labels = data[0].to(device), data[1].to(device)outputs = net(images)_, predictions = torch.max(outputs, 1)# 收集每个类别的正确预测for label, prediction in zip(labels, predictions):if label == prediction:correct_pred[classes[label]] += 1total_pred[classes[label]] += 1# 打印每个类别的准确率
for classname, correct_count in correct_pred.items():accuracy = 100 * float(correct_count) / total_pred[classname]print(f'Accuracy for class: {classname:5s} is {accuracy:.1f} %')

通常,模型在视觉上更相似的类别之间更容易混淆,例如“猫”和“狗”,或者“汽车”和“卡车”。

4.4 保存模型(可选但推荐)

训练好的模型应该被保存下来,以便未来直接加载使用,而无需重新训练。

PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

这行代码会将模型的所有可学习参数(权重和偏置)保存到指定路径。

五、结果分析与改进

5.1 训练结果分析

运行完上述代码后,你可能会得到一个在 55% - 65% 之间的整体准确率。对于这个简单的模型来说,这是一个相当不错且符合预期的结果。它证明了我们的 CNN 确实从数据中学到了一些有用的视觉特征。

通过观察各类别准确率,你可能会发现模型对“船”和“飞机”的识别效果较好(因为它们通常有相对统一的背景,如水面和天空),而对“猫”和“狗”的识别效果较差(因为它们形态多变,且可能出现在任何背景中)。

5.2 可视化预测结果

让我们随机抽取一些测试图像,看看模型的实际预测效果。

# 重新加载数据,因为之前的迭代器已经走完
dataiter = iter(testloader)
images, labels = next(dataiter)# 显示图像
imshow(torchvision.utils.make_grid(images[:4]))
print('GroundTruth: ', ' '.join(f'{classes[labels[j]]:5s}' for j in range(4)))# 加载模型(如果需要)
# net = Net()
# net.load_state_dict(torch.load(PATH))
# net.to(device)# 进行预测
outputs = net(images.to(device))
_, predicted = torch.max(outputs, 1)print('Predicted:   ', ' '.join(f'{classes[predicted[j]]:5s}' for j in range(4)))

对比 GroundTruth (真实标签) 和 Predicted (预测标签),可以直观地感受模型的表现。

5.3 常见问题与改进方向

我们当前模型的性能还有很大的提升空间。以下是一些常见的改进策略:

(1) 问题:准确率不高怎么办?
  • 增加训练周期:当前只训练了10个epoch,可以尝试增加到20、50甚至更多,观察损失是否还在下降。
  • 调整学习率0.001 是一个不错的起点,但不一定是最优的。可以尝试更小(如 0.0001)或更大(如 0.01)的学习率,或者使用学习率调度器(Learning Rate Scheduler)动态调整。
  • 模型复杂化
    • 加深:增加更多的卷积层和全连接层。
    • 加宽:增加每层卷积核(通道数)的数量。
  • 使用正则化技术:在更复杂的模型上,为了防止过拟合,应引入 Dropout 层或权重衰减(weight_decay),这些我们在前面的文章中已经介绍过。
  • 数据增强:对训练图像进行随机翻转、裁剪、旋转等操作,可以极大地扩充数据集,提升模型的泛化能力。我们将在下一篇文章中详细讲解。
(2) 问题:训练速度太慢?
  • 确认使用GPU:这是最关键的加速手段。
  • 优化DataLoader:适当增加 num_workers 的值可以加速数据加载,但值过大会增加内存开销。
  • 减小批处理大小 (batch_size):如果 GPU 显存不足,减小 batch_size 是必要的。
(3) 下一步:迁移学习

在实际应用中,从零开始训练一个模型往往不是最高效的方法。更常见的是使用迁移学习 (Transfer Learning),即在一个非常大的数据集(如 ImageNet)上预训练好的强大模型(如 ResNet, VGG)的基础上,针对我们自己的任务进行微调。这通常能以更少的训练时间和数据达到更高的准确率。我们将在后续文章中深入探讨这一高级技巧。

六、总结

恭喜你!通过本文的引导,你已经成功地完成了你的第一个完整的 CNN 图像分类项目。让我们回顾一下本次实战的核心收获:

  1. 端到端流程掌握:我们实践了从数据准备、模型构建、训练循环、评估分析到结果可视化的深度学习项目全流程。
  2. torchvisionDataLoader 的熟练运用:你学会了如何利用 PyTorch 生态提供的工具高效地加载和预处理像 CIFAR-10 这样的标准数据集。
  3. CNN 代码实现能力:你亲手将一个 CNN 架构从设计图纸变成了可执行的 PyTorch 代码,并理解了其中关键参数(如全连接层输入维度)的计算方法。
  4. 模型训练与评估的标准化:你掌握了编写标准训练循环、定义损失函数与优化器、以及在 traineval 模式间切换进行模型评估的最佳实践。
  5. 问题导向的优化思维:我们不仅满足于实现功能,更探讨了如何分析模型表现、定位问题,并提出了包括调整超参数、改进模型结构、数据增强等一系列行之有效的优化策略。

这个 CIFAR-10 分类项目是你深度学习之旅上的一个重要里程碑。它为你后续学习更复杂的模型(如 ResNet)、更高级的技术(如迁移学习)以及挑战更困难的任务打下了坚实的实践基础。希望你能动手修改代码,尝试我们提到的改进方向,真正将这些知识内化为你自己的技能。

在下一篇文章中,我们将聚焦于图像数据增强 (Data Augmentation),这是一个几乎所有计算机视觉任务中都必不可少的“免费午餐”,能有效提升模型的泛化能力。敬请期待!


http://www.lryc.cn/news/579116.html

相关文章:

  • CISSP知识点汇总-安全与风险管理
  • 智能学号抽取系统 V3.7.5 —— 一个基于 Vue.js 的交互式网页应用
  • 小架构step系列02:搭建工程
  • 智能检测原理和架构
  • STM32WB55VGY6TR 蓝牙OTA升级
  • ZED相机与Foxglove集成:加速机器人视觉调试效率的实用方案
  • 观测云 × AWS SSO:权限治理可观测实践
  • 计算机组成笔记:缓存替换算法
  • [202106][凤凰架构][构建可靠的大型分布式系统][周志明][著]
  • 车载软件架构 -- SOA服务分层设计原则
  • MacOS 安装brew 国内源【超简洁步骤】
  • 线程同步【Linux操作系统】
  • Kafka 运维与调优篇:构建高可用生产环境的实战指南
  • Java学习第六部分——API部分(续)
  • 腾讯云认证考试报名 - TDSQL数据库交付运维专家(TCCE PostgreSQL版)
  • 智慧城市的安全密码:商用密码如何守护万物互联?
  • 运用逆元优化组合计算#数论
  • Django服务开发镜像构建
  • C++主流编辑器特点比较
  • Java 并发编程的 CAS(Compare and Swap)是什么?
  • 讲解“/etc/ssh/sshd_config “的“HostKey /etc/ssh/ssh_host_ed25519_key“ 笔记250702
  • pdf删除一页 python实现(已验证)
  • 模板编译原理
  • 使用OpenCV识别图片相似度评分的应用
  • YOLOv11剪枝与量化(一)模型压缩的必要性
  • 深入理解C++11原子操作:从内存模型到无锁编程
  • SpringCloud系列(47)--SpringCloud Bus实现动态刷新定点通知
  • 04-动态规划
  • 数学建模_微分方程
  • 内存架构的十字路口:深入解析统一内存访问(UMA)与非一致内存访问(NUMA)