查看全课大纲(第 40 / 93 节)
- 1.计算机视觉概述
- 2.计算机视觉应用场景
- 3.困难与挑战
- 4.开源库介绍
- 5.图像显示与存储
- 6.CLAHE
- 7.腐蚀膨胀与开闭运算
- 8.滤波与边缘填充
- 9.各种滤波
- 10.图像金字塔
- 11.傅里叶变换
- 12.短时傅里叶变换与小波变换
- 13.颜色特征与颜色直方图
- 14.几何特征_边缘
- 15.几何特征-角点与斑点
- 16.局部特征-SIFT特征描述子
- 17.局部特征-SURF特征描述子
- 18.局部特征-ORB特征描述子
- 19.其他特征描述子
- 20.传统图像分割技术
- 21.GraphCut
- 22.GrabCut
- 23.人脸检测的传统方法
- 24.行人检测的传统方法HOG+SVM
- 25.行人检测DPM
- 26.人工神经网络的架构
- 27.目标函数与梯度下降
- 28.误差反向传播
- 29.可手算的神经网络
- 30.改进目标函数与克服梯度消散
- 31.神经网络之传统神经网络与深度网络的差异
- 32.各种梯度下降算法
- 33.如何选择合适的梯度下降算法
- 34.避免过适应
- 35.CNN的架构
- 36.CNN的误差反向传播_卷积层
- 37.CNN的误差反向传播_池化层
- 38.分类所用的数据集
- 39.Alexnet详解
- 40.VGG详解
- 41.GoogLeNet_v1
- 42.BatchNormalization
- 43.GoogLeNet_v3v4
- 44.ResNet
- 45.ResNeXt
- 46.CNN设计准则
- 47.图像检索与推土机距离
- 48.基于颜色相似性
- 49.基于形状相似性
- 50.基于纹理相似性
- 51.基于局部特征相似性
- 52.DL的二值哈希编码
- 53.高维空间中检索KDTree
- 54.高维空间中检索LSH
- 55.目标识别任务综述
- 56.RCNN
- 57.SPP-Net
- 58.Fast R-CNN
- 59.Faster R-CNN
- 60.R-CNN系列模型回顾
- 61.R-FCN的设计思路
- 62.R-FCN的实现
- 63.YOLOv1
- 64.YOLOv2_9000
- 65.YOLOv3
- 66.图像分割应用场景与数据集
- 67.上采样的实现方式
- 68.FCN网络架构的由来
- 69.FCN构建细节
- 70.DeepLabv1
- 71.CRF
- 72.DeepLabv2
- 73.DeepLabv3
- 74.特定的医学影像分割任务
- 75.U-Net
- 76.3D U-Net
- 77.V-Net
- 78.DenseNet
- 79.病理切片分析任务及数据集
- 80.病理切片分析
- 81.图说任务及其传统处理方法
- 82.深度语言模型与RNN
- 83.LSTM及其变种
- 84.NIC模型
- 85.SAT模型
- 86.图说的性能指标
- 87.判别模型与生成模型
- 88.KL散度与JS散度
- 89.VAE
- 90.GAN的基本原理
- 91.DCGAN与CGAN
- 92.Wasserstein GAN
- 93.SRGAN
VGG详解
小象实战讲义 · 基于深度学习的计算机视觉
在上一节我们学习了开启深度学习革命的AlexNet,它奠定了现代深度CNN的基础架构。然而,AlexNet的设计中仍有一些可以改进的地方,例如其使用了较大的卷积核(如11×11),单个卷积层的计算量较大。本节我们将介绍VGG网络,它通过“用小卷积核堆叠代替大卷积核”的核心思想,在等效感受野下减少了单个卷积层的参数量,同时加深了网络深度、提升了模型性能,并因其简洁规整的结构,成为后续许多视觉任务进行迁移学习的首选基础网络。
💡 核心导读
- VGG的核心改进:用多个连续的3×3小卷积核堆叠,替代AlexNet中的大卷积核(如11×11、5×5);在等效感受野下,这能减少单个卷积层的参数量,并通过ReLU激活引入更多非线性,增强模型的表达能力。
- “埃菲尔铁塔”结构:VGG网络呈现下宽上窄的规整结构,图像尺寸逐组减半,通道数逐组增加,是早期CNN的经典范式。
- 网络深度与性能:VGG探索了网络深度对性能的影响,提出了VGG11、VGG13、VGG16、VGG19等不同深度的变体,其中VGG16和VGG19最为常用。
- 迁移学习的基础:VGG结构简洁、性能稳定,其预训练模型(在ImageNet上训练所得)的特征提取部分具有通用性,成为小数据集任务上进行迁移学习(Fine-tuning)的理想起点。
VGG:对AlexNet的深化与规整
VGG网络由牛津大学视觉几何组(Visual Geometry Group)提出,并在2014年ImageNet图像分类竞赛中获得第二名。它并非一个颠覆性的创新,而是对AlexNet架构一次深思熟虑的“精装修”。
首先,我们来回顾一下AlexNet的“毛坯房”有哪些可以改进之处:
- 卷积核尺寸不一:AlexNet中混合使用了11×11、5×5、3×3等多种尺寸的卷积核,结构上不够统一。
- 使用了局部响应归一化(LRN):后续研究(包括VGG论文中的实验)表明,LRN层对模型性能的提升作用有限,反而增加了计算开销。
- 网络深度相对较浅:AlexNet仅有8层(5个卷积层+3个全连接层),VGG的作者希望探究更深的网络能否带来更好的性能。
VGG针对这些问题,提出了一个极其规整和简洁的设计方案,其整体结构像一座埃菲尔铁塔:底部(靠近输入图像)比较宽,随着网络向上(靠近输出),逐渐变窄、变深。
我们可以通过一个简化的视角来理解VGG的“组”结构。它将网络分为若干个阶段(组),每个阶段结束时,都会通过一个步长为2的最大池化(Max Pooling)层将特征图的空间尺寸(宽和高)缩小为原来的一半(面积变为原来的 1/4)。同时,通道数逐组加倍(64→128→256→512),而每组内部堆叠多少个卷积层则可多可少——这正是 VGG11/13/16/19 之间的主要差别。
import numpy as np
# 模拟VGG中特征图尺寸的变化过程(以VGG16为例,输入224x224)
# 我们只关注空间尺寸(H, W)和大致通道数(C)的变化
input_size = (224, 224, 3) # 高,宽,通道
print(f"输入图像尺寸: {input_size[0]}x{input_size[1]}x{input_size[2]}")
# 定义VGG16前向过程中,经过每个池化层后的特征图尺寸变化
# 格式: (阶段描述, 卷积操作后尺寸, 池化后尺寸)
# 注意:为简化,此处忽略卷积的padding细节,VGG中使用padding=1保持尺寸
stages = [
("2个卷积层(通道64)", (224, 224, 64), (112, 112, 64)),
("2个卷积层(通道128)", (112, 112, 128), (56, 56, 128)),
("3个卷积层(通道256)", (56, 56, 256), (28, 28, 256)),
("3个卷积层(通道512)", (28, 28, 512), (14, 14, 512)),
("3个卷积层(通道512)", (14, 14, 512), (7, 7, 512)),
]
current_size = input_size
for desc, after_conv, after_pool in stages:
print(f"\n{desc}:")
print(f" 卷积层输出: {after_conv[0]}x{after_conv[1]} (空间) x {after_conv[2]} (通道)")
print(f" 池化层输出: {after_pool[0]}x{after_pool[1]} (空间) x {after_pool[2]} (通道)")
current_size = after_pool
print(f"\n最终,特征图被展平,送入全连接层。")输入图像尺寸: 224x224x3
2个卷积层(通道64):
卷积层输出: 224x224 (空间) x 64 (通道)
池化层输出: 112x112 (空间) x 64 (通道)
2个卷积层(通道128):
卷积层输出: 112x112 (空间) x 128 (通道)
池化层输出: 56x56 (空间) x 128 (通道)
3个卷积层(通道256):
卷积层输出: 56x56 (空间) x 256 (通道)
池化层输出: 28x28 (空间) x 256 (通道)
3个卷积层(通道512):
卷积层输出: 28x28 (空间) x 512 (通道)
池化层输出: 14x14 (空间) x 512 (通道)
3个卷积层(通道512):
卷积层输出: 14x14 (空间) x 512 (通道)
池化层输出: 7x7 (空间) x 512 (通道)
最终,特征图被展平,送入全连接层。从输出可以清晰看到“埃菲尔铁塔”式的变化:空间尺寸从224→112→56→28→14→7逐级减半,而通道数则从3→64→128→256→512逐步增加。这种规整的设计是VGG的一大特点。
核心思想:小卷积核的堆叠艺术
VGG最核心、最精彩的贡献在于其用小卷积核堆叠代替大卷积核的设计思想。
为什么这么做? 在AlexNet中,第一层就使用了11×11的大卷积核。若输入通道数为 、输出通道数也为 ,一个 11×11 卷积层的参数数量是 ;即便只看单个输出通道,每个空间位置也要做 次乘加,计算量巨大。
VGG的作者思考:能否用一系列更小的卷积核来等效替代一个大卷积核,同时还能获得额外的好处?
答案是肯定的。例如,一个5×5的卷积核,其感受野(能“看到”的输入区域大小)是5×5。我们可以用两个3×3的卷积核堆叠来代替它:
- 第一个3×3卷积核的感受野是3×3。
- 第二个3×3卷积核作用在第一个卷积核输出的特征图上,但因为它“看”的是第一个卷积的结果,而第一个卷积的每个点又对应输入的一个3×3区域,所以第二个卷积核的等效感受野就扩大到了5×5(3 + 3 - 1 = 5)。
同理,一个7×7的卷积核可以用三个3×3的卷积核堆叠来替代。
这样做有什么好处?
参数数量大幅减少:这是最直接的好处。假设输入和输出通道数都是C。
- 一个7×7卷积层的参数量:。
- 三个3×3卷积层堆叠的参数量:。
- 参数量降为原来的约 55%(),即减少约 45%,计算量和模型存储开销都显著降低。
引入了更多的非线性:这是更深层次的优势。在堆叠的小卷积层之间,都使用了ReLU激活函数。相比于单一的大卷积核(本质是线性变换+一次非线性激活),多层小卷积核堆叠引入了多次非线性变换。这使得网络的表达能力更强,能够学习更复杂、更抽象的特征组合。就像活字印刷(小卷积核灵活组合)比雕版印刷(固定的大卷积核)更灵活、更具表现力一样。
import numpy as np
# 演示感受野的计算:堆叠两个3x3卷积等效于一个5x5卷积的感受野
def receptive_field_of_stacked_convs(kernel_size, num_layers):
"""计算堆叠多个相同尺寸卷积核后的等效感受野"""
rf = kernel_size
for _ in range(1, num_layers):
rf = rf + (kernel_size - 1)
return rf
# 计算堆叠2个3x3卷积的等效感受野
rf_2x3x3 = receptive_field_of_stacked_convs(3, 2)
print(f"堆叠 2 个 3x3 卷积的等效感受野大小: {rf_2x3x3}x{rf_2x3x3}")
# 计算堆叠3个3x3卷积的等效感受野
rf_3x3x3 = receptive_field_of_stacked_convs(3, 3)
print(f"堆叠 3 个 3x3 卷积的等效感受野大小: {rf_3x3x3}x{rf_3x3x3}")
# 参数量对比计算(假设输入输出通道数C=64)
C = 64
params_7x7 = 7 * 7 * C * C
params_3x3_stack = 3 * (3 * 3 * C * C)
print(f"\n假设通道数 C = {C}:")
print(f" 一个7x7卷积层参数量: {params_7x7:,}")
print(f" 三个3x3卷积层参数量: {params_3x3_stack:,}")
print(f" 参数量减少比例: {(1 - params_3x3_stack/params_7x7)*100:.1f}%")堆叠 2 个 3x3 卷积的等效感受野大小: 5x5
堆叠 3 个 3x3 卷积的等效感受野大小: 7x7
假设通道数 C = 64:
一个7x7卷积层参数量: 200,704
三个3x3卷积层参数量: 110,592
参数量减少比例: 44.9%计算证实了我们的分析:用3个3×3卷积层堆叠,确实获得了与7×7卷积层相同的感受野,同时参数量减少了约45%。VGG网络将这一思想贯彻到底,全部使用3×3的卷积核,构建了一个深度更大、结构更统一、效率更高的网络。
VGG家族:从11层到19层
VGG论文中系统性地探索了网络深度,提出了从A到E的多种配置,对应我们常说的VGG11、VGG13、VGG16、VGG19等。其结构可以用一个非常简洁的表格来描述:
所有配置共享同一套骨架:每个阶段(组)由若干 3×3 卷积(stride=1、padding=1)堆叠,阶段末尾接一个 2×2、stride=2 的最大池化;最后统一接 3 个全连接层(4096–4096–1000)与 Softmax。各组内部堆叠的卷积层数如下:
| 配置 | 组1 | 组2 | 组3 | 组4 | 组5 | 卷积层数 | 总层数(含3个全连接层) |
|---|---|---|---|---|---|---|---|
| A(VGG11) | 1 | 1 | 2 | 2 | 2 | 8 | 11 |
| A-LRN | 1(后接LRN) | 1 | 2 | 2 | 2 | 8 | 11 |
| B(VGG13) | 2 | 2 | 2 | 2 | 2 | 10 | 13 |
| C(16层,含1×1) | 2 | 2 | 3※ | 3※ | 3※ | 13 | 16 |
| D(VGG16) | 2 | 2 | 3 | 3 | 3 | 13 | 16 |
| E(VGG19) | 2 | 2 | 4 | 4 | 4 | 16 | 19 |
※ C 配置在组 3、4、5 的末尾各使用一个 1×1 卷积,其余卷积均为 3×3。
表格解读:
conv3-64这类写法表示一个 3×3 卷积、输出 64 个通道。所有 3×3 卷积都使用padding=1以保持空间尺寸,直到池化层才下采样。- A(VGG11):最浅的版本,8 个卷积层 + 3 个全连接层。
- A-LRN:在 A 的第一个卷积后加入 LRN 作为对照实验,论文结论是 LRN 收益有限、反而增加开销。
- B(VGG13):在 A 的基础上,在第 1、2 组各增加一个卷积层,共 10 个卷积层。
- C(16 层配置):在 B 的基础上,在第 3、4、5 组的末尾各加入一个 1×1 卷积,共 13 个卷积层。1×1 卷积相当于对通道做线性组合,实验收益有限,因此最终采用的 D 全部使用 3×3。
- D(VGG16):在 B 的基础上,在第 3、4、5 组的末尾各加入一个 3×3 卷积,共 13 个卷积层 + 3 个全连接层 = 16 层,这就是通常所说的 VGG16。
- E(VGG19):在 D 的基础上,在第 3、4、5 组再各加入一个 3×3 卷积,共 16 个卷积层 + 3 个全连接层 = 19 层。
一个有趣的发现是,尽管网络深度从 11 层增加到 19 层,但总参数量增长并不剧烈(从 1.33 亿到 1.44 亿)。这是因为参数绝大部分集中在顶部的全连接层(尤其第一个全连接层 7×7×512×4096 就有约 1.03 亿个参数),而 VGG 通过小卷积核堆叠加深卷积部分时,新增的参数量相对有限。这体现了其设计的高效性。
在ImageNet竞赛中,VGG16(D)和VGG19(E) 表现最佳,也因此成为最流行、最常用的VGG变体。
VGG的遗产:迁移学习的首选基石
VGG虽然在2014年的竞赛中屈居第二(冠军是GoogLeNet),但它有一个无可比拟的优势:结构极其规整、简洁、易懂。这使得VGG成为后续许多计算机视觉任务进行迁移学习(Transfer Learning) 和微调(Fine-tuning) 的首选基础网络(Backbone)。
为什么VGG特别适合迁移学习? 迁移学习的核心假设是:不同的图像任务(如分类猫狗、识别车辆、检测病变)在底层所依赖的视觉特征是通用的,例如边缘、角点、纹理、基本形状等。只有高层特征才与具体任务高度相关。
VGG网络恰好提供了一个完美的分层特征提取器:
- 底层(靠近输入):学习到的是通用、基础的特征(边、角、纹理)。
- 中层:学习到的是更复杂的图案和部件组合。
- 高层(靠近输出):学习到的是与ImageNet千类物体高度相关的抽象语义特征。
当我们有一个新的、但数据量较小的任务时(例如,只有几千张猫狗图片),我们可以这样做:
- 拿来主义:直接加载在ImageNet上预训练好的VGG16模型权重。
- 冻结底层:固定(冻结)网络前几层的参数,不让它们在新数据上更新。因为这些层提取的通用特征对我们的新任务依然有用。
- 微调高层:只让网络最后面的几层(通常是最后1-2个全连接层)在新数据上进行训练,学习与新任务(猫/狗)相关的特异性特征。
- 替换分类头:将原来的1000类分类层(softmax)替换为新的2类分类层(猫/狗)。
# 迁移学习/微调(Fine-tuning)的伪代码流程示意
# 原课基于TensorFlow 1.x,该写法在TF2起需改用`tf.keras`或迁移到PyTorch
# 1. 加载预训练的VGG16模型(不含顶部分类层)
base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
# 2. 冻结基础模型的前N层,使其在训练过程中不更新
for layer in base_model.layers[:N]:
layer.trainable = False
# 3. 在基础模型之上,添加新的自定义分类层
x = base_model.output
x = GlobalAveragePooling2D()(x) # 或 Flatten()
x = Dense(256, activation='relu')(x)
predictions = Dense(2, activation='softmax')(x) # 新任务:2类
# 4. 构建最终模型
model = Model(inputs=base_model.input, outputs=predictions)
# 5. 编译并训练,使用较小的学习率进行“微调”
model.compile(optimizer=optimizers.RMSprop(lr=1e-5),
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(new_training_data, new_training_labels, epochs=10, ...)通过这种方式,我们只需要几千张新图片和普通的CPU/GPU资源,就能训练出一个性能不错的猫狗分类器,完美解决了小数据集无法训练大模型的过拟合难题。VGG因其结构的经典和预训练模型的广泛可用性,在这一领域发挥了巨大作用,其影响持续至今。
📝 动手练一练
- 概念辨析:VGG网络用多个3×3卷积核堆叠来代替大卷积核(如7×7)。请简述这样做带来的两个主要好处,并解释“引入更多非线性”这一好处为什么能增强模型的表达能力。
👉 点击查看参考答案
主要好处:
- 大幅减少参数量和计算量:例如,3个3×3卷积堆叠的参数量(27C²)远少于1个7×7卷积(49C²),降低了模型复杂度和计算开销。
- 引入更多非线性,增强表达能力:在堆叠的每个3×3卷积层后都使用了ReLU激活函数。相比于单一线性变换+一次非线性激活,多层非线性变换的堆叠使得网络能够拟合更复杂、更抽象的函数关系,从而学习到更丰富的特征组合,模型表达能力更强。
- 迁移学习策略选择:假设你有两个新任务:A) 识别不同品种的花(数据量:5万张图片);B) 识别肺部X光片中的特定纹理(数据量:500张图片)。如果都计划使用VGG16预训练模型进行迁移学习,对于这两个任务,在“冻结多少层”和“学习率设置”上,策略会有什么不同?为什么?
👉 点击查看参考答案
任务A(数据量较大,5万张):
- 冻结层数:可以冻结较少的层(例如只冻结前一半),甚至可以考虑解冻全部层进行微调。因为数据量相对充足,更新更多参数也不至于严重过拟合。
- 学习率:可以使用相对较大的学习率(例如1e-4),因为需要调整的参数可能较多,数据量也能支持更积极的更新。
任务B(数据量很小,500张):
- 冻结层数:需要冻结绝大部分层(例如冻结除最后1-2个全连接层外的所有层)。因为数据量极小,如果更新底层参数(提取通用特征),极易导致过拟合。
- 学习率:必须使用非常小的学习率(例如1e-5或更小),进行精细的“微调”,避免大的权重更新破坏预训练好的通用特征,并减少过拟合。
本章小结
本节我们深入剖析了VGG网络,它是卷积神经网络发展史上的一个重要里程碑。VGG的核心贡献在于其“小卷积核堆叠”的设计哲学,以及由此构建的规整、深层的“埃菲尔铁塔”式网络结构。这一设计在等效感受野下降低了单个卷积块的参数量,并通过增加网络深度和非线性提升了模型性能;需要注意的是,VGG 全网络的总参数量(1.38 亿)和计算量(约 15.5 GFLOPs)其实都高于 AlexNet,不能把它理解为整体更“省”。尽管在当年的ImageNet竞赛中位列第二,但VGG因其极致的简洁性和有效性,成为后续众多计算机视觉任务进行迁移学习的黄金标准基础网络,其影响力深远。
放到今天看,VGG 在分类任务上早已被 ResNet、EfficientNet、ViT 等更深、更高效的骨干网络取代,其 1.38 亿参数中绝大部分集中在全连接层的设计也已被全局平均池化淘汰。但“小卷积核堆叠 + 分组下采样”的思路仍是理解现代 backbone 的经典入口;VGG 预训练特征至今仍在风格迁移、感知损失(perceptual loss)等场景中被用作特征提取器。
📋 行动清单
- 理解核心理念:能够清晰阐述为什么用多个3×3卷积堆叠优于单个大卷积核,并从参数量和非线性表达能力两方面进行说明。
- 熟悉网络结构:记住VGG16或VGG19的大致结构(几组、每组几个卷积、池化位置、尺寸变化趋势),并能画出其“埃菲尔铁塔”式的简化框图。
- 掌握迁移学习思路:理解基于预训练VGG模型进行微调(Fine-tuning)的基本步骤(冻结底层、修改顶层、小学习率训练),并能根据新任务的数据量大小,合理选择冻结策略和学习率。
—— 小象教研组