从入门到实战】PyTorch 搭建 CIFAR10 分类网络完整代码详解
·
本文将带你从数据加载、模型搭建、损失函数定义、优化器配置到完整训练流程,逐行拆解一份可直接运行的 CIFAR10 分类代码,帮你彻底理解 PyTorch 卷积神经网络的基础框架。
一、整体代码概览
这份代码实现了一个 3 层卷积 + 池化 + 全连接的 CNN 模型,在 CIFAR10 数据集上完成图像分类训练,核心流程如下: 数据加载 → 模型定义 → 损失函数+优化器配置 → 多轮训练循环 → 损失统计
python
运行
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential, CrossEntropyLoss
from torch.utils.data import DataLoader
# 1. 加载 CIFAR10 数据集
dataset=torchvision.datasets.CIFAR10(root='../dataset', train=True, download=True, transform=torchvision.transforms.ToTensor())
dataload=DataLoader(dataset, batch_size=4)
# 2. 定义卷积神经网络模型
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
# 3. 实例化模型、损失函数、优化器
tudui = Tudui()
loss_fn = CrossEntropyLoss()
optim=torch.optim.SGD(tudui.parameters(), lr=0.01)
# 4. 多轮训练循环
for epoch in range(20):
running_loss=0.0
for data in dataload:
imgs, targets = data
output = tudui(imgs)
result_loss = loss_fn(output, targets)
optim.zero_grad()
result_loss.backward()
optim.step()
print(result_loss)
running_loss += result_loss
print(f"第{epoch+1}轮训练总损失: {running_loss.item()}")
二、逐模块详细讲解
1. 数据加载模块
python
运行
import torchvision
from torch.utils.data import DataLoader
# 加载 CIFAR10 数据集
dataset=torchvision.datasets.CIFAR10(
root='../dataset',
train=True,
download=True,
transform=torchvision.transforms.ToTensor()
)
dataload=DataLoader(dataset, batch_size=4)
关键参数说明
root='../dataset':数据集的存储路径,不存在则会自动创建。train=True:加载训练集(train=False为测试集)。download=True:如果本地没有数据集,自动从网上下载。transform=torchvision.transforms.ToTensor():将 PIL 图像转为 PyTorch 张量,同时把像素值从0~255归一化到0~1,并调整维度顺序为(C, H, W)(PyTorch 模型输入要求)。DataLoader(dataset, batch_size=4):数据加载器,每次从数据集中取出 4 张图片作为一个批次(batch),后续模型将按批次处理数据。
2. 卷积神经网络模型定义
python
运行
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential(
# 第1个卷积+池化块
Conv2d(3, 32, 5, padding=2),
MaxPool2d(2),
# 第2个卷积+池化块
Conv2d(32, 32, 5, padding=2),
MaxPool2d(2),
# 第3个卷积+池化块
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
核心结构解析
class Tudui(nn.Module):所有 PyTorch 模型都必须继承nn.Module,这是模型的基类。super(Tudui, self).__init__():调用父类的初始化方法,必须写,否则模型参数无法被 PyTorch 正确注册。nn.Sequential:按顺序堆叠网络层,自动按顺序执行,简化代码结构。
各层作用与维度变化(以输入 (4, 3, 32, 32) 为例)
表格
| 层类型 | 配置 | 作用 | 输出形状 |
|---|---|---|---|
Conv2d(3, 32, 5, padding=2) |
输入通道 3,输出通道 32,卷积核 5×5,padding=2 | 提取图像低级特征(边缘、纹理),padding=2 保证输入输出尺寸不变 | (4, 32, 32, 32) |
MaxPool2d(2) |
池化核 2×2,步长 2 | 下采样,特征图尺寸减半,降低计算量 | (4, 32, 16, 16) |
Conv2d(32, 32, 5, padding=2) |
输入通道 32,输出通道 32,卷积核 5×5,padding=2 | 提取中级特征 | (4, 32, 16, 16) |
MaxPool2d(2) |
池化核 2×2 | 再次下采样 | (4, 32, 8, 8) |
Conv2d(32, 64, 5, padding=2) |
输入通道 32,输出通道 64,卷积核 5×5,padding=2 | 提取高级特征 | (4, 64, 8, 8) |
MaxPool2d(2) |
池化核 2×2 | 最后一次下采样 | (4, 64, 4, 4) |
Flatten() |
- | 将特征图展平为一维向量 | (4, 1024)(64×4×4=1024) |
Linear(1024, 64) |
输入 1024,输出 64 | 全连接层,特征映射 | (4, 64) |
Linear(64, 10) |
输入 64,输出 10 | 输出层,对应 CIFAR10 的 10 个分类 | (4, 10) |
forward(self, x):前向传播方法,定义数据在模型中的流动路径,x = self.model1(x)直接将数据喂给Sequential堆叠的层,无需手动逐行调用。
3. 损失函数与优化器配置
python
运行
tudui = Tudui()
loss_fn = CrossEntropyLoss()
optim=torch.optim.SGD(tudui.parameters(), lr=0.01)
损失函数 CrossEntropyLoss
- 作用:多分类任务专用损失函数,自动对模型输出的
logits做 Softmax 归一化,再计算与真实标签的交叉熵,衡量模型预测与真实标签的差距。 - 输入要求:模型输出形状为
(batch_size, 10)(10 个类别的原始分数),真实标签为(batch_size,)的整数(0~9)。
优化器 SGD
torch.optim.SGD(tudui.parameters(), lr=0.01):随机梯度下降优化器,用于更新模型参数。tudui.parameters():获取模型所有可训练参数(卷积核权重、全连接层权重等)。lr=0.01:学习率,控制参数更新的步长,过大会导致训练不稳定,过小则收敛过慢。
4. 完整训练循环
python
运行
for epoch in range(20):
running_loss=0.0
for data in dataload:
imgs, targets = data
output = tudui(imgs)
result_loss = loss_fn(output, targets)
# 反向传播与参数更新
optim.zero_grad()
result_loss.backward()
optim.step()
print(result_loss)
running_loss += result_loss
print(f"第{epoch+1}轮训练总损失: {running_loss.item()}")
关键步骤拆解
for epoch in range(20):训练 20 轮,每一轮遍历整个数据集一次,让模型多次学习所有数据。running_loss=0.0:记录每一轮的总损失,用于观察模型整体学习情况。imgs, targets = data:从DataLoader取出一个批次的数据,imgs是图像张量,targets是对应标签。output = tudui(imgs):前向传播,模型输出每个类别的预测分数。result_loss = loss_fn(output, targets):计算当前批次的损失值。optim.zero_grad():清空上一批次的梯度,PyTorch 默认会累积梯度,不清空会导致梯度计算错误。result_loss.backward():反向传播,自动计算所有参数的梯度。optim.step():根据梯度更新模型参数,让模型朝着降低损失的方向学习。running_loss += result_loss:累加当前批次的损失,用于统计每一轮的总损失。print(f"第{epoch+1}轮训练总损失: {running_loss.item()}"):打印每一轮的总损失,观察模型是否收敛(损失持续下降)。
三、关键概念与常见问题
1. 模型初始化的权重是什么?
你在调试时看到的 Conv2d.weight 张量,就是卷积层的可训练参数,形状为 (out_channels, in_channels, kernel_size, kernel_size),初始值为 PyTorch 默认初始化的随机小数,训练过程中会通过反向传播不断更新。
2. 为什么损失一开始在 2.3 左右?
CIFAR10 是 10 分类任务,模型随机初始化时相当于 “瞎猜”,交叉熵损失的理论值约为 ln(10) ≈ 2.3,随着训练进行,损失会逐步下降,说明模型在学习。
3. grad_fn 是什么?
result_loss 张量中的 grad_fn 是梯度函数,PyTorch 用它记录反向传播的计算路径,result_loss.backward() 就是通过这个函数自动计算梯度的。
四、训练效果优化建议
- 调整学习率:如果损失不下降,可尝试降低学习率(如
lr=0.001),或使用更稳定的Adam优化器:optim=torch.optim.Adam(tudui.parameters(), lr=0.001)。 - 增加训练轮数:20 轮训练可能不足以让模型收敛,可适当增加到 50 轮。
- 数据增强:给
transform添加随机翻转、裁剪等操作,提升模型泛化能力。 - 添加 Dropout 层:在全连接层之间添加
nn.Dropout(0.2),防止过拟合。
更多推荐



所有评论(0)