森说AI:用paddle2.x完成resnet50模型转写并实现前向对齐(1)

本文介绍了如何使用Paddle2.x重现实验PyTorch的ResNet50模型,详细解析了ResNet50的网络结构,卷积神经网络基础,包括卷积、填充、步幅、感受野等概念。同时,文章还讨论了批量操作、池化、ReLU激活函数、批归一化和丢弃法。通过代码实现卷积批归一化块、残差块,但最终未能完全复现模型,作者计划在后续文章中使用PyTorch进行比较。
摘要由CSDN通过智能技术生成

前言

这次飞桨的论文复现的作业是要求去将pytorch的resnet50模型来用paddle来实现,因为我以前根本没有接触过pytorch,所以这里面实现起来还是有点困难的,然后我们这里就开始实现吧!

基础

这里面给了一个pytorch的链接:https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py
但是我有点看不懂,我先去了解一下resnet50的模型结构是什么样的?我看了半天只是懂了点皮毛,然后我们一会结合代码实际看看!

然后这里面说是要对比最后两者的权重啥的,那个我torch的我直接去使用了一下别人已经得到的权重吧,然后将它的权重和我用paddle去实现的权重做一个对比。

resnet50网络结构

在这里插入图片描述
这里我们来看一下resnet50的网络结构:
参考:https://zhuanlan.zhihu.com/p/353235794
从这个图上我们可以看到主要是分成了5个步骤,这里输入的图片格式为:(3,224,224),然后第一层的话是一个7x7的卷积核,个数为64个,这个就是可以代表我们可以去提取特征的数目,然后卷积的步长为2,然后得到的是(3,112,1112)大小的形式,这里可能有人会问了,这是怎么得到的,卷积的计算过程是什么呢?那这里我们先来回顾一下卷积神经网络的基础
参考:https://www.paddlepaddle.org.cn/tutorials/projectdetail/1516119

卷积神经网络基础

在应用卷积之前是怎么做的呢?是将数据变成一维的进行计算,这里可以借鉴我以前的手写数字识别的文章,数据的大小就是WXH,但是这种方法有很多的弊端:空间信息丢失和当图片过大的话,数据就会呈平方式的增长,那样的话会导致参数过大引起过拟合。而使用卷积的话就不会,因为输入的还是原来的形式不用变成一维的,然后就是卷积核也不会受到图片的大小的影响

卷积

这里我们先看一下卷积的运算过程,看下面这张图:
在这里插入图片描述
这里先要去看卷积核就是中间的那个,然后在左面去对应,之后对应相乘相加得到结果

从这个图中我们可以看到,最后得到的形式与卷积核的大小一致,但是加了填充和步幅就不一定了

然后我们来看看下一步:
在这里插入图片描述
就是这样,相当于一个滑动的窗口在滑动,每滑动一次做一次卷积。
这里我们会发现通过卷积的时候输出的形式会变小,为了防止这种事情的发生我们这里可以去使用填充来解决,先看一下特征图的计算过程:
在这里插入图片描述
然后我们看下什么叫做填充:

填充

在这里插入图片描述
这里的话我们再计算一下:比如用4X4的输入与3X3的卷积进行计算,特征图是4-3+1=2
但是我们用1的填充后,输入变成了6X6,然后在计算一下特征图,为6-3+1=4,这样就不改变形式了
最后我们给出一个通用的公式,这样不用去计算填充后的输入,只要用原输入就可以啦
在这里插入图片描述
我们重新计算一下:4+2-3+1=4,ph指的是填充
之后我们看一下步幅:

步幅

这个是最好理解的,就是滑动的间隔的像素点:
在这里插入图片描述
然后的话有了步幅,那么得到的特征图的大小就有不一样了,这里我们给出最后的公式:
在这里插入图片描述
我们来计算一下:(4+0-2)/2 +1 =2
上面的内容可能我们了解的比较多,但是接下来的感受野可能就不是了解的很清楚了

感受野

在这里插入图片描述
感受野理解起来并不是很难,因为我们在输入图像经历卷积的时候,每个卷积核大小的面积区域就会得到一个点,那个那个原图像的区域就是特征图的对应的点的感受野,根据网络结构的加深,感受野会越来越大。

批量操作

在这里插入图片描述
这里面输出多少个特征图是由N决定的,特征图的通道数是由卷积核的数目决定的!已上面的图为例:N=2,所以最后也会生成2个特征图,然后卷积核为2,所以特征图通道数也为2

API介绍

这里主要用到下面这个API:
在这里插入图片描述
如果是图像处理就要2D,如果是视频处理就要用3D,然后说说这里面的参数,第一个是输入图像的通道数,比如说RGB图像就是3,然后是输出的通道数,上面我们也说了,就是卷积核的数目,接下来是卷积核的大小,然后是步长,填充

池化

介绍完了卷积,我们来说说池化!下面看一下这个图:
在这里插入图片描述
当我们得到特征图后,可以用一个值来代替一个区域的值,常用的是平均值池化和最大值池化,这样就可以减少特征图的大小从而减小计算量,然后我们看一下计算过程:
在这里插入图片描述
(4+0-2)/2 +1 = 2 大体上和我们上面的卷积计算过程差不多

在卷积神经网络中通常会用到2X2的池化窗口,并且步幅也为2,这时的话,输出特征图大小是原来特征图大小的一半

