深度学习之图像分类(七)ResNet 网络结构

目录

  • 深度学习之图像分类(七)ResNet 网络结构
    • 1. 前言
    • 2. Residual
    • 3. 网络配置
    • 4. 代码

本节学习ResNet网络结构,以及迁移学习入门,学习视频源于 Bilibili,感谢霹雳吧啦Wz,建议大家去看视频学习哦。

1. 前言

ResNet 是在 2015 年由微软实验室提出来的,斩获当年 ImageNet 竞赛中分类任务第一名,目标检测任务第一名,获得 COCO 数据集中目标检测第一名,图像分割第一名。总结而言就是 NB!更 NB 的是,这是中国人提出来的!原始论文为:Deep Residual Learning for Image Recognition。

网络越深,咱们能获取的信息越多,而且特征也越丰富。按理说,网络越来越深,至少能达到浅网络的性能,为什么呢?因为后面全部学习恒等映射就可以了呀。网络加深,不是想加就加的;恒等映射,不是说学就能学的。

但是根据实验表明,随着网络的加深,优化效果反而越差,测试数据和训练数据的准确率反而降低了。**这是由于网络的加深会造成梯度爆炸和梯度消失的问题。**目前针对这种现象已经有了解决的方法:通常是通过对数据进行标准化处理,权重初始化,以及 Batch Normalization 来处理的,这种方法可以保证网络在反向传播中采用随机梯度下降(SGD),从而让网络达到收敛。但是,这个方法仅对几十层的网络有用,当网络再往深处走的时候,这种方法就无用武之地了。并且退化问题(degradation problem,层数深的效果没有浅的好,下图左边部分 )严重。下图中实线是验证集,虚线是训练集。右图中 ResNet 层数越深,错误率越低,确实解决了退化问题。

网络亮点在于:

  • 超级深的网络结构(突破了 1000 层)
  • 提出 Residual 模块(搭建深网络的基础,学恒等不如学习0变换)
  • 使用 Batch Normalization 加速训练,丢弃了 Dropout

ResNet34 网络结构如下所示(conv_3, conv_4, conv_5 的第一个残差结构,虚线的残差结构需要注意):

2. Residual

Residual 可以把它理解为一个子网络,这个子网络经过堆叠可以构成一个很深的网络。下图给出了两种残差结构。左边的主要针对网络层数较少的网络;右边主要针对网络层数较多的网络。左边结构中,主线是将输入特征矩阵经过两个 3×33 \times 33×3 的卷积层,右边有一个弧线直接从输入连接到输出,与卷积后的特征矩阵按元素相加得到最终特征矩阵结果。右边的结构的主分支则是在输入与输出都加上了 1×11 \times 11×1 的卷积层,用来实现降维和升维。右边的参数节省了 94%。

注意:加了之后再经过 ReLU 激活函数。

注意:主分支与 shortcut 分支的输出特征矩阵的 shape 必须一致才能进行元素加法。回顾,GoogleNet 是在深度方向进行拼接。

a[l+2]=Relu(W[l+2](Relu(W[l+1]a[l]+b[l+1]))+b[l+2]+a[l])a^{[l+2]} = Relu(W^{[l+2]}(Relu(W^{[l+1]}a^{[l]} + b^{[l+1]})) + b^{[l+2]} + a^{[l]}) a[l+2]=Relu(W[l+2](Relu(W[l+1]a[l]+b[l+1]))+b[l+2]+a[l])
其中 bbb 为卷积层偏置 bias。

如何理解 Residual 呢?

假设我们要求解的映射为:H(x)H(x)H(x)。现在咱们将这个问题转换为求解网络的残差映射函数,也就是 F(x)F(x)F(x),其中 F(x)=H(x)−xF(x) = H(x)-xF(x)=H(x)−x。残差即观测值与估计值之间的差。这里 H(x)H(x)H(x) 就是观测值,xxx 就是估计值(也就是上一层Residual 输出的特征映射)。我们一般称 xxx 为 Identity Function(恒等变换),它是一个跳跃连接;称 F(x)F(x)F(x) 为 Residual Function。

