代码地址
https://github.com/xmy0916/DLNetwork

简介

Resnet在2015年由微软实验室提出,获得当年ImageNet竞赛中分类任务的第一名,目标检测第一名。获得coco数据集中目标检测第一名,图像分割第一名。作者Kaiming He、Xiangyu Zhang、Shaoqing Ren、Jian Sun等大佬,经常在很有名的深度学习文章中出现~并且ResNet网络结构是当前最主流的主干网络,被用于各种任务场景中。

重点

1、超深的网络结构,突破了1000层。
2、提出了残差结构。
3、使用了batch normalization加速训练,丢弃了dropout。

残差结构


左边的结构用于resnet网络层数较少的情况,比如resnet34及以下。右边的结构用于网络层数比较深的情况比如resnet50及以上。根据之前分析的GoogLeNet中大量使用1 * 1的卷积核的原因,右边的结构1 * 1的卷积核可以用来减少参数,并且控制残差块输入输出的维度相同。
对于残差结构为什么要输入输出维度以及尺寸相同,因为根据残差块的结构,右边的弧线将输入特征图和最后从残差块中得到的特征图做了一个矩阵相加,因此为了能够相加必须控制两个特征图的shape相同。
这里需要注意一点图示Relu的位置,在主线中Relu都是接在卷积层之后,在最后一个卷积核Relu是接在主分支与shortcut分支特征图相加了之后的

resnet的完整网络结构就是由这些残差块构成,根据层数的不同命名为resnet34、resnet50、resnet101等等,结构如下:

可以看到resnet网络都是先由一个size为7的卷积接一个size为3的最大池化然后接一系列的残差块最后接一个平均池化和全连接以及sofmax层构成。

这里举例一个简单的例子:

假设该残差块的输入的shape是[56,56,64],根据计算公式out = (in - F + 2P) / S + 1
所以在主分支:
out1 = (56 - 3 + 2 * 1 )/ 2 + 1 = 28(向下取整)
out2 = (28 - 3 + 2 * 1)/1 + 1 = 28(向下取整)
在shortcut分支:
out = (56 - 1 + 2 * 0) / 2 + 1= 28(向下取整)
最后得到的shape相同可以相加。
这里看一个完整的resnet34的结构:

根据resnet结构的表格,我们知道resnet34的残差块的数量为[3,4,6,3]所以看到resnet34的结构图中灰色绿色红色白色的残差块分别有[3,4,6,3]个:

然后观察仔细会发现有一些shortcut标注的是实线有一些是虚线,其实很简单,实线的残差结构输入输出的特征矩阵shape相同可以直接相加,而虚线的输入输出的特征矩阵shape不同不可以直接相加,因此在虚线结构上shortcut加上了一个1 * 1的卷积核来修改shape中的通道数相同,这也是前面提到的1 * 1的卷积核的作用之一,如下图所示:

BatchNormalization

这个方法是为了使我们一批的feature map满足均值为0,方差为1的分布规律。该方法可以加速网络的训练并且提高准确率。
截取一个简单的例子:

首先bn的公式:

图中的例子:
batch_size为2,通道数为2。
在做bn时:
对于通道1上所有的元素为:x1 = [1,1,1,2,0,-1,2,2]
对于通道2上所有的元素为:x2 = [-1,1,0,1,0,-1,3,1]
所以计算得到均值为u1 = sum(x1)/len(x1) = 1
所以计算得到均值为u2 = sum(x2)/len(x2) = 0.5
所以计算得到方差为p1 = sum((x1-u1)^2)/len(x1) = 1
所以计算得到方差为p2 = sum((x2-u2)^2)/len(x2) = 1.5
带入bn的公式即可,bn公式的分母加上了一个参数西格玛是为了分母不为0。

更多详细的解释可以移步:https://blog.csdn.net/qq_37541097/article/details/104434557

结构


代码

实现参考pytorch官方resnet代码:
https://github.com/pytorch/vision/tree/master/torchvision/models
本项目地址:
https://github.com/xmy0916/DLNetwork

项目简介

本项目全部在cifar10数据集上测试网络,使用torch vision中的cifar10数据集接口测试,不用自行下载数据集,该| |
数据集包含十类彩色数据飞机( airplane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。每张图片尺寸为32 * 32,对于pytorch官方实现的主干网络代码的预训练模型都是基于ImageNet数据集进行训练的,所以主干网络的代码最后的全连接层输出为1000分类,而我们需要修改成10分类。修改后加载官方预训练模型时需要将不匹配的参数给删除掉。
代码结构如下:

这里和LeNet的一样在cifar10上进行测试,首先采用两种策略进行测试:
1、 Training from scratch
2、 finetune on official pretrained model

策略一比较简单,基本不用修改代码结构,不多赘述,策略二进行了简单的修改:
首先在判断分类类别时如果不等于1000需要对torch.load加载的字典的值进行处理,就是把最后的全连接层的参数给删了赋值我们自己随机初始化的tensor即可,代码对应:

运行实验

训练

python3 train.py —model resnet

测试

python3 predict.py —model resnet

打印网络参数量

python3 params_print.py —model resnet

参数量对比:

vgg16:
Model Summary: 32 layers, 1.34302e+08 parameters, 1.34302e+08 gradients
googlenet:
Model Summary: 173 layers, 5.61015e+06 parameters, 5.61015e+06 gradients
resnet50:
Model Summary: 161 layers, 2.35285e+07 parameters, 2.35285e+07 gradients

实验结果

模型 LeNet AlexNet VggNet GoogLeNet ResNet
轮数 5 5 5 5 5
精度 0.622 0.800、0.860(加pretrain) 0.911(加pretrain) 0.932(加pretrain) 0.927(加pretrain)
日志 lenet.log alexnet.logalexnet_pretrin.log vgg16.log googlenet.log resnet.log