NiN
为什么要设置多通道?
既然卷积了,换句话就是缩小了图像,那为了保证图像精度最大程度上不丢失,就需要多个卷积核,这样就可以保证在降维的同时不失真。然后CNN的层数越多,越深,理论上整个网络中的参数也就越多,学习到原始图像的特征也就越全面。
所以,卷积的时候,通道数都是逐步递增的。也就是说,要把数据映射到更高维空间,在更高维空间里分类数据。至于为什么是一步步增加而不是突然增加到最高维,我觉得这有引入更多非线性的考虑。
全连接层的问题
收敛很快,但是容易产生过拟合
NiN用1*1卷积代替了全连接
原来的原连接是,把二维像素矩阵展开为一维,然后根据权重参数,进行mlp全连接。
1*1的卷积相当于原来的全连接层。类似于,对每一个像素去做全连接,按照输入像素逐一去做全连接层。 不改变输入输出形状,也不改变通道数
含并行连结的网络 GoogLeNet
2、3、4都有padding,所以输入和输出图像大小一样,如:都是28*28
通道数变化
第1条通道:经过1*1卷积后,192的输入通道数压到64输出通道数
第2条:为了让3*3的卷积的输入降低,来降低模型复杂度(卷积层可学习的参数个数是输入通道数*输出通道数*卷积核大小)192输入通道数,经过1*1conv之后,输出通道数降到为96,3*3的输入通道数为96,输出为128
第3条:因为要做5*5卷积,更贵、成本更大,所以要192输入之后,经过1*1conv之后,要降到16的输出。5*5的输入为16,输出通道数为32
第4条:经过maxpool之后输入通道数不改变,经过1*1conv之后,输出通道为32
至于,为什么卷积之后这个需要降到32不是64等之类的,都是调出来的。没有一个特定的原理。
1x1卷积最主要作用是融合不同通道的信息,降低通道数,减少卷积层的权重参数。
import d2l.torch
import torch
from torch import nn
from torch.nn import functional as F
class Inception(nn.Module):
#c1 - -c4是每条路径的输出通道数
def __init__(self,in_channels,c1,c2,c3,c4):
super(Inception, self).__init__()
# 线路1,单1x1卷积层
self.p1_1 = nn.Conv2d(in_channels=in_channels,out_channels=c1,kernel_size=1,stride=1,padding=0)
# 线路2,1x1卷积层后接3x3卷积层
self.p2_1 = nn.Conv2d(in_channels=in_channels,out_channels=c2[0],kernel_size=1,padding=0,stride=1)
self.p2_2 = nn.Conv2d(in_channels=c2[0],out_channels=c2[1],kernel_size=3,padding=1,stride=1)
# 线路3,1x1卷积层后接5x5卷积层
self.p3_1 = nn.Conv2d(in_channels=in_channels,out_channels=c3[0],kernel_size=1,padding=0,stride=1)
self.p3_2 = nn.Conv2d(in_channels=c3[0],out_channels=c3[1],kernel_size=5,padding=2,stride=1)
# 线路4,3x3最大汇聚层后接1x1卷积层,汇聚层主要作用是降低卷积层对位置的敏感性,1x1卷积层主要是融合不同通道的信息,降低通道数,减少卷积层的权重参数
self.p4_1 = nn.MaxPool2d(kernel_size=3,padding=1,stride=1)
self.p4_2 = nn.Conv2d(in_channels=in_channels,out_channels=c4,kernel_size=1,padding=0,stride=1)
def forward(self,X):
#使用torch.nn.functional中的relu()函数,而不是nn.ReLU()函数
#错误解决链接:https://discuss.pytorch.org/t/i-am-trying-to-build-a-simple-resnet-model-but-getting-an-error-conv2d-received-an-invalid-combination-of-arguments/134141
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)))
# 在通道维度上连结输出
return torch.cat((p1,p2,p3,p4),dim=1)
b1 = nn.Sequential(nn.Conv2d(in_channels=1,out_channels=64,kernel_size=7,padding=3,stride=2),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3,padding=1,stride=2))
b2 = nn.Sequential(nn.Conv2d(in_channels=64,out_channels=64,kernel_size=1,padding=0,stride=1),
nn.ReLU(),
nn.Conv2d(in_channels=64,out_channels=192,kernel_size=3,padding=1,stride=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=3,padding=1,stride=2))
b3 =
nn.Sequential(Inception(in_channels=192,c1=64,c2=(96,128),c3=(16,32),c4=32),
Inception(in_channels=256,c1=128,c2=(128,192),c3=(32,96),c4=64),
nn.MaxPool2d(kernel_size=3,padding=1,stride=2))
b4 =
nn.Sequential(Inception(in_channels=480,c1=192,c2=(96,208),c3=(16,48),c4=64),
Inception(in_channels=512,c1=160,c2=(112,224),c3=(24,64),c4=64),
Inception(in_channels=512,c1=128,c2=(128,256),c3=(24,64),c4=64),
Inception(in_channels=512,c1=112,c2=(144,288),c3=(32,64),c4=64),
Inception(in_channels=528,c1=256,c2=(160,320),c3=(32,128),c4=128),
nn.MaxPool2d(kernel_size=3,padding=1,stride=2))
b5 =
nn.Sequential(Inception(in_channels=832,c1=256,c2=(160,320),c3=(32,128),c4=128),
Inception(in_channels=832,c1=384,c2=(192,384),c3=(48,128),c4=128),
nn.AdaptiveAvgPool2d((1,1)),
nn.Flatten())
GoogLeNet模型的计算复杂,而且不如VGG那样便于修改通道数。 为了使Fashion-MNIST上的训练短小精悍,我们将输入的高和宽从224降到96,这简化了计算
GoogLeNet = nn.Sequential(b1,b2,b3,b4,b5,nn.Linear(1024,10))
#打印模型每一块的输出尺寸大小
X = torch.randn(size=(1,1,96,96))
for layer in GoogLeNet:
X = layer(X)
print(layer.__class__.__name__," output shape :\t",X.shape)
lr,num_epochs,batch_szie = 0.1,10,128
train_iter,test_iter = d2l.torch.load_data_fashion_mnist(batch_size=batch_szie,resize=96)
d2l.torch.train_ch6(GoogLeNet,train_iter,test_iter,num_epochs,lr,device=d2l.torch.try_gpu())
批量归一化
+ε是为了使结果大于0
u和σ是每一批小批量样本里算出来的均值和方差。
在深度神经网络的训练过程中,上一层参数的调整会导致之后每一层输入值的分布发生变化,这种现象使模型的训练变得很复杂。所以在深度神经网络模型的训练中,通常需要仔细选取初始参数并采取较小的学习率,这不但导致模型训练的效率低下,而且使得饱和非线性模型的训练极为困难。我们把这种现象称为内部协变量转移(covariate shift),并通过归一化(normalizing)每层的输入来解决这个问题。
神经网络的参数就是协变量,因为它是不可控的且在训练过程中是必须变化的,且对输出值会产生影响,所以当参数发生变化时,就发生了协变量转移。