AI-7 现代卷积神经网络
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
7.1. 深度卷积神经网络(AlexNet)
更多的数据和更高性能的硬件使得深度卷积神经网络在2012年出现突破。
2012年,AlexNet横空出世,首次证明了学习到的特征可以超越手工设计的特征。
AlexNet和LeNet的设计理念非常相似,但也存在显著差异:
结构上的区别:更胖,更深AlexNet比相对较小的LeNet5要深得多。AlexNet由八层组成:五个卷积层、两个全连接隐藏层和一个全连接输出层。
细节上的改进:1AlexNet使用ReLU而不是sigmoid作为其激活函数(ReLU激活函数在正区间的梯度总是1)
2添加了丢弃法,而非单独使用权重衰减
3进行了数据增强
在参数个数以及运算量上确实有了很大的增加。
练习
-
试着增加迭代轮数。对比LeNet的结果有什么不同?为什么?
迭代次数增加到40,精度有所增加。
-
AlexNet对Fashion-MNIST数据集来说可能太复杂了。
-
尝试简化模型以加快训练速度,同时确保准确性不会显著下降。
-
设计一个更好的模型,可以直接在28×28图像上工作。
-
对照组-左1
net = nn.Sequential(
# 这里使用一个11*11的更大窗口来捕捉对象。
# 同时,步幅为4,以减少输出的高度和宽度。
# 另外,输出通道的数目远大于LeNet
nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 使用三个连续的卷积层和较小的卷积窗口。
# 除了最后的卷积层,输出通道的数量进一步增加。
# 在前两个卷积层之后,汇聚层不用于减少输入的高度和宽度
nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Flatten(),
# 这里,全连接层的输出数量是LeNet中的好几倍。使用dropout层来减轻过拟合
nn.Linear(6400, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
# 最后是输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000
nn.Linear(4096, 10))
实验组右1-减少了一些层
net = nn.Sequential(
# 这里使用一个11*11的更大窗口来捕捉对象。
# 同时,步幅为4,以减少输出的高度和宽度。
# 另外,输出通道的数目远大于LeNet
nn.Conv2d(1, 96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 减小卷积窗口,使用填充为2来使得输入与输出的高和宽一致,且增大输出通道数
nn.Conv2d(96, 256, kernel_size=5, padding=2), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
# 使用三个连续的卷积层和较小的卷积窗口。
# 除了最后的卷积层,输出通道的数量进一步增加。
# 在前两个卷积层之后,汇聚层不用于减少输入的高度和宽度
nn.Conv2d(256, 256, kernel_size=3, padding=1), nn.ReLU(),
#nn.Conv2d(384, 384, kernel_size=3, padding=1), nn.ReLU(),
#nn.Conv2d(384, 256, kernel_size=3, padding=1), nn.ReLU(),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Flatten(),
# 这里,全连接层的输出数量是LeNet中的好几倍。使用dropout层来减轻过拟合
nn.Linear(6400, 4096), nn.ReLU(),
nn.Dropout(p=0.5),
#nn.Linear(4096, 4096), nn.ReLU(),
#nn.Dropout(p=0.5),
# 最后是输出层。由于这里使用Fashion-MNIST,所以用类别数为10,而非论文中的1000
nn.Linear(4096, 10))
看看效果如何? 诶,模型精简之后精度反而提升了!
修改了输入图像适合28*28
-
修改批量大小,并观察模型精度和GPU显存变化。
batchsize=128
batchsize=192
batchsize=256
批量增大,效果没变好?
-
分析了AlexNet的计算性能。
-
在AlexNet中主要是哪部分占用显存?
-
答:全连接层的参数占用大量内存
-
-
在AlexNet中主要是哪部分需要更多的计算?
-
答:卷积层需要进行大量的计算
-
-
计算结果时显存带宽如何?似乎够用
-
-
将dropout和ReLU应用于LeNet-5,效果有提升吗?再试试预处理会怎么样?
有提升
7.2. 使用块的网络(VGG)
AlexNet的形状不是很规则,不好迁移,而且最后的全连接层开销比较大。
VGG-11使用可复用的卷积块构造网络。不同的VGG模型可通过每个块中卷积层数量和输出通道数量的差异来定义。在VGG论文中,Simonyan和Ziserman尝试了各种架构。
特别是他们发现深层且窄的卷积(即3×3)比较浅层且宽的卷积更有效。 为啥深层且窄的卷积更有效嘞。
经验发现3x3 的卷积层 和 2x2的池化层 效果更好
练习:
-
打印层的尺寸时,我们只看到8个结果,而不是11个结果。剩余的3层信息去哪了?
conv_arch = ((1, 64), (1, 128), (2, 256), (2, 512), (2, 512))
答:因为后三个VGG块中,其实分别有尺寸相同两个卷积层。
-
与AlexNet相比,VGG的计算要慢得多,而且它还需要更多的显存。分析出现这种情况的原因。
因为VGG中有更多的卷积层,因此需要更多的计算和显存。
-
尝试将Fashion-MNIST数据集图像的高度和宽度从224改为96。这对实验有什么影响?
224
我遇到的问题是,在最后一个池化层前,图像就已经被我收束到1x1了。。。
7.3. 网络中的网络(NiN)
现在NiN虽然被用的不多,但是其设计理念非常有价值。
LeNet、AlexNet和VGG都有一个共同的设计模式:通过一系列的卷积层与汇聚层来提取空间结构特征;然后通过全连接层对特征的表征进行处理。 AlexNet和VGG对LeNet的改进主要在于如何扩大和加深这两个模块。 或者,可以想象在这个过程的早期使用全连接层。然而,如果使用了全连接层,可能会完全放弃表征的空间结构。 网络中的网络(NiN)提供了一个非常简单的解决方案:在每个像素的通道上分别使用多层感知机 (Lin et al., 2013。
NiN和AlexNet之间的一个显著区别是NiN完全取消了全连接层。 相反,NiN使用一个NiN块,其输出通道数等于标签类别的数量。最后放一个全局平均汇聚层(global average pooling layer),生成一个对数几率 (logits)。NiN设计的一个优点是,它显著减少了参数量,但是有时会增加训练模型的时间。
练习:
1调整NiN的超参数,以提高分类准确性。
lr, num_epochs, batch_size = 0.1, 10, 128 #这是原来的参数
lr, num_epochs, batch_size = 0.1, 20, 256 #调整批量大小和训练周期后,精度反而下降了
2为什么NiN块中有两个1×1卷积层?删除其中一个,然后观察和分析实验现象。
对照组(两个1x1卷积层) 实验组 (只保留一个)
很奇怪,效果反而变好了.....
3计算NiN的资源使用情况。
-
-
参数的数量是多少?
-
计算量是多少?
-
训练期间需要多少显存?
-
预测期间需要多少显存?
-
由于使用1X1卷积代替全连接,减少了参数量,降低了模型复杂度。
4一次性直接将384×5×5的表示缩减为10×5×5的表示,会存在哪些问题?
输出通道数减少,会导致显存占用量减小。但也会使得模型精度有所下降。
7.4. 含并行连结的网络(GoogLeNet)
在GoogLeNet中,基本的卷积块被称为Inception块(Inception block),Inception块由四条并行路径组成。相当于每条路径使用。 前三条路径使用窗口大小为1×1、3×3和5×5的卷积层,从不同空间大小中提取信息。每条线路的输出在通道维度上连结,并构成Inception块的输出。
至于为什么最后那样联结,估计是多次调试之后的经验结论。
练习:
-
GoogLeNet有一些后续版本。尝试实现并运行它们,然后观察实验结果。这些后续版本包括:
-
添加批量规范化层 (Ioffe and Szegedy, 2015)(batch normalization),在 7.5节中将介绍;
-
对Inception模块进行调整 (Szegedy et al., 2016);
-
使用标签平滑(label smoothing)进行模型正则化 (Szegedy et al., 2016);
-
加入残差连接 (Szegedy et al., 2017)。( 7.6节将介绍)。
-
-
使用GoogLeNet的最小图像大小是多少?
-
将AlexNet、VGG和NiN的模型参数大小与GoogLeNet进行比较。后两个网络架构是如何显著减少模型参数大小的?
7.5. 批量规范化
批量规范化(batch normalization) (Ioffe and Szegedy, 2015),这是一种流行且有效的技术,可持续加速深层网络的收敛速度。 再结合在 7.6节中将介绍的残差块,批量规范化使得研究人员能够训练100层以上的网络。
原因:
1应用多层感知机来预测房价的例子( 4.10节)。 使用真实数据时,我们的第一步是标准化输入特征,使其平均值为0,方差为1。 直观地说,这种标准化可以很好地与我们的优化器配合使用,因为它可以将参数的量级进行统一。
2对于典型的多层感知机或卷积神经网络,训练时中间层中的变量可能具有更广的变化范围:不论是沿着从输入到输出的层,跨同一层中的单元,或是随着时间的推移,模型参数的随着训练更新变幻莫测。 批量规范化的发明者非正式地假设,这些变量分布中的这种偏移可能会阻碍网络的收敛。
3第三,更深层的网络很复杂,容易过拟合。 这意味着正则化变得更加重要。
我们在下面讨论这两种情况:全连接层和卷积层,他们的批量规范化实现略有不同:
全连接层:通常,我们将批量规范化层置于全连接层中的仿射变换和激活函数之间。
卷积层:对于卷积层,我们可以在卷积层之后和非线性激活函数之前应用批量规范化。
和暂退法一样,批量规范化层在训练模式和预测模式下的计算结果也是不一样的。
练习:
1在使用批量规范化之前,我们是否可以从全连接层或卷积层中删除偏置参数?为什么?
规范化之前不能,因为偏置参数可以帮助模型更好的学习数据。使用批量规范化后可以,因为批量规范的过程就已经有偏置的环节。
2比较LeNet在使用和不使用批量规范化情况下的学习率。
2.1绘制训练和测试准确度的提高。2.2学习率有多高?
使用批量规范化 不使用规范化
结果发现,使用批量规范化,比不适用精度会高一些,至于学习率只能说比较平稳的
3我们是否需要在每个层中进行批量规范化?
输出层中不需要做BN,因为softmax会选择最大的一个值作为分类结果,做不做BN都没有影响。
4可以通过批量规范化来替换暂退法吗?行为会如何改变?
批量规范化关注控制过程变量的大小来实现正则化;暂退法是通过在数据中增加噪声来实现正则化,实现手段不完全相同。
5确定参数beta
和gamma
,并观察和分析结果。
6查看高级API中有关BatchNorm
的在线文档,以查看其他批量规范化的应用。
7研究思路:可以应用的其他“规范化”转换?可以应用概率积分变换吗?全秩协方差估计可以么?
差不多学会了再来研究吧..
7.6. 残差网络(ResNet)
如果我们能将新添加的层训练成恒等映射(identity function)f(x)=x,新模型和原模型将同样有效。 同时,由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。大概是通过扩大了函数类的容量,从而使其更可能接近最优解。
针对这一问题,何恺明等人提出了残差网络(ResNet) (He et al., 2016)。
练习:
1图7.4.1中的Inception块与残差块之间的主要区别是什么?在删除了Inception块中的一些路径之后,它们是如何相互关联的?
inception块有四个路径,而残差块只有两个路径。且inception是将不同路径的通道数连接,残差块则是加到一起。
2参考ResNet论文 (He et al., 2016)中的表1,以实现不同的变体。
略
3对于更深层次的网络,ResNet引入了“bottleneck”架构来降低模型复杂性。请试着去实现它。
使用1x1卷积来减少计算量
改
class bottleNeck(nn.Module):
def __init__(self, input_channels, num_channels,
use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = nn.Conv2d(input_channels, num_channels,
kernel_size=1, stride=strides)
self.bn1 = nn.BatchNorm2d(num_channels)
self.comv2 = nn.Conv2d(input_channels, num_channels,
kernel_size=3,padding = 1, stride=strides)
self.bn2 = nn.BatchNorm2d(num_channels)
self.conv3 = nn.Conv2d(input_channels, num_channels,
kernel_size=1, stride=strides)
self.bn3 = nn.BatchNorm2d(num_channels)
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
Y = Y.relu(Y)
Y = conv3(Y)
return Y
4在ResNet的后续版本中,作者将“卷积层、批量规范化层和激活层”架构更改为“批量规范化层、激活层和卷积层”架构。请尝试做这个改进。详见 (He et al., 2016)中的图1。
参考7.7blcok1
5为什么即使函数类是嵌套的,我们仍然要限制增加函数的复杂性呢?
答:第一是因为复杂的函数会增加拟合的计算要求,第二是增加了过拟合的风险
7.7. 稠密连接网络(DenseNet)
借助泰勒展开,稠密层将函数展开为多项。在跨层连接上,不同于ResNet中将输入与输出相加,稠密连接网络(DenseNet)在通道维上连结输入与输出。DenseNet的主要构建模块是稠密块和过渡层。
在稠密快中,使用了ResNet改良版的“批量规范化、激活和卷积”架构。在前向传播中,我们将每个卷积块的输入和输出在通道维上连结。
而过渡层可以用来控制模型复杂度。 它通过1×1卷积层来减小通道数,并使用步幅为2的平均汇聚层减半高和宽,从而进一步降低模型复杂度。
练习:
1为什么我们在过渡层使用平均汇聚层而不是最大汇聚层?
因为过度层前输入的信息都应该是对结果有贡献的,过渡层只是为了降低模型复杂度,但不应放弃某些特征。
2DenseNet的优点之一是其模型参数比ResNet小。为什么呢?
DenseNet比传统的卷积网络所需要的参数更少,因此密集连接带来了特征重用,不需要重新学习冗余的特征图,而且维度拼接的操作,带来了丰富的特征信息,利用更少的卷积就能获得很多的特征图。
3DenseNet一个诟病的问题是内存或显存消耗过多。
3.1真的是这样吗?可以把输入形状换成224×224,来看看实际的显存消耗。3.2有另一种方法来减少显存消耗吗?需要改变框架么?
似乎并非如此,随着输入的增大,显存并没有出现很大改变。