一文读懂现代卷积神经网络—网络中的网络(NiN)
目录
为啥主卷积层第一层stride=4?其他的都为stride=1
1. 第一层:11×11 卷积 + stride=4—— 快速捕获全局特征并降维
2. 第二层:5×5 卷积 + stride=1—— 聚焦局部特征细化
3. 第三层:3×3 卷积 + stride=1—— 提取更精细的局部特征
4. 第四层:3×3 卷积 + stride=1—— 映射到类别空间
网络中的网络(NiN)
针对 “全连接层易丢失空间结构” 的问题,提出了全新的特征处理思路:用 “像素级多层感知机(1x1 卷积)” 替代全连接层在通道上的处理,保留空间特征的结构信息。
核心创新:1x1 卷积与全局平均池化
- 1x1 卷积(像素级 MLP):
传统 CNN 中,卷积层提取空间特征后,需通过全连接层 “扁平化” 处理(丢失空间维度)。NiN 在每个卷积层后插入 1x1 卷积层(等价于在每个像素的通道维度上应用多层感知机):1x1 卷积核不改变空间尺寸(高、宽),仅对通道维度进行非线性变换(如输入通道数为 C,1x1 卷积可将其映射到新的通道数,实现通道级特征融合),既保留空间结构,又完成特征的非线性处理。- 全局平均池化(替代全连接层):
传统全连接层参数多且易过拟合。NiN 在最后一层卷积后使用 “全局平均池化”:对每个通道的所有像素取平均值,直接得到与类别数相等的通道数(如 10 类分类任务,输出 10 个通道的平均值),既减少参数,又保持了通道与类别的对应关系。与前序模型的差异:
打破了 “卷积 + 汇聚→全连接” 的固定模式,通过 1x1 卷积在保持空间结构的前提下处理通道特征,避免了早期全连接层对空间信息的破坏,为后续 CNN(如 GoogLeNet)的 “多尺度特征融合” 提供了思路。
NiN的应用

