前面我们已经认识了卷积、填充、步幅、通道、汇聚层和 LeNet。到这里,你已经知道 CNN 为什么适合处理图片:它像拿着小模板一样,在图片局部寻找边缘、纹理和形状。

但 LeNet 毕竟是早期网络。它擅长识别手写数字这种“小而规整”的图片,一旦面对真实世界的彩色照片,就会明显吃力:猫可能趴着、跑着、被遮住一半;汽车可能从不同角度出现;背景里还会有树、路牌和人群。图片越复杂,模型就越需要更大的容量、更强的特征提取能力和更稳定的训练方法。

这一篇,我们进入“现代卷积神经网络”的第一站:AlexNetVGG

你可以把这篇看成 CNN 从“能用”走向“强大”的关键转折:

  • AlexNet 告诉我们:网络可以做得更深更大,只要有足够数据、算力和合适的训练技巧。
  • VGG 告诉我们:复杂网络不一定要靠复杂设计,很多时候用简单模块反复堆叠,反而更清晰、更强大。

一、为什么 LeNet 不够用了?

LeNet 的经典任务是手写数字识别,比如 MNIST 数据集里的 28x28 灰度图片。这个任务很适合入门,因为图片小、背景干净、类别少,模型不用太复杂也能学得不错。

但真实图片要难很多:

  1. 图片更大
    手写数字是 28x28,ImageNet 里的图片通常会被处理成 224x224 左右,像素数量一下子增加几十倍。

  2. 颜色更多
    手写数字通常是单通道灰度图,而真实图片是 RGB 三通道。模型不只要看亮暗,还要理解颜色组合。

  3. 类别更多
    MNIST 只有 10 类数字,ImageNet 有 1000 类物体,难度完全不是一个量级。

  4. 变化更多
    同样是“狗”,可能有不同品种、姿势、光照、背景和遮挡。模型必须学到更抽象、更稳定的特征。

所以,现代 CNN 的核心问题就变成了:

如何让网络更深、更宽、更会提取特征,同时还能训练得动?

AlexNet 就是在这个问题上,给出了震撼全场的答案。

先看一张对比表,帮助你快速建立整体印象:

项目 LeNet AlexNet
典型任务 手写数字识别 大规模自然图片识别
输入图片 28x28 灰度图 更大的彩色图片,常见为 224x224 左右
网络规模 较小,结构简单 更深更宽,参数量大幅增加
卷积设计 早期经典卷积结构 更深的卷积堆叠,特征提取能力更强
激活函数 常见为 Sigmoid 或 Tanh 大量使用 ReLU
训练技巧 主要依赖基础训练流程 引入 Dropout、GPU 加速等关键技巧
历史意义 CNN 的早期代表 让深度学习在视觉领域真正爆发

二、AlexNet:深度学习复兴的代表作

2012 年,AlexNet 在 ImageNet 图像识别比赛中表现惊人,让大家重新意识到:深度神经网络真的能在真实图片识别上击败传统方法。

如果 LeNet 像一个小型识别器,那么 AlexNet 就像全面升级后的视觉系统:更大的卷积层、更多通道、更深的结构、更强的算力支持。

1. AlexNet 做对了什么?

AlexNet 的成功不是只靠“层数更多”,而是几件事情同时到位。

更深、更宽

AlexNet 有 5 个卷积层和 3 个全连接层,参数量大约 6000 万。和 LeNet 相比,它的“脑容量”大了很多,可以学习更复杂的图片特征。

通俗理解:

  • 低层卷积:识别边缘、颜色块、简单纹理。
  • 中层卷积:组合出局部形状,比如眼睛、轮子、花瓣。
  • 高层卷积:组合出更完整的物体部件,甚至接近“这是一只猫”的判断。
使用 ReLU 激活函数

早期神经网络常用 Sigmoid 或 Tanh,但它们在深层网络中容易出现梯度变小的问题,训练会很慢。

AlexNet 大量使用 ReLU

nn.ReLU()

ReLU 的规则很简单:

  • 正数保留;
  • 负数变成 0。

它看起来朴素,但非常好用。对初学者来说,你只要先记住一句话:

ReLU 让深层网络训练得更快,也更不容易卡住。

使用 GPU 加速

AlexNet 的训练离不开 GPU。卷积网络里的大量计算,本质上是矩阵和张量运算,GPU 正好擅长并行处理这些计算。

这件事对深度学习发展非常关键:不是只有算法重要,算力也直接决定了模型能不能训练出来。

