查看全课大纲(第 41 / 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
GoogLeNet_v1
GoogLeNet V1
小象实战讲义 · 基于深度学习的计算机视觉
在上一节,我们学习了VGGNet,它通过堆叠小卷积核来构建深层网络,取得了ImageNet 2014竞赛的第二名。那么,当年的冠军是谁?它又凭借什么巧思战胜了VGGNet呢?本节我们将揭晓答案——GoogLeNet V1(亦称Inception V1)。它没有简单地加深或加宽网络,而是通过一种名为“Inception”的模块化结构,并行处理多尺度特征,并巧妙地引入1×1卷积来降低计算量,最终以更少的参数实现了更高的性能。学完本节,你将理解Inception模块的核心思想,掌握1×1卷积的降维原理,并了解GoogLeNet如何通过全局平均池化和辅助分类器等设计优化网络。
💡 核心导读
- 背景与动机:在AlexNet之后,主流思路是加深、加宽网络,但这会带来参数爆炸和梯度消失等问题。GoogLeNet另辟蹊径,旨在提升性能的同时减少参数。
- 核心组件:Inception模块:其核心思想是“保留所有可能性”,将不同尺寸的卷积核(1×1, 3×3, 5×5)和池化操作并行处理,再将结果合并,以增强网络对多尺度特征的适应性。
- 关键技术:1×1卷积:源自Network-in-Network (NiN)思想,用于跨通道的信息整合与特征降维,是Inception模块中控制计算量的关键“瓶颈层”。
- 结构优化:使用全局平均池化(GAP) 替代大部分全连接层,大幅减少参数量;引入辅助分类器,在训练时为深层网络的前几层提供额外的梯度信号,缓解梯度消失问题。
- 整体架构:GoogLeNet V1由多个Inception模块堆叠而成,辅以池化层进行下采样,最终通过GAP和单个全连接层输出分类结果。
从“加深加宽”到“保留所有可能性”
在AlexNet大获成功之后,深度学习社区的一个直观发展方向是让网络变得更深(层数更多)和更宽(每层神经元更多)。VGGNet就是“加深”路线的杰出代表。然而,单纯地增加深度和宽度会带来两个显著问题:
- 参数数量剧增:尤其是全连接层,参数数量与神经元数量的平方成正比,极易导致模型过拟合,且对计算资源要求极高。
- 梯度消失/爆炸:网络过深时,误差反向传播到浅层时信号会变得极其微弱或剧烈,导致训练困难。
GoogLeNet(V1)在2014年ImageNet竞赛中夺冠,其设计哲学并非直接对抗“深”和“宽”,而是换了一个角度来实现网络的“宽度”。它的核心思路可以概括为一句话:“保留所有可能性”。
这是什么意思呢?在传统网络中,设计者需要人为决定在某一层使用3×3卷积、5×5卷积还是进行池化操作。GoogLeNet则认为,与其费心去试验哪种操作最好,不如将它们全部保留,让网络自己去学习和组合。具体来说,它设计了一个名为 Inception 的核心模块,在该模块中,1×1卷积、3×3卷积、5×5卷积和3×3最大池化这四种操作被并行执行,它们的结果在深度(通道)维度上进行拼接(Concatenate),形成一个更丰富的特征表示。这种结构天然地增加了网络对多尺度特征的适应能力。
Inception模块的演进:引入1×1卷积瓶颈
最初的Inception模块想法很直接,就是上述四种操作的并行与合并。我们称这个版本为“朴素版”(Naive Version)。
朴素版Inception模块结构示意图:
输入特征图
│
├─────────────── 1×1 卷积 ───────────────┐
│ │
├─────────────── 3×3 卷积 ───────────────┤
│ ├─── 通道拼接 (Concat) ──→ 输出特征图
├─────────────── 5×5 卷积 ───────────────┤
│ │
└─────────────── 3×3 最大池化 ────────────┘然而,这个朴素版本存在一个严重问题:计算量巨大。尤其是5×5卷积核,参数量多,计算昂贵。直接并行堆叠这些操作会导致特征图的通道数急剧增加,后续层的计算负担呈指数级增长。
为了解决这个问题,GoogLeNet借鉴了 Network-in-Network (NiN) 的思想。NiN的核心是在传统的卷积层之后插入一个由全连接层构成的小型网络(即“网络中的网络”),以增强局部感知区域内的非线性。研究者后来发现,这个“小型网络”可以用一个非常简单的操作来等效实现:1×1卷积。
1×1卷积的奥妙:
- 操作本质:它不考虑像素的空间邻域关系,只对同一个空间位置上的所有通道值进行线性组合。你可以把它想象成用一根“吸管”垂直穿透一个多层蛋糕(每一层代表一个通道),把吸管里的各层奶油混合起来,得到一个新的值。
- 核心作用:跨通道的信息整合与特征降维/升维。
- 降维:如果1×1卷积的输出通道数小于输入通道数,它就实现了降维,减少了后续计算的负担。
- 升维:如果输出通道数大于输入通道数,则实现了升维,可以创建更多特征组合。
GoogLeNet将1×1卷积作为 “瓶颈层” 引入到Inception模块中,形成了改进版本。具体做法是:在昂贵的3×3和5×5卷积操作之前,先使用1×1卷积进行降维;在3×3池化操作之后,也使用1×1卷积来调整通道数。
改进版Inception模块结构示意图(带1×1卷积瓶颈):
输入特征图 (例如: 28×28×192)
│
├── 1×1 卷积 (64) ──────────────────────────────────────┐
│ │
├── 1×1 卷积 (96) ──→ 3×3 卷积 (128) ───────────────────┤
│ ├── 通道拼接 ──→ 输出特征图
├── 1×1 卷积 (16) ──→ 5×5 卷积 (32) ────────────────────┤
│ │
└── 3×3 最大池化 ──→ 1×1 卷积 (32) ──────────────────────┘计算量对比: 假设输入为 28×28×192 的特征图。
- 朴素版参数量约为:
1*1*192*64 + 3*3*192*128 + 5*5*192*32 = 387,072 - 改进版参数量约为:
1*1*192*64 + (1*1*192*96 + 3*3*96*128) + (1*1*192*16 + 5*5*16*32) + 1*1*192*32 = 163,328
可以看到,通过引入1×1卷积瓶颈,参数量降低了一半以上,同时输出的特征图维度(28×28×256)也比朴素版(28×28×416)更紧凑,进一步减轻了后续层的压力。
下面我们用一个小例子来直观感受1×1卷积如何实现跨通道的线性组合与降维。
import numpy as np
# 模拟一个小的输入特征图块:空间尺寸2x2,通道数3 (C=3)
# 每个通道是一个2x2矩阵
input_feature_map = np.array([
[[1, 2], # 通道0
[3, 4]],
[[5, 6], # 通道1
[7, 8]],
[[9, 10], # 通道2
[11,12]]
]) # 形状为 (3, 2, 2),即 (C, H, W)。为了计算方便,我们之后会调整轴。
# 定义1x1卷积核:输出通道数K=2,输入通道数C=3。
# 卷积核形状为 (K, C, 1, 1) = (2, 3, 1, 1)
# 每个输出通道对应一组3个权重,分别与输入的3个通道相乘。
conv1x1_kernel = np.array([
[[[0.1]], [[0.2]], [[0.3]]], # 输出通道0的3个权重
[[[0.4]], [[0.5]], [[0.6]]] # 输出通道1的3个权重
]) # 形状 (2, 3, 1, 1)
# 为了计算,我们将输入调整形状为 (C, H, W) -> (H, W, C) 以方便点乘
input_HWC = np.transpose(input_feature_map, (1, 2, 0)) # 形状 (2, 2, 3)
# 初始化输出:空间尺寸不变(H,W),输出通道K=2
output = np.zeros((2, 2, 2))
# 对空间中的每个位置(i,j)执行1x1卷积
for i in range(2):
for j in range(2):
# 获取当前位置所有通道的值,形状 (3,)
pixel_vector = input_HWC[i, j, :]
# 对每个输出通道k,计算其在该位置的输出值
for k in range(2):
# 取出第k个输出通道对应的权重向量,形状 (3,)
weight_vector = conv1x1_kernel[k, :, 0, 0]
# 计算点积(线性组合),这里省略了偏置项和非线性激活
output[i, j, k] = np.dot(pixel_vector, weight_vector)
print("输入特征图 (形状 H,W,C):")
print(input_HWC)
print("\n1x1卷积核权重 (形状 K,C,1,1):")
print(conv1x1_kernel[:,:,0,0]) # 简化为(K, C)显示
print("\n1x1卷积输出 (形状 H,W,K),未加激活):")
print(output)输入特征图 (形状 H,W,C):
[[[ 1 5 9]
[ 2 6 10]]
[[ 3 7 11]
[ 4 8 12]]]
1x1卷积核权重 (形状 K,C,1,1):
[[0.1 0.2 0.3]
[0.4 0.5 0.6]]
1x1卷积输出 (形状 H,W,K),未加激活):
[[[ 3.8 8.3]
[ 4.4 9.8]]
[[ 5. 11.3]
[ 5.6 12.8]]]从输出可以看出,对于空间位置(0,0):
- 输入通道值:
[1, 5, 9] - 输出通道0:
0.1*1 + 0.2*5 + 0.3*9 = 3.8 - 输出通道1:
0.4*1 + 0.5*5 + 0.6*9 = 8.3这正是一个跨通道的线性组合。由于我们设置输出通道K=2小于输入通道C=3,这个操作同时实现了降维。
全局平均池化:替代全连接层
全连接层是传统神经网络参数的主要来源。在AlexNet中,全连接层参数约占58.6兆;在VGG16中,三层全连接层的参数更是高达约1.24亿(7×7×512×4096 + 4096×4096 + 4096×1000),占其总参数的绝大部分。如此庞大的参数量极易导致过拟合,并且是计算瓶颈。
GoogLeNet采用 全局平均池化 来替代大部分全连接层。GAP的操作非常简单:对于最后一个卷积层输出的每个特征图(通道),直接计算其所有像素的平均值,将这个标量作为该通道的代表。假设最后一层卷积输出为 7×7×1024,经过GAP后,就变成了 1×1×1024。这1024个值再送入一个轻量的全连接层(或直接接Softmax)进行分类。
优势对比:
- 传统全连接:
7×7×1024的特征图展平为50176维向量,再与4096个神经元全连接,仅这一层就需要约2.06 亿个参数(50176 × 4096)。 - 全局平均池化:将每个
7×7的特征图压缩为1个值,得到1024维向量,参数量和计算量骤降。
GAP不仅极大减少了参数、防止过拟合,还使网络对输入图像的空间变换更具鲁棒性,因为它聚合的是全局信息。用GAP替代全连接层已成为后续深度学习模型的一个重要趋势。
import numpy as np
# 模拟一个微型的最后一层卷积输出:假设只有2个通道,每个通道4x4
# 形状 (通道数C, 高H, 宽W) = (2, 4, 4)
last_conv_output = np.array([
[[1, 2, 3, 4], # 通道0
[5, 6, 7, 8],
[9,10,11,12],
[13,14,15,16]],
[[16,15,14,13], # 通道1
[12,11,10, 9],
[ 8, 7, 6, 5],
[ 4, 3, 2, 1]]
])
print("最后一层卷积输出 (形状 C,H,W):")
print("通道0:")
print(last_conv_output[0])
print("\n通道1:")
print(last_conv_output[1])
# 全局平均池化:对每个通道的所有元素求平均
gap_result = np.mean(last_conv_output, axis=(1, 2))
print("\n全局平均池化(GAP)结果 (形状 C,):")
print(gap_result)
# 假设接一个简单的全连接层到3个类别(权重随机初始化)
np.random.seed(42) # 固定随机种子,保证输出可复现
fc_weights = np.random.randn(3, 2) # 形状 (输出类别数, 输入特征数=通道数C)
fc_bias = np.random.randn(3)
logits = np.dot(fc_weights, gap_result) + fc_bias
print("\n全连接层权重 (3个类别, 2个输入特征):")
print(fc_weights)
print("\n全连接层偏置:")
print(fc_bias)
print("\n全连接层输出 (Logits):")
print(logits)最后一层卷积输出 (形状 C,H,W):
通道0:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]
[13 14 15 16]]
通道1:
[[16 15 14 13]
[12 11 10 9]
[ 8 7 6 5]
[ 4 3 2 1]]
全局平均池化(GAP)结果 (形状 C,):
[8.5 8.5]
全连接层权重 (3个类别, 2个输入特征):
[[ 0.49671415 -0.1382643 ]
[ 0.64768854 1.52302986]
[-0.23415337 -0.23413696]]
全连接层偏置:
[ 1.57921282 0.76743473 -0.46947439]
全连接层输出 (Logits):
[ 4.62603656 19.21854108 -4.44994221]辅助分类器:缓解梯度消失
GoogLeNet V1 共有 22 个带参数的层(初始卷积与分类层 + 9 个 Inception 模块内的卷积层),在当时属于非常深的网络。深度网络训练面临的老大难问题——梯度消失——再次出现。误差信号从深层反向传播到浅层时,梯度值会越来越小,导致浅层权重更新缓慢,训练效率低下。
GoogLeNet提出了一个巧妙的解决方案:辅助分类器。在网络中间的某些层(具体是第 3 个和第 6 个 Inception 模块,即 4a 与 4d 的输出之后)引出分支,各自接一个小的分类器(包含池化、卷积、全连接和Softmax)。在训练阶段,总损失函数由主分类器损失和辅助分类器损失加权求和构成。这样,梯度就可以通过这些辅助分支直接注入到网络的中部,为浅层提供更强烈的训练信号,有效加速收敛。
注意:辅助分类器仅在训练时使用,在测试或推理阶段会被丢弃,因此不会增加最终的预测耗时。
GoogLeNet V1 整体架构
综合以上所有组件,GoogLeNet V1的整体架构如下图所示(文字描述):
GoogLeNet V1 架构概览 (文字描述版):
输入 (224x224x3)
│
├─ 初始卷积层 (7x7 conv, stride 2) + MaxPool
│
├─ 初始卷积层 (3x3 conv) + LRN (局部响应归一化,当时保留,后被证明效果有限)
│
├─ MaxPool
│
├─ Inception (3a) 模块
│
├─ Inception (3b) 模块
│
├─ MaxPool
│
├─ Inception (4a) 模块
│ └─ [辅助分类器1分支]
├─ Inception (4b) 模块
├─ Inception (4c) 模块
├─ Inception (4d) 模块
│ └─ [辅助分类器2分支]
├─ Inception (4e) 模块
│
├─ MaxPool
│
├─ Inception (5a) 模块
├─ Inception (5b) 模块
│
├─ 全局平均池化 (GAP)
│
├─ 全连接层 (输出维度: 1000,对应ImageNet类别)
│
└─ Softmax整个网络由9个Inception模块堆叠而成,中间穿插池化层进行空间下采样。两个辅助分类器像“脚手架”一样,在训练时支撑着深层网络的梯度流动。最终,通过GAP和单个全连接层输出预测结果,极大精简了模型尾部。
📝 动手练一练
- 概念辨析:GoogLeNet V1中的“保留所有可能性”思想,具体是如何在Inception模块中实现的?它与单纯地增加网络宽度(每层神经元数)有何本质区别?
👉 点击查看参考答案
实现方式:Inception模块通过并行执行1×1卷积、3×3卷积、5×5卷积和3×3最大池化这四种操作,并将它们的结果在通道维度上进行拼接(Concat)。这样,网络无需人为选择单一操作,而是同时利用不同尺寸的感受野和多尺度信息。
本质区别:单纯增加网络宽度是在同一操作类型下增加更多的卷积核(通道数),本质是增加同质化的特征提取能力。而Inception的“宽度”是结构上的宽度,它增加了操作类型的多样性,是异质性的特征融合。后者能以更高效的方式提升网络表达能力,避免参数的无意义膨胀。
- 计算实践:假设某层输入特征图为
14×14×384,我们想用一个改进版Inception模块处理它。其中一条分支计划先使用1×1卷积降至96维,再进行3×3卷积输出128维。请计算仅这一条分支的参数量(忽略偏置)。如果去掉1×1卷积瓶颈,直接进行3×3卷积输出128维,参数量又是多少?对比两者。
👉 点击查看参考答案
带1×1卷积瓶颈的分支:
- 1×1卷积参数量:
1 * 1 * 384 * 96 = 36,864 - 3×3卷积参数量:
3 * 3 * 96 * 128 = 110,592总参数量:36,864 + 110,592 = 147,456
无1×1卷积瓶颈的分支: 3×3卷积参数量:3 * 3 * 384 * 128 = 442,368
对比:使用1×1卷积瓶颈将这条分支的参数量从 442,368 减少到 147,456,降低了约 66.7%。这充分证明了1×1卷积作为瓶颈层在控制计算复杂度方面的巨大威力。
本章小结
GoogLeNet V1是深度学习模型设计史上的一座里程碑。它没有跟随当时“加深加宽”的潮流,而是通过结构创新,在提升性能的同时巧妙地控制了模型复杂度。其核心贡献在于:
- Inception模块:提出“保留所有可能性”的并行多尺度特征提取与融合思想。
- 1×1卷积瓶颈:有效实现跨通道交互与特征降维,大幅减少计算量。
- 全局平均池化:用更高效、参数更少的方式替代庞大的全连接层,成为后续网络设计的标准操作之一。
- 辅助分类器:为训练极深网络提供了一种缓解梯度消失的实用技巧。
这些设计体现了深度学习从“暴力堆叠”向“精巧结构”演进的重要一步,为后续更强大的模型(如Inception系列后续版本、ResNet等)奠定了基础。
今天,Inception 系列在分类任务上已被 ResNet、EfficientNet、ViT 等取代,GoogLeNet 本身也不再是实用选择。但它留下的三件“遗产”仍随处可见:1×1 卷积作为通道升降维的标准工具(如 MobileNet 的逐点卷积)、全局平均池化作为分类头的默认写法、以及辅助损失/深监督(deep supervision)在分割与检测网络中的广泛使用。
📋 行动清单
- 在脑海中绘制出改进版Inception模块的结构图,并理解数据流如何从输入经过四条并行路径最终合并为输出。
- 编写一段简单的代码,对一个模拟的多通道特征图执行1×1卷积操作,验证其跨通道线性组合与降维的效果。
- 思考:如果让你设计一个更轻量化的Inception模块,可能会从哪些方面进行改进?(提示:卷积核分解、通道分组等)
—— 小象教研组