ResNet网络诞生背景
来源:百度百科
孙剑(1976年10月—2022年6月14日),男,出生于西安 [6],人工智能领域科学家 [7],生前为旷视科技首席科学家、旷视研究院院长、西安交通大学人工智能学院首任院长 [6]。
孙剑于1997年在西安交通大学获工学学士学位,2000年在西安交通大学获工学硕士学位,2003年在西安交通大学获工学博士学位 [4];2003年在微软亚洲研究院担任首席研究员,2016年7月加入旷视科技任首席科学家和旷视研究院(Megvii Research)负责人;2022年6月14日,因突发疾病抢救无效逝世,年仅45岁。 [6]
孙剑主要研究方向是计算机视觉和计算摄影学、人脸识别和基于深度学习的图像理解。 [6]
网络加深带来的问题是什么呢
模型退化的原因在于:
1.不可能让w永远保持为1
2.每一层经过激活函数的处理,会带来特征的信息损失(有效特征或者冗余特征)
ResNet网络创新的地方-残差块
批量规范化层(归一化,是含有网络参数的),是需要通过反向传播来更新参数的。
之前的GoogLeNet网络中的局部归一化是不含网络参数的。
2种残差块结构
(一种含有1*1卷积,保证与输入具有相同的宽高和通道数,进而方便和输入进行相加运算,一种是不含的)
1X1卷积(通过改变步长来和批量规范化层输出的特征大小保持一致卷积核的数量,来保证通道数是一致的,以便进行相加操作)
具体的搭建
import torch
from torch import nn
from torchsummary import summary
#借鉴GoogLeNet网络的Inception模块的搭建,因此可以先搭建一个残差块(模板式,后续直接调用即可)
class Residual(nn.Module):
#定义初始化函数
def __init__(self,input_channels,num_channels,use_1conv =False,strides=1):
super(Residual).__init__()
self.ReLu = nn.ReLU()
self.conv1 = nn.Conv2d(in_channels=input_channels ,out_channels=num_channels,kernel_size=3,padding=1,stride=strides)
self.conv2 = nn.Conv2d(in_channels=num_channels ,out_channels= num_channels,kernel_size=3,padding=1)
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)
##两种残差块,有参数的是有1*1卷积的,use_1conv为真时,有1*1卷积块,为假时,为空,一般填充为0,会发现参数不够用
if use_1conv:
self.conv3 = nn.Conv2d(in_channels=input_channels ,out_channels= num_channels,kernel_size=1,stride=strides)
else:
self.conv3 = None
#利用前向传播,来进行一个模型的构建
def forward(self,x):
y = self.ReLu(self.bn1(self.conv1(x)))
y = self.bn2(self.conv2(y))
#条件判断,是否有conv3,conv3 = None,条件为假,则没有
if self.conv3:
x = self.conv3(x)
y =self.ReLu(y+x)
return y
ResNet网络创新的地方-BN操作
归一化处理,消除了物理量纲对结果的影响(同一起跑线,那个特征对结果的影响较大,对应的w就多一些更新)
第四步的作用在于普遍性(而不是单纯的几个激活函数的效果比较好)
既要收敛速度快,还不要近似于线性函数使得弱化网络性能。
第四步得到的性能是最差的情况是和没有处理的性能是一样的。
ResNet网络参数详解
以ResNet-18为例
网络结构,巧妙,简单,这才是真正厉害的地方
3*3最大池化的作用是降低分辨率(尺寸变为原来的一半)
步幅为1,填充为1,3*3的卷积核,特征图大小是不变的
计算特征图大小的公式不要忘了哦!
上图中右括号处写的3个(表示带有1*1卷积+不带有的构成一个小结构,3个就是共有3个这样的小结构)
注意一下,这个1*1的卷积层,填充一般为0,步幅的大小,与刚进入残差块的第一个卷积核的步幅大小一致。即1*1卷积的卷积核个数和步长是根据实时的变换而变换,不是固定不变的步长。
全局平均池化模块,在上一篇GoogLeNet中强调了,这里就不再提了。
ResNet网络模型搭建
import torch
from torch import nn
from torchsummary import summary
#借鉴GoogLeNet网络的Inception模块的搭建,因此可以先搭建一个残差块(模板式,后续直接调用即可)
class Residual(nn.Module):
#定义初始化函数,use_1conv =False默认为假,是不存在1*1卷积函数的。
def __init__(self, input_channels,num_channels,use_1conv =False,strides=1):
super(Residual,self).__init__()
self.ReLu = nn.ReLU()
self.conv1 = nn.Conv2d(in_channels=input_channels ,out_channels=num_channels,kernel_size=3,padding=1,stride=strides)
self.conv2 = nn.Conv2d(in_channels=num_channels ,out_channels= num_channels,kernel_size=3,padding=1)
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)
##两种残差块,有参数的是有1*1卷积的,use_1conv为真时,有1*1卷积块,为假时,为空,一般填充为0,会发现参数不够用
if use_1conv:
self.conv3 = nn.Conv2d(in_channels=input_channels ,out_channels= num_channels,kernel_size=1,stride=strides)
else:
self.conv3 = None
#利用前向传播,来进行一个模型的构建
def forward(self,x):
y = self.ReLu(self.bn1(self.conv