26 网络中的网络 NiN【动手学深度学习v2】

NiN
- 这个网络现在用的不多,但是它所提出的很多重要的概念在之后的很多网络中都会被用到
LeNet、AlexNet和VGG
- LeNet、AlexNet和VGG都有一个共同的设计模式:通过一系列的卷积层与汇聚层来提取空间结构特征,然后通过全连接层对特征的表征进行处理
- AlexNet和VGG对LeNet的改进主要在于如何扩大和加深卷积层和汇聚层这两个模块
全连接层的问题
- 在AlexNet和VGG的最后面都使用了两个比较大的全连接层,最后再通过一个全连接层作为输出(全连接层比较占用网络参数空间,整个网络的绝大多数参数都集中在全连接层)
- 最重要的问题是会带来过拟合
- 假设使用卷积层的话,参数的个数等于输入的通道数(Ci) * 输出的通道数(Co) * 卷积窗口的高度(k) * 卷积窗口的宽度(k),但是如果使用全连接层的话,它的参数个数就等于整个输入的像素(也就是输入通道数) * 输入的高 * 输入的宽 * 输出中所有的像素(也就是输出通道数)
- 在卷积层的最后一层的输出并没有变成 1 * 1,很多情况下就是 7 * 7,这样的话其实对于全连接层来说输入的像素还是比较多的
- 另外,如果使用全连接层,可能会完全放弃表征的空间结构

- LeNet中16是最后一个卷积层的输出通道数,5 * 5表示经过卷积操作之后最终输出压缩成了5 * 5的大小,120是输出的隐藏层的大小(下一个全连接层的输入通道数)
- AlexNet中最后一个卷积层的输出通道数是256
- VGG最后一个卷积层的输出通道数变成了512,经过多次卷积操作之后最终输出的大小也只压缩到了7 * 7,因此它所占用的内存量确实是比较大的,差不多占用700M左右,其中绝大部分参数都集中在卷积层之后的第一个全连接层
大量的参数会带来很多问题
- 占用大量的内存
- 占用很多的计算带宽(一个很大的矩阵乘法根本不是受限于计算,而是几乎是受限于不断访问内存所带来的限制)
- 很容易过拟合(某一层的参数过多很容易导致模型收敛特别快,反过来讲,就需要使用大量的正则化避免该层学到所有的东西)
NiN
为了解决这些问题,就提出了NiN
- NiN的思想是完全不要全连接层(比如2021年由谷歌提出的MLP-Mixer,提出MLP又能够替代CNN了,但是实际上它和NiN是一个东西,只是NiN认为全连接层不是很好,所以就用卷积层来替代全连接层,不要全连接层;而MLP-Mixer认为卷积层不行,还是得用全连接层,所以实际上是一个东西),在每个像素的通道上分别使用多层感知机,也就是在每个像素位置(针对每个高度和宽度)应用一个全连接层。这样将权重连接到了每个空间位置,可以将其视为1 * 1的卷积层,或者看作在每个像素位置上独立作用的全连接层,从另一个角度讲,就是将空间维度中的每个像素视为单个样本,将通道维度视为不同特征
NiN块
NiN中最重要的概念叫做NiN块(VGG之后的网络基本上都有自己局部的卷积神经网络架构,也就是块状结构)
NiN块的结构如下图所示

- 回顾:卷积层的输入和输出由四维张量组成,张量的每个轴分别对应样本、通道、高度和宽度;全连接层的输入和输出通常是分别对应于样本和特征的二维张量
- NiN块由一个卷积层和两个全连接层(实际上是窗口大小为1 * 1、步幅为1、无填充的卷积层)组成(因为1 * 1的卷积层等价于全连接层,所以这里使用的是1 * 1的卷积层;步幅为1,无填充,所以卷积层输出的形状和输入的形状是一样的,也就是不会改变输入的形状,同时也不会改变通道数)
- NiN块中两个1 * 1的卷积层其实是起到了全连接层的作用,这两个卷积层充当了带有ReLu激活函数的逐像素全连接层
- 1 * 1的卷积运算如下图所示

- NiN块可以认为是一个简单的神经网络:一个卷积层+两个全连接层。唯一的不同是这里的全连接层是对每一个像素做全连接,对于每个输入的每个像素的权重都是一样的,不会根据输入的具体情况发生变化,所以可以认为是按照输入的每个像素逐一做全连接
NiN架构
- 没有全连接层
- 交替使用NiN块和步幅为2的最大池化层(最大池化层的作用的就是高宽减半),逐步减小高宽(输出的尺寸)和增大通道数
- 最后使用全局平均池化层得到输出(全局平均池化层:该池化层的高宽等于输入的高宽,等价于对每一个通道取平均值(这里应该是取平均值吧,视频中沐神说的是最大值,应该是口误吧),作为这个类别的预测,再通过softmax就能得到每个类别的概率了),其输入通道数是标签类别数,最后不需要使用全连接层,也是一个比较极端的设计
VGG net 和 NiN net