1*1卷积层和全连接层的对比
特性 1×1 卷积 全连接层 参数共享 是(同一卷积核应用于所有位置) 否(每个神经元参数独立) 空间信息保留 是(保持特征图尺寸) 否(需展平为向量) 计算效率 高(可并行计算) 低(需逐像素计算) 应用场景 特征融合、降维 / 升维 分类任务的最后几层
为啥除了主卷积层 还要加入NIN卷积层
在 NiN 网络中,除了主卷积层(如 11×11、5×5 卷积)外,还添加两个 1×1 卷积层(NiN 卷积层)的主要目的是增强网络的非线性表达能力和跨通道信息交互,同时减少参数数量。以下是详细解释:
1. 增强非线性表达能力
传统 CNN 中,卷积层后接 ReLU 激活函数引入非线性,但这种非线性仅在单个通道内生效。NiN 通过堆叠两个 1×1 卷积层 + ReLU,实现了跨通道的非线性变换:
- 单个卷积层:仅对空间局部区域进行线性变换,不同通道间缺乏交互。
- 1×1 卷积层:将同一位置的不同通道值进行加权求和(线性组合),再通过 ReLU 引入非线性。多个 1×1 卷积层堆叠后,可学习更复杂的特征组合。
示例:
假设主卷积层输出 96 个通道的特征图,第一个 1×1 卷积层可将这 96 个通道的信息整合为新的特征表示,第二个 1×1 卷积层进一步变换,形成更抽象的特征。2. 跨通道信息交互
1×1 卷积的核心作用是在不改变空间尺寸的情况下,实现不同通道间的信息融合:
- 传统卷积(如 3×3、5×5):主要捕获空间局部信息,通道间交互依赖后续层。
- 1×1 卷积:直接对同一位置的所有通道进行线性组合,相当于对每个像素点进行 “全连接” 操作。
数学表达:
对于输入特征图X[i,j,c](位置(i,j)的第c个通道),1×1 卷积的输出为:Y[i,j,k] = ReLU(∑_{c=0}^{C_in-1} W[k,c] × X[i,j,c] + b[k])其中,每个输出通道
k是所有输入通道的加权和,再通过 ReLU 激活。3. 替代全连接层,减少参数
传统 CNN 在特征提取后使用全连接层进行分类,存在两个问题:
- 空间信息丢失:需将特征图展平为向量。
- 参数过多:全连接层参数密集,易过拟合。
NiN 的改进:
- 1×1 卷积 + 全局平均池化替代全连接层:
- 1×1 卷积直接在特征图上操作,保持空间结构。
- 全局平均池化将每个通道的特征图压缩为一个标量,大幅减少参数。
参数对比:
假设输入为 5×5×384 的特征图,输出 10 个类别:
- 全连接层:参数数量 =
5×5×384×10 + 10 = 96,010- NiN 方式:
- 1×1 卷积(384→10):参数数量 =
1×1×384×10 + 10 = 3,850- 全局平均池化:无参数
总参数减少约 96%!4. NiN 块的直观理解
NiN 块的结构可类比为 “微型多层感知机(MLP)” 作用于每个空间位置:
- 主卷积层(如 11×11):捕获空间局部特征。
- 第一个 1×1 卷积层:对每个像素点的通道进行第一次非线性变换(相当于 MLP 的隐藏层)。
- 第二个 1×1 卷积层:进行第二次非线性变换(相当于 MLP 的输出层)。
这种结构使网络能够学习更复杂的特征交互,同时保持卷积的平移不变性。
5. 实验验证:1×1 卷积的效果
Hinton 在 NiN 论文中通过实验证明,添加 1×1 卷积层可显著提升模型性能:
- 基准模型:仅使用主卷积层 + 池化层,Top-1 准确率为 69.8%。
- 添加 1×1 卷积:准确率提升至 73.9%。
- 进一步添加全局平均池化:准确率提升至 74.8%,同时参数减少 90% 以上。
6. 与现代网络的联系
NiN 的 1×1 卷积思想被后续网络广泛采用:
- GoogLeNet(Inception 模块):使用 1×1 卷积进行降维,减少计算量。
- ResNet(残差块):使用 1×1 卷积调整通道数,实现残差连接。
- MobileNet、ShuffleNet:使用深度可分离卷积(包含 1×1 卷积)大幅减少参数。
总结
在主卷积层后添加 1×1 卷积层的核心目的是:
- 增强非线性:通过跨通道的非线性变换学习更复杂的特征。
- 减少参数:替代全连接层,降低模型复杂度。
- 提高泛化能力:通过全局平均池化和 1×1 卷积,减少过拟合风险。
为啥主卷积层第一层stride=4?其他的都为stride=1
NiN 网络中卷积层的
kernel_size和stride参数设置(第一层 11×11+stride=4,后续层逐步减小卷积核并固定 stride=1),是为了适配特征提取的层级需求:从全局到局部、从粗糙到精细,同时平衡计算效率。以下是具体原因:1. 第一层:11×11 卷积 + stride=4—— 快速捕获全局特征并降维
在网络最浅层,优先捕获图像的全局结构信息(如衣物的整体轮廓、大致形状),同时通过大步长快速降低特征图尺寸,减少后续计算量。
大卷积核(11×11)的作用:
输入图像为 224×224(Fashion-MNIST resize 后),11×11 的大卷积核可以覆盖更大的空间范围,直接捕捉全局特征(例如 “裙子的整体轮廓”“外套的大致版型”)。如果用小卷积核(如 3×3),需要多层堆叠才能覆盖相同范围,会增加计算量。大步长(stride=4)的作用:
计算公式:输出尺寸 =(输入尺寸 - kernel_size) / stride + 1
对于 224×224 输入:(224 - 11) / 4 + 1 ≈ 54,输出特征图尺寸从 224×224 压缩到 54×54,直接减少 76% 的空间尺寸。
这么做的原因是:
- 浅层特征图的 “空间冗余度高”(相邻像素信息相似),大步长可以高效去除冗余,降低后续层的计算压力;
- 全局特征不需要过高的空间分辨率(54×54 足够保留整体结构),过度保留反而会增加无效计算。
2. 第二层:5×5 卷积 + stride=1—— 聚焦局部特征细化
在第一层全局特征的基础上,进一步提取中等尺度的局部特征(如衣物的边缘、纹理走向),同时不再大幅降维,保留细节信息。
中等卷积核(5×5)的作用:
经过第一层降维后,特征图尺寸为 54×54,此时需要关注比全局更精细的特征(例如 “衣领的形状”“袖口的边缘”)。5×5 卷积核的覆盖范围(比 11×11 小)更适合捕捉这类局部特征,同时避免因卷积核过大导致的特征模糊。固定步长(stride=1)的作用:
配合padding=2((54 - 5 + 2×2)/1 + 1 = 54),输出特征图尺寸保持 54×54 不变。
原因是:此时特征图已经足够小(54×54),不需要再通过大步长降维;相反,需要保留足够的空间分辨率,让局部特征(如边缘、纹理)的位置信息不丢失,为后续更精细的提取做准备。3. 第三层:3×3 卷积 + stride=1—— 提取更精细的局部特征
在全局和中等局部特征的基础上,进一步捕捉细微特征(如布料的纹理细节、图案花纹)。
小卷积核(3×3)的作用:
经过第二层池化后,特征图尺寸降至 26×26(54→26),此时需要更精细的特征(例如 “毛衣的针织纹理”“裤子的缝线”)。3×3 是最小的有效卷积核(1×1 无法捕捉空间相关性),能够精准提取相邻像素的局部关联,是捕捉细微特征的最优选择。stride=1 的必要性:
配合padding=1((26 - 3 + 2×1)/1 + 1 = 26),输出尺寸保持 26×26。此时特征图已经很小,且细微特征对空间位置敏感(例如纹理的走向),必须用 stride=1 避免位置信息丢失。4. 第四层:3×3 卷积 + stride=1—— 映射到类别空间
将前面提取的所有特征(全局→局部→精细)映射到 10 个类别通道(对应 Fashion-MNIST 的 10 类衣物),为最终分类做准备。
- 为什么仍用 3×3+stride=1:
经过第三层池化后,特征图尺寸为 12×12(26→12),此时的核心任务是将特征与类别关联(例如 “针织纹理→毛衣”“缝线细节→裤子”)。3×3 卷积可以在不破坏特征空间结构的前提下,将 384 个特征通道映射到 10 个类别通道;stride=1 配合padding=1保持尺寸不变(12×12),确保每个类别通道能完整保留前面的特征信息,最后通过自适应池化压缩为 1×1 即可得到类别得分。总结:参数设计的底层逻辑
NiN 的卷积层参数设置遵循 特征提取的层级规律:
- 浅层(第一层):用大卷积核 + 大步长,优先捕获全局结构,快速降维以提高效率;
- 深层(第二、三、四层):用小卷积核 + 小步长,逐步聚焦局部细节,保留空间信息以提高特征精度。
这种设计既符合人类视觉系统的感知逻辑(先整体后局部),又平衡了计算成本与特征表达能力,是现代 CNN(如 AlexNet、VGG)的通用设计原则。
完整代码
"""
文件名: 7.3
作者: 墨尘
日期: 2025/7/13
项目名: dl_env
备注: 实现NiN(Network in Network)网络架构并在Fashion-MNIST数据集上训练
"""
import torch
from torch import nn
from d2l import torch as d2l
# 手动显示图像相关库
import matplotlib.pyplot as plt # 绘图库
import matplotlib.text as text # 用于修改文本绘制(解决符号显示问题)
# -------------------------- 核心解决方案:解决文本显示问题 --------------------------
# 定义替换函数:将Unicode减号(U+2212,可能导致显示异常)替换为普通减号(-)
def replace_minus(s):
if isinstance(s, str): # 判断输入是否为字符串
return s.replace('\u2212', '-') # 替换特殊减号为普通减号
return s # 非字符串直接返回
# 重写matplotlib的Text类的set_text方法,解决减号显示异常
original_set_text = text.Text.set_text # 保存原始的set_text方法
def new_set_text(self, s):
s = replace_minus(s) # 调用替换函数处理文本中的减号
return original_set_text(self, s) # 调用原始方法设置文本
text.Text.set_text = new_set_text # 应用重写后的方法
# -------------------------- 字体配置(确保中文和数学符号正常显示)--------------------------
plt.rcParams["font.family"] = ["SimHei"] # 设置中文字体(支持中文显示)
plt.rcParams["text.usetex"] = True # 使用LaTeX渲染文本(提升数学符号显示效果)
plt.rcParams["axes.unicode_minus"] = True # 确保负号正确显示(避免显示为方块)
plt.rcParams["mathtext.fontset"] = "cm" # 设置数学符号字体为Computer Modern(更美观)
d2l.plt.rcParams.update(plt.rcParams) # 让d2l库的绘图工具继承上述字体配置
# NiN块是NiN网络的基本构建单元,由一个普通卷积层(主卷积层)和两个1x1卷积层(NiN卷积层)组成
# 1x1卷积层可以看作是对每个像素点的全连接层,增加了模型的非线性表达能力
def nin_block(in_channels, out_channels, kernel_size, strides, padding):
return nn.Sequential(
# 主卷积层:使用指定的kernel_size进行特征提取
nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),
nn.ReLU(), # ReLU激活函数引入非线性
# NiN卷积层1:1x1卷积,增强跨通道信息交互和非线性
nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU(),
# NiN卷积层2:进一步增强模型的非线性表达能力
nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU())
if __name__ == '__main__':
# 构建完整的NiN网络
net = nn.Sequential(
# 第一个NiN块,使用较大的11x11主卷积核捕捉全局特征
nin_block(1, 96, kernel_size=11, strides=4, padding=0), # 主卷积层:11x11
nn.MaxPool2d(3, stride=2), # 最大池化层,降低特征图尺寸
# 第二个NiN块,增加通道数到256
nin_block(96, 256, kernel_size=5, strides=1, padding=2), # 主卷积层:5x5
nn.MaxPool2d(3, stride=2), # 第二次降采样
# 第三个NiN块,通道数增加到384
nin_block(256, 384, kernel_size=3, strides=1, padding=1), # 主卷积层:3x3
nn.MaxPool2d(3, stride=2), # 第三次降采样
# 随机丢弃 50% 的神经元,防止过拟合
nn.Dropout(0.5), # Dropout层,防止过拟合
# 最后一个NiN块,将通道数映射为类别数10
nin_block(384, 10, kernel_size=3, strides=1, padding=1), # 主卷积层:3x3
# 自适应平均池化,将特征图大小调整为1x1
nn.AdaptiveAvgPool2d((1, 1)),
# 将四维的输出(B, C, 1, 1)转成二维的输出(B, 10)
nn.Flatten())
# 测试网络结构,打印每一层的输出形状
X = torch.rand(size=(1, 1, 224, 224))
for layer in net:
X = layer(X)
print(layer.__class__.__name__, 'output shape:\t', X.shape)
# 设置训练超参数
lr, num_epochs, batch_size = 0.1, 10, 128
# 加载Fashion-MNIST数据集,调整图像大小为224x224以适应网络输入
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=224)
# 使用GPU进行训练,如果可用的话
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
plt.show(block=True) # block=True:保持图像窗口打开,直到手动关闭
代码解析
主卷积层与 NiN 卷积层说明:
主卷积层:
- 每个
nin_block中的第一个卷积层(即nn.Conv2d)- 使用指定的 kernel_size(11x11、5x5 或 3x3)
- 负责提取空间特征并调整通道数
NiN 卷积层:
- 每个
nin_block中的后两个卷积层- 固定使用 1x1 卷积核
- 负责跨通道信息融合和增强非线性表达能力
网络中的主卷积层具体参数:
- 第一层:11x11,stride=4(捕获全局特征)
- 第二层:5x5,stride=1(提取局部特征)
- 第三层:3x3,stride=1(提取更精细特征)
- 第四层:3x3,stride=1(映射到类别空间)
这种结构设计使得网络能够:
- 通过主卷积层捕获空间特征
- 通过 NiN 卷积层增强特征表达能力
- 整体上比传统 CNN 使用更少的参数实现更好的性能
实验结果

更多推荐
所有评论(0)