Mxnet练习: ResNet网络
程序员文章站
2024-03-14 20:46:35
...
**由于新模型在添加层后可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。然而在实践中,添加过多的层后训练误差往往不降反升。 针对这⼀问题,何恺明等⼈提出了残差⽹络(ResNet)**
残差块:
左图虚线框中的部分需要直接拟合出该映射f(x),而右图
虚线框中的部分则需要拟合出有关恒等映射的残差映射f(x) − x。
相对左图的优点:
残差映射在实际中往往更容易优化;
当理想映射f(x)极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动;
在残差块中,输⼊可通过跨层的数据线路更快地向前传播。
ResNet沿⽤了VGG全3 × 3卷积层的设计。残差块⾥⾸先有2个有相同输出通道数的3 × 3卷积层。每个卷积层后接⼀个批量归⼀化层和ReLU**函数。
残差块的实现:
class Residual(nn.Block):
def __init__(self,num_channels,use_1x1conv=False,strides=1,**kwargs):
super(Residual,self).__init__(**kwargs)
self.conv1 = nn.Conv2D(num_channels,kernel_size=3,padding=1,
strides=strides)
self.conv2 = nn.Conv2D(num_channels,kernel_size=3,padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2D(num_channels,kernel_size=1,strides=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm()
self.bn2 = nn.BatchNorm()
def forward(self,X):
Y = nd.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
return nd.relu(Y+X)
ResNet的前两层跟之前介绍的GoogLeNet中的⼀样:在输出通道数为64、步幅为2的7 × 7卷积层后接步幅为2的3 × 3的最⼤池化层。不同之处在于ResNet每个卷积层后增加的批量归⼀化层。
net = nn.Sequential()
net.add(nn.Conv2D(64,kernel_size=7,strides=2,padding=3),
nn.BatchNorm(),nn.Activation('relu'),
nn.MaxPool2D(pool_size=3,strides=2,padding=1))
GoogLeNet在后⾯接了4个由Inception块组成的模块。 ResNet则使⽤4个由残差块组成的模块,每个模块使⽤若⼲个同样输出通道数的残差块。第⼀个模块的通道数同输⼊通道数⼀致。由于之前已经使⽤了步幅为2的最⼤池化层,所以⽆须减小⾼和宽。之后的每个模块在第⼀个残差块⾥将上⼀个模块的通道数翻倍,并将⾼和宽减半。
#ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块
def resnet_block(num_channels,num_residuals,first_block=False):
blk = nn.Sequential()
for i in range(num_residuals):#num_residual个残差块
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
#4个代码块,每个代码块2个残差块
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))
每个模块⾥有4个卷积层(不计算1 × 1卷积层),加上最开始的卷积层和最后的全连接层,共计18层。这个模型通常也被称为ResNet-18。