使用 Dropout 防止过拟合

AlexNet 的全连接层很大,参数很多,很容易“死记硬背”训练集。为了解决这个问题,它使用了 Dropout

Dropout 的思想很像课堂随机点名:

训练时随机让一部分神经元暂时不工作,逼迫其他神经元也学会独立完成任务。

这样模型不能过度依赖某几个神经元,泛化能力会更好。

nn.Dropout(p=0.5)

p=0.5 表示训练时大约随机关闭一半神经元。

2. AlexNet 的结构长什么样?

下面是一份适合学习理解的 AlexNet 写法。为了贴近真实彩色图片,输入通道写成 3;如果你用灰度图数据集练习,可以把第一层的 3 改成 1

先看这张示意图,你不用运行代码,也能先把“过程”和“结果”看明白:

输入图片:224x224x3

卷积1 + ReLU:提取边缘和纹理,特征图约 55x55x96

最大汇聚:压缩空间尺寸,特征图约 27x27x96

卷积2 + ReLU:组合更复杂的局部特征,特征图约 27x27x256

最大汇聚:继续缩小尺寸,特征图约 13x13x256

卷积3/4/5 + ReLU:提取更高层语义,特征图约 13x13x256

最大汇聚:得到更紧凑的特征图,约 6x6x256

Flatten + 全连接层:把二维特征整理成一维向量

输出 1000 类分数:例如猫、狗、汽车

易读表格(按层总结):

类型 & 参数 计算公式 输出尺寸
输入 原始图像 224 × 224 × 3
Conv1 Conv2d(3,96,k=11,s=4,p=2) (224 - 11 + 2×2)/4 + 1 = 55 55 × 55 × 96
Pool1 MaxPool2d(k=3,s=2) floor((55 - 3)/2) + 1 = 27 27 × 27 × 96
Conv2 Conv2d(96,256,k=5,p=2) (27 - 5 + 2×2)/1 + 1 = 27 27 × 27 × 256
Pool2 MaxPool2d(k=3,s=2) floor((27 - 3)/2) + 1 = 13 13 × 13 × 256
Conv3 Conv2d(256,384,k=3,p=1) (13 - 3 + 2×1)/1 + 1 = 13 13 × 13 × 384
Conv4 Conv2d(384,384,k=3,p=1) 同上 = 13 13 × 13 × 384
Conv5 Conv2d(384,256,k=3,p=1) 同上 = 13 13 × 13 × 256
Pool3 MaxPool2d(k=3,s=2) floor((13 - 3)/2) + 1 = 6 6 × 6 × 256
Flatten 展平 6 × 6 × 256 = 9216 9216 (一维向量)

读这张表,抓住三个点:

  1. 前半段是“找线索”,先抓边缘、纹理,再慢慢组合成更复杂的形状。
  2. 汇聚层的作用是“缩小图片”,让特征图越来越紧凑,计算也越来越轻。
  3. 后半段是“做判断”,把整理好的特征交给全连接层,最后输出类别分数。

一句话概括:

前面负责“看图找线索”,后面负责“把线索汇总成答案”。

import torch
from torch import nn

net = nn.Sequential(
    # 第一层卷积:用较大的卷积核先快速提取边缘和轮廓
    # Conv1: nn.Conv2d(3,96,k=11,s=4,p=2)
    # 计算:_out = (224 - 11 + 2*2) / 4 + 1 = 55 -> 输出 55x55x96
    nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2), nn.ReLU(),
    # Pool1: nn.MaxPool2d(k=3,s=2)
    # 计算:_out = floor((55 - 3) / 2) + 1 = 27 -> 输出 27x27x96
    nn.MaxPool2d(kernel_size=3, stride=2),

    # 第二组卷积:进一步组合局部纹理和形状信息
    # Conv2: nn.Conv2d(96,256,k=5,p=2)
    # 计算:_out = (27 - 5 + 2*2) / 1 + 1 = 27 -> 输出 27x27x256
    nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
    # Pool2: nn.MaxPool2d(k=3,s=2)
    # 计算:_out = floor((27 - 3) / 2) + 1 = 13 -> 输出 13x13x256
    nn.MaxPool2d(kernel_size=3, stride=2),

    # 第三组卷积:逐步提取更抽象的高层特征
    # Conv3: nn.Conv2d(256,384,k=3,p=1)
    # 计算:_out = (13 - 3 + 2*1) / 1 + 1 = 13 -> 输出 13x13x384
    nn.Conv2d(256, 384, kernel_size=3, padding=1), nn.ReLU(),
    # Conv4: nn.Conv2d(384,384,k=3,p=1)
    # 计算:保持尺寸 13x13 -> 输出 13x13x384
    nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
    # Conv5: nn.Conv2d(384,256,k=3,p=1)
    # 计算:保持尺寸 13x13 -> 输出 13x13x256
    nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
    # Pool3: nn.MaxPool2d(k=3,s=2)
    # 计算:_out = floor((13 - 3) / 2) + 1 = 6 -> 输出 6x6x256
    nn.MaxPool2d(kernel_size=3, stride=2),

    # 把二维特征图展开成一维向量,送入全连接层
    nn.Flatten(),
    # 展平计算:6 x 6 x 256 = 9216 -> 所以 Linear 的输入为 9216
    # 第一个全连接层:开始把图像特征汇总成类别线索
    nn.Linear(9216, 4096), nn.ReLU(),
    # Dropout:训练时随机丢掉一部分神经元,减少过拟合
    nn.Dropout(p=0.5),
    # 第二个全连接层:继续整合高层特征
    nn.Linear(4096, 4096), nn.ReLU(),
    # 再做一次 Dropout:让模型不要太依赖少数神经元
    nn.Dropout(p=0.5),
    # 输出层:给出 1000 个类别的预测分数
    nn.Linear(4096, 1000)
)

