一、ResNet简单介绍
VGGNet的提出,说明了通过提升网络模型的深度,可以提高网络的表达能力,从AlexNet的7层,到VGGNet的16或者19层,再到GoogLeNet的22层。可后来我们发现深度CNN网络达到一定深度后再一味地增加层数并不能带来进一步地分类性能提高,反而会招致网络收敛变得更慢。如下图:56层简单堆叠的网络模型在训练和测试集上表现反而没有20层的效果好。因为非常非常深的神经网络是很难训练的,因为存在梯度消失和梯度爆炸问题。
而ResNets(残差网络)的提出,则能更好的解决这个模型层数加深后带来的精度下降的问题。ResNets中提出了跳跃连接(Skip connection),它可以从某一层网络层获取激活,然后迅速反馈给另外一层,甚至是神经网络的更深层。我们可以利用跳跃连接构建能够训练深度网络的ResNets,网络深度可以达到152层。
二、ResNet50结构介绍
Resnet50里面有两种残差块结构,
第一种输入输出的模型大小保持一致。如下图:
并且先经过1*1的卷积进行降低维度,然后再与3*3的核的进行卷积,最后再经过1*1的卷积恢复恢复,方便与输入连接。
第二种输入输出模型的大小不一致,用于下采样,并且降低feature map通道数时。具体结构如下图:
三、ResNet50具体实现
1.Pytorch实现
import torch
import torch.nn as nn
from torchvision.models import resnet50
from torchvision import transforms
from PIL import Image
Layers = [3, 4, 6, 3]
class Bottleneck(nn.Module):
def __init__(self, in_channels, filters, stride=1, is_downsample = False):
super(Bottleneck, self).__init__()
filter1, filter2, filter3 = filters
self.conv1 = nn.Conv2d(in_channels, filter1, kernel_size=1, stride=stride, bias=False)
self.bn1 = nn.BatchNorm2d(filter1)
self.conv2 = nn.Conv2d(filter1, filter2, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(filter2)
self.conv3 = nn.Conv2d(filter2, filter3, kernel_size=1, stride=1, bias=False)
self.bn3 = nn.BatchNorm2d(filter3)
self.relu = nn.ReLU(inplace=True)
self.is_downsample = is_downsample
self.parameters()
if is_downsample:
self.downsample = nn.Sequential(nn.Conv2d(in_channels, filter3, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(filter3))
def forward(self, X):
X_shortcut = X
X = self.conv1(X)
X = self.bn1(X)
X = self.relu(X)
X = self.conv2(X)
X = self.bn2(X)
X = self.relu(X)
X = self.conv3(X)
X = self.bn3(X)
if self.is_downsample:
X_shortcut = self.downsample(X_shortcut)
X = X + X_shortcut
X = self.relu(X)
return X
class ResNetModel(nn.Module):
def __init__(self):
super(ResNetModel, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(num_features=64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1