那么我们要求解的问题变成了 H(x)=F(x)+xH(x) = F(x)+xH(x)=F(x)+x。有小伙伴可能会疑惑,我们干嘛非要经过 F(x)F(x)F(x) 之后在求解 H(x)H(x)H(x) 啊!整这么麻烦干嘛,直接搞不好吗,神经网络那么强(三层全连接可以拟合任何函数)!我们来分析分析:如果是采用一般的卷积神经网络的话,原先我们要求解的是 H(x)=F(x)H(x) = F(x)H(x)=F(x) 这个值对不?那么,我们现在假设,在我的网络达到某一个深度的时候,咱们的网络已经达到最优状态了,也就是说,此时的错误率是最低的时候,再往下加深网络的化就会出现退化问题(错误率上升的问题)。我们现在要更新下一层网络的权值就会变得很麻烦,权值得是一个让下一层网络同样也是最优状态才行,对吧?我们假设输入输出特征尺寸不变,那么下一层最优的状态就是学习一个恒等映射,不改变输入特征是最好的,这样后续的计算会保持,错误了就和浅一层一样了。但是这是很难的,试想一下,给你个 3×33 \times 33×3 卷积,数学上推导出来恒等映射的卷积核参数有一个,那就是中间为 1,其余为 0。但是它不是想学就能学出来的,特别是初始化权重离得远时。

但是采用残差网络就能很好的解决这个问题。还是假设当前网络的深度能够使得错误率最低,如果继续增加咱们的 ResNet,为了保证下一层的网络状态仍然是最优状态,咱们只需要把令 F(x)=0F(x)=0F(x)=0 就好啦!因为 xxx 是当前输出的最优解,为了让它成为下一层的最优解也就是希望咱们的输出 H(x)=xH(x)=xH(x)=x 的话,是不是只要让 F(x)=0F(x)=0F(x)=0 就行了?这个太方便了,只要卷积核参数都足够小,乘法加法之后就是 0 呀。当然上面提到的只是理想情况,咱们在真实测试的时候 xxx 肯定是很难达到最优的,但是总会有那么一个时刻它能够无限接近最优解。采用 Residual 的话,也只用小小的更新 F(x)F(x)F(x) 部分的权重值就行啦!不用像一般的卷积层一样大动干戈!

注意:如果残差映射(F(x)F(x)F(x))的结果的维度与跳跃连接(xxx)的维度不同,那咱们是没有办法对它们两个进行相加操作的,必须对xxx进行升维操作,让他俩的维度相同时才能计算。升维的方法有两种:

  • 全 0 填充
  • 采用 1×11 \times 11×1 卷积

3. 网络配置

下图给出了不同 ResNet 的配置图。

对于需要进行下采样的残差结构(conv_3, conv_4, conv_5 的第一个残差结构),论文使用如下形式(原论文有多个形式,我们这里说的是最后作者选择的形式),主线部分 3×33 \times 33×3 的卷积层使用 stride = 2,实现下采样;虚线部分的 1×11 \times 11×1 卷积且 stride = 2:

值得注意的是,pytorch 官方实现和原始论文有些许不同:

注意一些细节:对于 conv_2 一开始,在 Resnet18 和 ResNet34 得到的就是期望的 [56, 56, 64] 的输入;但是对于 ResNet50,ResNet101 和 ResNet152 而言,通过最大池化后得到的输出 shape 是 [56, 56, 64] ,而实线残差结构所需要的输入 shape 是 [56, 56, 256] ,所以 conv_2 对应的第一个也是虚线残差层,仅旁路只调整特征矩阵的深度,高和宽都不变。