这里最容易让人疑惑的是:9216 为什么会变成 4096

答案是,它不是“自己变过去”的,而是被这一层全连接层重新映射了:nn.Linear(9216, 4096)

你可以把它理解成三步:

  1. nn.Flatten() 先把 6 x 6 x 256 的特征图拉平成一个 9216 维向量。
  2. nn.Linear(9216, 4096) 再用一组可学习的权重,把 9216 个输入特征组合成 4096 个更紧凑、更适合分类的特征。
  3. 后面的 nn.Linear(4096, 4096)nn.Linear(4096, 1000) 继续把这些特征加工成最终的类别分数。

所以,9216 不是“直接等于” 4096,而是“经过全连接层压缩和重组后,输出成 4096 维表示”。

再往下看这几层:

  • 第一个 nn.Linear(9216, 4096):把特征从 9216 维压到 4096 维,相当于做一次信息筛选和重组。
  • nn.ReLU():引入非线性,让模型不只是线性叠加,而是能学到更复杂的关系。
  • nn.Dropout(p=0.5):训练时随机丢掉一部分神经元,减少过拟合。
  • 第二个 nn.Linear(4096, 4096):继续融合和提炼特征,维度不变,但表示会更抽象。
  • 第二个 nn.ReLU():再加一次非线性变换。
  • 第二个 nn.Dropout(p=0.5):再做一次正则化,进一步抑制过拟合。
  • 最后的 nn.Linear(4096, 1000):把 4096 维特征映射到 1000 个类别分数,对应 ImageNet 的 1000 类。

第一次看这段代码,不用急着记每个数字。先看清这条主线:

  1. 前面连续用卷积层提取图片特征。
  2. 中间用汇聚层逐步缩小图片尺寸。
  3. 后面用全连接层把特征汇总成类别判断。

如果再把结果说得更直白一点,就是:

  • 输入一张图片;
  • 中间不断把“大图”变成“更抽象、更紧凑的特征图”;
  • 最后输出一个类别分数列表,分数最高的类别就是模型的预测结果。

3. AlexNet 的入门记忆法

AlexNet 的意义可以浓缩成一句话:

它证明了“大数据 + 大模型 + GPU + ReLU + Dropout”这条路真的能走通。

从这以后,计算机视觉正式进入深度学习时代。


三、VGG:用简单积木搭出强大网络

AlexNet 很厉害,但它的结构看起来有点“不规整”:有 11x11 卷积、5x5 卷积、3x3 卷积,设计上有不少经验味道。

研究者自然会问:

有没有一种更简单、更标准的方式来搭建深层 CNN?

VGG 的答案是:有。全部用小卷积核,像搭积木一样叠起来。

1. VGG 的核心思想

VGG 最重要的设计非常朴素:

  • 卷积层尽量使用 3x3 卷积核,并配合 padding=1 保持高宽不变。
  • 汇聚层使用 2x2 最大汇聚,并配合 stride=2 让高宽减半。
  • 多个卷积层组成一个块,多个块再组成整个网络。

这就是 VGG 的魅力:结构非常整齐。

2. 为什么喜欢 3x3 卷积?

