1. 从一张手绘草图说起为什么还要回头翻CNN的经典模型前阵子帮一个刚入行的朋友梳理图像分类项目的技术选型他问了我一个问题“现在Transformer都满天飞了我直接上ViT不就行了为什么还要看LeNet、AlexNet这些老古董”这个问题其实特别有代表性。我当时的回答是你可以直接开ViT但如果连卷积核在特征图上滑动的感受野是怎么一步步扩大的都说不清楚遇到模型不收敛或者精度卡在某个瓶颈时你连从哪儿下手排查都不知道。CNN的经典模型框架本质上不是让你背网络结构图去应付面试而是给你一套“设计直觉”。从1998年的LeNet-5到2015年的ResNet这中间每一次结构演进都对应着一个具体的工程问题梯度消失怎么办、计算量太大怎么压、深层网络退化怎么解。你把这些脉络理清楚了再看今天任何一个新的视觉骨干网络都能快速判断它到底在解决什么问题、代价是什么、适不适合你手头的场景。这篇文章面向的是刚接触深度学习视觉方向的学生、需要做模型选型但非科班出身的工程师以及想系统回顾CNN发展脉络的从业者。我会把LeNet-5、AlexNet、VGG、GoogLeNet、ResNet这五个最具代表性的框架拆开来讲重点不在复述论文而在于说清楚每个模型“为什么长这样”“当时遇到了什么坑”“放到今天该怎么用”。文中涉及的结构参数和设计取舍一部分来自原始论文的公开信息一部分是我自己在复现和迁移这些模型时积累的经验判断两者会明确区分。2. LeNet-5卷积网络的第一套完整骨架2.1 它到底解决了什么问题LeNet-5诞生于上世纪九十年代目标非常明确手写数字识别。输入是32×32的灰度图输出是0到9共10个类别。在今天看来这个任务简单得不像话但它的价值在于第一次把“卷积池化全连接”这套组合拳完整地跑通了而且用的是反向传播来端到端训练。你可以把它理解为卷积神经网络的“最小可行产品”——结构不复杂但该有的组件一个不少。我经常建议刚入门的朋友先把LeNet-5手撸一遍不是为了用它做项目而是通过它理解三个核心概念局部连接、权值共享、空间下采样。这三个概念是后面所有CNN变体的基石不理解它们看后面的模型就是看天书。2.2 逐层拆解每个设计选择背后的逻辑LeNet-5的结构按照输入到输出依次是输入层32×32→ C1卷积层6个5×5卷积核→ S2池化层2×2平均池化→ C3卷积层16个5×5卷积核→ S4池化层2×2平均池化→ C5卷积层120个5×5卷积核→ F6全连接层84个神经元→ 输出层10个神经元。这里有几个细节值得注意。第一输入为什么是32×32而不是28×28因为原始手写数字图片是28×28的但论文作者特意做了padding目的是让边缘像素也能被卷积核的中心覆盖到。这个思路在今天看来很朴素但它揭示了一个重要原则卷积操作对边缘信息的处理是不对称的适当的padding可以缓解这个问题。第二C3层为什么用16个卷积核而不是更多这受限于当时的计算资源。而且C3层并不是把S2的所有6个通道都连到每一个C3的卷积核上而是有选择地连接。这种“部分连接”的设计在当时是为了减少参数量但后来被证明对特征多样性也有一定帮助。不过到了AlexNet之后这种手动设计连接方式的做法就被全连接式的卷积取代了因为GPU的算力上来了没必要再省这点参数。第三激活函数用的是tanh和sigmoid不是ReLU。这不是因为tanh更好而是因为ReLU那时候还没被引入到CNN里。你用今天的眼光看会觉得sigmoid在深层网络里容易导致梯度消失但在LeNet-5这个深度总共7层含参数层5层下问题还不明显。2.3 放到今天该怎么用说实话今天没有人会拿LeNet-5去做实际项目它的精度和容量都远远不够。但它的价值在于教学和快速验证。如果你想验证一个新的损失函数或者优化器在卷积网络上的基本行为用LeNet-5跑MNIST是最快的几分钟就能出一轮结果。另外在一些极低算力的嵌入式场景下比如某些MCU级别的芯片上做简单的数字或简单图形分类LeNet-5的变体仍然有生存空间因为它的参数量只有大约6万个量化之后可以压到几十KB。注意复现LeNet-5时原始论文用的是平均池化而不是最大池化激活函数是tanh。如果你换成最大池化和ReLU精度会略有提升但那就不是严格的LeNet-5了。做教学演示时建议保持原样做工程迁移时则可以大胆替换。3. AlexNet深度学习的引爆点与工程智慧3.1 为什么是它点燃了深度学习AlexNet在2012年的ImageNet竞赛上把top-5错误率从26%左右直接拉到了15%出头这个提升幅度在当年是断崖式的。但它的意义远不止精度——它证明了“大数据大模型GPU”这条路走得通。在此之前学术界对深度网络的训练普遍持悲观态度觉得梯度消失和过拟合没法解决。AlexNet用几个非常工程化的手段把这些障碍绕过去了。3.2 结构上的关键改动与设计动机AlexNet的整体结构是输入224×224×3→ 5个卷积层 → 3个全连接层 → 1000类输出。相比LeNet-5它的改动可以归纳为四个方面。第一激活函数从sigmoid/tanh换成了ReLU。这个改动的动机很直接sigmoid在输入较大或较小时梯度接近零深层网络反向传播时梯度会迅速衰减。ReLU在正区间梯度恒为1训练速度在当年实测快了数倍。我自己的复现经验是同样的网络结构把tanh换成ReLU收敛到相同损失值所需的epoch数大约能减少30%到40%。第二用了Dropout。AlexNet在全连接层后面加了Dropout训练时随机丢弃一部分神经元。这个操作的目的是强制网络不依赖某些特定的神经元路径从而提升泛化能力。全连接层的参数量占了AlexNet总参数的绝大部分约6000万中的5800万是最容易过拟合的地方所以Dropout加在这里性价比最高。第三用了重叠池化。传统的池化是不重叠的比如2×2的窗口步长也是2。AlexNet把步长设成了2但窗口设成了3×3这样池化窗口之间就有重叠。论文里说这样做能轻微提升精度同时降低过拟合。从今天的视角看这个改动的影响其实有限但它体现了一个思路池化操作的设计空间比想象中大。第四用了局部响应归一化LRN。这个操作后来被证明作用不大在VGG和ResNet里都被去掉了。但在当时作者认为它能模拟生物神经元的侧抑制机制增强特征的对比度。如果你今天复现AlexNetLRN可以直接跳过对精度几乎没有影响。3.3 双GPU训练带来的结构分裂AlexNet当年是在两块GPU上训练的因为单块GPU的显存装不下整个网络。这导致它的卷积层被分成了两组每组只在自己的GPU上处理一部分特征图只在特定层才进行跨GPU的信息交换。这个设计在今天看来是硬件限制下的妥协但它无意中带来了一点正则化的效果——两组特征图各自学习到了不同的特征模式。今天你用单卡复现AlexNet时不需要刻意去模拟这种分组直接把所有通道合并处理就行。但如果你想深入理解分组卷积的思想AlexNet的这个设计可以作为一个起点它和后来ResNeXt里的分组卷积在思路上有相通之处。3.4 实操中的坑与经验复现AlexNet时最容易踩的坑是输入尺寸。原始论文里输入是224×224但如果你仔细算一下卷积和池化的尺寸变化会发现第一层卷积之后特征图的大小对不上。实际上当年论文里有一些细节没有完全交代清楚后来有研究者指出实际使用的输入尺寸可能是227×227。我的建议是如果你要严格复现用227×227如果你只是拿它做迁移学习的骨干输入尺寸可以按你的数据集来调整不必死磕原始论文。另一个坑是学习率的设置。AlexNet原始论文用的初始学习率是0.01并且在验证集损失不再下降时手动除以10。这个策略在今天看来很粗糙但当时没有余弦退火、没有warmup能跑通就不错了。你今天复现时可以用余弦退火或者OneCycle策略收敛会更稳。4. VGG用深度和规整性换性能4.1 核心思想把结构做简单把深度做上去VGG的设计哲学和AlexNet完全不同。AlexNet用了各种不同大小的卷积核11×11、5×5、3×3而VGG全部用3×3的小卷积核池化全部用2×2最大池化整个网络的结构极其规整。它的核心发现是两个3×3卷积堆叠的感受野等于一个5×5卷积三个3×3堆叠等于一个7×7卷积但参数量更少非线性更强。这个发现的影响非常深远。它告诉后来的人与其用一个大卷积核不如用多个小卷积核堆叠。参数量方面假设输入输出通道数都是C一个5×5卷积的参数量是25C²两个3×3卷积的参数量是18C²少了将近三成。非线性方面两个3×3卷积之间可以插入两次ReLU而一个5×5卷积只能插入一次前者的表达能力更强。4.2 VGG16与VGG19的结构对比VGG有两个常用版本VGG16和VGG19数字代表含参数层的总数。VGG16包含13个卷积层和3个全连接层VGG19包含16个卷积层和3个全连接层。两者的卷积层都分成5个block每个block后面接一个最大池化层通道数从64开始每经过一个block翻倍依次是64、128、256、512、512。配置项VGG16VGG19卷积层数1316全连接层数33总参数量约1.38亿约1.44亿卷积层分布2,2,3,3,32,2,4,4,4Top-5错误率ImageNet约9.0%约8.8%从表中可以看出VGG19比VGG16多了3个卷积层精度提升不到0.2个百分点但参数量和计算量都增加了。在实际项目中VGG16的性价比更高VGG19除非你有特别的精度需求否则不太推荐。4.3 全连接层带来的参数量问题VGG最被诟病的地方就是它的全连接层。第一个全连接层的输入是512×7×725088维输出是4096维这一层的参数量就是25088×4096≈1.03亿占了整个网络参数的七成以上。这不仅消耗大量显存还容易过拟合。后来大家用VGG做迁移学习时普遍的做法是把最后的全连接层去掉只保留卷积部分作为特征提取器然后在后面接自己的分类头。这样做的好处是卷积部分的参数量只有约1500万而且对输入尺寸没有严格限制因为是全卷积结构可以接受任意大小的输入图片。4.4 用VGG做迁移学习的实操要点用VGG做迁移学习时有几个经验值得分享。第一如果你用的是预训练权重输入图片的预处理必须和原始训练时一致也就是按ImageNet的均值和标准差做归一化否则精度会明显下降。第二VGG的卷积部分学习率要设得比新加的分类头低通常低一个数量级因为预训练的卷积特征已经很好用了不需要大改。第三如果你的数据集和ImageNet差异很大比如医学影像、卫星图像可以考虑只冻结前两个block后面的block参与微调这样效果通常比全部冻结要好。提示VGG的参数量大推理速度慢在移动端和嵌入式设备上基本不可用。如果你需要轻量级模型直接看MobileNet系列不要试图压缩VGG性价比太低。5. GoogLeNet用多尺度并行卷积打破“更深即更好”的执念5.1 Inception模块的设计直觉GoogLeNet的核心创新是Inception模块。它的基本思路是在同一层里并行使用不同大小的卷积核1×1、3×3、5×5和池化操作然后把所有输出在通道维度上拼接起来。这样做的动机是图像中的目标大小不一有的占满整个画面有的只占一个小角落用单一尺寸的卷积核很难同时兼顾。并行多尺度可以让网络自己学习该关注哪种尺度的特征。但直接并行多尺度会带来一个严重问题计算量爆炸。5×5卷积在通道数较多时参数量非常大。所以GoogLeNet在3×3和5×5卷积之前以及池化之后都加了1×1卷积来做降维。这个1×1卷积的作用是减少输入通道数从而降低后续卷积的计算量。举个例子如果输入是256通道直接做5×5卷积输出64通道参数量是256×5×5×64409600如果先用1×1卷积把256通道降到32通道再做5×5卷积输出64通道参数量是256×1×1×32 32×5×5×64 8192 51200 59392只有原来的七分之一左右。5.2 辅助分类器的作用与争议GoogLeNet在中间层插入了两个辅助分类器目的是缓解深层网络梯度消失的问题。辅助分类器在训练时参与损失计算推理时被去掉。这个设计在当时是有意义的因为GoogLeNet有22层比VGG还深梯度传播确实是个问题。但后来的实践表明辅助分类器的作用有限而且增加了训练的复杂性。在ResNet之后大家发现用残差连接解决梯度问题比辅助分类器更优雅、更有效。所以如果你今天复现GoogLeNet辅助分类器可以保留也可以去掉对最终精度的影响通常在0.5个百分点以内。5.3 全局平均池化的引入GoogLeNet是第一个用全局平均池化替代全连接层的经典模型。具体做法是在最后一个Inception模块之后对每个通道的特征图做平均直接得到一个向量然后接一个softmax分类。这样做的好处是参数量大幅减少而且有一定的正则化效果。这个设计后来被几乎所有后续模型采用包括ResNet。它的影响是深远的它让模型对输入尺寸更加灵活也减少了过拟合的风险。我在实际项目中发现用全局平均池化替代全连接层后小数据集上的过拟合现象明显减轻验证集精度通常能提升1到2个百分点。5.4 GoogLeNet的后续演进GoogLeNet后来演进了多个版本Inception-v2引入了Batch NormalizationInception-v3用了卷积分解把5×5拆成两个3×3把3×3拆成1×3和3×1Inception-v4和Inception-ResNet结合了残差连接。这些改进的核心思路是一致的在保持多尺度并行结构的前提下不断降低计算量、提升训练稳定性。如果你要在项目中使用Inception系列我的建议是直接用Inception-v3或Inception-v4的预训练权重不要从GoogLeNet原始版本开始。v3和v4在精度和速度上都明显更优而且预训练权重更容易获取。6. ResNet残差连接如何让“深”不再是一个问题6.1 退化问题深层网络的反直觉现象在ResNet之前一个普遍的经验是网络越深精度应该越高。但实验发现当网络深度超过一定限度后精度反而下降了。这不是过拟合导致的训练误差也在上升而是优化困难导致的。这个问题被称为“退化问题”。退化问题的本质是在深层网络中恒等映射即让某一层的输出等于输入很难学习。理论上如果新增的层都学成恒等映射深层网络的性能至少不应该比浅层网络差。但实际训练中让多层非线性变换逼近恒等映射非常困难导致深层网络的有效容量反而下降。6.2 残差块的核心机制ResNet的解决方案非常简洁在卷积层旁边加一条捷径连接把输入直接加到输出上。公式是y F(x) x其中F(x)是残差函数x是恒等映射。这样网络只需要学习残差F(x)而不是完整的映射。如果最优映射接近恒等映射网络只需要把F(x)学成接近零就行这比学成恒等映射容易得多。残差块有两种设计基本块和瓶颈块。基本块用于ResNet-18和ResNet-34由两个3×3卷积组成。瓶颈块用于ResNet-50及更深的版本由1×1、3×3、1×1三个卷积组成1×1卷积负责先降维再升维目的是减少3×3卷积的计算量。模型残差块类型层数参数量Top-5错误率ResNet-18基本块18约1170万约10.9%ResNet-34基本块34约2180万约9.8%ResNet-50瓶颈块50约2560万约7.8%ResNet-101瓶颈块101约4450万约7.0%ResNet-152瓶颈块152约6020万约6.7%从表中可以看出ResNet-50是一个性价比很高的选择参数量只有VGG16的不到两成但精度更高。这也是为什么ResNet-50成为后来目标检测、语义分割等任务中最常用的骨干网络。6.3 捷径连接的类型与选择ResNet的捷径连接有两种恒等捷径和投影捷径。恒等捷径直接相加不引入额外参数投影捷径用一个1×1卷积调整通道数和空间尺寸再相加。当输入输出通道数一致且空间尺寸相同时用恒等捷径当通道数或尺寸发生变化时用投影捷径。在实际实现中ResNet在每个stage的第一个残差块使用投影捷径其余使用恒等捷径。这个设计在原始论文中有详细说明但很多开源实现为了简化会在所有需要调整维度的地方都用投影捷径这样参数量会略多但精度差异很小。6.4 复现ResNet时的常见错误复现ResNet时最容易犯的错误是捷径连接的位置。原始论文中捷径连接是加在第二个卷积的BN之后、ReLU之前。也就是说残差块的输出是ReLU(F(x) x)而不是ReLU(F(x)) x。这个顺序如果搞错了训练会变得不稳定精度也会下降。另一个常见错误是BN层的位置。在原始ResNet中BN是在卷积之后、ReLU之前。有些实现会把BN放在卷积之前这是不对的会导致训练行为完全不同。如果你从零复现ResNet建议对照原始论文的图示逐层核对不要凭记忆写。注意ResNet的预训练权重有v1和v2两个版本。v1是原始版本v2把BN和ReLU放到了卷积之前预激活训练更容易收敛但精度差异不大。做迁移学习时v1的权重更常见建议优先用v1。7. 五个模型的横向对比与选型建议7.1 从参数量、计算量、精度三个维度看模型参数量计算量GFLOPsImageNet Top-5错误率适用场景LeNet-5约6万极低MNIST上约1%教学、极低算力嵌入式AlexNet约6000万约1.5约15.3%教学、小规模图像分类VGG16约1.38亿约15.5约9.0%迁移学习、特征提取GoogLeNet约700万约1.5约9.2%移动端、多尺度场景ResNet-50约2560万约4.1约7.8%通用骨干网络、检测分割从这张表可以清楚地看到ResNet-50在精度和计算量之间取得了最好的平衡。VGG16虽然精度不错但计算量是ResNet-50的近四倍参数量是五倍多在今天已经不太适合作为首选骨干。GoogLeNet的参数量和计算量都很低但精度略逊于ResNet-50适合对速度要求高的场景。7.2 不同场景下的选型逻辑如果你在做移动端或嵌入式部署优先考虑GoogLeNet的轻量变体或者MobileNet系列不要用VGG和ResNet-50以上的版本。如果你在做服务器端的图像分类ResNet-50是默认选择精度不够就上ResNet-101或ResNet-152。如果你在做迁移学习且数据集很小VGG16的卷积特征在某些纹理密集型任务上仍然有优势但整体上ResNet-50更稳。如果你只是做教学演示或者快速验证想法LeNet-5和AlexNet足够了训练快、代码简单、不容易出错。不要一上来就用ResNet-152跑MNIST那是杀鸡用牛刀而且容易因为过拟合导致结果反而不如简单模型。7.3 从经典模型中学到的设计原则回顾这五个模型可以提炼出几条对今天仍然有效的设计原则。第一小卷积核堆叠优于大卷积核这是VGG证明的。第二多尺度并行特征提取优于单一尺度这是GoogLeNet证明的。第三残差连接是训练深层网络的关键这是ResNet证明的。第四全局平均池化可以替代全连接层减少参数量并提升泛化能力这是GoogLeNet和ResNet共同证明的。第五1×1卷积是调整通道数和降低计算量的有效工具这在GoogLeNet和ResNet中都有体现。这些原则在今天的新模型设计中仍然被广泛使用。你去看EfficientNet、ConvNeXt这些较新的卷积网络会发现它们的基本组件和设计思路都能在这些经典模型中找到影子。所以花时间理解经典模型不是怀旧而是打地基。8. 自己动手复现时的几个实操建议8.1 从LeNet-5开始但不要停在LeNet-5如果你刚开始学PyTorch或TensorFlow我建议的复现顺序是LeNet-5 → AlexNet → VGG16 → ResNet-50。每复现一个就对比一下和上一个模型在结构上的差异问自己“为什么要做这个改动”。LeNet-5半天就能搞定AlexNet一天VGG16一天ResNet-50两天。一周之内你就能把这五个模型全部跑通这个时间投入是非常值得的。复现时不要直接抄网上的代码先自己画一遍结构图标注每一层的输入输出尺寸和通道数然后再写代码。写完之后用torchsummary或者model.summary()打印一下每层的参数量和你手算的对比。如果对不上说明你对某个层的理解有偏差回去查。8.2 用小数据集快速验证不要一上来就用ImageNet那个数据集太大训练一轮要很久。用CIFAR-10或者CIFAR-100甚至用MNIST的彩色版本先把流程跑通。CIFAR-10的图片是32×32的直接喂给VGG和ResNet需要调整输入尺寸或者修改网络的第一层。我的做法是把CIFAR-10的图片resize到224×224虽然计算量大了但不用改网络结构省事。如果你用CIFAR-10训练ResNet-50注意把第一个卷积层的步长从2改成1或者去掉第一个最大池化层否则特征图会被降得太小。这个细节在原始论文里没有提因为ImageNet的图片够大但CIFAR-10的图片小不改的话最后特征图只剩1×1信息损失太多。8.3 预训练权重是加速实验的利器除非你的目标是研究网络结构本身否则做项目时一律用预训练权重。PyTorch的torchvision.models里包含了所有这些经典模型的预训练版本一行代码就能加载。用预训练权重的好处是收敛快、精度高、对超参数不敏感。我自己的经验是用预训练权重做迁移学习通常只需要训练10到20个epoch就能达到不错的效果而从零训练可能需要100个epoch以上。加载预训练权重时注意两点第一如果num_classes和预训练时不一致要把最后的全连接层替换掉第二如果输入图片的通道数不是3比如灰度图要把第一个卷积层的输入通道改成1但这样预训练权重就不能直接用了需要重新训练或者复制通道。8.4 训练过程中的监控与调参训练这些模型时我习惯监控三个指标训练损失、验证损失、验证精度。如果训练损失下降但验证损失上升说明过拟合了需要加正则化或者数据增强。如果训练损失和验证损失都不下降说明学习率可能太大了或者网络结构有问题。如果训练损失下降很慢说明学习率太小了。学习率方面用预训练权重时初始学习率设成0.001比较稳妥用Adam或SGD都可以。从零训练时SGD加动量通常比Adam效果更好初始学习率设成0.01或0.1配合学习率衰减策略。Batch size在显存允许的情况下尽量大一些32或64是常见选择太小会导致BN层的统计量不稳定。我在实际项目里踩过的一个坑是用预训练权重时忘了把BN层设为eval模式导致BN的均值和方差在训练时被更新预训练学到的统计量被破坏精度反而下降。后来养成了习惯加载预训练权重后先冻结BN层训练几轮之后再解冻。这个技巧在小数据集上特别有用。8.5 模型保存与复现性训练完成后保存模型时不要只保存state_dict还要保存优化器的状态、当前的epoch数、学习率调度器的状态这样方便断点续训。另外设置随机种子torch.manual_seed、np.random.seed、random.seed可以保证结果可复现虽然完全复现很难但至少能减少随机性带来的波动。如果你要把模型部署到生产环境建议把模型转成ONNX格式然后用ONNX Runtime或者TensorRT做推理加速。这些经典模型对ONNX的支持都很好转换过程基本不会遇到问题。转完之后对比一下PyTorch和ONNX的输出确保数值误差在可接受范围内。这五个经典CNN框架每一个都值得你花时间亲手跑一遍。不是为了记住它们的结构而是为了理解每一次结构变化背后的工程动机。当你下次面对一个新的视觉任务时这些积累会帮你更快地做出合理的选型判断而不是盲目追新。