目录
一、前言
二、论文解读
1、ResNetV2结构与ResNet结构对比
2、关于残差结构的不同尝试
3、关于激活的尝试
三、模型复现
1.Residual Block
3、ResNet50V2架构复现
4.ResNet50V2模型结构大图
一、前言
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊|接辅导、项目定制
● 难度:夯实基础⭐⭐
● 语言:Python3、Pytorch3
● 时间:2月10日-2月17日
🍺要求:
1.根据本文的Tensorflow代码,编写Pytorch代码
2.了解ResnetV2和Resnet的区别
3.改进地方可以迁移到哪里呢
二、论文解读
论文:Identity Mappings in Deep Residual Networks
论文的主要贡献:
分析了残差块的传播公式
提出了一种新的残差单元
从理论上证明为什么残差网络有效(为什么可以让梯度在网络中顺畅传递而不会爆炸和消失)
1、ResNetV2结构与ResNet结构对比
🧲 改进点:
(a)original表示原始的ResNet的残差结构,(b)proposed表示新的ResNet的残差结构。
主要差别就是
(a)结构先卷积后进行BN和激活函数计算,最后执行addition后再进行ReLU计算;(b)结构先进性BN和激活函数计算后卷积,把addition后的ReLU计算放到了残差结构内部。
📌 改进结果:作者使用这两种不同的结构再CIFAR-10数据集上做测试,模型用的是1001层的ResNet模型。从图中的结果我们可以看出,(b)proposed的测试集错误率明显更低一些,达到了4.92%的错误率。(a)original的测试集错误率是7.61%
2、关于残差结构的不同尝试
(b-f)中的快捷连接被不同的组件阻碍。为了简化插图,我们不显示BN层,这里所有的单位均采用权值层之后的BN层。图中(a-f)都是作者对残差结构的shortcut部分进行的不同尝试,作者对不同shortcut结构的尝试结果如下表所示。
作者用不同的shortcut结构的ResNet-110在CIFAR-10数据集上做测试,发现最原始的(a)original结构是最好的,也就是identity mapping恒等映射是最好的。
3、关于激活的尝试
最好的结果是(e)full pre-activation,其次是(a)original。
三、模型复现
1.Residual Block
''' 残差块
Arguments:
x: 输入张量
filters: integer, filters, of the bottleneck layer.
kernel_size: default 3, kernel size of the bottleneck layer.
stride: default 1, stride of the first layer.
conv_shortcut: default False, use convolution shortcut if True, otherwise identity shortcut.
name: string, block label.
Returns:
Output tensor for the residual block.
'''
def block2(x, filters, kernel_size=3, stride=1, conv_shortcut=False, name=None):
preact = BatchNormalization(name=name+'_preact_bn')(x)
preact = Activation('relu', name=name+'_preact_relu')(preact)
if conv_shortcut:
shortcut = Conv2D(4*filters, 1, strides=stride, name=name+'_0_conv')(preact)
else:
shortcut = MaxPooling2D(1, strides=stride)(x) if stride>1 else x
x = Conv2D(filters, 1, strides=1, use_bias=False, name=name+'_1_conv')(preact)
x = BatchNormalization(name=name+'_1_bn')(x)
x = Activation('relu', name=name+'_1_relu')(x)
x = ZeroPadding2D(padding=((1, 1), (1, 1)), name=name+'_2_pad')(x)
x = Conv2D(filters, kernel_size, strides=stride, use_bias=False, name=name+'_2_conv')(x)
x = BatchNormalization(name=name+'_2_bn')(x)
x = Activation('relu', name=name+'_2_relu')(x)
x = Conv2D(4*filters, 1, name=name+'_3_conv')(x)
x = layers.Add(name=name+'_out')([shortcut, x])
return x
2.堆叠Residual Block
def stack2(x, filters, blocks, stride1=2, name=None):
x = block2(x, filters, conv_shortcut=True, name=name+'_block1')
for i in range(2, blocks):
x = block2(x, filters, name=name+'_block'+str(i))
x = block2(x, filters, stride=stride1, name=name+'_block'+str(blocks))
return x
3、ResNet50V2架构复现
''' 构建ResNet50V2 '''
def ResNet50V2(include_top=True, # 是否包含位于网络顶部的全链接层
preact=True, # 是否使用预激活
use_bias=True, # 是否对卷积层使用偏置
weights='imagenet',
input_tensor=None, # 可选的keras张量,用作模型的图像输入
input_shape=None,
pooling=None,
classes=1000, # 用于分类图像的可选类数
classifer_activation='softmax'): # 分类层激活函数
img_input = Input(shape=input_shape)
x = ZeroPadding2D(padding=((3, 3), (3, 3)), name='conv1_pad')(img_input)
x = Conv2D(64, 7, strides=2, use_bias=use_bias, name='conv1_conv')(x)
if not preact:
x = BatchNormalization(name='conv1_bn')(x)
x = Activation('relu', name='conv1_relu')(x)
x = ZeroPadding2D(padding=((1, 1), (1, 1)), name='pool1+pad')(x)
x = MaxPooling2D(3, strides=2, name='pool1_pool')(x)
x = stack2(x, 64, 3, name='conv2')
x = stack2(x, 128, 4, name='conv3')
x = stack2(x, 256, 6, name='conv4')
x = stack2(x, 512, 3, strides=1, name='conv5')
if preact:
x = BatchNormalization(name='post_bn')(x)
x = Activation('relu', name='post_relu')(x)
if include_top:
x = GlobalAveragePooling2D(name='avg_pool')(x)
x = Dense(classes, activation=classifer_activation, name='predictions')(x)
else:
if pooling=='avg':
# GlobalAveragePooling2D就是将每张图片的每个通道值各自加起来再求平均,
# 最后结果是没有了宽高维度,只剩下个数与平均值两个维度
# 可以理解成变成了多张单像素图片
x = GlobalAveragePooling2D(name='avg_pool')(x)
elif pooling=='max':
x = GlobalMaxPooling2D(name='max_pool')(x)
model = Model(img_input, x, name='resnet50v2')
return model
4.ResNet50V2模型结构大图