你可能会问:如果想看更大范围,直接用 5x5 或 7x7 不就行了吗?

可以,但 3x3 有两个好处。

好处一:参数更少

一个 5x5 卷积核有 25 个位置,一个 3x3 卷积核只有 9 个位置。小卷积核通常更省参数。

好处二:非线性更多

两个 3x3 卷积层叠起来,感受野大致相当于一个 5x5 卷积层,但中间多了一次 ReLU。

也就是说,网络不仅看到了更大范围,还多了一次“思考和变换”的机会。

通俗理解:

与其让一个大侦探一眼看完,不如让两个小侦探连续分析,每一步都做一次判断。

3. VGG 块:把重复结构封装起来

VGG 的代码很好理解,因为它本来就是模块化思想。

下面先看一张直观示意图,再看代码:

输入:224x224x3

Block1: 1x Conv(3x3) → Pool — 输出约 112x112x64

Block2: 1x Conv(3x3) → Pool — 输出约 56x56x128

Block3: 2x Conv(3x3) → Pool — 输出约 28x28x256

Block4: 2x Conv(3x3) → Pool — 输出约 14x14x512

Block5: 2x Conv(3x3) → Pool — 输出约 7x7x512

Flatten → 全连接层 → 输出类别分数

如何读这张图:每个 Block 先用若干个 3x3 卷积提取特征(padding=1 保持尺寸),之后用 2x2 最大汇聚把高宽减半;通道数在后面逐步增加以学习更抽象的特征。

易读表格(VGG — 按 Block 汇总):

类型 & 参数 计算公式/说明 输出尺寸
输入 原始图像 224 × 224 × 3
Block1 Conv 1 × Conv(3x3), out=64, p=1 (224 - 3 + 2×1)/1 + 1 = 224 224 × 224 × 64
Block1 Pool MaxPool(2x2, s=2) floor((224 - 2)/2) + 1 = 112 112 × 112 × 64
Block2 Conv 1 × Conv(3x3), out=128, p=1 (112 - 3 + 2×1)/1 + 1 = 112 112 × 112 × 128
Block2 Pool MaxPool(2x2, s=2) floor((112 - 2)/2) + 1 = 56 56 × 56 × 128
Block3 Conv 2 × Conv(3x3), out=256, p=1 保持 56 → 56 × 56 × 256 56 × 56 × 256
Block3 Pool MaxPool(2x2, s=2) floor((56 - 2)/2) + 1 = 28 28 × 28 × 256
Block4 Conv 2 × Conv(3x3), out=512, p=1 保持 28 → 28 × 28 × 512 28 × 28 × 512
Block4 Pool MaxPool(2x2, s=2) floor((28 - 2)/2) + 1 = 14 14 × 14 × 512
Block5 Conv 2 × Conv(3x3), out=512, p=1 保持 14 → 14 × 14 × 512 14 × 14 × 512
Block5 Pool MaxPool(2x2, s=2) floor((14 - 2)/2) + 1 = 7 7 × 7 × 512
Flatten 展平 7 × 7 × 512 = 25088 25088 (一维向量)

读这张表,抓住三个重点:

  1. 每个 Block 先做若干次 3x3 卷积,再用一次 2x2 最大汇聚把高宽减半。
  2. 越往后,特征图越小,但通道数越多,说明网络在学习更抽象的表示。
  3. 最后展平后的 25088 维向量,就是分类头真正接收的输入。

一句话概括:

VGG 用统一的小卷积块,不断提特征、降尺寸,最后把抽象后的信息送去分类。

from torch import nn

def vgg_block(num_convs, in_channels, out_channels):
    # layers 列表用于按顺序收集本块的层
    layers = []
    for _ in range(num_convs):
        # 每个卷积使用 3x3 卷积核,padding=1 保持高宽不变
        # 这样在堆叠多个卷积层时,感受野逐步增大,但每层仍保留空间分辨率
        layers.append(nn.Conv2d(in_channels, out_channels,
                                kernel_size=3, padding=1))
        # 每个卷积后接一个非线性激活,通常用 ReLU
        layers.append(nn.ReLU())
        # 为下一层卷积更新输入通道数
        in_channels = out_channels
    # 块末尾添加最大汇聚层,将高宽减半,压缩空间并保留重要特征
    layers.append(nn.MaxPool2d(kernel_size=2, stride=2))
    # 返回一个按序组合的模块,便于在主网络中重复使用
    return nn.Sequential(*layers)