通常我们都使用 ImageNet 的预训练参数(迁移学习),能够快速训练出一个理想的模型,尽管在小数据集下也能训练一个好的模型。注意使用别人预训练模型参数时,要注意别人的预处理方式,例如 ImageNet 的常见正则化为:

transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])

迁移学习大白文理解如下所示,假设网络训练完成之后,网络第一个卷积层可能学到了角点信息,第二个卷积层可能学会了分析纹理,再到后来的卷积层学到了如何识别部件。全连接层基于这些信息学会如何分类目标类别。通常浅层的卷积操作是通用的,所以可以直接使用用于初始化。

常见的迁移学习方式包括:

  • 载入权重后训练所有参数
  • 载入权重后只训练最后几层参数
  • 在数权重后在原有网络基础上再加一层全连接,仅训练最后一个全连接层

4. 代码

ResNet 实现代码如下所示:

import torch.nn as nn
import torchclass BasicBlock(nn.Module):expansion = 1def __init__(self, in_channel, out_channel, stride=1, downsample=None, **kwargs):super(BasicBlock, self).__init__()self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel,kernel_size=3, stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(out_channel)self.relu = nn.ReLU()self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel,kernel_size=3, stride=1, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(out_channel)self.downsample = downsampledef forward(self, x):identity = xif self.downsample is not None:identity = self.downsample(x)out = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out += identityout = self.relu(out)return outclass Bottleneck(nn.Module):"""注意:原论文中,在虚线残差结构的主分支上,第一个1x1卷积层的步距是2,第二个3x3卷积层步距是1。但在pytorch官方实现过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,这么做的好处是能够在top1上提升大概0.5%的准确率。可参考Resnet v1.5 https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch"""expansion = 4def __init__(self, in_channel, out_channel, stride=1, downsample=None,groups=1, width_per_group=64):super(Bottleneck, self).__init__()width = int(out_channel * (width_per_group / 64.)) * groupsself.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=width,kernel_size=1, stride=1, bias=False)  # squeeze channelsself.bn1 = nn.BatchNorm2d(width)# -----------------------------------------self.conv2 = nn.Conv2d(in_channels=width, out_channels=width, groups=groups,kernel_size=3, stride=stride, bias=False, padding=1)self.bn2 = nn.BatchNorm2d(width)# -----------------------------------------self.conv3 = nn.Conv2d(in_channels=width, out_channels=out_channel*self.expansion,kernel_size=1, stride=1, bias=False)  # unsqueeze channelsself.bn3 = nn.BatchNorm2d(out_channel*self.expansion)self.relu = nn.ReLU(inplace=True)self.downsample = downsampledef forward(self, x):identity = xif self.downsample is not None:identity = self.downsample(x)out = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out = self.relu(out)out = self.conv3(out)out = self.bn3(out)out += identityout = self.relu(out)return outclass ResNet(nn.Module):def __init__(self,block,blocks_num,num_classes=1000,include_top=True,groups=1,width_per_group=64):super(ResNet, self).__init__()self.include_top = include_topself.in_channel = 64self.groups = groupsself.width_per_group = width_per_groupself.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2,padding=3, bias=False)self.bn1 = nn.BatchNorm2d(self.in_channel)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = self._make_layer(block, 64, blocks_num[0])self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2)self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2)self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2)if self.include_top:self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # output size = (1, 1)self.fc = nn.Linear(512 * block.expansion, num_classes)for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')def _make_layer(self, block, channel, block_num, stride=1):downsample = Noneif stride != 1 or self.in_channel != channel * block.expansion:downsample = nn.Sequential(nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(channel * block.expansion))layers = []layers.append(block(self.in_channel,channel,downsample=downsample,stride=stride,groups=self.groups,width_per_group=self.width_per_group))self.in_channel = channel * block.expansionfor _ in range(1, block_num):layers.append(block(self.in_channel,channel,groups=self.groups,width_per_group=self.width_per_group))return nn.Sequential(*layers)def forward(self, x):x = self.conv1(x)x = self.bn1(x)x = self.relu(x)x = self.maxpool(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)if self.include_top:x = self.avgpool(x)x = torch.flatten(x, 1)x = self.fc(x)return xdef resnet34(num_classes=1000, include_top=True):# https://download.pytorch.org/models/resnet34-333f7ec4.pthreturn ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)def resnet50(num_classes=1000, include_top=True):# https://download.pytorch.org/models/resnet50-19c8e357.pthreturn ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top)def resnet101(num_classes=1000, include_top=True):# https://download.pytorch.org/models/resnet101-5d3b4d8f.pthreturn ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top)def resnext50_32x4d(num_classes=1000, include_top=True):# https://download.pytorch.org/models/resnext50_32x4d-7cdf4587.pthgroups = 32width_per_group = 4return ResNet(Bottleneck, [3, 4, 6, 3],num_classes=num_classes,include_top=include_top,groups=groups,width_per_group=width_per_group)def resnext101_32x8d(num_classes=1000, include_top=True):# https://download.pytorch.org/models/resnext101_32x8d-8ba56ff5.pthgroups = 32width_per_group = 8return ResNet(Bottleneck, [3, 4, 23, 3],num_classes=num_classes,include_top=include_top,groups=groups,width_per_group=width_per_group)# load ImageNet pretrain parameter
def main():device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")my_output_channel = 5# load pretrain weights# download url: https://download.pytorch.org/models/resnet34-333f7ec4.pthmodel_weight_path = "./resnet34-pre.pth"assert os.path.exists(model_weight_path), "file {} does not exist.".format(model_weight_path)# option1net = resnet34()net.load_state_dict(torch.load(model_weight_path, map_location=device))# change fc layer structurein_channel = net.fc.in_featuresnet.fc = nn.Linear(in_channel, my_output_channel)# option2# net = resnet34(num_classes=my_output_channel)# pre_weights = torch.load(model_weight_path, map_location=device)# del_key = []# for key, _ in pre_weights.items():#     if "fc" in key:#         del_key.append(key)## for key in del_key:#     del pre_weights[key]## missing_keys, unexpected_keys = net.load_state_dict(pre_weights, strict=False)# print("[missing_keys]:", *missing_keys, sep="\n")# print("[unexpected_keys]:", *unexpected_keys, sep="\n")

