0%

卷积神经网络

1、二维卷积层

二维卷积层:有高和宽两个空间维度用于处理图像数据,通常使用更直观的互相关运算来代替卷积运算。将输出与卷积核做互相关,并加上一个标量偏差来得到输出。

在训练模型时,通常先对卷积核初始化,然后不断迭代卷积核与偏差。使用corr2d函数来实现:

1
2
3
4
5
6
7
def corr2d(X,K):
h,w = K.shape
Y = nd.zeros((X.shape[0] - h + 1,X.shape[1] - w + 1))
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i,j] = (X[ i:i+h , j: j+w]*K).sum()
return Y

在构造函数中声明了weight和bias,并在forward中利用corr2d函数来实现卷积核。

1
2
3
4
5
6
7
def __init__(self,kernel_size,**kwargs):
super(Conv2D,self).__init__(**kwargs)
self.weight = self.params.get('weight',shape=kernel_size)
self.bias = self.params.get('bias',shape=(1,))

def forward(self,x):
return corr2d(x, self.weight.data()) + self.bias.data()

一般通过数据学习得到核数组:首先构造一个卷积层,将其卷积核初始化为随机数组,并在每一次迭代中,使用平方误差来比较输出Y和卷积层的输出,并计算梯度来更新权重。

由于核数组都是学习出来的,所以卷积层使用互相关运算还是卷积运算都不影响模型预测时的输出。

特征图:输出可看作输入在空间维度上的表征。

影响X的前向计算的所有可能输入区域,叫做X的感受野。

2、填充与步幅

填充:在输入高、宽的两侧填充元素;用于增加输出的高、宽。

步幅:卷积窗口从输入窗口左上方开始,依次滑动,每次滑行的行数和列数称为步幅,用于减少输出的高、宽。

3、多输入、多输出通道

1、多输入时,构造输入通道数与输入数据的通道数相同的卷积核,从而能够互相关运算,对每个通道做互相关,然后通过add_n函数来进行累加