这里有三个参数:

  • num_convs:这个块里放几个卷积层;
  • in_channels:输入通道数;
  • out_channels:输出通道数。

每个 VGG 块都做两件事:

  1. 用若干个 3x3 卷积提取特征;
  2. 用最大汇聚把图片高宽减半。

4. 用 VGG 块搭网络

有了 vgg_block,搭网络就像写配置表。

conv_arch = (
    (1, 64),
    (1, 128),
    (2, 256),
    (2, 512),
    (2, 512)
)

这里的意思是:前两个 Block 先各放 1 个卷积层,后面三个 Block 变成 2 个卷积层。

为什么这样设计?核心原因有两个:

  1. 前面的特征图还比较大,先用 1 个 3x3 卷积就足够提取边缘、纹理这类浅层特征,同时计算量不会太重。
  2. 越往后,图片已经被汇聚层不断缩小,信息也越来越抽象,这时就更适合叠加更多卷积层,让网络在同一个尺度上做更细致的特征组合。

可以把它概括成:

  • 前面像“先扫一遍”,快速找出轮廓和局部线索。
  • 后面像“再细看几遍”,把这些线索进一步组合成更复杂的语义特征。

所以,1 × Conv(3x3) 和 2 × Conv(3x3) 的区别,不是随便写的,而是在控制“计算成本”和“特征表达能力”之间做平衡:

前面少堆一点,后面多堆一点,既保留了效率,也让深层特征学得更充分。

根据 conv_arch 构建完整的 VGG 特征提取部分:

def vgg(conv_arch):
    net = []
    in_channels = 3
    for (num_convs, out_channels) in conv_arch:
        # 使用前面定义的 vgg_block,按配置堆叠
        net.append(vgg_block(num_convs, in_channels, out_channels))
        in_channels = out_channels
    return nn.Sequential(*net)

# 示例:构建 VGG 特征提取器,并接上简单的分类头(说明用途,实际训练时可替换)
vgg_features = vgg(conv_arch)
vgg_classifier_example = nn.Sequential(
    nn.Flatten(),
    nn.Linear(7*7*512, 4096), nn.ReLU(),
    nn.Dropout(0.5),
    nn.Linear(4096, 4096), nn.ReLU(),
    nn.Dropout(0.5),
    nn.Linear(4096, 1000)
)

每一项 (卷积层个数, 输出通道数) 表示一个 VGG 块。越往后,图片的高宽越来越小,但通道数越来越多。

这很符合 CNN 的基本规律:

  • 前面:图片大,特征简单,通道少。
  • 后面:图片小,特征抽象,通道多。

5. VGG 的价值

VGG 不一定是最高效的网络,但它非常适合学习。因为它让我们看清了一个现代 CNN 的常见设计套路:

卷积提特征,汇聚降尺寸;重复多次后,再做分类。

更重要的是,VGG 让大家意识到:

深度网络可以由标准模块堆叠出来,结构越清晰,越容易复用和改进。


四、AlexNet 和 VGG 对比

为了让你更容易记住,我们把两者放在一起看:

网络 核心特点 解决的问题 小白记忆
AlexNet 更大网络、ReLU、Dropout、GPU 证明深度 CNN 能处理复杂真实图片 深度学习视觉时代的引爆点
VGG 统一使用 3x3 卷积块 让网络结构更标准、更容易加深 用标准积木搭深层网络

AlexNet 更像“打响第一枪”,VGG 更像“整理出一套清晰施工方法”。


五、小结

这一篇你不需要记住所有层数和参数,只要抓住下面几件事:

  1. LeNet 适合小图片,面对复杂真实图片能力有限。
  2. AlexNet 通过更大模型、ReLU、Dropout 和 GPU,证明深度 CNN 可以大规模成功。
  3. VGG 用简单的 3x3 卷积块搭建深层网络,让 CNN 结构变得更标准。
  4. CNN 越往后,通常高宽越小、通道越多、特征越抽象。

如果用一句话收尾:

AlexNet 让深度学习在视觉领域爆发,VGG 让深层卷积网络变得像搭积木一样清楚。

下一篇,我们继续往前走:既然 VGG 后面的全连接层又大又重,能不能把它去掉?既然一层里到底该用 1x1、3x3 还是 5x5 卷积很难选,能不能干脆让它们一起上?

下一站:NiN 与 GoogLeNet

(注:文档部分内容参考《动手学深度学习》)
《动手学深度学习》现代卷积神经网络:https://zh.d2l.ai/chapter_convolutional-modern/index.html

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