各位同学好,今天和大家分享一下 TensorFlow 深度学习中如何搭载 Resnet18 和 Resnet34 残差神经网络,残差网络利用 shotcut 的方法成功解决了网络退化的问题,在训练集和校验集上,都证明了的更深的网络错误率越小。
论文中给出的具体的网络结构如下:
Resnet50 网络结构我已经在之前的博客中复现过,感兴趣的可以看一下:
感谢简书大佬画的残差网络结构图:
一个残差单元的结构如下。输入为X ;weight layer 代表卷积层,这里是指 convolution卷积层 + batch normalization批标准化层 ;relu 是激活函数 ; identity 是将输入 X 经过变换后与卷积层的输出结果相加,下面会详细说明。
残差块中的第一个卷积层 self.conv1,主要用于下采样特征提取。
如果步长 strides=1,由于padding='same',该层的输入和输出特征图的size不变。属于结构图中左侧蓝色部分。
如果步长 strides=2,表示该层输出的特征图的 size 是输入的特征图 size 的一半。由于卷积核的size是 3*3 ,卷积核移动时会出现滑窗无法覆盖所有的像素格的现象。可能会出现,该层的输出特征图size不等于输入size的一半。通过padding='same'自动填充输入图像,让输出size等于一半的输入size。属于结构图中的左侧后三种颜色的部分
残差块中的第二个卷积层 self.conv2,主要用于进一步提取特征,不进行下采样。
规定其步长 stride=1,由于padding='same',该层的输入和输出特征图的size不变。
完成卷积部分convblock之后,接下来看短接部分identityblock
identity 负责将输入 X的shape 变换到和卷积部分的输出的shape相同。
如果第一个卷积层 self.conv1 的步长 strides=1,那么输入特征图的 shape 和卷积层输出的特征图的 shape 相同,这时 identity 不需要变换输入特征图 X 的shape。
如果第一个卷积层 self.conv1 的步长 strides=2,那么输入特征图的 size 变成了原来的一半。这时,为了能将输入 X 和 卷积层输出结果相加,需要通过 identity 重塑输入 X 的shape。这里使用的是 1*1 卷积传递特征,1*1的卷积核遍历所有的像素格后不会改变特征图的size,设置步长strides=2,成功将特征图的size变成原来的一半。属于结构图中的左侧后三种颜色的部分。
这样,我们就完成了对单个残差块中所有层的初始化,接下来将层之间的前向传播过程写在 call() 函数中。这里需要注意的就是 layers.add([out, identity]) ,将卷积层的输出特征图的结果和输入的特征图相加。identity 只负责将输入特征图的 shape 变换成和卷积部分输出特征图的 shape 相同。
该部分的代码如下:
上面我们已经成功完成了一个残差块,然而一个残差结构是由多个残差块叠加而成的。下面是放大了的结构图,可见 resnet18 中每一个残差结构是由 2 个残差单元组合而成
我们定义一个函数 build_resblock 用来组合残差结构。这里需要注意的是,blocks 代表一个残差结构需要堆叠几个残差单元,resnet18 和 32 中是2个。看结构图可知,在残差结构中只有第一个残差单元具备下采样改变特征图 size 的能力。因此第一个残差块的步长 stride,需要根据输入来确定。而除第一个以外的残差块都不会改变特征图的size,因此固定步长stride=1。每一个残差结构的卷积核个数都是相同的,要通过输入来确定。
上面我们已经完成了残差块的构建,现在我们需要做的就是将这些残差结构按顺序堆叠在一起就能组建残差网络。
首先我们看初始化函数中的代码。self.stem 是用来处理原始输入图像的,假设原始输入的shape为 [224, 224, 3],根据网络结构图设置预处理卷积层的各个参数。通过最大池化 layers.MaxPool2D 指定步长为2,将预处理卷积层的特征图的size减半
接下去就可以根据每个残差结构的配置参数,第一个残差结构 self.layer1 由图可知,没有进行下采样,因此步长 stride=1,第一个残差结构中的卷积核个数统一是64个,每个残差结构由2个残差单元组成 layer_dims=[2,2,2,2],初始化时都是调用的上面定义的残差结构函数 build_resblock。
第二个残差结构 self.layer2 由图可知,第一个残差块进行了下采样,因此,要指定步长 strides=2,特征图的 size 减半,特征图的个数统一都是128。同理其他两个残差结构。
最后将残差层的输出结果经过全局平均池化后放入全连接层,得出分类结果。 layers.GlobalAveragePooling2D() 是在通道维度上对w和h维度求平均值。将特征图的shape从 [b, w, h, c] 变成 [b, 1, 1, c]
完成对所有层的初始化 __init__ 之后,在 call() 方法中定义层与层之间的前向传播的方法。
resnet18 和 resnet34 的差别就在每个残差结构所包含的残差块的个数不同,因此,只需稍作修改修改。我们再看一下这张网络结构表。
resnet18 网络的每个残差层的残差块个数都是2,因此设置参数 ResNet([2, 2, 2, 2]) ,即可返回网络结构。同理,只需要指定 resnet34 网络中每个残差层的残差块个数 ResNet([3, 4, 6, 3]) 。返回得到了输出层的结果,这时只需设置网络输入层的输入维度 model.build(),那么整个网络就构建完了。
利用 model.summary() 查看网络具体的结构,其中第一个 multiple 的值等于 [None, 512],第二个 multiple 的值等于 [None, 1000]。下表中,layer 是 sequential 的 Output Shape 代表每一个残差层的输出 shape
resnet18 网络结构如下:
resnet34 网络结构如下:
到此这篇resnet18网络结构代码(resnet34网络结构)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/21833.html