一、残差网络
残差网络于2016年发表,在ImageNet数据集上碾压众多目标检测与图像分割模型,他的出现对深度神经网络来说i具有重大的历史意义。
在这之前,理论上来说,神经网络深度越深可以提取到更多的数据特征,但事实上会出现会出出现梯度消失的情况。因为神经网络的训练过程是误差反向传播不断更新梯度,从而优化模型参数的过程。但当网络层数加深时,梯度在传播过程中会逐渐消失(梯度弥散),无法有效地对前层网络全重进行优化。
二、ResNet网络解析
相比于传统的平原网络,ResNet增加了跳跃连接(shortcut connection)。相邻卷积之间有激活函数,shortcut后也相应的有激活函数。输出实现恒等映射。
class ResidualBlock(nn.Module):
"""
实现子module: Residual Block
"""
def __init__(self, inchannel, outchannel, stride=1, shortcut=None):
super(ResidualBlock, self).__init__()
self.left = nn.Sequential(
nn.Conv2d(inchannel, outchannel, 3, stride, 1, bias=False),
nn.BatchNorm2d(outchannel),
nn.ReLU(inplace=True),
nn.Conv2d(outchannel, outchannel, 3, 1, 1, bias=False),
nn.BatchNorm2d(outchannel)
)
self.right = shortcut
def forward(self, x):
out = self.left(x)
residual = x if self.right is None else self.right(x)
out += residual
return F.relu(out)
这样做的优势:因为恒等映射, 所以可以对残差部分进行冻结训练。
在传统的平原神经网络里边,可能网络训练已经达到最优,但随着网络的继续epoch,全重不断更新,但可能会导致全重朝着的错误的方向更新。
ResNe网络结构:
class ResNet(nn.Module):
"""
实现主module:ResNet34
ResNet34包含多个layer,每个layer又包含多个Residual block
用子module来实现Residual block,用_make_layer函数来实现layer
"""
def __init__(self, blocks, num_classes=1000):
super(ResNet, self).__init__()
self.model_name = 'resnet34'
# 前几层: 图像转换
self.pre = nn.Sequential(
nn.Conv2d(3, 64, 7, 2, 3, bias=False),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True),
nn.MaxPool2d(3, 2, 1))
# 重复的layer,分别有3,4,6,3个residual block
self.layer1 = self._make_layer(64, 64, blocks[0])
self.layer2 = self._make_layer(64, 128, blocks[1], stride=2)
self.layer3 = self._make_layer(128, 256, blocks[2], stride=2)
self.layer4 = self._make_layer(256, 512, blocks[3], stride=2)
# 分类用的全连接
self.fc = nn.Linear(512, num_classes)
def _make_layer(self, inchannel, outchannel, block_num, stride=1):
"""
构建layer,包含多个residual block
"""
shortcut = nn.Sequential(
nn.Conv2d(inchannel, outchannel, 1, stride, bias=False),
nn.BatchNorm2d(outchannel),
nn.ReLU()
)
layers = []
layers.append(ResidualBlock(inchannel, outchannel, stride, shortcut))
for i in range(1, block_num):
layers.append(ResidualBlock(outchannel, outchannel))
return nn.Sequential(*layers)
def forward(self, x):
x = self.pre(x)
l1_out = self.layer1(x)
l2_out = self.layer2(l1_out)
l3_out = self.layer3(l2_out)
l4_out = self.layer4(l3_out)
p_out = F.avg_pool2d(l4_out, 7)
fea = p_out.view(p_out.size(0), -1)
out=self.fc(fea)
return l1_out,l2_out,l3_out,l4_out,fea,out
每一层的输出 = 上一层输出 + 上边所有层的卷积结果
梯度表示为:
即使深层卷积深度不断增加,出现梯度为0的情况,但由于多了个‘1’(shortcurt connnect),梯度反向传播的时候也能持续传递下去,不会出现阻断的情况。