- VGG块由多个卷积层和一个最大池化层组成;NiN块由一个卷积层和两个当做全连接层用的1 * 1的卷积层
- VGG网络由四个VGG块和两个大的全连接层,最后通过一个输出通道数为1000的全连接层得到1000类
- NiN网络由一个NiN块和一个步幅为2、3 * 3的最大池化层不断重复,如果将最后重复部分的NiN块的通道数设置为标签类别的数量的话,则最后就可以用全局平均池化层(global average pooling layer)代替全连接层来得到输出(一个对数几率(logits)),也就是对每个类的预测(这里说NiN网络是由一个NiN块和一个最大池化层不断重复堆叠组成的,为什么上图中NiN网络里每个最大池化层都是一样的,而每个NiN块都长得不一样?NiN块中第一层的卷积窗口形状通常是由用户设置,随后的卷积窗口形状固定为1 * 1,这里应该是受到AlexNet的启发,使用了窗口形状为11 * 11、5 * 5、3 * 3的卷积层,输出通道数与AlexNet中相同)
- 第一个NiN块的第一个卷积层的步幅为什么是4?通过长为4的步幅的卷积操作使得输出的尺寸大幅度减小
- NiN完全取消了全连接层,这样做的好处是减少了模型所需参数的数量,但是在实践中,这种设计有时会增加模型的训练时间
总结
- NiN的核心思想是NiN块,NiN块是一个卷积层加上两个1 * 1的卷积层作为全连接层来使用(也就是说对每个像素增加了非线性,或者说对每个像素的通道数做了全连接层,而且是非线性的,因为有两层)
- NiN使用全局平均池化层(通道数量为所需的输出数量)来替代VGG和AlexNet中的全连接层(全局全连接层因为比较大,所以也不需要学一个比较大的全连接层),这样做的好处是不容易过拟合,显著减少网络参数的数量
- 整体来讲,NiN的架构比较简单,就是NiN块和最大池化层的重复堆叠,直到最后的全局平均池化层,因为完全放弃了全连接层,所以它的参数个数大大减少了,使得参数的总量与类别数的多少无关
- NiN的设计影响了许多后续卷积神经网络的设计
Q&A
- 1、一个超级宽的单隐藏层MLP难以训练是因为显存不够大吗?QA P3 - 00:07
- 2、Pytorch跑起来是不是就要占用4G左右的显存,为什么我6G的显存VGG跑起来刚好跑125个batchsize?QA P3 - 00:55
- 3、请问Pytorch或者MXNet训练的模型上线,一般采用什么部署到生产环境,全部用C++重写吗,还是采用tensorflow serving的解决方案呢?QA P3 - 05:39
- 4、这里做分类不用softmax吗?QA P3 - 08:00
- 5、adaptive average pooling相对于softmax有什么优势吗?QA P3 - 08:42
- 6、全局的平均池化层在设计上是不是很关键,所带来的影响有哪些?QA P3 - 10:08
每次卷积的后面都能够加全局最大池化层,它唯一的作用是将卷积输出feature的高宽压缩成为1 * 1,通道数不变,它的主要效果是将输入变小了,而且它没有可学习的参数。加入全局池化层可以让计算变得更简单,但是它最主要的好处是降低了模型的复杂度,加入全局平均池化层同时也会提升模型的泛化性能,提高模型的精度,坏处就是模型的收敛变慢了(绝大部分情况下在考虑模型时,模型的精度优先于模型的收敛速度)
- 7、为什么NiN块中使用2个1 *1的卷积层,而不是1个或者3个,是由于参数数量的原因吗?QA P3 - 12:47
- 8、请再解释一下两个1 *1的卷积层对每个像素增加了非线性性是什么意思?QA P3 - 13:27
这意味着对每个像素的输入通道数做了一个有两个隐含层的MLP,MLP中有ReLu函数,因此不管是1层还是2层都是增加了非线性性
- 9、可以演示一下预测代码吗,我用了几次都有问题QA P3 - 14:27
- 10、Pytorch构建的网络权重是自动初始化的吗?QA P3 - 16:08
----end----
其他参考:
1、《动手学深度学习》课程安排,https://courses.d2l.ai/zh-v2/assets/pdfs/part-1_10.pdf
2、《动手学深度学习》,https://zh-v2.d2l.ai/chapter_convolutional-modern/nin.html