亚马逊一般在哪些网站上做推广,建设课程网站的目的,景安网站备案 不去拍照,东莞网络营销网络培训学校原理
ResNet 解决了什么问题#xff1f; 一言以蔽之#xff1a;解决了深度的神经网络难以训练的问题。 具体的说#xff0c;理论上神经网络的深度越深#xff0c;其训练效果应该越好#xff0c;但实际上并非如此#xff0c;层数越深会导致越差的结果并且容易产生梯度爆炸…原理
ResNet 解决了什么问题 一言以蔽之解决了深度的神经网络难以训练的问题。 具体的说理论上神经网络的深度越深其训练效果应该越好但实际上并非如此层数越深会导致越差的结果并且容易产生梯度爆炸或梯度消失等问题。 ResNet 怎么解决的 提出了一个残差学习网络的框架该框架解决了上述问题。 残差网络的架构 整个架构如上图所示。
首先我们要学习的东西是 H(x)假设现在已经有了一个浅的网络然后我们要在上面新加一些层让网络变得更深如果按传统的做法那么新加的层就继续跟之前一样进行学习就行了。但是现在在新加的层中我们不直接去学 H(x)而是应该去学 H(x) - x。x 就是之前比较浅的网络已经学到的那个东西也就是在新加的层中不去重新学个东西而只是学学到的东西和真实的东西二者之间的残差 H(x) - x然后该层最后的输出结果 F(x) 再加上原始数据 x 就是最终结果也就是 F(x) x 此时优化的目标就不再是原始的 H(x)而是 H(x) - x 这个东西。
这就是 ResNet 的核心思想。
我感觉有一篇文章讲的很好可以参考一下ResNet网络详细讲解
下面是论文原文的描述
在本文中我们通过引入一个深度残差学习框架来解决退化问题。我们不希望每几个堆叠层直接拟合一个期望的底层映射而是明确地让这些层拟合一个残差映射。在形式上我们将期望的底层映射表示为H ( x )并让堆叠的非线性层拟合F ( x )的另一个映射 H ( x ) - x。原始映射被重铸成F ( x ) x。我们假设优化残差映射比优化原始的、未引用的映射更容易。在极端情况下如果一个恒等映射是最优的那么将残差推到零比用一堆非线性层拟合一个恒等映射更容易。
F ( x ) x的表达式可以通过具有捷径连接的前馈神经网络来实现(图2 )。快捷方式连接[ 2、33、48]是那些跳过一个或多个层的连接。在我们的例子中快捷连接只是执行身份映射它们的输出被添加到堆叠层的输出中(图2 )。身份捷径连接既不增加额外的参数也不增加计算复杂度。整个网络仍然可以通过反向传播的SGD进行端到端的训练并且可以很容易地使用公共库(例如, Caffe )实现无需修改求解器。
我们在ImageNet [ 35 ]上进行了全面的实验来展示退化问题并评估我们的方法。研究表明
1 )我们的深度残差网络易于优化但对应的普通网络(简单地堆叠层)在深度增加时表现出更高的训练误差 2 )我们的深度残差网络可以很容易地从大幅增加的深度中获得精度增益产生的结果明显优于以前的网络。
在ImageNet分类数据集上[ 35 ]我们通过极深的残差网络获得了优异的结果。我们的152层残差网络是ImageNet上有史以来最深层的网络但仍比VGG网络具有更低的复杂度[ 40 ]。我们的集成在ImageNet测试集上有3.57 %的top - 5误差并在ILSVRC 2015分类竞赛中获得第一名。在其他识别任务上也具有出色的泛化性能并引领我们在ILSVRC COCO 2015竞赛中进一步获得第1名ImageNet检测、ImageNet定位、COCO检测和COCO分割。这有力的证据表明残差学习原理具有一般性我们预期它在其他视觉和非视觉问题中也适用。
代码复现
这里给出我自己的模型代码
import torch
from torch import nn# 基本残差块
class BasicBlock(nn.Module):expansion 1参数解释in_ch输入通道数block_ch输出通道数stride步长通过该参数我们就可以实现网络结构中特征图Size减半、通道数增加一倍的效果downSample其本身也是一个网络用来实现残差网络中的跳跃连接也就是论文中虚线和实线同时跳跃连接也是用来区别基本残差块和瓶颈残差块的二者区别如下基本残差块输入输出通道数相同瓶颈残差块输入输出通道数不同需要进行升维操作才能对位相加另外二者的结构不同可以通过论文看到def __init__(self, in_ch, block_ch, stride1, downSampleNone):super().__init__()self.downSample downSample# 从网络结构图中可以看到先进行第一层卷积self.conv1 nn.Conv2d(in_ch, block_ch, kernel_size3, stridestride, padding1, biasFalse)# 在网络模型中添加一个二维批归一化Batch Normalization层。# 批归一化是一种用于加速神经网络训练并提高其性能的技术,类似于将上面所输出的数据进行了统一整理self.bn1 nn.BatchNorm2d(block_ch)# 激活函数self.relu1 nn.ReLU()# 第二层卷积self.conv2 nn.Conv2d(block_ch, block_ch * self.expansion, kernel_size3, stride1, padding1, biasFalse)self.bn2 nn.BatchNorm2d(block_ch * self.expansion)self.relu2 nn.ReLU()def forward(self, x):identity x# 如果downSample参数不为空说明其需要升维也就是论文中虚线的样子if self.downSample is not None:# 升维让输入输出的通道数对齐identity self.downSample(x)out self.relu1(self.bn1(self.conv1(x)))out self.bn2(self.conv2(out))# 这里就是论文中的输出与原始输入进对位相加的步骤out identity# 对位相加结束后再进行 relu 函数的激活然后输出结果return self.relu2(out)# 瓶颈残差块
class Bottleneck(nn.Module):# 从论文的网络结构图中不难发现瓶颈残差块在第三层卷积时通道数会放大四倍# 因此定义一个 expansion 变量expansion 4参数解释in_ch输入通道数block_ch输出通道数stride步长通过该参数我们就可以实现网络结构中特征图Size减半、通道数增加一倍的效果downSample其本身也是一个网络用来实现残差网络中的跳跃连接也就是论文中虚线和实线同时跳跃连接也是用来区别基本残差块和瓶颈残差块的二者区别如下基本残差块输入输出通道数相同瓶颈残差块输入输出通道数不同需要进行升维操作才能对位相加另外二者的结构不同可以通过论文看到def __init__(self, in_ch, block_ch, stride1, downSampleNone):super().__init__()self.downSample downSample# 从网络结构图中可以看到先进行第一层卷积self.conv1 nn.Conv2d(in_ch, block_ch, kernel_size1, stridestride, biasFalse)# 在网络模型中添加一个二维批归一化Batch Normalization层。# 批归一化是一种用于加速神经网络训练并提高其性能的技术,类似于将上面所输出的数据进行了统一整理self.bn1 nn.BatchNorm2d(block_ch)# 激活函数self.relu1 nn.ReLU()# 第二层卷积self.conv2 nn.Conv2d(block_ch, block_ch, kernel_size3, stride1, padding1, biasFalse)self.bn2 nn.BatchNorm2d(block_ch)self.relu2 nn.ReLU()# 第三层卷积self.conv3 nn.Conv2d(block_ch, block_ch * self.expansion, kernel_size1, stride1, biasFalse)self.bn3 nn.BatchNorm2d(block_ch * self.expansion)self.relu3 nn.ReLU()def forward(self, x):identity x# 如果downSample参数不为空说明其需要升维也就是论文中虚线的样子if self.downSample is not None:# 升维让输入输出的通道数对齐identity self.downSample(x)out self.relu1(self.bn1(self.conv1(x)))out self.relu2(self.bn2(self.conv2(out)))out self.bn3(self.conv3(out))# 这里就是论文中的输出与原始输入进对位相加的步骤out identity# 对位相加结束后再进行 relu 函数的激活然后输出结果return self.relu3(out)# 残差网络
class ResNet(nn.Module):in_ch: 默认为3因为残差网络就是用来图片分类的所以输入通道数默认为 3num_classes分类的数量默认设置为100即 100 种分类block用来区别是 基本残差块 还是 瓶颈残差块block_num每个残差块所需要堆叠的次数也是论文中提供的有def __init__(self, in_ch3, num_classes100, blockBottleneck, block_num[3, 4, 6, 3]):super().__init__()# 因为在各层之间通道数会发生变化因此要进行跟踪self.in_ch in_ch# 对于残差网络来说不管是什么类型其一开始都要进行 7x7 的卷积和 3x3 的池化# 因此我们直接照搬即可论文中已经有了self.conv1 nn.Conv2d(in_ch, 64, kernel_size7, stride2, padding3, biasFalse)self.bn1 nn.BatchNorm2d(64)self.maxpool1 nn.MaxPool2d(kernel_size3, stride2, padding1)self.in_ch 64# 将残差块堆叠起来形成一个一个的残差层进而构建成ResNetself.layer1 self._make_layer(block, 64, block_num[0], stride1)self.layer2 self._make_layer(block, 128, block_num[1], stride2)self.layer3 self._make_layer(block, 256, block_num[2], stride2)self.layer4 self._make_layer(block, 512, block_num[3], stride2)# 最后是全连接层做预测的self.fc_layer nn.Sequential(nn.Linear(512*block.expansion*7*7, num_classes),nn.Softmax(dim-1))def _make_layer(self, block, block_ch, block_num, stride2):layers []downSample nn.Conv2d(self.in_ch, block_ch * block.expansion, kernel_size1, stridestride)layers [block(self.in_ch, block_ch, stridestride, downSampledownSample)]self.in_ch block_ch * block.expansionfor _ in range(1, block_num):layers [block(self.in_ch, block_ch)]return nn.Sequential(*layers)def forward(self, x):out self.maxpool1(self.bn1(self.conv1(x))) #(1, 3, 224, 224) - (1, 64, 56, 56)out self.layer1(out)out self.layer2(out)out self.layer3(out)out self.layer4(out)out out.reshape(out.shape[0], -1)out self.fc_layer(out)return outif __name__ __main__:# 接下来进行测试# 这行代码创建了一个形状为 (1, 3, 224, 224) 的四维张量 x# 其中包含了一个大小为 1 的批次中的一个 224x224 像素的 RGB 图像。x torch.randn(1, 3, 224, 224)resnet ResNet(in_ch3, num_classes100, blockBottleneck, block_num[2, 2, 2, 2])y resnet(x)print(y.shape)