目录
[1. 特征提取模块](#1. 特征提取模块)
[2. 全连接分类模块](#2. 全连接分类模块)
[1. 导入依赖库 和 数据加载](#1. 导入依赖库 和 数据加载)
[2. 搭建 CNN 卷积神经网络模型](#2. 搭建 CNN 卷积神经网络模型)
[3. 设备、损失函数与优化器配置](#3. 设备、损失函数与优化器配置)
[4. 模型训练函数](#4. 模型训练函数)
[5. 模型测试函数](#5. 模型测试函数)
[6. 主程序入口](#6. 主程序入口)
前言
上一节我们使用简单的多层感知机(MLP)完成了手写数字识别,但多层感知机存在明显缺陷。本节课我们引入卷积神经网络(CNN),用更适配图像任务的方案重新实现手写数字识别,理解 CNN 核心特性与图像任务的设计思路。
一、多层感知机的缺陷
- 丢失空间局部信息: 多层感知机接收图像数据时,全连接层会把二维图像矩阵展平成一维向量。这一操作破坏图片原生的二维空间结构,像素之间原本相邻的位置关系被打乱,丢失图像局部空间信息。模型无法感知局部像素组合构成的特征,不具备局部性,很难提取图像里边缘、轮廓这类局部特征。
- 缺少平移不变性 :图像识别任务期望:同一个数字,出现在图片左上角或是右下角,模型都能正确识别。但 MLP 不具备平移不变性,物体在画面中位置发生偏移,展平之后向量完全改变,模型识别效果会大幅下降。

为了解决局部性、平移不变性这两个核心需求,卷积神经网络应运而生。
二、从全连接层到卷积
1、什么是卷积
图像本质是由像素构成的二维矩阵。我们定义一个固定大小窗口(例如 3×3),这个窗口就是卷积核 。
卷积核从图像左上角开始,和对应区域 3×3 像素矩阵做互相关运算,计算得到一个新像素值,这个值就代表该局部区域的图像特征。卷积核按照设定步长,从左到右、从上到下滑动,持续做互相关运算,最终生成新的像素矩阵,以此捕获图像局部特征,满足局部性需求。
2、卷积核
卷积核的尺寸、滑动步长均可自定义,会直接改变输出特征图尺寸。
直接滑动卷积核会造成图片边缘像素被利用不足,为解决该问题引入填充(padding):在原图四周补充像素,让边缘区域也可以完整参与卷积运算。
彩色图像具备多通道,卷积核通道数需要和输入图像通道数保持一致。每个通道单独做互相关运算,再将各通道结果相加,得到单张输出特征图。
我们可以设置多个卷积核 ,每个卷积核负责提取一类特征,一个卷积核对应一张特征图。好比从不同视角观察物体,让网络能够学习边缘、纹理等多种图像特征,这一层即为卷积层

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 卷积输出特征图的尺寸由输入尺寸、卷积核大小、步长、填充共同决定,标准计算公式如下:
参数说明 :
:输入特征图的高、宽
:卷积核尺寸
:边缘填充层数
:卷积滑动步长
:向下取整运算 本文网络验证 :MNIST 输入尺寸为
,设置
,代入公式计算:
,卷积后尺寸保持不变,完美契合代码设计。 |


3、感受野和特征映射
堆叠多层卷积层后,特征图上单个像素,对应回原始输入图像的区域范围就是感受野 。
类比:在高楼中,楼层越高,视野覆盖地面范围越大。浅层卷积感受野小,捕捉边缘、线条这类基础特征;深层卷积感受野更大,可以捕获轮廓、整体结构等高层特征。
特征映射:图像原始局部特征,经过卷积运算,最终浓缩为特征图上单个像素值。

4、池化层(汇聚层)
在图像网络中,我们希望逐步降低特征图的空间分辨率,聚合局部信息。随着网络层数加深,神经元对应的感受野不断扩大。图像分类任务最终需要基于整张图片全局信息判断,池化层用来逐步聚合信息,生成更粗粒度的特征,同时保留卷积层提取特征的优势。
池化两大作用:降低卷积层对特征位置的敏感度,实现空间降采样。
- 最大池化 :在指定池化窗口内,选取最大像素值作为输出结果,核心作用是保留区域内最显著的纹理、轮廓特征,弱化位置偏差干扰。
举例 :设定 2×2 池化窗口,窗口内像素值为 \[1, 3, 2, 6],最大池化会选取最大值 6 作为该区域的输出特征。
- 平均池化 :计算池化窗口内所有像素值的平均值作为输出结果,能够平滑区域特征、弱化局部噪声,保留整体灰度信息。
举例 :沿用上述 2×2 窗口像素 \[1, 3, 2, 6],平均值计算为 (1+3+2+6)÷4=3,最终输出结果为 3。
三、卷积神经网络整体结构构建
相较于传统多层感知机,卷积神经网络针对图像任务做了专属结构优化,整体可划分为特征提取模块 与全连接模块两大核心部分,分工明确、各司其职,共同完成图像特征提取与分类预测。
1. 特征提取模块
该模块是CNN的核心核心组件,由卷积层、非线性激活函数、池化层交替堆叠组成。通过多层卷积运算自动提取图像浅层纹理、边缘及深层轮廓、全局特征,搭配池化层完成特征降维、去除冗余信息,在保留关键特征的同时大幅降低计算参数量。
2. 全连接分类模块
经过特征提取模块得到的多维特征图,会通过展平操作转换为一维特征向量,再输入多层全连接网络。该模块负责对提取到的优质特征进行融合与映射,最终输出对应类别的预测结果,完成手写数字的多分类任务。

四、代码分步实现与逐段讲解
本节将完整 CNN 手写数字识别代码进行模块化拆分,分为数据处理、网络搭建、参数配置、训练测试、程序入口五大模块,逐段解析功能逻辑,方便理解卷积网络的完整训练流程。
1. 导入依赖库 和 数据加载
首先导入 PyTorch 核心工具库,加载 MNIST 官方数据集,并通过 DataLoader 实现批量数据加载,为后续网络训练提供数据支撑。
python
# 导入所需工具库
import torch
from torch import nn # 搭建神经网络模块
from torch.utils.data import DataLoader # 批量数据加载器
from torchvision import datasets # 官方视觉数据集
from torchvision.transforms import ToTensor # 图像转张量
加载MNIST训练集:60000张28*28手写数字图像
training_data = datasets.MNIST(
root='./data',
train=True,
download=True,
transform=ToTensor(),
)
加载MNIST测试集:10000张28*28手写数字图像
test_data = datasets.MNIST(
root='./data',
train=False,
download=True,
transform=ToTensor(),
)
批量加载数据,批次大小64,训练集打乱数据,测试集不打乱
train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=False)
MNIST 数据集无需手动预处理,框架自动完成灰度化、归一化。通过 DataLoader 分批读取数据,避免一次性加载全部数据导致显存溢出,同时打乱训练集数据可以有效防止模型过拟合。
2. 搭建 CNN 卷积神经网络模型
本次网络分为两大模块:特征提取层(卷积+激活+池化) + 分类输出层(全连接网络)。卷积层负责提取图像局部特征,池化层降维去冗余,全连接层完成最终分类。
python
# 自定义CNN网络模型
class cnnnet(nn.Module):
def __init__(self):
super().__init__()
# 特征提取模块:多层卷积+激活+池化,提取图像深层特征
self.features = nn.Sequential(
# 第一层卷积:输入1通道(灰度图),输出16通道,5*5卷积核,补边2,步长1
# 输出尺寸:28*28,尺寸不变,提取浅层边缘、纹理特征
nn.Conv2d(1,16,kernel_size = 5,stride = 1,padding = 2),
nn.ReLU(), # 非线性激活函数
nn.MaxPool2d(kernel_size = 2), # 最大池化降维:16*14*14
# 第二层卷积:输入16通道,输出32通道,提取中层组合特征
nn.Conv2d(16, 32, kernel_size = 5,stride = 1,padding = 2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2), # 再次降维:32*7*7
# 第三、四层卷积:进一步升维,提取高层全局特征
nn.Conv2d(32, 64,kernel_size = 5,stride = 1,padding = 2),
nn.ReLU(),
nn.Conv2d(64, 128, kernel_size=5, stride=1, padding=2),
nn.ReLU()
)
全连接模块:将卷积特征展平,通过全连接层输出分类结果
self.classifier = nn.Sequential(
nn.Flatten(), # 多维特征展平为一维向量
nn.Linear(12877, 128), # 第一层全连接
nn.ReLU(),
nn.Linear(128,256), # 第二层全连接
nn.ReLU(),
nn.Linear(256, 10) # 输出10个数字类别的预测分值
)
前向传播逻辑
def forward(self,x):
x = self.features(x) # 卷积提取特征
y = self.classifier(x) # 全连接分类
return y
网络通过多次卷积升维提取丰富特征,搭配池化层压缩特征尺寸、降低计算量。区别于 MLP,CNN 全程保留图像二维空间结构,完美利用图像局部特征与平移不变性,大幅提升识别精度。
3. 设备、损失函数与优化器配置
python
# 自动适配设备:优先GPU(cuda)、苹果MPS、最后CPU
device = torch.device('cuda' if torch.cuda.is_available() else "mps" if torch.backends.mps.is_available() else "cpu" )
model = cnnnet() # 实例化网络
model.to(device) # 模型迁移至运算设备
losser = nn.CrossEntropyLoss() # 多分类专用交叉熵损失
optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # Adam自适应优化器
使用 Adam 优化器替代 SGD,具备自适应学习率特性,收敛速度更快、效果更稳定。交叉熵损失专门适配多分类任务,是图像分类的标准配置。
4. 模型训练函数
python
# 模型训练函数
def train(model,device,train_dataloader,loss_fn,optimizer):
length = len(train_dataloader.dataset) # 训练集总样本数
num_batches = len(train_dataloader) # 训练集总批次
model.train() # 开启训练模式
sum_loss = 0.0 # 累计总损失
correct = 0 # 累计正确样本数
# 逐批次训练
for X,y in train_dataloader:
X=X.to(device)
y=y.to(device)
y_pred = model(X) # 前向传播预测
loss = loss_fn(y_pred,y) # 计算损失
optimizer.zero_grad() # 清空历史梯度
loss.backward() # 反向传播求梯度
optimizer.step() # 优化器更新参数
sum_loss += loss.item()
correct += (y_pred.argmax(1) == y).sum().item()
输出本轮训练指标
print(f"平均准确率{correct / length:.4f},平均损失{sum_loss / num_batches:.4f} ")
严格遵循深度学习四大训练流程:前向传播、计算损失、反向求导、参数更新。自动统计每轮训练的准确率与损失,直观监控模型收敛状态。
5. 模型测试函数
python
# 模型测试函数
def test(model,device,test_dataloader,loss_fn):
length = len(test_dataloader.dataset)
num_batches = len(test_dataloader)
model.eval() # 开启评估模式
sum_loss =0.0
correct = 0
# 关闭梯度计算,节省显存、提升推理速度
with torch.no_grad():
for X,y in test_dataloader:
X=X.to(device)
y=y.to(device)
y_pred = model(X)
loss = loss_fn(y_pred,y)
sum_loss += loss.item()
correct += (y_pred.argmax(1) == y).sum().item()
print(f"测试集平均准确率{correct/length:.4f},测试平均损失{sum_loss/num_batches:.4f}")
模型测试阶段需开启评估模式并关闭梯度计算,既可以节省显存资源、加快推理速度,又能避免模型参数发生更新。该阶段仅执行前向推理,通过输入测试数据完成预测,计算测试集损失与分类准确率,客观衡量模型的泛化能力。
6. 主程序入口
python
if __name__ == "__main__":
epochs = 100 # 迭代训练100轮
print("开始训练")
for i in range(epochs):
print(f"第{i + 1}轮训练")
train(model, device, train_dataloader, losser, optimizer)
print("开始测试")
test(model, device, test_dataloader, losser)

从训练结果可以直观体现卷积神经网络的优异性能,模型仅经过首轮训练,准确率便已突破90%,收敛速度十分可观。

模型最终测试准确率为99.41%,相较于传统MLP模型,识别精度与稳定性均实现大幅提升,充分验证了CNN在图像分类任务中的优越性。
五、实验总结
本文针对传统多层感知机在图像任务上的缺陷,引入卷积神经网络完成 MNIST 手写数字识别任务。CNN 依靠局部感知、权值共享、平移不变性三大核心优势,有效保留图像二维空间特征,解决了 MLP 展平图像丢失空间信息、位置敏感的问题。通过卷积层逐层提取浅层纹理与深层语义特征,配合池化层降维去冗余,结合全连接层完成特征融合与分类,构建了完整的图像识别网络链路。
从实验结果可以看出,相较于传统 MLP 模型,本次搭建的 CNN 网络收敛速度更快、识别精度大幅提升,最终测试准确率可达 99% 以上,充分证明了卷积神经网络在计算机视觉任务中的绝对优势。本次实验重在梳理卷积神经网络的基础原理与完整训练流程,暂不开展超参数调优、网络结构优化等进阶操作。