李沐-ResNet
ResNet
Deep Residual Learning for Image Recognition
微软研究院的Kaiming He,孙剑等四名华人
2015
摘要
- 网络越深越强,更深的神经网络难训练,提出了一种残差学习框架来减轻网络训练,152层。当时googlenet并行性高,resnet比VGG深8倍,但是resnet深,结构简单,训练起来快
- 在ImageNet测试集上取得了
3.57%的错误率。 - CIFAR-10上分析了100层和1000层的残差网络
- 也赢得了ImageNet检测任务,ImageNet定位任务,COCO检测和COCO分割任务的第一名。
- 网络深了,梯度消失/爆炸,从一开始就阻碍了收敛。然而,这个问题通过标准初始化和中间标准化层在很大程度上已经解决,这使得数十层的网络能通过具有反向传播的随机梯度下降(SGD)开始收敛。
- 当更深的网络能够开始收敛时,暴露了一个退化问题:随着网络深度的增加,准确率达到饱和然后迅速下降。意外的是,这种退化不是由过拟合引起的,并且在适当的深度模型上添加更多的层会导致更高的训练误差
创新点
- 提出了退化现象,极深的残差网络易于优化,但当深度增加时,对应的“简单”网络(简单堆叠层)表现出更高的训练误差
- 提出残差块
- 提出BN,批归一化,丢弃dropout
创新点讲解
两种残差结构BasicBlock和BottleNeck
-
左边的残差结构称为BasicBlock是针对层数较少网络,例如ResNet18层和ResNet34层网络。
-
右边的残差结构BottleNeck是针对网络层数较多的网络,例如ResNet101,ResNet152等。
-
为什么深层网络要使用右侧的残差结构呢。因为,右侧的残差结构能够减少网络参数与运算量。同样输入一个channel为256的特征矩阵,如果使用左侧的残差结构需要大约1170648个参数,但如果使用右侧的残差结构只需要69632个参数。
-
1*1卷积核可以做到升降纬度,为了使的残差块可以同通道相加,需要通过1*1卷积把输入升纬度。在BottleNeck中一个256通道的图先通过1*1卷积
BasicBlock
在resnet18和resnet34中使用了BasicBlock,输入输出通道数均为64,不需要1*1卷积
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = nn.BatchNorm2d(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
BottleNeck
在resnet50、resnet101、resnet152使用了Bottlenect构造网络。Bottleneck Block中使用了1×1卷积层。如输入通道数为256,1×1卷积层会将通道数先降为64,经过3×3卷积层后,再将通道数升为256。1×1卷积层的优势是在更深的网络中,用较小的参数量处理通道数很大的输入。
class Bottleneck(nn.Module):
expansion = 4
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(Bottleneck, self).__init__()
self.conv1 = conv1x1(inplanes, planes)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = conv3x3(planes, planes, stride)
self.bn2 = nn.BatchNorm2d(planes)
self.conv3 = conv1x1(planes, planes * self.expansion)
self.bn3 = nn.BatchNorm2d(planes * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
Batch Normalization
-
Batch Normalization(批归一化)是深度学习中一种重要的归一化技术,由 Sergey Ioffe 和 Christian Szegedy 在 2015 年提出。
-
**原paper中,BN被建议插入在(每个)ReLU激活层前面,因为ReLU激活函数的输出非负,不能近似为高斯分布。**但是后面实验表明,放在前后的差异似乎不大,甚至放在ReLU后还好一些。
-
一个batch有几个图,比如batchsize为4就是4个图拼到一起然后计算均值方差然后归一化。
-
如何做到每个批次的图片组合是随机的,并且同一张图片不会重复出现在同一个批次中:
DataLoader的shuffle=True参数会在每个 epoch 开始时打乱数据顺序,从而保证不同 epoch 的批次组合不同。 -
在推理(Inference)时,如果只有一张图片(无法组成完整的 Batch),可以通过以下方法处理:
- 大多数现代深度学习框架(如 PyTorch、TensorFlow)允许直接输入单张图片,无需强制补全 Batch。
- 全局统计量:PyTorch 的
model.eval()会自动切换 BN 到推理模式。Batch Size=1,BN 层也能正常工作。input.unsqueeze(0) - 如果模型强制要求固定 Batch Size(某些老旧框架),可以通过以下方式补全:batch_tensor = input_tensor.repeat(4, 1, 1, 1)
-








