网站制作建设公司哪家好,秘密入口3秒自动转接连接,no.7主题wordpress,推广费用一般多少钱深度残差网络#xff08;Deep residual network, ResNet#xff09;的提出是CNN图像史上的一件里程碑事件#xff0c;并且让深度学习真正可以继续做下去#xff0c;斩获2016 CVPR Best Paper。此外ResNet的作者都是中国人#xff0c;一作何恺明。ResNet被提出以后很多的网… 深度残差网络Deep residual network, ResNet的提出是CNN图像史上的一件里程碑事件并且让深度学习真正可以继续做下去斩获2016 CVPR Best Paper。此外ResNet的作者都是中国人一作何恺明。ResNet被提出以后很多的网络都使用或借鉴了这个结构。该论文的被引用量更是突破了10w。
论文地址[1512.03385] Deep Residual Learning for Image Recognition (arxiv.org) 在2015的5个竞赛中都获得了第一名而且远远甩开第二名。 1. ResNet概述
1.1 研究背景 无论是VGG还是GoogleNet都表明增加网络的深度的重要性网络越深可以提取到越高级的特征。 那么是否意味着直接简单的将网络堆深是不是就可以了 增加网络深度首先会带来的问题就是梯度消失或者梯度爆炸导致难以收敛但这个问题已经可以通过合适的权重初始化手段Xavier初始化MSRA 初始化还有Batch Normalization解决。 另一个问题便是网络退化现象这既不是梯度消失或者梯度爆炸导致也不是过拟合导致的如下图所示56层网络在训练集和测试集上的误差都比20层网络大。并且他们经过实验发现即使更深的网络使用3倍的迭代次数退化现象仍存在。 1.2 残差学习 于是论文提出了一个残差学习框架用来训练那些非常深的神经网络重新定义了网络的学习方式让网络可以直接学习输入信息与输出信息的差异(即残差)然后将浅层的特征与深层的特征直接相加进行融合即使残差为0通过一个Shortcut connection也可以实现identity mapping恒等映射通俗理解就是即使什么都没学到也不会比原来更差。经过这个结构Shortcut connection既没有引入额外的参数也没有增加计算的复杂度相加的复杂度很低微不足道。 残差网络
易于优化收敛解决退化问题让网络可以更深准确率大大提升 通过这个结构论文中提出了ResNet合集随着深度增加网络性能还在增加在高达152层时网络仍然有着较低的复杂度比VGG16还低并且在 ImageNet的测试集上进行评测达到了3.57%的错误率这一结果赢得 ILSVRC 2015 分类比赛的第一名超过了人类的水平。最后甚至还实验了超过1000层的网络。 1.3 Resnet机理 resnet有效的真实原因还有待研究原论文也只是做出了猜测并没有很严格的理论证明。后续也有非常多的人做出了不一样的或者进一步的论证
1.恒等映射这一路梯度是1可以防止梯度消失。虽然网络退化和梯度消失没什么关系但Resnet确实可以防止梯度消失加快收敛速度
2.集成学习。ResNet相当于几个网络的集成。 3.神经网络难以拟合恒等映射 纵观深度神经网络的发展为了让网络能力越来越强在神经网络引入了很多非线性。这也使得特征随着层层前向传播得到完整保留什么也不做的可能性都微乎其微有的时候 “什么都不做”反而是最好的但是“什么都不做”恒等映射恰好是当前神经网络最难做到的东西之一。可以认为Residual Learning的初衷其实是让模型的内部结构至少有恒等映射的能力。以保证在堆叠网络的过程中网络至少不会因为继续堆叠而产生退化。
4. 还有一些别的说法 众所周知深度学习和炼丹一样ResNet有效因为因为实验结果就是好。
1.4 结论
1.残差网络可以解决网络退化问题可以重新让网络变得更深性能越好
2.残差网络收敛速度更快 2. 网络结构详解
1.ResNet183450101152 网络结构类似于VGG除第一个卷积层外全部采用3×3卷积padding1。除第一次下采样外下采样通过步长为2的卷积来实现。此外还在卷积后激活前使用了BN。 其中根据Block类型可以将这五种ResNet分为两类(1) 一种基于BasicBlock浅层网络ResNet18, 34都由BasicBlock搭成(2) 另一种基于Bottleneck深层网络ResNet50, 101, 152乃至更深的网络都由Bottleneck搭成。Block相当于积木每个layer都由若干Block搭建而成再由4层layer组成整个网络。 BasicBlock包括两个3×3卷积如下图除了conv2_x通过最大池化降采样外每一层第一个BasicBlock的第一次卷积步长为2进行下采样。 对于下采样的残差连接会出现两者尺寸和通道数不一样的问题无法直接相加论文中给出了两种方案A浅层使用1×1卷积核步长为2同时不改变通道数仍然使用恒等映射直接相加但是对于新增加的维度则全部使用0来代替。这种方案不增加任何的参数。(B)还是使用1x1的卷积步长为2但是输出的通道数设置需要和下采样后的通道数匹配如下图。 论文中对比了两种方案的效果还加了一种C方案无论是否降采样都使用1×1卷积后再连接实际中不会用C方案因为他增加了很多参数。由于参数量ABC所以误差也会有一点点不同。A方案已经可以解决退化的问题且不会增加额外的参数但通过0填充的维度确实没有进行残差学习。一般采用B方案 为了减低深层次网络参数量和计算量深层次网络采用了Bottleneck。先通过1×1卷积降维四倍减少通道数再使用3×3卷积通道数不变最后通过1×1卷积升回维度。 同样的除了conv2_x通过最大池化降采样外每一层第一个Bottleneck进行下采样1×1卷积降维两倍3×3卷积步长为2进行下采样1×1卷积升高4倍维度。 最后使用全局平均池化连接一个输出为1000的全连接层使用Softmax完成分类。
2.ResNet20324456110 论文在CIFAR-10数据集也进行了实验。针对32×32的小尺寸这个网络变种只有三个阶段。第一个7×7卷积变成3×3步长也为1没了最大池化层。 设置卷积层个数n{357918200}可以得到ResNet20324456110。此外设置n200得到了1202层。在训练这个1000多层的网络的时候没有遇到训练退化的问题并且他的训练误差小于0.1%。尽管1202层网络的训练误差和110层网络是相似的但是它的测试结果却要差于110层的网络。论文猜测可能是由于过拟合造成的。因为对于这样的一个小数据集也许并不需要一个有着1202层网络进行训练。 3. ResNet在Pytorch实现
1.手动实现ResNet
1.ResNet183450101152
import torch
import torch.nn as nn
import torch.nn.functional as F# 定义基本块 BasicBlock
class BasicBlock(nn.Module):expansion 1def __init__(self, in_channels, out_channels, stride1):super(BasicBlock, self).__init__()# 第一个卷积层3x3卷积核stride用于控制步幅self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels) # 批归一化self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stride1, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels)self.shortcut nn.Sequential()# 如果步幅不为1或输入通道数不等于输出通道数*expansion使用额外的卷积层来匹配维度if stride ! 1 or in_channels ! self.expansion * out_channels:self.shortcut nn.Sequential(nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(self.expansion * out_channels))def forward(self, x):out F.relu(self.bn1(self.conv1(x)))out self.bn2(self.conv2(out))out self.shortcut(x) # 将残差连接到输出out F.relu(out)return out# 定义瓶颈结构 Bottleneck
class Bottleneck(nn.Module):expansion 4def __init__(self, in_channels, out_channels, stride1):super(Bottleneck, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels)self.conv3 nn.Conv2d(out_channels, out_channels * self.expansion, kernel_size1, biasFalse)self.bn3 nn.BatchNorm2d(out_channels * self.expansion)self.relu nn.ReLU(inplaceTrue)self.shortcut nn.Sequential()# 如果步幅不为1或输入通道数不等于输出通道数*expansion使用额外的卷积层来匹配维度if stride ! 1 or in_channels ! out_channels * self.expansion:self.shortcut nn.Sequential(nn.Conv2d(in_channels, out_channels * self.expansion, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(out_channels * self.expansion))def forward(self, x):residual xout self.relu(self.bn1(self.conv1(x)))out self.relu(self.bn2(self.conv2(out)))out self.bn3(self.conv3(out))residual self.shortcut(residual) # 匹配维度out residual # 将残差连接到输出out self.relu(out)return out# 定义ResNet模型
class ResNet(nn.Module):def __init__(self, block, layers, num_classes1000):super(ResNet, self).__init__()self.in_channels 64self.conv1 nn.Conv2d(3, 64, kernel_size7, stride2, padding3, biasFalse)self.bn1 nn.BatchNorm2d(64)self.maxpool nn.MaxPool2d(kernel_size3, stride2, padding1)self.layer1 self._make_layer(block, 64, layers[0], stride1)self.layer2 self._make_layer(block, 128, layers[1], stride2)self.layer3 self._make_layer(block, 256, layers[2], stride2)self.layer4 self._make_layer(block, 512, layers[3], stride2)self.avgpool nn.AdaptiveAvgPool2d((1, 1))self.fc nn.Linear(512 * block.expansion, num_classes)def _make_layer(self, block, out_channels, blocks, stride1):layers []layers.append(block(self.in_channels, out_channels, stride))self.in_channels out_channels * block.expansionfor _ in range(1, blocks):layers.append(block(self.in_channels, out_channels))return nn.Sequential(*layers)def forward(self, x):x F.relu(self.bn1(self.conv1(x)))x self.maxpool(x)x self.layer1(x)x self.layer2(x)x self.layer3(x)x self.layer4(x)x self.avgpool(x)x torch.flatten(x, 1)x self.fc(x)return x# 不同深度的ResNet模型的创建函数
def ResNet18():return ResNet(BasicBlock, [2, 2, 2, 2])def ResNet34():return ResNet(BasicBlock, [3, 4, 6, 3])def ResNet50():return ResNet(Bottleneck, [3, 4, 6, 3])def ResNet101():return ResNet(Bottleneck, [3, 4, 23, 3])def ResNet152():return ResNet(Bottleneck, [3, 8, 36, 3])2.ResNet20324456110
# 定义基本块 BasicBlock
class BasicBlock(nn.Module):expansion 1def __init__(self, in_channels, out_channels, stride1):super(BasicBlock, self).__init__()self.conv1 nn.Conv2d(in_channels, out_channels, kernel_size3, stridestride, padding1, biasFalse)self.bn1 nn.BatchNorm2d(out_channels)self.conv2 nn.Conv2d(out_channels, out_channels, kernel_size3, stride1, padding1, biasFalse)self.bn2 nn.BatchNorm2d(out_channels)self.shortcut nn.Sequential()if stride ! 1 or in_channels ! self.expansion * out_channels:self.shortcut nn.Sequential(nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size1, stridestride, biasFalse),nn.BatchNorm2d(self.expansion * out_channels))def forward(self, x):out F.relu(self.bn1(self.conv1(x)))out self.bn2(self.conv2(out))out self.shortcut(x)out F.relu(out)return out# 定义ResNet模型
class ResNet(nn.Module):def __init__(self, block, num_blocks, num_classes1000):super(ResNet, self).__init__()self.in_channels 16self.conv1 nn.Conv2d(3, 16, kernel_size3, stride1, padding1, biasFalse)self.bn1 nn.BatchNorm2d(16)self.layer1 self._make_layer(block, 16, num_blocks[0], stride1)self.layer2 self._make_layer(block, 32, num_blocks[1], stride2)self.layer3 self._make_layer(block, 64, num_blocks[2], stride2)self.avgpool nn.AdaptiveAvgPool2d((1, 1))self.fc nn.Linear(64 * block.expansion, num_classes)def _make_layer(self, block, out_channels, num_blocks, stride):strides [stride] [1] * (num_blocks - 1)layers []for stride in strides:layers.append(block(self.in_channels, out_channels, stride))self.in_channels out_channels * block.expansionreturn nn.Sequential(*layers)def forward(self, x):x F.relu(self.bn1(self.conv1(x)))x self.layer1(x)x self.layer2(x)x self.layer3(x)x self.avgpool(x)x torch.flatten(x, 1)x self.fc(x)return x# 创建不同深度的ResNet模型的函数
def ResNet20():return ResNet(BasicBlock, [3, 3, 3])def ResNet32():return ResNet(BasicBlock, [5, 5, 5])def ResNet44():return ResNet(BasicBlock, [7, 7, 7])def ResNet56():return ResNet(BasicBlock, [9, 9, 9])def ResNet110():return ResNet(BasicBlock, [18, 18, 18])2.使用Pytorch官方实现
ResNet — Torchvision 0.16 documentation (pytorch.org)ResNet — Torchvision 0.16 documentation (pytorch.org) model resnet18(weightsDEFAULT) # model resnet18(weightsDEFAULT)
model resnet34() # model resnet34(weightsDEFAULT)
model resnet50() # model resnet50(weightsDEFAULT)
model resnet101() # model resnet101(weightsDEFAULT)
model resnet152() # model resnet152(weightsDEFAULT) 50层以上有两个版本的预训练权重V2基于新的训练技巧。当然weightsDEFAULT会自动使用最新最好的。