1
2
3
def corr2d_multi_in(X,K):
#首先沿X和K的第0维(通道维)遍历,然后用*将结果列表变为add_n函数的位置参数(positional argument)来相加
return nd.add_n(*[d2l.corr2d(x,k) for x , k in zip(X,K)]13

2、卷积核输入、输出通道数为Ci、Co,高和宽分别为Kh和Kw。若希望有多输出时,为每个通道分别创建核数组Ci x Kh x Kw,并将其在输出通道维上连结,卷积核形状为C0 X Ci x Kh x Kw,互相关时每个输出通道结果由卷积核在该通道核数组与整个输入数组计算得来。

1
2
3
def corr2d_multi_in_out(X,K):
#对K的第0维遍历,每次同输入X做互相关运算,所有结果用stack函数合并在一起
return nd.stack(*[corr2d_multi_in(X,K) for k in k])

4、1 x 1 卷积层

1 X 1 卷积主要发生在通道维上,输入与输出具有相同的高宽,输出中每个元素来自输入中在高、宽上相同位置的元素在不同通道之间的按权重累加。将通道维作为特征维,将高、宽上的元素当成数据样本,则其作用与全连接层等价。

1 X 1卷积层通常用来调整网络层之间的通道数,并控制模型复杂度。

5、池化层

目的:为了缓解卷积层对位置的过度敏感性。

池化层:每次对输入数据的一个固定形状窗口中的元素计算输出,直接计算池化窗口内的元素最大值或平均值,也叫最大池化或者平均池化。p X q 池化层

同样池化层也能设置填充和步幅。

1
2
pool2d = nn.MxaPool2D(1, kernel_size=(5,3),padding=(2,1),strides=(3,4))
#使用高、宽分别为5,3的卷积核;在高宽上的填充数分别为2,1;高宽上步幅分别为3,4

在处理多通道输入数据时,池化层会对每个输入通道分别池化,而不是像卷积层那样将各通道输入按通道相加。因此,池化层的输出通道数与输入通道数相等。

6、卷积神经网络

单隐藏层的多层感知机分类图像:将图像像素逐行展开,得到长为28*28=784的向量,并输入进全连接层中。局限性:1、同一列像素相隔远;2、对于大尺寸图像,全连接层可能导致模型过大。

卷积层如何解决:1、保留输入形状,使像素在高、宽两个方向的相关性均能被有效识别;2、通过滑动窗口将同一卷积核与不同位置的输入重复计算,从而避免参数尺寸过大。

LeNet模型

分为卷积层块与全连接层块。

卷积层基本单位:卷积层+最大池化层。卷积层用于识别图像中空间模式,最大池化层用于降低卷积层对位置的敏感性。

卷积层输出形状为(批量大小,通道,高,宽);当其输出传入全连接层块时,全连接层会将小批量中的每个样本变平。形状将改变为二维,第一维是小批量中的样本,第二维是每个样本变平后的向量表示,且向量长度为通道*高 *宽。

AlexNet模型

1、包含8层变换,其中有5层卷积和2层全连接隐藏层,全连接输出个数为4096;

2、将激活函数由sigmoid改成了更简单的Relu,在不同参数初始化方法下使模型更容易训练,且在正区间的梯度恒为1;

3、利用丢弃法来控制全连接层的模型复杂度;

4、引入大量的图像增广,从而进一步扩充数据集来缓解过拟合;

5、利用了更多的卷积层和更大的参数空间来拟合大规模数据集ImageNet,是浅层网络与深度神经网络的分界线。

VGG模型

VGG提出了可以通过重复使用简单的基础块来构建深度模型的思路;其组成规律是:连续使用数个相同的填充为1、窗口形状为3X3的卷积层后接上一个步幅为2、窗口形状为2X2的最大池化层。卷积层保持输入的高、宽不变,而池化层则对其减半。使用vgg_block函数来实现基础的VGG卷积层模块,可指定卷积层数量num_convs和输出通道数num_channels。

1
2
3
4
5
6
def vgg_block(num_convs, num_channels):
blk = nn.Sequential()
for _ in range(num_convs):
blk.add(nn.Conv2D(num_channels, kernel_size=3,padding=1,activation='relu'))
blk.add(nn.MaxPool2D(pool_size=2, strides=2))
return blk

VGG由卷积模块后接全连接层模块构成,卷积层串联数个vgg_block,其超参数由变量conv_arch定义,指定了VGG块中卷积层个数与输出通道数。下面构造VGG,5个卷积块,前2块用单卷积层,后3块用双卷积层。第一块输出通道为64,之后每次输出通道数翻倍,直至512。总共8个卷积,3个全连接,因此称为VGG-11。

1
2
3
4
5
6
7
8
9
10
11
12
conv_arch = ((1,64),(1,128),(2,256),(2,512),(2,512))

def vgg(conv_arch):
net = nn.Sequential()
#卷积层部分
for (num_convs, num_channels) in conv_arch:
net.add(vgg_block(num_convs, num_channels))
#全连接层部分
net.add(nn.Dense(4096, activation='relu'), nn.Dropout(0.5),
nn.Dense(4096, activation='relu'), nn.Dropout(0.5),
nn.Dense(10))
return net

NIN模型

上述模型共通处均是:先以卷积层构成的模块充分抽取空间特征,再以全连接层构成的模块来输出分类结果。

NIN则提出了另外一个思路:串联多个由卷积层和全连接层构成的小网络来构建深层网络。

卷积层通常输入、输出:四维数组(样本、通道、高、宽);全连接层输入输出:二维数组(样本、特征)。故利用1 X 1卷积层来代替全连接层,从而使空间信息自然传递至后面层。

NIN块:由一个卷积层和两个充当全连接层的1 X 1卷积层串联而来,可自由设置第一个卷积层超参数。

NIN

NIN设计:除了使用NIN块以外,NIN去掉了AlexNet最后的3个全连接层,并使用输出通道数等于标签类别数的NIN块,然后使用全局平均池化层对每个通道中所有元素求平均,并直接进行分类。NIN这个设计的好处:显著减少模型参数尺寸,从而缓解过拟合。

GoogLeNet模型

GoolLeNet中的基础卷积块为Inception块,有4条并行的线路,前3条线路使用窗口分别为1X1,3X3,5X5的卷积层来抽取不同的空间尺寸下的信息,且中间2条线路会对输入先做1X1卷积来减少输入通道数,以降低模型复杂度;第四条线路则用3X3池化层后接1X1卷积层来改变通道数;且均使用了合适的填充来使输入、输出高宽一致。

GooLeNet

GooLeNet跟VGG一样,在主体卷积部分使用5个模块,每个模块之前使用步幅为2的3 X 3最大池化层来减小输出高宽。

模块一:使用一个64通道的7X7卷积层;

模块二:使用2个卷积层,64通道的1X1卷积层,然后是将通道增大3倍的3X3卷积层,对应Inception的线路二;

模块三:串联2个完整的Inception块;

模块四:串联5个Inception块;

模块五:2个Inception块,其后紧跟输出层,故同NIN一样使用全局平均池化层来将每个通道的高、宽变成1

7、批量归一化

数据标准化处理:任一特征在数据集所有样本上的均值为0、标准差为1,可以使各个特征的分布相近。对于浅层模型,数据标准化预处理已经足够了,但对于深层网络,模型参数更新仍容易造成剧烈变化。

批量归一化层(batch normalization):为应对深层模型的挑战,在训练时,利用小批量上的均值和标准差,不断调整神经网络中间输出,从而使整个神经网络在各层的中间输出数值更稳定。

全连接层的批量归一化

对于全连接层:将批量归一化层放在全连接层的仿射交换与激活函数之间。

对卷积层做批量归一化

对于卷积层:批量归一化发生在卷积计算之后、应用激活函数之前。

8、残差网络ResNet

实践中,添加过多层后训练误差往往不降反升,即使利用批量归一化使训练深层模型更加容易,该问题仍存在。

残差块:当理想映射f(x)极接近恒等映射时,残差映射也易于捕捉恒等映射的细微波动。

ResNet块

ResNet沿用了VGG全3X3卷积层的设计,残差块中首先由2个相同输出通道数的3X3卷积层,每个卷积层之后接一个批量归一化层和RELU激活函数,然后将输入跳过这2个卷积运算后再加在最后的RELU激活函数前。这样设计要求2个卷积层的输入、输出形状一样,从而可以相加。

ResNet的前两层跟GoogLeNet一样,在输出通道为64、步幅为2的7X7卷积层后接步幅为2的3X3最大池化层。

1
2
3
4
net = nn.Sequential()
net.add(nn.Conv2D(64,kernel_size=7,strides=2,padding=3),
nn.BatchNoem(),nn.Activation('relu'),
nn.MaxPool2D(pool_size=3,strides=2,padding=1))

不同在于其每个卷积层后增加的批量归一层,GoogLeNet在后面会接4个Inception组成的模块,而ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。我们用resnet_block函数来实现:

1
2
3
4
5
6
7
8
def resnet_block(num_channels, num_residuals, first_block=False):
blk = nn.Sequential()
for i in range(num_residuals):
if i == 0 and not first_block:
blk.add(Residual(num_channels, use_1x1conv=True, strides=2))
else:
blk.add(Residual(num_channels))
return blk

接着使用ResNet加入所有残差块,这里每个模块使用2个残差块

1
2
3
4
5
6
7
net.add(resnet_block(64, 2, first_block=True),
resnet_block(128,2),
resnet_block(256,2),
resnet_block(512,2))

net.add(nn.GlobalAvgPool2D(), nn.Dense(10))
#加入全局平均池化层后接上全连接层输出

9、稠密连接网络DenseNet

DenseNet

DenseNet主要构建模块时稠密块和过渡层,前者定义了输入、输出是如何连结的,后者则用来控制通道数,使之不过大。

由于每个稠密块都会带来通道数的增加,使用过多则会带来过于复杂的模型,过度层用来控制模型复杂度。它通过1X1卷积层来减少通道数,并使用步幅为2的的平均池化层减半高、宽,从而进一步降低模型复杂度。

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!