深度学习——ResNet18 / 34

ResNet在2015年由微软实验室提出,斩获当年ImageNet竞赛中分类任务第一名,目标检测第一名。获得COCO数据集中目标检测第一名,图像分割第一名。

ResNet34层模型的结构简图:

ResNet网络中的亮点:

  • 超深的网络结构(突破1000层)
  • 提出residual模块(残差结构)
  • 使用Batch Normalization加速训练(丢弃dropout)

在ResNet网络提出之前,传统的卷积神经网络都是通过将一系列卷积层与下采样层进行堆叠得到的。但是当堆叠到一定网络深度时,就会出现两个问题。1)梯度消失或梯度爆炸。 2)退化问题(degradation problem)。

对于这两个问题的解决:

  • 通过数据的预处理以及在网络中使用BN(Batch Normalization)层能够解决梯度消失或梯度爆炸问题。
  • 通过residual结构(残差结构)来减轻退化问题(随着网络层数的加深,效果会变差。如下图)。
    在这里插入图片描述

使用残差结构,随着网络的加深,效果没有变差,反而变得更好了。
在这里插入图片描述
ResNet论文中给出了两种残差结构。左边的残差结构是针对层数较少的网络,例如ResNet18层和ResNet34层网络。右边的是针对网络层数较多的网络,例如ResNet101,ResNet152等。

  • 深层网络使用右侧的残差结构能够减少网络参数与运算量。
  • 同样输入一个channel为256的特征矩阵,如果使用左侧的残差结构,需要大约1170648个参数,使用右侧的残差结构只需要69632个参数。明显搭建深层网络时,使用右侧的残差结构更合适。

在这里插入图片描述
在这里插入图片描述

  • 主分支是由两层3x3的卷积层组成
  • 右侧的连接线是shortcut(捷径)分支,也称捷径分支
  • 主分支与shortcut的输出特征矩阵shape必须相同。输出矩阵相同才能进行相加。

在这里插入图片描述

  • 如上图ResNet34网络结构中存在虚线的残差结构。虚线的残差结构具有降维的作用,并在捷径分支上通过1x1的卷积核进行降维处理。注意步距stride,以及捷径分支上的卷积核的个数(与主分支上的卷积核个数相同)。

在这里插入图片描述

  • 针对ResNet50/101/152的残差结构如上图。在该残差结构中,主分支使用了三个卷积层。
  • 第一层使用1x1的卷积层来压缩channel维度,第二层是3x3卷积层,第三层是1x1的卷积层用来还原channel维度(注意主分支上第一层卷积层和第二次卷积层所使用的卷积核个数是相同的,第三次是第一层的4倍)。
  • 该残差结构所对应的虚线残差结构如下图右侧所示,同样在捷径分支上有一层1x1的卷积层,它的卷积核个数与主分支上的第三层卷积层卷积核个数相同,注意每个卷积层的步距。

下面这幅图是原论文给出的不同深度的ResNet网络结构配置,表中的xN表示将该残差结构重复N次。
在这里插入图片描述

  • 对于我们ResNet18/34/50/101/152,表中conv3_x, conv4_x, conv5_x所对应的一系列残差结构的第一层残差结构都是虚线残差结构。因为这一系列残差结构的第一层都有调整输入特征矩阵shape的使命。
  • 对于我们ResNet50/101/152,其实在conv2_x所对应的一系列残差结构的第一层也是虚线残差结构。因为它需要调整输入特征矩阵的channel。第一层残差结构需要将shape从[56, 56, 64] --> [56, 56, 256]。注意,这里只调整channel维度,高和宽不变(而conv3_x, conv4_x, conv5_x所对应的一系列残差结构的第一层虚线残差结构不仅要调整channel还要将高和宽缩减为原来的一半)。
以下是绘制resnet34训练集准确度曲线和损失函数曲线的PyTorch代码: ```python import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt # Define the ResNet34 model class ResNet34(nn.Module): def __init__(self): super(ResNet34, self).__init__() self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(64) self.relu = nn.ReLU(inplace=True) self.layer1 = nn.Sequential( nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1, bias=False), nn.BatchNorm2d(64), ) self.layer2 = nn.Sequential( nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1, bias=False), nn.BatchNorm2d(128), nn.ReLU(inplace=True), nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1, bias=False), nn.BatchNorm2d(128), ) self.layer3 = nn.Sequential( nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1, bias=False), nn.BatchNorm2d(256), nn.ReLU(inplace=True), nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1, bias=False), nn.BatchNorm2d(256), ) self.layer4 = nn.Sequential( nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1, bias=False), nn.BatchNorm2d(512), nn.ReLU(inplace=True), nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1, bias=False), nn.BatchNorm2d(512), ) self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.fc = nn.Linear(512, 10) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.layer1(x) + x x = self.layer2(x) + x x = self.layer3(x) + x x = self.layer4(x) + x x = self.avgpool(x) x = x.view(x.size(0), -1) x = self.fc(x) return x # Define the training function def train(net, trainloader, criterion, optimizer, epoch): net.train() running_loss = 0.0 correct = 0 total = 0 for i, data in enumerate(trainloader, 0): inputs, labels = data inputs, labels = inputs.cuda(), labels.cuda() optimizer.zero_grad() outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() _, predicted = outputs.max(1) total += labels.size(0) correct += predicted.eq(labels).sum().item() print('Epoch %d, Loss: %.3f, Train Acc: %.3f%%' % (epoch, running_loss/(i+1), 100.*correct/total)) return running_loss/(i+1), 100.*correct/total # Define the test function def test(net, testloader, criterion, epoch): net.eval() running_loss = 0.0 correct = 0 total = 0 with torch.no_grad(): for i, data in enumerate(testloader, 0): inputs, labels = data inputs, labels = inputs.cuda(), labels.cuda() outputs = net(inputs) loss = criterion(outputs, labels) running_loss += loss.item() _, predicted = outputs.max(1) total += labels.size(0) correct += predicted.eq(labels).sum().item() print('Epoch %d, Loss: %.3f, Test Acc: %.3f%%' % (epoch, running_loss/(i+1), 100.*correct/total)) return running_loss/(i+1), 100.*correct/total # Load the CIFAR10 dataset transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) transform_test = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test) testloader = torch.utils.data.DataLoader(testset, batch_size=100, shuffle=False, num_workers=2) # Initialize the ResNet34 model, loss function, and optimizer net = ResNet34().cuda() criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4) # Train the model for 200 epochs train_loss = [] train_acc = [] test_loss = [] test_acc = [] for epoch in range(1, 201): train_loss_epoch, train_acc_epoch = train(net, trainloader, criterion, optimizer, epoch) test_loss_epoch, test_acc_epoch = test(net, testloader, criterion, epoch) train_loss.append(train_loss_epoch) train_acc.append(train_acc_epoch) test_loss.append(test_loss_epoch) test_acc.append(test_acc_epoch) # Plot the training and test curves plt.figure() plt.plot(range(1, 201), train_loss, label='Train Loss') plt.plot(range(1, 201), test_loss, label='Test Loss') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend() plt.show() plt.figure() plt.plot(range(1, 201), train_acc, label='Train Acc') plt.plot(range(1, 201), test_acc, label='Test Acc') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend() plt.show() ``` 希望这个代码对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dunkle.T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值