现代卷积神经网络(二):NiN与GoogLeNet,打破常规的结构魔法
上一篇我们讲了 AlexNet 和 VGG,它们都把“深度 CNN 真能做事”这件事证明得很扎实。
AlexNet 更像是一次突破:堆更深、更宽的卷积层,性能明显上去了。VGG 则把网络设计“模块化”:一组组 3x3 卷积 + 最大汇聚,像积木一样反复堆叠,结构清晰、好复用。
但网络越深、通道越多,问题也就越明显:
-
全连接层太重
卷积层辛苦提特征,最后一展平接全连接层,参数量会瞬间爆炸,不仅训练慢,还特别吃显存。 -
卷积核大小很难选
1x1、3x3、5x5 各有优点。小核抓细节,大核看范围,到底该选谁?只选一个尺度就容易漏掉另一种特征。
于是,今天的两个主角就登场了:
- NiN(Network in Network,网络中的网络):用 1x1 卷积在“每个像素位置”做小型处理,再配合全局平均汇聚,削弱甚至替代笨重的全连接层。
- GoogLeNet:用 Inception 块把多种卷积路线并行,让网络自己学会组合不同尺度的特征。
一、先认识 1x1 卷积:最小但很关键
在理解 NiN 和 GoogLeNet 之前,我们必须先把 1x1 卷积讲清楚,因为它是这两类网络里的“高频零件”。
很多小白第一次看到 1x1 卷积会疑惑:卷积核只有一个格子,它连旁边像素都看不到,有什么用?
答案是:
1x1 卷积不负责看空间邻居,它主要负责融合通道信息、改变通道数量。
换句话说,它不是在“看邻居”,而是在“调通道”。所以它虽然小,但很关键。
说明一下:本文统一用“通道”这个说法,你也可能在别的资料里看到它被称为“特征图数”或“feature maps”。本质都是指同一维度上的多张特征图。
假设某一层的输出形状是:
批量大小 x 64通道 x 高 x 宽
某个像素位置上,不是只有一个数字,而是有 64 个通道值。1x1 卷积会在同一个位置,把这 64 个数字加权组合成新的通道。
你可以把它理解成:
- 空间位置不动(不看邻居);
- 只在“通道维度”做一次线性组合,再加上非线性激活。
所以它像什么?
它像一个“站在每个像素点上的小型全连接层”,专门把通道信息重新混合。
如果输出通道数是 32,那么每个像素点上的 64 维向量,就会被变换成 32 维向量,这就是“降维”;反过来变成 128 维,就是“升维”。
举个更具体的小例子(只看一个像素点):
原来这个像素点有 3 个通道值: [2, 0, 1]
1x1 卷积想要输出 2 个通道
第 1 个输出通道 = 2*0.5 + 0*0.2 + 1*0.1 = 1.1
第 2 个输出通道 = 2*(-0.3) + 0*0.7 + 1*0.4 = -0.2
你可以看到,1x1 卷积做的其实就是“通道方向的加权组合”。
这里的权重没有“必须加起来等于 1”的要求,它们是训练中自动学习的参数,可以是任意实数(正的、负的都可以),还通常会带一个偏置项。只有在像注意力这类“归一化权重”的场景,才会特意让权重和为 1。
再用一个小示意图把过程画出来(只看一个像素点):
输入通道(3个): [x1, x2, x3]
| | |
| | | (权重矩阵)
v v v
输出通道(2个): [y1, y2]
y1 = w11*x1 + w12*x2 + w13*x3
y2 = w21*x1 + w22*x2 + w23*x3
这说明 1x1 卷积的“过程结果”就是:在同一空间位置上,把多个通道线性组合成新的通道。
1x1 卷积常见作用有三个(也是它在现代 CNN 里最常见的三种“岗位”):
- 升维:把通道数变多,增强表达能力。
- 降维:把通道数变少,减少后续计算量。
- 通道融合:让不同通道之间产生新的组合。
这颗“小螺丝”,后面会反复出现,你会发现它是“省计算 + 增表达”的万能小工具。
二、NiN:把全连接层变轻
1. 全连接层为什么麻烦?
传统 CNN 常见结构是:
卷积层 -> 汇聚层 -> 卷积层 -> 汇聚层 -> 展平 -> 全连接层 -> 输出
问题就出在“展平 -> 全连接层”。
展平之后,本来是一个 C×H×WC\times H\times WC×H×W 的三维特征图,直接被拉成一条超长向量。然后再接全连接层,就会产生大量参数。
简单画一下这个过程:
特征图(C x H x W)
| 展平
v
超长向量(长度 = C*H*W)
| 全连接
v
分类输出
结果就是:只要 C、H、WC、H、WC、H、W 稍微大一点,全连接层的参数量就会急剧增长。
假设卷积层最后输出:
512通道 x 7高 x 7宽
一展平就是:
512 x 7 x 7 = 25088
如果再接一个 4096 神经元的全连接层,参数量大约是:
25088 x 4096 ≈ 1亿
这还只是其中一层。参数多会带来三个麻烦:
- 训练慢;
- 占显存;
- 容易过拟合(尤其是数据集不够大时)。
NiN 的思路就是:能不能少用这种庞大的全连接层?甚至让它“隐身”,只用卷积和汇聚完成分类?
2. NiN 块:卷积后面接小网络
NiN 的全称是 Network in Network,直译就是“网络中的网络”。
它的想法是:普通卷积层只是在局部区域里提取特征,但每个位置上不同通道之间的组合也很重要。那就在普通卷积后面,加上几个 1x1 卷积,让每个像素位置都拥有一个“小型网络”来处理通道信息。
你可以把一个 NiN 块理解成:
- 先用常规卷积“看空间”;
- 再用 1x1 卷积“拌通道”;
- 把通道混合得更聪明。
用流程图直观表示就是:
这个流程的结果是:既保留空间信息,又在通道维度上做更复杂的组合。
代码如下:
from torch import nn
def nin_block(in_channels, out_channels, kernel_size, strides, padding):
# NiN 块:一个普通卷积 + 两个 1x1 卷积
# 普通卷积负责提取局部空间特征,后面的 1x1 卷积负责通道混合与非线性变换
return nn.Sequential(
# 主卷积:决定感受野大小、步幅与边界填充
nn.Conv2d(in_channels, out_channels,
kernel_size, stride=strides, padding=padding),
nn.ReLU(),
# 1x1 卷积:在每个像素位置上做通道融合,等价于位置上的小型全连接层
nn.Conv2d(out_channels, out_channels, kernel_size=1),
nn.ReLU(),
# 再叠一层 1x1 卷积:进一步增加通道间的非线性组合能力
nn.Conv2d(out_channels, out_channels, kernel_size=1),
nn.ReLU()
)
你可以把这个块理解成:
- 先用普通卷积看局部空间;
- 再用两个 1x1 卷积混合通道;
- 每一步后面都接 ReLU,增加非线性表达能力。
这样做的好处是:空间信息和通道信息都能被充分处理,但参数量又不会像大规模全连接层那样“爆炸”。
3. 全局平均汇聚:不再暴力展平
NiN 还有一个非常重要的设计:全局平均汇聚(Global Average Pooling)。
传统做法:
最后特征图 -> 展平 -> 全连接层 -> 类别分数
NiN 的做法:
最后特征图的通道数 = 类别数 -> 每个通道求平均 -> 类别分数
举个例子,假设要做 10 分类。NiN 让最后一层卷积直接输出 10 个通道:
- 第 1 个通道负责“类别 1”的证据;
- 第 2 个通道负责“类别 2”的证据;
- …
- 第 10 个通道负责“类别 10”的证据。
每个通道可能还是一张小图,比如 5x5。全局平均汇聚就把每张小图求一个平均值,得到 10 个数,正好对应 10 个类别。
这一步的直觉是:
- 如果某个通道整体都“亮”,说明它的类别证据强;
- 平均一下,就得到一个稳定的类别分数。
而且这种做法几乎不引入额外参数,对过拟合更友好。
nn.AdaptiveAvgPool2d((1, 1))
这行代码的意思是:不管输入特征图的高宽是多少,都平均汇聚成 1x1。
你可以把它理解成“把一整张特征图压成一个代表值”。
举个更直观的小例子(只看一个通道):
输入特征图是 2x2:
[[1, 3],
[5, 7]]
全局平均汇聚结果 = (1 + 3 + 5 + 7) / 4 = 4
这说明它并不关心空间位置细节,而是只保留“整体强度”。
再用一个小示意图表示它的过程和结果:
通道内的特征图(5x5) --> 求平均 --> 一个数(该通道的分数)
4. NiN 的入门记忆法
NiN 的核心可以记成两句话:
用 1x1 卷积在每个像素位置上做通道融合。
用全局平均汇聚减少对大型全连接层的依赖。
它让 CNN 变得更轻,也启发了后面很多网络设计,比如后来几乎所有现代 CNN 都会用到全局平均汇聚的思路。
三、GoogLeNet:不同卷积路线一起上
VGG 的思路很统一:几乎都用 3x3 卷积。
但真实图片很复杂。有些特征很小,比如边缘、纹理;有些特征更大,比如眼睛、轮子、脸部轮廓。不同大小的卷积核,擅长看的范围不一样。
如果只用一种卷积核,你就像只拿一种“放大镜”去看世界,细节可能很清楚,但整体结构又看不全。
那到底该用哪个?
GoogLeNet 的回答很直接:
不要只选一个,让它们并行工作。
这就是 Inception 块。
它的核心思想是:同一层里开多条路,让网络自己学会拼不同尺度的特征。
1. Inception 块的四条路线
一个 Inception 块里,输入会被分成四条路线:
-
1x1 卷积路线
负责快速做通道变换。 -
1x1 卷积 + 3x3 卷积路线
先降维,再看中等范围。 -
1x1 卷积 + 5x5 卷积路线
先降维,再看更大范围。 -
最大汇聚 + 1x1 卷积路线
先提取强特征,再调整通道数。
四条路线的输出高宽保持一致,最后在通道维度上拼接起来。
可以把它想成“四个小专家各自做一份报告,最后把报告合并”。
用一张并行流程图表示:
结果就是:同一层同时看到小范围、中范围、大范围的特征,再把它们合在一起。
import torch
from torch import nn
from torch.nn import functional as F
class Inception(nn.Module):
def __init__(self, in_channels, c1, c2, c3, c4):
super().__init__()
# 路线1:1x1 卷积,快速做通道变换
self.p1_1 = nn.Conv2d(in_channels, c1, kernel_size=1)
# 路线2:先 1x1 降维,再用 3x3 看中等范围
self.p2_1 = nn.Conv2d(in_channels, c2[0], kernel_size=1)
self.p2_2 = nn.Conv2d(c2[0], c2[1], kernel_size=3, padding=1)
# 路线3:先 1x1 降维,再用 5x5 看更大范围
self.p3_1 = nn.Conv2d(in_channels, c3[0], kernel_size=1)
self.p3_2 = nn.Conv2d(c3[0], c3[1], kernel_size=5, padding=2)
# 路线4:先最大汇聚提强特征,再用 1x1 调整通道数
self.p4_1 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
self.p4_2 = nn.Conv2d(in_channels, c4, kernel_size=1)
def forward(self, x):
# 四条路线并行计算,最后在通道维度拼接
p1 = F.relu(self.p1_1(x))
p2 = F.relu(self.p2_2(F.relu(self.p2_1(x))))
p3 = F.relu(self.p3_2(F.relu(self.p3_1(x))))
p4 = F.relu(self.p4_2(self.p4_1(x)))
# dim=1 表示在通道维度拼接
return torch.cat((p1, p2, p3, p4), dim=1)
这段代码看起来比 VGG 复杂,但主线很清楚:
同一个输入,走四条不同路线;每条路线看问题的角度不同;最后把结果合并。
换个说法:网络不需要在“选一个卷积核”,而是直接把多个尺度都用上,再让自己学会如何组合。
2. 为什么路线里经常先放 1x1 卷积?
如果直接对很多通道做 5x5 卷积,计算量会很大。GoogLeNet 在 3x3 和 5x5 卷积前面加 1x1 卷积,常常是为了先减少通道数。
可以直观理解成:先把“厚厚的一摞书”压薄,再让大卷积核去翻。这样既保留信息,又不会让计算量爆炸。
给一个具体的“参数量对比”小例子:
假设输入通道 = 128,输出通道 = 32
直接 5x5 卷积参数量:
5*5*128*32 = 102,400
先 1x1 降维到 16 通道,再做 5x5:
1*1*128*16 = 2,048
5*5*16*32 = 12,800
总计 = 14,848
结果非常直观:先降维再卷积,参数量大幅减少。
举个直观例子:
512通道 -> 先用1x1压到32通道 -> 再做5x5卷积
这样后面的 5x5 卷积就轻很多。
所以,GoogLeNet 里的 1x1 卷积很像一个“中转站”:
- 先把厚厚的通道压薄;
- 再交给大卷积核处理;
- 最后把不同路线的结果拼起来。
3. GoogLeNet 的价值
GoogLeNet 的厉害之处在于:它很深,但参数量并不夸张。
它综合使用了几个关键思想:
- 多路线并行,提取不同尺度特征;
- 1x1 卷积降维,减少计算量;
- 借鉴全局平均汇聚,减少大型全连接层。
它告诉我们:
网络不一定只能一层接一层往下排,也可以在同一层里分出多条路,再把结果合并。
四、NiN 和 GoogLeNet 对比
| 网络 | 重点设计 | 解决的问题 | 小白记忆 |
|---|---|---|---|
| NiN | 1x1 卷积 + 全局平均汇聚 | 全连接层太重 | 用小网络替代大尾巴 |
| GoogLeNet | Inception 多路线并行 | 卷积核大小难选择 | 1x1、3x3、5x5 一起上 |
这两个网络都体现了一个变化:
CNN 不再只是简单地“卷积、汇聚、全连接”,而是开始认真设计网络内部的信息流动方式。
对新手来说,可以把它理解成:以前只是“堆层数”,现在开始“讲策略”。
再用一个生活化的对比小例子:
如果你关心“轻量、省参数” -> 更像 NiN
如果你关心“多尺度、多视角” -> 更像 GoogLeNet
五、小结
这一篇你需要记住四件事:
- 1x1 卷积虽然不看邻居像素,但能融合通道、升降维、减少计算量,是现代 CNN 的高频工具。
- NiN 用 1x1 卷积增强局部表达,用全局平均汇聚减少全连接层,让网络更轻、更不容易过拟合。
- GoogLeNet 的 Inception 块让多种卷积路线并行,自动覆盖不同尺度的特征。
- 现代 CNN 的设计重点,逐渐从“堆更多层”变成“让信息更聪明地流动”。
如果用一句话收尾:
NiN 让网络变轻,GoogLeNet 让网络变聪明。
下一篇我们要面对一个更深层的问题:网络越深真的越好吗?为什么有时加深之后反而更难训练?这就要轮到 批量规范化(Batch Normalization) 和 ResNet 出场了。
(注:文档部分内容参考《动手学深度学习》)
《动手学深度学习》现代卷积神经网络:https://zh.d2l.ai/chapter_convolutional-modern/index.html
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)