深度学习之图像分类(七)--ResNet网络结构相关推荐

  1. 深度学习之图像分类(九)--ResNeXt 网络结构

    深度学习之图像分类(九)ResNeXt 网络结构 目录 深度学习之图像分类(九)ResNeXt 网络结构 1. 前言 2. 组卷积 3. ResNeXt block 分析 4. 代码 本节学习 Res ...

  2. 深度学习之图像分类(十六)-- EfficientNetV2 网络结构

    深度学习之图像分类(十六)EfficientNetV2 网络结构 目录 深度学习之图像分类(十六)EfficientNetV2 网络结构 1. 前言 2. 从 EfficientNetV1 到 Eff ...

  3. 深度学习之图像分类(十二)--MobileNetV3 网络结构

    深度学习之图像分类(十二)MobileNetV3 网络结构 目录 深度学习之图像分类(十二)MobileNetV3 网络结构 1. 前言 2. 更新 BlocK (bneck) 3. 重新设计激活函数 ...

  4. 深度学习之图像分类(十四)--ShuffleNetV2 网络结构

    深度学习之图像分类(十四)ShuffleNetV2 网络结构 目录 深度学习之图像分类(十四)ShuffleNetV2 网络结构 1. 前言 2. Several Practical Guidelin ...

  5. 深度学习之图像分类(十)--MobileNetV1 网络结构

    深度学习之图像分类(十)MobileNetV1 网络结构 目录 深度学习之图像分类(十)MobileNetV1 网络结构 1. 前言 2. DW 卷积 3. Depthwise Separable C ...

  6. 深度学习在图像分类中的发展

    深度学习是一门比较年轻的研究方向,从机器视觉到语音识别,以及自然语言识别等领域都有它的身影.说实话,喵哥此前只是知道有这个学科,但是并不清楚它到底是什么,怎么使用它.其实现在也是一无所知,但是我越发觉 ...

  7. 深度学习之图像分类(十九)-- Bottleneck Transformer(BoTNet)网络详解

    深度学习之图像分类(十九)Bottleneck Transformer(BoTNet)网络详解 目录 深度学习之图像分类(十九)Bottleneck Transformer(BoTNet)网络详解 1 ...

  8. 深度学习之图像分类(六)--Inception进化史

    深度学习之图像分类(六)Inception进化史 目录 深度学习之图像分类(六)Inception进化史 1. Inception V1 2. Inception V2 2.1 卷积分解(Factor ...

  9. 深度学习之图像分类(二十六)-- ConvMixer 网络详解

    深度学习之图像分类(二十六)ConvMixer 网络详解 目录 深度学习之图像分类(二十六)ConvMixer 网络详解 1. 前言 2. A Simple Model: ConvMixer 2.1 ...

  10. 干货丨深度学习、图像分类入门,从VGG16卷积神经网络开始

    刚开始接触深度学习.卷积神经网络的时候非常懵逼,不知道从何入手,我觉得应该有一个进阶的过程,也就是说,理应有一些基本概念作为奠基石,让你有底气去完全理解一个庞大的卷积神经网络: 本文思路: 一.我认为 ...

最新文章

  1. 数据可视化配色指南:三大配色方法,做出咨询报告一样的图表丨附在线工具资源...
  2. java对象的访问定位_JVM创建对象及访问定位过程详解
  3. 前端学习(2135):ES模块化的导出和导入
  4. Java中boolean类型占用多少个字节
  5. aws lambda_在AWS Lambda上运行“ Hello World”
  6. 背水一战 Windows 10 (53) - 控件(集合类): ItemsControl 的布局控件 - ItemsStackPanel, ItemsWrapGrid...
  7. 转载:为某燃气公司做的一部卡通角色广告动画
  8. 汇新云智慧城市解决方案——以区块链加速智慧城市建设
  9. Ctfhub解题 彩蛋
  10. php 设置允许跨域请求
  11. u3d occlusion 遮挡剔除
  12. java forward怎么用_java中forward是什么
  13. 服务器软硬件安装和配置,Windows Server 2016-系统安装软硬件要求
  14. Python 实现用GUI设计有界面的词云生成器
  15. 婴儿体重身高增长标准对照表
  16. 用于图片文本识别的Tesseract-OCR的安装说明(windows10)
  17. 读的, 且经过美化, 能在所有 JavaScript 环境中运行, 并且应该和对应手写的 JavaScript 一样快或者更快.
  18. JAVA JSP水费管理系统JSP电费管理系统JSP缴费管理系统JSP水费缴费系统JSP水电费管理
  19. 四川大学计算机考研课程表,2019年四川大学研究生教学运行作息时间表.doc
  20. 【Codeforces Round #570 (Div. 3) A-H 】解题报告

热门文章

  1. 携程合体去哪儿,与途牛度假旅游市场争高下
  2. 线性代数知识框架梳理
  3. 【原创】Magisk Root隐藏模块 Shamiko安装
  4. Halcon教程十四:训练自己的模型然后识别相似的图像
  5. YOLO v2算法详解
  6. 计算机基础考试大一计算题,计算机基础复习(大一)计算机基础上机题库
  7. pgAdmin4的备份和还原数据
  8. 千月2021全新改版影视app系统源码 影视app全新双端开源系统 全开源 带投屏 带教程
  9. 【传感器】HT1621 LCD驱动芯片
  10. 超详细的四类数据库去重实现方案汇总(转载)