前言卷积神经网络CNN的崛起彻底革新了计算机视觉领域。从 2012 年 AlexNet 打破传统机器学习壁垒到 VGG、GoogLeNet、ResNet 等模型不断突破性能极限现代 CNN 不仅推动了学术研究的跨越式发展更在工业界落地了图像分类、目标检测、语义分割等海量应用。本章系统拆解AlexNet、VGG、GoogLeNet、ResNet四大经典模型的设计逻辑、结构细节与技术突破深入解析批量规范化BatchNorm的核心原理、计算流程与实战用法基于 CIFAR-10 数据集完成端到端图像分类实战覆盖数据预处理、模型搭建、训练调优与结果可视化全流程结合实际学习场景总结避坑指南配套阶段性学习计划最后预告下章核心内容。全文兼顾理论深度与实战可操作性适合深度学习入门者夯实 CNN 基础也适合进阶开发者梳理经典模型演进脉络。一、现代 CNN 演进脉络从 LeNet 到 ResNet在深入经典模型前需先理清现代 CNN 的演进逻辑 ——网络深度逐步加深、卷积核尺寸逐步缩小、特征提取效率持续提升、梯度传播难题不断被解决。1.1 前 CNN 时代的困境2012 年之前图像分类任务主流依赖手工特征提取 传统分类器如 SVM的组合。手工特征如 SIFT、HOG依赖人工设计泛化能力差难以适配复杂场景传统全连接网络处理图像时参数规模随图像像素数爆炸如 224×224 彩色图输入全连接层需 15 万 参数易过拟合且训练效率极低。1.2 现代 CNN 的核心突破方向现代 CNN 通过三大核心设计彻底解决上述困境局部感知与权重共享卷积层仅感知局部特征同一卷积核遍历全图大幅减少参数量深层特征分层提取浅层提取边缘、纹理等基础特征深层组合基础特征形成部件、目标等高级特征梯度与正则化优化ReLU 激活、批量规范化、残差连接等技术解决深层网络梯度消失 / 爆炸、过拟合等问题。1.3 经典模型演进时间线1998 年LeNet-57 层—— 首个成功应用的 CNN用于手写数字识别奠定 CNN 基础架构2012 年AlexNet8 层—— 深度学习 CV 开山之作ImageNet 竞赛夺冠开启 CNN 主导时代2014 年VGG16/19 层—— 验证 “深度 性能”堆叠 3×3 小卷积核构建深层网络2014 年GoogLeNetInception v122 层—— 多尺度特征融合1×1 卷积降维平衡性能与效率2015 年ResNet18/34/50/101/152 层—— 残差连接突破深层瓶颈解决网络退化问题。二、经典现代 CNN 模型深度解析2.1 AlexNet深度学习 CV 的 “破冰者”20122.1.1 核心定位与突破AlexNet 由 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 设计在 2012 年 ImageNet 竞赛中将 Top-5 错误率从传统方法的 26% 降至16%以绝对优势夺冠首次证明深层 CNN 在大规模复杂图像任务中的有效性直接点燃深度学习在 CV 领域的研究热潮。2.1.2 网络结构细节输入224×224×3 彩色图AlexNet 共 8 层5 个卷积层 3 个全连接层输出 1000 类分类结果结构拆解如下卷积层 1Conv196 个 11×11 卷积核步长 4ReLU 激活输出 55×55×96分组双 GPU 训练每组 48 通道池化层 1Pool13×3 最大池化步长 2输出 27×27×96引入重叠池化池化核尺寸 步长提升特征提取能力卷积层 2Conv2256 个 5×5 卷积核步长 1padding2ReLU 激活输出 27×27×256分组双 GPU 训练池化层 2Pool23×3 最大池化步长 2输出 13×13×256卷积层 3Conv3384 个 3×3 卷积核步长 1Padding1ReLU 激活输出 13×13×384单 GPU卷积层 4Conv4384 个 3×3 卷积核步长 1Padding1ReLU 激活输出 13×13×384分组双 GPU卷积层 5Conv5256 个 3×3 卷积核步长 1Padding1ReLU 激活输出 13×13×256分组双 GPU池化层 3Pool33×3 最大池化步长 2输出 6×6×256全连接层 1FC14096 神经元ReLU 激活Dropoutp0.5防止过拟合全连接层 2FC24096 神经元ReLU 激活Dropoutp0.5全连接层 3FC31000 神经元Softmax 激活输出分类概率。2.1.3 关键技术创新影响深远ReLU 激活函数替代传统 Sigmoid/Tanh公式为f(x)max(0,x)解决梯度消失问题收敛速度提升 10 倍以上Dropout 正则化训练时随机丢弃 50% 神经元打破神经元共线性抑制过拟合成为后续模型标配局部响应归一化LRN对相邻通道特征归一化模拟生物视觉 “侧抑制” 机制增强泛化能力后续被 BatchNorm 替代双 GPU 并行训练模型拆分到两块 GPU突破硬件算力限制为大模型训练提供范式数据增强训练时随机裁剪、水平翻转、颜色抖动扩充数据多样性进一步降低过拟合风险。2.1.4 优缺点总结优点首次验证深层 CNN 有效性技术创新ReLU、Dropout奠定现代 CNN 基础缺点卷积核尺寸大11×11、5×5参数量达 60M计算成本高LRN 效果有限后续被 BatchNorm 取代。2.2 VGG极简堆叠深度为王20142.2.1 核心定位与突破VGGVisual Geometry Group由牛津大学视觉几何团队设计2014 年 ImageNet 竞赛亚军。核心贡献是验证 “网络深度是性能的关键”通过堆叠 3×3 小卷积核替代大卷积核构建 16~19 层深层网络结构极简、扩展性强。2.2.2 核心设计理念小卷积核替代大卷积核VGG 证明2 个 3×3 卷积核堆叠感受野等价于 1 个 5×5 卷积核3 个 3×3 卷积核堆叠感受野等价于 1 个 7×7 卷积核但有两大优势非线性更强每堆叠 1 个 3×3 卷积核多 1 层 ReLU 激活增强特征表达能力参数量更少3 个 3×3 卷积核参数量3×3×C2×3远少于 1 个 7×7 卷积核7×7×C2计算效率更高。2.2.3 VGG 块与网络结构1VGG 块核心复用单元VGG 网络由5 个 VGG 块堆叠而成每个 VGG 块结构统一N 个 3×3 卷积层Padding1保持特征图尺寸 ReLU 激活 1 个 2×2 最大池化层步长 2下采样。PyTorch 实现 VGG 块import torch from torch import nn from d2l import torch as d2l def vgg_block(num_convs, in_channels, out_channels): 构建VGG块 num_convs: 卷积层数量 in_channels: 输入通道数 out_channels: 输出通道数 layers [] for _ in range(num_convs): layers.append(nn.Conv2d(in_channels, out_channels, kernel_size3, padding1)) layers.append(nn.ReLU()) in_channels out_channels layers.append(nn.MaxPool2d(kernel_size2, stride2)) return nn.Sequential(*layers)2VGG-16 结构最经典版本输入224×224×3共 16 层13 个卷积层 3 个全连接层块 12 个卷积层64 通道→ 池化 → 输出 112×112×64块 22 个卷积层128 通道→ 池化 → 输出 56×56×128块 33 个卷积层256 通道→ 池化 → 输出 28×28×256块 43 个卷积层512 通道→ 池化 → 输出 14×14×512块 53 个卷积层512 通道→ 池化 → 输出 7×7×512全连接层 14096 神经元 ReLU Dropout全连接层 24096 神经元 ReLU Dropout全连接层 31000 神经元 Softmax。2.2.4 VGG-16 PyTorch 实现# 定义VGG-16的卷积块配置(卷积层数, 输出通道数) conv_arch ((2, 64), (2, 128), (3, 256), (3, 512), (3, 512)) def vgg(conv_arch): 构建VGG网络 conv_blks [] in_channels 3 # 输入RGB图像3通道 # 堆叠卷积块 for (num_convs, out_channels) in conv_arch: conv_blks.append(vgg_block(num_convs, in_channels, out_channels)) in_channels out_channels # 全连接层 return nn.Sequential( *conv_blks, nn.Flatten(), nn.Linear(512 * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 1000) ) # 初始化VGG-16模型 vgg16 vgg(conv_arch) # 测试输入输出维度 X torch.randn(1, 3, 224, 224) for blk in vgg16: X blk(X) print(blk.__class__.__name__, output shape:\t, X.shape)2.2.5 优缺点总结优点结构极简规整易理解、易实现、易扩展小卷积核堆叠增强非线性特征提取能力强缺点参数量巨大VGG-16 约 138M全连接层占比超 90%计算与内存成本高深层网络仍存在梯度消失风险。2.3 GoogLeNetInception v1多尺度融合高效轻量20142.3.1 核心定位与突破GoogLeNet 由 Google 团队设计2014 年 ImageNet 竞赛冠军。核心突破是平衡性能与效率提出Inception 块实现多尺度特征并行提取用1×1 卷积降维大幅减少参数量参数量仅为 AlexNet 的 1/12约 6M可高效部署在低算力设备。2.3.2 Inception 块核心创新单元1设计动机单一尺寸卷积核难以适配不同尺度目标如大目标需大卷积核小目标需小卷积核。Inception 块通过并行 4 条路径同时提取 1×1、3×3、5×5 尺度特征最后在通道维度拼接实现多尺度特征融合。2结构细节Inception 块包含 4 条并行路径输出通道拼接路径 11×1 卷积直接提取细粒度特征输出通道数 C1路径 21×1 卷积→3×3 卷积1×1 卷积降维减少 3×3 卷积计算量再提取中等尺度特征输出通道数 C2路径 31×1 卷积→5×5 卷积1×1 卷积降维再提取大尺度特征输出通道数 C3路径 43×3 最大池化→1×1 卷积池化降维再提取特征输出通道数 C4最终输出C1C2C3C4 通道特征图尺寸与输入一致。31×1 卷积的核心作用降维减少 3×3、5×5 卷积的输入通道数大幅降低计算量如 192 通道→96 通道计算量减半升维可灵活调整输出通道数适配后续网络层增强非线性1×1 卷积后接 ReLU增加网络非线性表达能力。2.3.3 GoogLeNet 整体结构输入224×224×3前置层1 个 7×7 卷积 池化→1 个 3×3 卷积 池化主体层9 个 Inception 块堆叠分 3 组每组 3 个块组间用池化下采样辅助分类器2 个中间层辅助分类器缓解梯度消失增强监督输出层全局平均池化替代全连接层 Softmax输出 1000 类概率。2.3.4 优缺点总结优点多尺度特征融合表达能力强1×1 卷积降维参数量少、计算高效全局平均池化减少过拟合缺点Inception 块结构复杂设计时需手动调优各路径通道数后续深层版本Inception v2~v4虽优化但复杂度持续上升。2.4 ResNet残差连接突破深层瓶颈20152.4.1 核心定位与突破ResNet残差网络由何恺明团队设计2015 年 ImageNet 竞赛冠军现代 CNN 的里程碑模型。核心突破是解决深层网络的 “退化问题”当网络层数超过 20 层后准确率不升反降梯度消失 / 爆炸导致浅层参数无法更新。ResNet 通过残差连接跳跃连接让网络学习 “残差函数” 而非直接学习输出使梯度可通过恒等映射直接回传浅层支持训练 1000 层超深网络且性能随深度增加持续提升。2.4.2 残差块核心创新单元1残差学习原理传统网络学习直接映射H(x)F(x)x为输入F(x)为网络层学习的特征H(x)为输出ResNet 学习残差映射H(x)F(x)xF(x)为残差特征x为恒等映射直接跳过当前层。核心优势若最优映射为恒等映射H(x)x网络只需学习F(x)0比学习恒等映射更容易梯度可通过x直接回传避免反向传播中梯度消失深层网络可有效训练。2基础残差块ResNet-18/34适用于浅层 ResNet结构2 个 3×3 卷积层 ReLU 激活 残差连接输入输出通道数一致时直接恒等映射不一致时用 1×1 卷积调整通道数。PyTorch 实现基础残差块class Residual(nn.Module): 基础残差块 def __init__(self, input_channels, num_channels, use_1x1convFalse, strides1): super().__init__() # 第一个卷积层 self.conv1 nn.Conv2d(input_channels, num_channels, kernel_size3, padding1, stridestrides) self.bn1 nn.BatchNorm2d(num_channels) # 第二个卷积层 self.conv2 nn.Conv2d(num_channels, num_channels, kernel_size3, padding1) self.bn2 nn.BatchNorm2d(num_channels) # 1×1卷积调整通道数/步长 if use_1x1conv: self.conv3 nn.Conv2d(input_channels, num_channels, kernel_size1, stridestrides) else: self.conv3 None def forward(self, X): Y torch.relu(self.bn1(self.conv1(X))) Y self.bn2(self.conv2(Y)) if self.conv3: X self.conv3(X) Y X # 残差连接 return torch.relu(Y)3瓶颈残差块ResNet-50/101/152适用于深层 ResNet用1×1 卷积降维→3×3 卷积提取特征→1×1 卷积升维减少深层网络计算量如 ResNet-50 用瓶颈块参数量仅为同深度基础块的 1/4。2.4.3 ResNet-18 整体结构最常用轻量版本输入224×224×3初始层1 个 7×7 卷积64 通道步长 2→ BatchNorm → ReLU → 3×3 最大池化步长 2残差块组4 组残差块每组 2 个基础残差块通道数依次为 64→128→256→512组间用步长 2 下采样输出层全局平均池化 → 全连接层1000 神经元→ Softmax。2.4.4 ResNet-18 PyTorch 实现# 定义VGG-16的卷积块配置(卷积层数, 输出通道数) conv_arch ((2, 64), (2, 128), (3, 256), (3, 512), (3, 512)) def vgg(conv_arch): 构建VGG网络 conv_blks [] in_channels 3 # 输入RGB图像3通道 # 堆叠卷积块 for (num_convs, out_channels) in conv_arch: conv_blks.append(vgg_block(num_convs, in_channels, out_channels)) in_channels out_channels # 全连接层 return nn.Sequential( *conv_blks, nn.Flatten(), nn.Linear(512 * 7 * 7, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(), nn.Dropout(0.5), nn.Linear(4096, 1000) ) # 初始化VGG-16模型 vgg16 vgg(conv_arch) # 测试输入输出维度 X torch.randn(1, 3, 224, 224) for blk in vgg16: X blk(X) print(blk.__class__.__name__, output shape:\t, X.shape)2.4.5 优缺点总结优点残差连接解决深层退化问题支持超深网络训练性能强、泛化能力好成为 CV 领域 “通用骨架”目标检测、分割等任务均基于 ResNet缺点深层版本ResNet-101/152计算量仍较大需 GPU 算力支持残差连接增加内存占用。三、批量规范化BatchNorm深层网络的 “加速器”3.1 核心问题内部协变量偏移ICS训练深层网络时前层参数更新会导致后层输入分布持续变化如前层权重微调输出均值 / 方差改变后层需不断适应新分布这种现象称为内部协变量偏移ICS。ICS 的危害1. 收敛速度慢后层需反复适应新分布训练效率极低2. 梯度消失 / 爆炸输入分布超出激活函数敏感区如 Sigmoid 两端饱和区梯度趋近于 0 或无穷大3. 对初始化敏感参数初始值稍有偏差网络易陷入局部最优。3.2 BatchNorm 核心原理批量规范化BatchNormBN由 Ioffe 和 Szegedy 于 2015 年提出核心思想将输入数据标准化为 “均值 0、方差 1” 的标准分布再通过可学习参数恢复表达能力强制每一层输入分布稳定彻底解决 ICS 问题。3.3 BatchNorm 计算流程4 步走针对某一层输入特征x形状m×C×H×Wm为 batch sizeC为通道数H/W为特征图高 / 宽BN 对每个通道独立规范化公式如下步骤 1计算批量均值μB​μB​m⋅H⋅W1​∑i1m​∑j1H​∑k1W​xi,j,k​当前批次所有样本、所有空间位置的均值按通道计算步骤 2计算批量方差σB2​σB2​m⋅H⋅W1​∑i1m​∑j1H​∑k1W​(xi,j,k​−μB​)2当前批次所有样本、所有空间位置的方差按通道计算步骤 3标准化x^x^σB2​ϵ​x−μB​​ϵ10−5防止分母为 0输出均值 0、方差 1步骤 4缩放与偏移y恢复表达能力yγx^βγ缩放因子、β偏移因子可学习参数维度与通道数一致作用若标准化破坏原始特征分布网络可通过γ和β自适应调整恢复表达能力最终输出y均值β、方差γ2。3.4 训练与推理的差异训练阶段用当前 batch 的均值 / 方差μB​/σB2​规范化同时维护移动平均均值 / 方差running_mean/running_var公式running_mean0.9×running_mean0.1×μB​running_var0.9×running_var0.1×σB2​0.9 为动量系数累积全局统计量推理阶段用训练阶段累积的移动平均均值 / 方差规范化不使用当前 batch 统计量原因推理时 batch size 可能为 1统计量无意义需用全局统计量保证稳定性。3.5 BatchNorm 的核心作用加速收敛稳定输入分布使梯度始终处于激活函数敏感区收敛速度提升 5~10 倍缓解梯度消失 / 爆炸避免输入分布极端化梯度稳定在合理范围降低初始化敏感性参数初始值可在更大范围选择无需精细调优正则化效果批量统计量引入噪声抑制过拟合减少 Dropout 依赖支持大学习率可使用更大学习率进一步加速训练。3.6 PyTorch 中的 BatchNorm 实战1常用 API二维卷积层 BNnn.BatchNorm2d(num_features)num_features 通道数全连接层 BNnn.BatchNorm1d(num_features)三维卷积层 BNnn.BatchNorm3d(num_features)。2使用位置黄金法则卷积层 / 全连接层 → BatchNorm → 激活函数BN 在激活函数前效果最优。错误用法卷积→激活→BN激活后分布易饱和BN 效果差。3实战示例ResNet 残差块中的 BN前文 ResNet 残差块已嵌入 BN核心代码片段class Residual(nn.Module): def __init__(self, input_channels, num_channels, use_1x1convFalse, strides1): super().__init__() self.conv1 nn.Conv2d(input_channels, num_channels, kernel_size3, padding1, stridestrides) self.bn1 nn.BatchNorm2d(num_channels) # BN层对应输出通道数 self.conv2 nn.Conv2d(num_channels, num_channels, kernel_size3, padding1) self.bn2 nn.BatchNorm2d(num_channels) # BN层 # ... 后续代码 def forward(self, X): Y torch.relu(self.bn1(self.conv1(X))) # 卷积→BN→激活 Y self.bn2(self.conv2(Y)) # ... 后续代码3.7 优缺点与注意事项优点即插即用适配所有 CNN效果显著工业界模型标配缺点依赖 batch size小 batch8时效果差统计量噪声大增加少量参数γ/β与计算量注意BN 层权重衰减设为 0γ/β无需正则化推理时模型需设为eval()模式冻结移动平均统计量。四、图像分类实战基于 PyTorch 与 CIFAR-104.1 任务与数据集介绍4.1.1 任务目标训练一个 CNN 模型对 CIFAR-10 数据集的 10 类彩色图像进行分类目标准确率≥85%。4.1.2 CIFAR-10 数据集规模60000 张 32×32 彩色图50000 训练集 10000 测试集类别飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、卡车每类 6000 张特点图像分辨率低32×32但场景复杂、目标多样适合入门级 CNN 实战。4.2 环境准备与依赖安装# 安装PyTorch与torchvisionGPU版本需CUDA支持 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 安装辅助库 pip install matplotlib d2l torchinfo4.3 数据预处理与加载4.3.1 数据增强训练集随机裁剪32×32→28×28再补回 32×32增加位置鲁棒性随机水平翻转概率 0.5增强视角多样性归一化像素值 [0,255]→[-1,1]适配 ReLU 激活转换为张量适配 PyTorch 输入格式。4.3.2 数据加载代码import torch import torchvision import torchvision.transforms as transforms from torch.utils.data import DataLoader # 设备配置优先GPU device torch.device(cuda if torch.cuda.is_available() else cpu) # 训练集预处理含数据增强 transform_train transforms.Compose([ transforms.RandomCrop(32, padding4), # 随机裁剪 transforms.RandomHorizontalFlip(), # 随机水平翻转 transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) # 归一化到[-1,1] ]) # 测试集预处理无增强仅归一化 transform_test transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) # 加载数据集 trainset torchvision.datasets.CIFAR10( root./data, trainTrue, downloadTrue, transformtransform_train ) testset torchvision.datasets.CIFAR10( root./data, trainFalse, downloadTrue, transformtransform_test ) # 数据加载器batch size128shuffleTrue打乱训练集 trainloader DataLoader(trainset, batch_size128, shuffleTrue, num_workers2) testloader DataLoader(testset, batch_size128, shuffleFalse, num_workers2) # 类别标签 classes [airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck]4.4 模型搭建改进版 ResNet-18针对 CIFAR-1032×32 小图简化 ResNet-18移除初始 7×7 卷积与池化改用 3×3 卷积避免特征图尺寸过小。import torch.nn as nn import torch.nn.functional as F # 残差块复用前文Residual类 class Residual(nn.Module): def __init__(self, input_channels, num_channels, use_1x1convFalse, strides1): super().__init__() self.conv1 nn.Conv2d(input_channels, num_channels, kernel_size3, padding1, stridestrides) self.bn1 nn.BatchNorm2d(num_channels) self.conv2 nn.Conv2d(num_channels, num_channels, kernel_size3, padding1) self.bn2 nn.BatchNorm2d(num_channels) if use_1x1conv: self.conv3 nn.Conv2d(input_channels, num_channels, kernel_size1, stridestrides) else: self.conv3 None def forward(self, X): Y F.relu(self.bn1(self.conv1(X))) Y self.bn2(self.conv2(Y)) if self.conv3: X self.conv3(X) Y X return F.relu(Y) # 改进版ResNet-18适配CIFAR-10 class CIFARResNet18(nn.Module): def __init__(self): super().__init__() # 初始层3×3卷积适配32×32输入 self.b1 nn.Sequential( nn.Conv2d(3, 64, kernel_size3, padding1, stride1), nn.BatchNorm2d(64), nn.ReLU() ) # 残差块组 self.b2 nn.Sequential(*self._resnet_block(64, 64, 2, first_blockTrue)) self.b3 nn.Sequential(*self._resnet_block(64, 128, 2)) self.b4 nn.Sequential(*self._resnet_block(128, 256, 2)) self.b5 nn.Sequential(*self._resnet_block(256, 512, 2)) # 输出层 self.avg_pool nn.AdaptiveAvgPool2d((1,1)) self.fc nn.Linear(512, 10) def _resnet_block(self, input_channels, num_channels, num_residuals, first_blockFalse): blk [] for i in range(num_residuals): if i 0 and not first_block: blk.append(Residual(input_channels, num_channels, use_1x1convTrue, strides2)) else: blk.append(Residual(num_channels, num_channels)) return blk def forward(self, X): X self.b1(X) X self.b2(X) X self.b3(X) X self.b4(X) X self.b5(X) X self.avg_pool(X) X torch.flatten(X, 1) X self.fc(X) return X # 初始化模型并移至GPU model CIFARResNet18().to(device)4.5 损失函数、优化器与训练配置import torch.optim as optim # 损失函数交叉熵损失分类任务标配 criterion nn.CrossEntropyLoss() # 优化器Adam自适应学习率收敛快、稳定 optimizer optim.Adam(model.parameters(), lr0.001, weight_decay1e-4) # 学习率调度器每10轮学习率减半 scheduler optim.lr_scheduler.StepLR(optimizer, step_size10, gamma0.5) # 训练轮数 NUM_EPOCHS 254.6 训练与评估循环import matplotlib.pyplot as plt # 记录训练过程 history { train_loss: [], train_acc: [], test_loss: [], test_acc: [] } def train_epoch(model, loader, optimizer, criterion, device): 训练1轮 model.train() running_loss 0.0 correct 0 total 0 for i, data in enumerate(loader, 0): inputs, labels data[0].to(device), data[1].to(device) # 清零梯度 optimizer.zero_grad() # 前向传播、计算损失、反向传播、参数更新 outputs model(inputs) loss criterion(outputs, labels) loss.backward() optimizer.step() # 统计损失与准确率 running_loss loss.item() _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() # 每100个batch打印日志 if i % 100 99: print(f[{epoch 1}, {i 1}] loss: {running_loss / 100:.3f}) running_loss 0.0 # 计算本轮训练准确率 train_acc 100 * correct / total return running_loss / len(loader), train_acc def test_epoch(model, loader, criterion, device): 测试1轮 model.eval() test_loss 0.0 correct 0 total 0 with torch.no_grad(): for data in loader: images, labels data[0].to(device), data[1].to(device) outputs model(images) loss criterion(outputs, labels) test_loss loss.item() _, predicted torch.max(outputs.data, 1) total labels.size(0) correct (predicted labels).sum().item() test_acc 100 * correct / total return test_loss / len(loader), test_acc # 开始训练 print(开始训练...) for epoch in range(NUM_EPOCHS): # 训练 train_loss, train_acc train_epoch(model, trainloader, optimizer, criterion, device) # 测试 test_loss, test_acc test_epoch(model, testloader, criterion, device) # 更新学习率 scheduler.step() # 记录历史 history[train_loss].append(train_loss) history[train_acc].append(train_acc) history[test_loss].append(test_loss) history[test_acc].append(test_acc) # 打印本轮结果 print(fEpoch {epoch1} - fTrain Loss: {train_loss:.4f}, Train Acc: {train_acc:.2f}%, fTest Loss: {test_loss:.4f}, Test Acc: {test_acc:.2f}%) # 保存模型 torch.save(model.state_dict(), cifar_resnet18.pth) print(训练完成模型已保存为 cifar_resnet18.pth)4.7 结果可视化与分析# 绘制损失曲线 plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(history[train_loss], labelTrain Loss) plt.plot(history[test_loss], labelTest Loss) plt.title(Loss Curve) plt.xlabel(Epoch) plt.ylabel(Loss) plt.legend() # 绘制准确率曲线 plt.subplot(1, 2, 2) plt.plot(history[train_acc], labelTrain Acc) plt.plot(history[test_acc], labelTest Acc) plt.title(Accuracy Curve) plt.xlabel(Epoch) plt.ylabel(Accuracy (%)) plt.legend() plt.tight_layout() plt.show()预期结果训练 25 轮后测试准确率≥85%训练准确率≥95%损失曲线训练损失持续下降测试损失先降后稳定无明显过拟合准确率曲线训练与测试准确率同步上升后期趋于平稳。4.8 模型推理与单张图像测试from PIL import Image # 加载模型 model.load_state_dict(torch.load(cifar_resnet18.pth)) model.eval() # 预处理单张图像 def preprocess_image(image_path): transform transforms.Compose([ transforms.Resize((32, 32)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) image Image.open(image_path).convert(RGB) image transform(image).unsqueeze(0).to(device) return image # 推理 def predict_image(image_path): image preprocess_image(image_path) with torch.no_grad(): outputs model(image) _, predicted torch.max(outputs, 1) return classes[predicted[0]] # 测试替换为你的图像路径 image_path test_plane.jpg print(f预测结果{predict_image(image_path)})五、实际学习场景 避坑指南5.1 经典模型学习避坑1AlexNet坑 1直接用原始 AlexNet 训练小数据集如 CIFAR-10→过拟合严重✅ 解法减少卷积层通道数、增加 Dropout 概率、强化数据增强坑 2忽略 LRN 层→复现准确率低✅ 解法严格按原始结构实现 LRN或直接用 BatchNorm 替代效果更好。2VGG坑 1训练 VGG-16 时显存爆炸→GPU 内存不足✅ 解法减小 batch size如从 128 降至 32、用梯度累积、换 ResNet 等轻量模型坑 2全连接层参数量过大→训练慢、过拟合✅ 解法用全局平均池化替代全连接层减少 90% 参数量。3ResNet坑 1残差块中 BN 位置错误激活后→收敛慢、准确率低✅ 解法严格遵循卷积→BN→激活顺序坑 2深层 ResNet50 层训练时梯度消失→准确率不升✅ 解法用瓶颈残差块、增加 BatchNorm、调整学习率。5.2 BatchNorm 实战避坑坑 1小 batch8用 BatchNorm→效果差、不稳定✅ 解法改用 LayerNorm、增大 batch size、用梯度累积模拟大 batch坑 2推理时模型不设eval()→结果错误✅ 解法推理前必须model.eval()训练前model.train()坑 3BN 层加权重衰减→性能下降✅ 解法优化器中仅对卷积 / 全连接层权重加衰减BN 层γ/β不加。5.3 图像分类实战避坑坑 1数据增强过度→数据失真、准确率下降✅ 解法仅用随机裁剪、翻转等基础增强避免复杂扭曲、噪声坑 2学习率过大→损失震荡、不收敛✅ 解法初始学习率设 0.001Adam用学习率调度器动态调整坑 3过拟合训练 acc95%测试 acc70%→泛化差✅ 解法增加数据增强、加 Dropout / 权重衰减、减小模型复杂度、早停。5.4 硬件与环境避坑坑 1用 CPU 训练深层 CNN→速度极慢1 轮需数小时✅ 解法用 GPU 训练NVIDIA 显卡CUDA≥11.8无 GPU 用 Google Colab坑 2PyTorch 版本不兼容→代码报错✅ 解法安装稳定版 PyTorch2.0避免最新测试版。六、学习计划 下章预告6.1 阶段性学习计划7 天掌握本章内容第 1 天CNN 基础复习学习目标掌握卷积、池化、激活函数核心原理任务复习 LeNet-5 结构推导卷积输出维度公式实现基础卷积层。第 2 天AlexNet 与 VGG学习目标理解 AlexNet 创新点、VGG 小卷积核设计逻辑任务复现 AlexNet 与 VGG-16 代码对比两者参数量差异训练小数据集验证效果。第 3 天GoogLeNet 与 ResNet学习目标掌握 Inception 块多尺度融合、残差连接解决退化问题任务实现 Inception 块与 ResNet-18 残差块对比 ResNet 与 VGG 训练速度与准确率。第 4 天批量规范化BatchNorm学习目标理解 ICS 问题、BatchNorm 计算流程与训练 / 推理差异任务在 ResNet 中嵌入 BatchNorm对比有无 BN 的训练收敛速度总结 BN 使用技巧。第 5 天图像分类实战数据与模型学习目标掌握 CIFAR-10 数据预处理、数据增强、模型搭建任务完成数据加载代码搭建改进版 ResNet-18调试模型维度匹配问题。第 6 天训练调优与结果分析学习目标掌握损失函数、优化器选择学习率调度结果可视化任务完成训练与评估循环调优超参数学习率、batch size绘制损失 / 准确率曲线。第 7 天复盘与项目实战学习目标梳理经典模型演进脉络总结避坑指南任务用 ResNet-18 训练自定义小数据集如花卉分类完成端到端项目记录问题与解决方法。6.2 下章内容预告下一章将聚焦计算机视觉进阶任务核心内容包括目标检测基础从图像分类到目标检测的任务演进边界框、交并比IoU、非极大值抑制NMS核心概念经典目标检测模型R-CNN、Fast R-CNN、Faster R-CNN 两阶段检测模型YOLO、SSD 单阶段检测模型的设计逻辑与优缺点PyTorch 目标检测实战基于 Faster R-CNN 训练 PASCAL VOC 数据集实现目标检测、模型评估与结果可视化进阶优化技巧锚框设计、损失函数调优、数据增强适配、模型轻量化部署。七、结尾互动恭喜你完成本章学习从 AlexNet 到 ResNet从理论到实战你已掌握现代 CNN 核心技术与图像分类端到端流程为后续计算机视觉进阶打下坚实基础。互动环节点赞如果本章内容对你有帮助点赞支持鼓励创作更多深度学习干货收藏收藏本文方便后续复习经典模型结构、BatchNorm 原理与实战代码关注关注我持续获取《动手学深度学习》系列完整笔记、实战代码与进阶教程下章目标检测内容更精彩评论区交流欢迎在评论区留言你在学习本章时遇到了哪些问题你最想深入了解哪个模型AlexNet/VGG/ResNet你希望后续实战什么数据集或任务我会逐一回复和大家一起交流学习、共同进步