科讯怎么建设网站,宜宾seo快速排名,大连鼎信网站建设公司地址,销售网站设计论文#xff1a;https://arxiv.org/pdf/1512.03385.pdf
Deep Residual Learning for Image Recognition
本模块主要是阅读论文#xff0c;会做简单的翻译#xff08;至少满足我自己能看明白#xff09;。
Introduction 由上图可见#xff0c;在20层和56层的网络上训练的…论文https://arxiv.org/pdf/1512.03385.pdf
Deep Residual Learning for Image Recognition
本模块主要是阅读论文会做简单的翻译至少满足我自己能看明白。
Introduction 由上图可见在20层和56层的网络上训练的训练误差和测试误差的变化可以看到层数加深不一定能带来性能上的提升甚至更糟了。这就引出了文章的疑问有些和直觉相反的结论为什么不是层数约多结果越好呢文中给出的解释是梯度消失/爆炸问题从一开始就阻碍了收敛虽然这一问题已经通过normalized initialization和intermediate normalization layers在很大程度上得到了解决这使得几十层的网络可以收敛但是当层数逐渐增加出现了“退化”问题degradation。这里的“退化”指的是随着网络的加深accuracy先逐渐升高到达饱和然后迅速衰退。文中指出这一问题并非由过拟合导致并不是模型过于复杂。这一问题也说明了我们并不能简单的认为通过堆叠层数来优化模型。 论文中通过引入一个deep residual learning框架深度残差。并不使用简单堆叠层数来获得一个满意的潜在映射而是让这些层复合残差映射。
定义
H(x)满意的潜在映射
F(x)堆叠的非线性层产生的映射并且满足关系F(x) : H(x) - x
原始映射F(x) x
假设优化残差映射要比优化原始映射更容易。极端点来说对于一个恒等映射将残差推到0要比吧非线性层推到0更简单不记得在哪里听到过理论上通过增加层数的方法来改善模型非常符合直觉因为我们直觉上觉得可以无限的加yx这样的变换来无限制的增加层数。但是就像论文中描写的那样我们并没有得到更高的准确度而是出现了退化现象这可能是因为DL太善于计算非线性了反而没办法在线性上给一个比较好的表现我觉得这是一个直觉上比较好的解释故在此记录。
F(x) x这一形式在前馈神经网络中可以采用“shortcut connections”来实现。这里所谓的“shortcut connections”指的是跳过一层或多层。在本文中这种连接仅仅是做“identity mapping”恒等映射在数学中指一个函数将每个元素映射到其自身即输入和输出相等的映射。这一做法并不会增加额外的参数也不会增加计算的复杂度反正yx也没什么影响也没必要更新这就使得整个网络仍然可以采用反向传播SGD进行end-to-end的训练。文章中提到采用的152层的网络虽然比之前的VGG网络要深但是实际上并没有那么复杂。
Deep Residual Learning
Residual Learning
在前面的定义中H(x)满意的潜在映射。让我们先把H(x)看做是由几个堆叠的层拟合的潜在映射这里的几个不一定指的是整个网络那么此时x表示的就是这些层中第一层的输入。假设多个非线性层可以逐渐逼近residual function即H(x- x假设输入和输出的dimension相同这里看起来有点autoencoder那个感觉。因此与其期待用堆叠的层去近似H(x)不如让这些层去拟合residual function F(x) : H(x) - x。尽管这两种形式都应该能够渐进地逼近所需的函数(如假设的那样)但学习的难易程度可能有所不同。
简单来说就是不再直接的拟合函数而是拟合残差函数并且这么做的原因是这样更容易学。
如果添加的层可以构造为identity mapping恒等映射那么较深的模型的训练误差不应该大于较浅的模型。退化问题表明在逼近多个非线性层的恒等映射时可能存在困难。使用residual方法之后如果恒等映射是最优的那么就可以简单的把多个非线性层的权重向0逼近这就近似于恒等变换。
Identity Mapping by Shortcuts
对每个堆叠在一起的层every few stacked layers使用residual learning构建出building block。将这个building block定义为
y F(x, {W_i}) x
x, y对应的堆叠在一起的层的输入和输出。
F(x, {W_i})学到的residual mapping我们最后想得到的是H(x)但是这里学到的是F(x, {W_i})也就是H(x) - x但是没有关系我们最后输出的是F(x, {W_i}) x也就是H(x) - x x这种思路很像小时候做那种数列找规律求和虽然直接算很难算但是可以加上一项之后先算出来结果最后再把加上来的项去掉
在figure 2中我们有两层也就是这里σ代表ReLU为了简化这里省略了bias。Fx采用shortcut connection和element-wise addition简单来说就是直接拽过来加上既然要拽过来直接加那么一定要满足维度的一致。这种方法既不引入额外的参数也不增加计算复杂度。那么如果我们要改变输入输出的通道数时可以执行一个线性投影 F(x, {W_i})可以表示多层的卷积。
Network Architectures 卷积层大多具有3×3滤波器并遵循两个简单的设计规则:(i)对于相同的输出特征图大小各层具有相同数量的滤波器;(ii)如果特征图大小减半则滤波器的数量增加一倍以保持每层的时间复杂度。我们通过步长为2的卷积层直接执行下采样。网络以一个全局平均池化层和一个带有softmax的1000路全连接层结束。图3(中)加权层总数为34层。 对中间的plain network增加shortcut connections就能改成residual版本。当输入输出维度相同时可以直接使用identity shortcut(Eqn.(1))(y F(x, {W_i}) x) 当维度增加时(图3中的虚线快捷方式)我们考虑两个方案: (A)快捷方式仍然执行恒等映射为增加维度填充额外的0。这个方案不引入额外的参数; (B) 中的投影shortcut用于匹配维度(通过1×1卷积完成)。
对于这两个方案当快捷键跨越两个大小的特征映射时它们的步幅为2。
Implementation
从图像或其水平翻转中随机采样224×224裁剪并减去每像素平均值。使用中的标准颜色增强。在每次卷积之后和激活之前采用批归一化(BN)。初始化权重并从头开始训练所有的plain/residual网络。使用SGD的小批量大小为256。学习率从0.1开始当误差趋于平稳时除以10模型的训练次数可达60 × 104次。
Experiments
Deeper Bottleneck Architectures 对于每个残差函数F我们使用3层而不是2层的(图5)。这三层是1×1, 3×3和1×1卷积其中1×1层负责减少然后增加(恢复)维度使3×3层成为输入/输出维度较小的瓶颈。图5给出了一个例子其中两种设计具有相似的时间复杂度。无参数标识快捷方式对于瓶颈体系结构尤其重要。如果将图5(右)中的标识快捷方式替换为投影可以看出由于shortcut连接到两个高维端点时间复杂度和模型尺寸都增加了一倍。
关于论文的理解
卷积后特征图尺寸变化H_out (H_in 2P - K) / S 1
转载神经网络学习小记录20——ResNet50模型的复现详解_resnet50复现-CSDN博客末尾的resnet50结构图 同时针对之前论文中的结构图要说明的是有部分内容上面的结构图和论文都没有直接说明比如conv1_x中需要加padding否则(224-7)/21是没办法成112的这里padding3conv2_x中stride1其他stride2比如56-28(56-1)/2128
注意在每一个小的block里面有些直接计算发现数字不对的都是加了padding。
比如conv2_x里面3×3那个就加了padding1
代码实现
1
参考https://www.youtube.com/watch?vDkNIBBBvcPs
import torch
import torch.nn as nnclass block(nn.Module):def __init__(self, inchannels, out_channels, identity_downsampleNone, stride1):super(block, self).__init__()# 每一个resnet的block的输入和输出的通道数的比值都是1/4也就是说通道数扩大了4倍self.expension 4self.conv1 nn.Conv2d(inchannels, outchannels, kernel_size1, stride1, padding0)self.bn1 nn.BatchNorm2d(out_channels)self.conv2 nn.Conv2d(outchannels, outchannels, kernel_size3, stridestride, padding1)self.bn2 nn.BatchNorm2d(out_channels)self.conv3 nn.Conv2d(outchannels, outchannels*self.expansion, kernel_size1, stride1, padding0)self.bn3 nn.BatchNorm2d(out_channels*self.expansion)self.relu nn.ReLU()self.identity_downsample identity_downsampledef forward(self, x):identity xx self.conv1(x)x self.bn1(x)x self.relu(x)x self.conv2(x)x self.bn2(x)x self.relu(x)x self.conv3(x)x self.bn3(x)if self.identity_downsample is not None:identity self.identity_downsample(identity)# 方案A or 方案Bx identityx self.relu(x)return xclass ResNet(nn.Module):def __init__(self, block, layers, image_channels, num_classes):super(ResNet, self).__init__()# 在res50中block的堆叠是3 4 6 3# conv1_x# 刚刚输入的时候channel是3在这里conv一下转成64self.in_channels 64self.conv1 nn.Conv2d(image_channels, 64, kernel_size7, stride2, padding3)self.bn1 nn.BatchNorm2d(64)self.relu nn.ReLU()# conv2_xself.maxpool nn.MaxPool2d(kernel_size3, stride2, padding1)# 理论上我们在这里就可以开始一层一层写了比如# self.layer1 ...# self.layer2 ...# 直接定义一个函数替我们写self.layer1 self._make_layer(block, layer[0], out_channel64, stride1)self.layer2 self._make_layer(block, layer[1], out_channel128, stride2)self.layer3 self._make_layer(block, layer[2], out_channel256, stride2)self.layer4 self._make_layer(block, layer[3], out_channel512, stride2)self.avgpool nn.AdaptiveAvgPool2d((1,1))self.fc nn.Linear(512*4, num_classes)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)x self.avgpool(x)x x.reshape(x.shape[0], -1)x self.fc(x)return xdef _make_layer(self, block, num_residual_block, out_channels, stride):identity_downsample Nonelayers []# 如果不能直接相加channel数量变化了# 比如说conv2_x中的第一个block输入64输出256这种显然没办法把64硬加到256上if stride ! 1 or self.inchannels ! out_channels*4:identity_downsample nn.Sequential(nn.Conv2(self.in_channels, out_channels*4, kernel_size1,stride1),nn.BatchNorm2d(out_channels*4))layers.append(block(self.inchannels, out_channels, identity_downsample, stride))self.inchannels out_channels*4for i in range(num_residual_block-1):layers.append(block(self.in_channels, out_channels))return nn.Sequential(*layers)def ResNet50(img_channels, num_classes1000):return ResNet(block, [3,4,6,3], img_channels, num_classes)2
神经网络学习小记录20——ResNet50模型的复现详解_resnet50复现-CSDN博客
https://github.com/pytorch/vision/blob/main/torchvision/models/resnet.py
ResNet50 with PyTorch | Kaggle
GitHub - JayPatwardhan/ResNet-PyTorch: Basic implementation of ResNet 50, 101, 152 in PyTorch
3
Writing ResNet from Scratch in PyTorch
与1中的类似引用如下
class ResidualBlock(nn.Module):def __init__(self, in_channels, out_channels, stride 1, downsample None):super(ResidualBlock, self).__init__()self.conv1 nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size 3, stride stride, padding 1),nn.BatchNorm2d(out_channels),nn.ReLU())self.conv2 nn.Sequential(nn.Conv2d(out_channels, out_channels, kernel_size 3, stride 1, padding 1),nn.BatchNorm2d(out_channels))self.downsample downsampleself.relu nn.ReLU()self.out_channels out_channelsdef forward(self, x):residual xout self.conv1(x)out self.conv2(out)if self.downsample:residual self.downsample(x)out residualout self.relu(out)return outclass ResNet(nn.Module):def __init__(self, block, layers, num_classes 10):super(ResNet, self).__init__()self.inplanes 64self.conv1 nn.Sequential(nn.Conv2d(3, 64, kernel_size 7, stride 2, padding 3),nn.BatchNorm2d(64),nn.ReLU())self.maxpool nn.MaxPool2d(kernel_size 3, stride 2, padding 1)self.layer0 self._make_layer(block, 64, layers[0], stride 1)self.layer1 self._make_layer(block, 128, layers[1], stride 2)self.layer2 self._make_layer(block, 256, layers[2], stride 2)self.layer3 self._make_layer(block, 512, layers[3], stride 2)self.avgpool nn.AvgPool2d(7, stride1)self.fc nn.Linear(512, num_classes)def _make_layer(self, block, planes, blocks, stride1):downsample Noneif stride ! 1 or self.inplanes ! planes:downsample nn.Sequential(nn.Conv2d(self.inplanes, planes, kernel_size1, stridestride),nn.BatchNorm2d(planes),)layers []layers.append(block(self.inplanes, planes, stride, downsample))self.inplanes planesfor i in range(1, blocks):layers.append(block(self.inplanes, planes))return nn.Sequential(*layers)def forward(self, x):x self.conv1(x)x self.maxpool(x)x self.layer0(x)x self.layer1(x)x self.layer2(x)x self.layer3(x)x self.avgpool(x)x x.view(x.size(0), -1)x self.fc(x)return xnum_classes 10
num_epochs 20
batch_size 16
learning_rate 0.01model ResNet(ResidualBlock, [3, 4, 6, 3]).to(device)# Loss and optimizer
criterion nn.CrossEntropyLoss()
optimizer torch.optim.SGD(model.parameters(), lrlearning_rate, weight_decay 0.001, momentum 0.9) # Train the model
total_step len(train_loader) Layers in PyTorch Now coming to the different types of layers available in PyTorch that are useful to us: nn.Conv2d: These are the convolutional layers that accepts the number of input and output channels as arguments, along with kernel size for the filter. It also accepts any strides or padding if we want to apply thosenn.BatchNorm2d: This applies batch normalization to the output from the convolutional layernn.ReLU: This is a type of activation function applied to various outputs in the networknn.MaxPool2d : This applies max pooling to the output with the kernel size givennn.Dropout: This is used to apply dropout to the output with a given probabilitynn.Linear: This is basically a fully connected layernn.Sequential: This is technically not a type of layer but it helps in combining different operations that are part of the same step 看起来和之前的resnet50不太一样的原因是这里是34层的。 因此这里没有313这样的结构了。但是本质上是一样的。 ————————————————————————————
一些题外话虽然在很多地方看到说resnet已经是很老的模型了但是相比于之前的CNN方法而言在方法上确实是非常厉害的创新虽然现在似乎CNN已经被调侃的像上世纪的产物了orz。似乎现在已经是transformer的天下了……