查看全课大纲(第 41 / 93 节)
  1. 1.计算机视觉概述
  2. 2.计算机视觉应用场景
  3. 3.困难与挑战
  4. 4.开源库介绍
  5. 5.图像显示与存储
  6. 6.CLAHE
  7. 7.腐蚀膨胀与开闭运算
  8. 8.滤波与边缘填充
  9. 9.各种滤波
  10. 10.图像金字塔
  11. 11.傅里叶变换
  12. 12.短时傅里叶变换与小波变换
  13. 13.颜色特征与颜色直方图
  14. 14.几何特征_边缘
  15. 15.几何特征-角点与斑点
  16. 16.局部特征-SIFT特征描述子
  17. 17.局部特征-SURF特征描述子
  18. 18.局部特征-ORB特征描述子
  19. 19.其他特征描述子
  20. 20.传统图像分割技术
  21. 21.GraphCut
  22. 22.GrabCut
  23. 23.人脸检测的传统方法
  24. 24.行人检测的传统方法HOG+SVM
  25. 25.行人检测DPM
  26. 26.人工神经网络的架构
  27. 27.目标函数与梯度下降
  28. 28.误差反向传播
  29. 29.可手算的神经网络
  30. 30.改进目标函数与克服梯度消散
  31. 31.神经网络之传统神经网络与深度网络的差异
  32. 32.各种梯度下降算法
  33. 33.如何选择合适的梯度下降算法
  34. 34.避免过适应
  35. 35.CNN的架构
  36. 36.CNN的误差反向传播_卷积层
  37. 37.CNN的误差反向传播_池化层
  38. 38.分类所用的数据集
  39. 39.Alexnet详解
  40. 40.VGG详解
  41. 41.GoogLeNet_v1
  42. 42.BatchNormalization
  43. 43.GoogLeNet_v3v4
  44. 44.ResNet
  45. 45.ResNeXt
  46. 46.CNN设计准则
  47. 47.图像检索与推土机距离
  48. 48.基于颜色相似性
  49. 49.基于形状相似性
  50. 50.基于纹理相似性
  51. 51.基于局部特征相似性
  52. 52.DL的二值哈希编码
  53. 53.高维空间中检索KDTree
  54. 54.高维空间中检索LSH
  55. 55.目标识别任务综述
  56. 56.RCNN
  57. 57.SPP-Net
  58. 58.Fast R-CNN
  59. 59.Faster R-CNN
  60. 60.R-CNN系列模型回顾
  61. 61.R-FCN的设计思路
  62. 62.R-FCN的实现
  63. 63.YOLOv1
  64. 64.YOLOv2_9000
  65. 65.YOLOv3
  66. 66.图像分割应用场景与数据集
  67. 67.上采样的实现方式
  68. 68.FCN网络架构的由来
  69. 69.FCN构建细节
  70. 70.DeepLabv1
  71. 71.CRF
  72. 72.DeepLabv2
  73. 73.DeepLabv3
  74. 74.特定的医学影像分割任务
  75. 75.U-Net
  76. 76.3D U-Net
  77. 77.V-Net
  78. 78.DenseNet
  79. 79.病理切片分析任务及数据集
  80. 80.病理切片分析
  81. 81.图说任务及其传统处理方法
  82. 82.深度语言模型与RNN
  83. 83.LSTM及其变种
  84. 84.NIC模型
  85. 85.SAT模型
  86. 86.图说的性能指标
  87. 87.判别模型与生成模型
  88. 88.KL散度与JS散度
  89. 89.VAE
  90. 90.GAN的基本原理
  91. 91.DCGAN与CGAN
  92. 92.Wasserstein GAN
  93. 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就是“加深”路线的杰出代表。然而,单纯地增加深度和宽度会带来两个显著问题:

  1. 参数数量剧增:尤其是全连接层,参数数量与神经元数量的平方成正比,极易导致模型过拟合,且对计算资源要求极高。
  2. 梯度消失/爆炸:网络过深时,误差反向传播到浅层时信号会变得极其微弱或剧烈,导致训练困难。

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和单个全连接层输出预测结果,极大精简了模型尾部。

📝 动手练一练

  1. 概念辨析:GoogLeNet V1中的“保留所有可能性”思想,具体是如何在Inception模块中实现的?它与单纯地增加网络宽度(每层神经元数)有何本质区别?
👉 点击查看参考答案

实现方式:Inception模块通过并行执行1×1卷积、3×3卷积、5×5卷积和3×3最大池化这四种操作,并将它们的结果在通道维度上进行拼接(Concat)。这样,网络无需人为选择单一操作,而是同时利用不同尺寸的感受野和多尺度信息。

本质区别:单纯增加网络宽度是在同一操作类型下增加更多的卷积核(通道数),本质是增加同质化的特征提取能力。而Inception的“宽度”是结构上的宽度,它增加了操作类型的多样性,是异质性的特征融合。后者能以更高效的方式提升网络表达能力,避免参数的无意义膨胀。

  1. 计算实践:假设某层输入特征图为 14×14×384,我们想用一个改进版Inception模块处理它。其中一条分支计划先使用1×1卷积降至96维,再进行3×3卷积输出128维。请计算仅这一条分支的参数量(忽略偏置)。如果去掉1×1卷积瓶颈,直接进行3×3卷积输出128维,参数量又是多少?对比两者。
👉 点击查看参考答案

带1×1卷积瓶颈的分支:

  1. 1×1卷积参数量:1 * 1 * 384 * 96 = 36,864
  2. 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是深度学习模型设计史上的一座里程碑。它没有跟随当时“加深加宽”的潮流,而是通过结构创新,在提升性能的同时巧妙地控制了模型复杂度。其核心贡献在于:

  1. Inception模块:提出“保留所有可能性”的并行多尺度特征提取与融合思想。
  2. 1×1卷积瓶颈:有效实现跨通道交互与特征降维,大幅减少计算量。
  3. 全局平均池化:用更高效、参数更少的方式替代庞大的全连接层,成为后续网络设计的标准操作之一。
  4. 辅助分类器:为训练极深网络提供了一种缓解梯度消失的实用技巧。

这些设计体现了深度学习从“暴力堆叠”向“精巧结构”演进的重要一步,为后续更强大的模型(如Inception系列后续版本、ResNet等)奠定了基础。

今天,Inception 系列在分类任务上已被 ResNet、EfficientNet、ViT 等取代,GoogLeNet 本身也不再是实用选择。但它留下的三件“遗产”仍随处可见:1×1 卷积作为通道升降维的标准工具(如 MobileNet 的逐点卷积)、全局平均池化作为分类头的默认写法、以及辅助损失/深监督(deep supervision)在分割与检测网络中的广泛使用。

📋 行动清单

  • 在脑海中绘制出改进版Inception模块的结构图,并理解数据流如何从输入经过四条并行路径最终合并为输出。
  • 编写一段简单的代码,对一个模拟的多通道特征图执行1×1卷积操作,验证其跨通道线性组合与降维的效果。
  • 思考:如果让你设计一个更轻量化的Inception模块,可能会从哪些方面进行改进?(提示:卷积核分解、通道分组等)

—— 小象教研组