Relu激活函数

在这里插入图片描述
在以前的时候用sigmoid会多一点,但是因为会出现梯度消失的情况,所以现在多用Relu函数

批归一化(Batch Normalization)

这个也叫做BN层, 在ResNet中也是应用到了,其目的是对神经网络中间层的输出进行标准化处理,使得中间层的输出更加稳定。我们一般会对数据去使用归一化但是随着网络结构的深入,数据对后面的层影响就不那么大了,所以我们也要对中间层的输出也做归一化处理,这样有很多好处:可以加快收敛,使用更大的学习率去学习,最重要的可以抑制过拟合

丢弃法Dropout

这也是一种抑制过拟合的方法:
在这里插入图片描述
这里面丢弃的是随机的!
学到这里差不多了,我们来继续上面的去看ResNet的网络结构!

再看ResNet网络结构

在这里插入图片描述

党部整除的时候:卷积向下取整,池化向上取整。

我们来说下过程,第一层的话是(224-7)/2 +1= 109呀,为什么这上面写的是112呀,为啥呀?气死我了,弄了半个点才弄明白是因为这里面有一个3的填充,然后我们计算一下:(224-7+6)/2 + 1=112.5 = 112,合理了,哈哈。然后接下来是一个最大池化,我们计算一下:(112+0-3)/2+1=55.5=56,合理,这回会算了,然后到这步我们的特征图的形状为(64,56,56)。然后下面我们结合代码去做

paddle代码实现

这里先说一下1X1卷积核的重要性,它不是为了改变特征图的大小而是为了改变特征图的维度,通过使用改变卷积核的数目来改变维度的大小。
然后像下面这种结构我们也称为瓶颈结构:
在这里插入图片描述
因为输入的维度是256,先通过1X1的卷积核,将维度变成64,然后进行3x3的卷积操作,之后在经历1X1的卷积来增加维度为256,这样大大减少了计算的参数。我们在来看一下示意图:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用Paddle实现50层一维ResNet网络: ```python import paddle import paddle.nn as nn class BasicBlock(nn.Layer): expansion = 1 def __init__(self, in_channels, out_channels, stride=1, downsample=None): super(BasicBlock, self).__init__() self.conv1 = nn.Conv1D(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias_attr=False) self.bn1 = nn.BatchNorm1D(out_channels) self.relu = nn.ReLU() self.conv2 = nn.Conv1D(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias_attr=False) self.bn2 = nn.BatchNorm1D(out_channels) self.downsample = downsample def forward(self, x): identity = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out class Bottleneck(nn.Layer): expansion = 4 def __init__(self, in_channels, out_channels, stride=1, downsample=None): super(Bottleneck, self).__init__() self.conv1 = nn.Conv1D(in_channels, out_channels, kernel_size=1, bias_attr=False) self.bn1 = nn.BatchNorm1D(out_channels) self.conv2 = nn.Conv1D(out_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias_attr=False) self.bn2 = nn.BatchNorm1D(out_channels) self.conv3 = nn.Conv1D(out_channels, out_channels * self.expansion, kernel_size=1, bias_attr=False) self.bn3 = nn.BatchNorm1D(out_channels * self.expansion) self.relu = nn.ReLU() self.downsample = downsample def forward(self, x): identity = 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) if self.downsample is not None: identity = self.downsample(x) out += identity out = self.relu(out) return out class ResNet(nn.Layer): def __init__(self, block, layers, input_channels=12, num_classes=10): super(ResNet, self).__init__() self.in_channels = 64 self.conv1 = nn.Conv1D(input_channels, 64, kernel_size=7, stride=2, padding=3, bias_attr=False) self.bn1 = nn.BatchNorm1D(64) self.relu = nn.ReLU() self.maxpool = nn.MaxPool1D(kernel_size=3, stride=2, padding=1) self.layer1 = self._make_layer(block, 64, layers[0]) self.layer2 = self._make_layer(block, 128, layers[1], stride=2) self.layer3 = self._make_layer(block, 256, layers[2], stride=2) self.layer4 = self._make_layer(block, 512, layers[3], stride=2) self.avgpool = nn.AdaptiveAvgPool1D(1) self.fc = nn.Linear(512 * block.expansion, num_classes) def _make_layer(self, block, out_channels, blocks, stride=1): downsample = None if (stride != 1) or (self.in_channels != out_channels * block.expansion): downsample = nn.Sequential( nn.Conv1D(self.in_channels, out_channels * block.expansion, kernel_size=1, stride=stride, bias_attr=False), nn.BatchNorm1D(out_channels * block.expansion), ) layers = [] layers.append(block(self.in_channels, out_channels, stride, downsample)) self.in_channels = out_channels * block.expansion for _ in range(1, blocks): layers.append(block(self.in_channels, out_channels)) 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) x = self.avgpool(x) x = paddle.flatten(x, 1) x = self.fc(x) return x def ResNet50(input_channels=12, num_classes=10): return ResNet(Bottleneck, [3, 4, 6, 3], input_channels, num_classes) ``` 可以使用以下代码使用paddle.summary可视化该模型: ```python import paddle.summary as summary model = ResNet50(input_channels=12, num_classes=10) summary(model, (12, 1024)) ``` 其中,(12, 1024) 表示输入数据的维度为 (batch_size, input_channels, input_length),在这里我们假设输入数据长度为 1024。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值