Resnet是He 等人在2015年提出的网络。用来解决在之前的一些网络,在网络层数增多时,反而其在训练集上error表现下降的现象。这种现象并不是由于过拟合导致的,因为在过拟合的时候,应该是train效果很好,test的效果很差。
下图即为Resnet的两个核心结构,左边的图是在Resnet18/34使用的短路结构,这时的Resnet block使用两个3*3卷积,并add输入X。
右图为在Resnet50/101/152时的Resnet block,这时block由1*1卷积先降低输入的维度,在进行3*3卷积,之后再用1*1卷积来提高维度,并与输入X 完成add。这时有两种情况,第一种情况是X维度与F(x)维度一致,这样可以直接相加;第二种情况就是X维度与F(x)维度不一致,这时就需要改变输入的维度在与F(x)相加。
在右边的block图中为什么不像 左边的block一样,直接使用两个3*3卷积呢,是因为此时输入通道数较高,如果直接使用两个3*3卷积,计算参数量很大,咱们可以来算一下:
1.两个3*3 卷积 3*3*256*256*2 = 1,179,648
2.一个3*3卷积和两个1*1卷积 3*3*64*64+1*1*256*64*2 = 69,632
可以看到第一种方式是第二种方式参数的16.9倍。
接下来就解析两种不同的方式,因为输入和输出的通道可能是不同的,所以就有了这两种方式,先放一下论文里面的图。
如在Resnet50时,conv3_x最后一个block传入conv4_x第一个block的时候,就会产生上述这种问题。所以在每个卷积层的第一个block需要改变输入的通道。
下面我们看看在pytorch代码中这两种block方式对应的。
class Residual(nn.Module):
def __init__(self,input_channels, num_channels, use_1x1conv=False, strides=1, **kwargs):
super(Residual, self).__init__(**kwargs)
self.conv1 = nn.Conv2d(input_channels, num_channels,kernel_size=3, padding=1, stride=strides)
self.conv2 = nn.Conv2d(num_channels, num_channels, kernel_size=3, padding=1)
if use_1x1conv://是否需要改变输入的通道
self.conv3 = nn.Conv2d(input_channels, num_channels, kernel_size=1, stride=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)
self.relu = nn.ReLU(inplace=True)
def forward(self, X):
Y = self.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3://改变输入的通道
X = self.conv3(X)
Y += X
Y =self.relu(Y)
return Y
def resnet_block(input_channels, num_channels, num_residuals, first_block=False):
blk = []
for i in range(num_residuals):
if i == 0 and not first_block://在每一个CONV_x的第一个block添加需要变换输入的block
blk.append(Residual(input_channels, num_channels, use_1x1conv=True, strides=2))
else:
blk.append(Residual(num_channels, num_channels))
return blk
在残差块的传入参数中都加入一个变量use_1x1conv,来判断是否为conv_x的第一个block,如果是,就在该block中新加一个1*1卷积,来变换输入,保证输入与输出的通道一致,这样才可以让两者相加。
面试遇到的问题:参考了该blog
1.为什么之前的网络不能训练深层的网络,ResNet就可以了呢?
解答:神经网络越来越深的时候,反传回来的梯度之间的相关性会越来越差,最后接近白噪声。因为图像是具备局部相关性的,那其实可以认为梯度也应该具备类似的相关性,这样更新的梯度才有意义,如果梯度接近白噪声,那梯度更新可能根本就是在做随机扰动。
2.为什么短路连接可以帮助梯度更好的回传?
解答:ResNet block至少保持梯度不会退化,因为学习那个恒等印射,至少完成了模型不退化,因为输出有着一部分与输入一样为X。这样就提高了梯度相关性。