NiN-Network In Network:网络中的网络
现在已经很少使用了,但是提出来的一些概念到现在也被持续使用,所以还是很有了解到必要的
全连接层的问题
全连接层会带来巨大的参数空间,几乎整个网络的参数都集中在全连接层;
全连接层最重要的问题是:容易带来过拟合
卷积层只需要较少的参数: x x
但卷积层后的第一个全连接层的参数:
- LeNet:16x5x5x120 = 48k
- AlexNet:256x5x5x4096 = 26M
- VGG:512x7x7x4096 = 102M
NiN
为了解决这个问题——NiN的思路就是:完全不要全连接层!
LeNet、AlexNet 和 VGG 都有一个共同的设计模式:
- 通过一系列的 卷积层 与 汇聚层 来提取空间结构特征;
- 然后通过全连接层对特征的表征进行处理。
AlexNet 和 VGG 对 LeNet 的改进主要在于如何扩大和加深这两个模块。 或者,可以想象在这个过程的早期使用全连接层。
然而,如果使用了全连接层,可能会完全放弃表征的空间结构。 网络中的网络(NiN)提供了一个非常简单的解决方案:在每个像素的通道上分别使用多层感知机
NiN块
一个卷积层后跟两个全连接层
- 步幅1,无填充,输出形状跟卷积层输出一样
- 起到全连接层的作用
我们知道1x1的卷积层可以等价于全连接层。所以上图两个卷积层可以认为是两个全连接层,唯一的作用就是在通道数上做一些混合,就如下图显示的:
NiN架构
-
无全连接层
-
交替使用NiN块和步幅为2的最大池化层,并逐步减少高宽和增大通道数
-
最后使用全局平均池化层得到输出,使其输入通道数是类别数
全局平均池化层:
窗口的高宽=输入的高宽
也就是对于每个通道,拿出最大的那个值
VGG:VGG块 * 4 + 4096MLP * 2 + 输出数为1000的输出层
NiN:(NiN块 + MaxPooling)*3 + NiN块 + Global Mean Pooling
总结
-
NiN使用由一个卷积层和多个1×1卷积层组成的块。该块可以在卷积神经网络中使用,可以使每个像素都具有非线性。
-
NiN去除了容易造成过拟合的全连接层,将它们替换为全局平均汇聚层(即在所有位置上进行求和)。该汇聚层通道数量为所需的输出数量(例如,Fashion-MNIST的输出为10)。
-
移除全连接层可减少过拟合,同时显著减少NiN的参数。
-
NiN的设计影响了许多后续卷积神经网络的设计。
代码实现
NiN块
import torch
from torch import nn
from d2l import torch as d2l
def nin_block(in_channels, out_channels, kernel_size, strides, padding):
return nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size, strides, padding),
nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU(),
nn.Conv2d(out_channels, out_channels, kernel_size=1), nn.ReLU())
NiN模型
net = nn.Sequential(
# 因为数据集是灰度图,所以输入通道是1
nin_block(1, 96, kernel_size=11, strides=4, padding=0),
nn.MaxPool2d(3, stride=2),
nin_block(96, 256, kernel_size=5, strides=1, padding=2),
nn.MaxPool2d(3, stride=2),
nin_block(256, 384, kernel_size=3, strides=1, padding=1),
nn.MaxPool2d(3, stride=2),
nn.Dropout(0.5),
# 标签类别数是10
nin_block(384, 10, kernel_size=3, strides=1, padding=1),
nn.AdaptiveAvgPool2d((1, 1)),
# 将四维的输出转成二维的输出,其形状为(批量大小,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)