(pytorch-深度学习)实现残差网络(ResNet)

实现残差网络(ResNet)

  • 我们一般认为,增加神经网络模型的层数,充分训练后的模型理论上能更有效地降低训练误差。
  • 理论上,原模型解的空间只是新模型解的空间的子空间。也就是说,如果我们能将新添加的层训练成恒等映射f(x)=xf(x) = xf(x)=x,新模型和原模型将同样有效。
  • 由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。
  • 然而在实践中,添加过多的层后训练误差往往不降反升。即使利用批量归一化带来的数值稳定性使训练深层模型更加容易,该问题仍然存在。针对这一问题,何恺明等人提出了残差网络(ResNet)。它在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经网络的设计。

残差块

残差块的结构在之前的blog中详细解释了,感兴趣的可以去看。

ResNet沿用了VGG全3×33\times 33×3卷积层的设计。

  • 残差块里首先有2个有相同输出通道数的3×33\times 33×3卷积层。
  • 每个卷积层后接一个批量归一化层和ReLU激活函数
  • 然后输入跳过这两个卷积运算后直接加在最后的ReLU激活函数前。
  • 这样的设计要求两个卷积层的输出与输入形状一样,从而可以相加
  • 如果想改变通道数,就需要引入一个额外的1×11\times 11×1卷积层来将输入变换成需要的形状后再做相加运算。

残差块的实现如下。它可以设定输出通道数、是否使用额外的1×11\times 11×1卷积层来修改通道数以及卷积层的步幅。

import time
import torch
from torch import nn, optim
import torch.nn.functional as Fdevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')class Residual(nn.Module):  def __init__(self, in_channels, out_channels, use_1x1conv=False, stride=1):super(Residual, self).__init__()self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, stride=stride)self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)if use_1x1conv:self.conv3 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride)else:self.conv3 = Noneself.bn1 = nn.BatchNorm2d(out_channels)self.bn2 = nn.BatchNorm2d(out_channels)def forward(self, X):Y = F.relu(self.bn1(self.conv1(X)))Y = self.bn2(self.conv2(Y))if self.conv3:X = self.conv3(X)return F.relu(Y + X)

查看输入和输出形状一致的情况。

blk = Residual(3, 3)
X = torch.rand((4, 3, 6, 6))
blk(X).shape # torch.Size([4, 3, 6, 6])

我们也可以在增加输出通道数的同时减半输出的高和宽。

blk = Residual(3, 6, use_1x1conv=True, stride=2)
blk(X).shape # torch.Size([4, 6, 3, 3])

ResNet模型

ResNet的前两层跟GoogLeNet中的一样:

  • 在输出通道数为64、步幅为2的7×77\times 77×7卷积层后接步幅为2的3×33\times 33×3的最大池化层。
  • 每个卷积层后增加批量归一化层。
net = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),nn.BatchNorm2d(64), nn.ReLU(),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
  • ResNet使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。
  • 第一个模块的通道数同输入通道数一致无须减小高和宽(之前已经使用了步幅为2的最大池化层)。
  • 之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半
def resnet_block(in_channels, out_channels, num_residuals, first_block=False):if first_block:assert in_channels == out_channels # 第一个模块的通道数同输入通道数一致blk = []for i in range(num_residuals):if i == 0 and not first_block:blk.append(Residual(in_channels, out_channels, use_1x1conv=True, stride=2))else:blk.append(Residual(out_channels, out_channels))return nn.Sequential(*blk)

接着我们为ResNet加入所有残差块。这里每个模块使用两个残差块

net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
net.add_module("resnet_block2", resnet_block(64, 128, 2))
net.add_module("resnet_block3", resnet_block(128, 256, 2))
net.add_module("resnet_block4", resnet_block(256, 512, 2))

加入全局平均池化层后接上全连接层输出。

class GlobalAvgPool2d(nn.Module):# 全局平均池化层可通过将池化窗口形状设置成输入的高和宽实现def __init__(self):super(GlobalAvgPool2d, self).__init__()def forward(self, x):return F.avg_pool2d(x, kernel_size=x.size()[2:])class FlattenLayer(torch.nn.Module):def __init__(self):super(FlattenLayer, self).__init__()def forward(self, x): # x shape: (batch, *, *, ...)return x.view(x.shape[0], -1)
net.add_module("global_avg_pool", GlobalAvgPool2d()) # GlobalAvgPool2d的输出: (Batch, 512, 1, 1)
net.add_module("fc", nn.Sequential(FlattenLayer(), nn.Linear(512, 10))) 
  • 这里每个模块里有4个卷积层(不计算1×11\times 11×1卷积层),加上最开始的卷积层和最后的全连接层,共计18层。这个模型通常也被称为ResNet-18。
  • 通过配置不同的通道数和模块里的残差块数可以得到不同的ResNet模型,例如更深的含152层的ResNet-152。虽然ResNet的主体架构跟GoogLeNet的类似,但ResNet结构更简单,修改也更方便。这些因素都导致了ResNet迅速被广泛使用。
X = torch.rand((1, 1, 224, 224))
for name, layer in net.named_children():X = layer(X)print(name, ' output shape:\t', X.shape)
0  output shape:	 torch.Size([1, 64, 112, 112])
1  output shape:	 torch.Size([1, 64, 112, 112])
2  output shape:	 torch.Size([1, 64, 112, 112])
3  output shape:	 torch.Size([1, 64, 56, 56])
resnet_block1  output shape:	 torch.Size([1, 64, 56, 56])
resnet_block2  output shape:	 torch.Size([1, 128, 28, 28])
resnet_block3  output shape:	 torch.Size([1, 256, 14, 14])
resnet_block4  output shape:	 torch.Size([1, 512, 7, 7])
global_avg_pool  output shape:	 torch.Size([1, 512, 1, 1])
fc  output shape:	 torch.Size([1, 10])

获取数据

def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):"""Download the fashion mnist dataset and then load into memory."""trans = []if resize:trans.append(torchvision.transforms.Resize(size=resize))trans.append(torchvision.transforms.ToTensor())transform = torchvision.transforms.Compose(trans)mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)if sys.platform.startswith('win'):num_workers = 0  # 0表示不用额外的进程来加速读取数据else:num_workers = 4train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)return train_iter, test_iter
batch_size = 256
# 如出现“out of memory”的报错信息,可减小batch_size或resize
train_iter, test_iter = load_data_fashion_mnist(batch_size, resize=96)

训练模型

def train(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs):net = net.to(device)print("training on ", device)loss = torch.nn.CrossEntropyLoss()for epoch in range(num_epochs):train_l_sum, train_acc_sum, n, batch_count, start = 0.0, 0.0, 0, 0, time.time()for X, y in train_iter:X = X.to(device)y = y.to(device)y_hat = net(X)l = loss(y_hat, y)optimizer.zero_grad()l.backward()optimizer.step()train_l_sum += l.cpu().item()train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()n += y.shape[0]batch_count += 1test_acc = evaluate_accuracy(test_iter, net)print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))
lr, num_epochs = 0.001, 5
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
train(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/507976.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(pytorch-深度学习)实现稠密连接网络(DenseNet)

稠密连接网络(DenseNet) ResNet中的跨层连接设计引申出了数个后续工作。稠密连接网络(DenseNet)与ResNet的主要区别在于在跨层连接上的主要区别: ResNet使用相加DenseNet使用连结 ResNet(左)…

(pytorch-深度学习)语言模型-学习笔记

语言模型 自然语言处理中最常见的数据是文本数据。我们可以把一段自然语言文本看作一段离散的时间序列。 假设一段长度为TTT的文本中的词依次为w1,w2,…,wTw_1, w_2, \ldots, w_Tw1​,w2​,…,wT​,那么在离散的时间序列中: wtw_twt​(1≤t…

(pytorch-深度学习)循环神经网络

循环神经网络 在nnn元语法中,时间步ttt的词wtw_twt​基于前面所有词的条件概率只考虑了最近时间步的n−1n-1n−1个词。如果要考虑比t−(n−1)t-(n-1)t−(n−1)更早时间步的词对wtw_twt​的可能影响,需要增大nnn。 这样模型参数的数量将随之呈指数级增长…

配置jupyter-pytorch深度学习环境

配置jupyter-pytorch深度学习环境 安装anaconda3新建环境,命名为pytorch在虚拟环境里安装jupyter activate pytorch pip install jupyter安装可视化插件,ipywidgets,并且关联 pip install ipywidgets jupyter nbextension enable --py wid…

(pytorch-深度学习)SE-ResNet的pytorch实现

SE-ResNet的pytorch实现 残差块: class Resiual_block(nn.Module):def __init__(self, in, middle_out, out, kernel_size3, padding1):self.out_channel middle_outsuper(Resiual_block, self).__init__()self.shortcut nn.Sequential(nn.Conv2d(nin, nout, ke…

(pytorch-深度学习)循环神经网络的从零开始实现

循环神经网络的从零开始实现 首先,我们读取周杰伦专辑歌词数据集: import time import math import numpy as np import torch from torch import nn, optim import torch.nn.functional as F import sys sys.path.append("..") device tor…

(pytorch-深度学习)使用pytorch框架nn.RNN实现循环神经网络

使用pytorch框架nn.RNN实现循环神经网络 首先,读取周杰伦专辑歌词数据集。 import time import math import numpy as np import torch from torch import nn, optim import torch.nn.functional as Fimport sys sys.path.append("..") device torch.d…

(pytorch-深度学习)通过时间反向传播

通过时间反向传播 介绍循环神经网络中梯度的计算和存储方法,即通过时间反向传播(back-propagation through time)。 正向传播和反向传播相互依赖。正向传播在循环神经网络中比较直观,而通过时间反向传播其实是反向传播在循环神经…

(pytorch-深度学习)门控循环单元(GRU)

门控循环单元(GRU) 循环神经网络中的梯度计算 当时间步数较大或者时间步较小时,循环神经网络的梯度较容易出现衰减或爆炸。虽然裁剪梯度可以应对梯度爆炸,但无法解决梯度衰减的问题。通常由于这个原因,循环神经网络在…

(pytorch-深度学习)长短期记忆(LSTM)

长短期记忆(LSTM) LSTM 中引入了3个门,即 输入门(input gate)遗忘门(forget gate)输出门(output gate)以及与隐藏状态形状相同的记忆细胞(某些文献把记忆细…

(pytorch-深度学习)深度循环神经网络

深度循环神经网络 循环神经网络只有一个单向的隐藏层,在深度学习应用里,我们通常会用到含有多个隐藏层的循环神经网络,也称作深度循环神经网络。 下图演示了一个有LLL个隐藏层的深度循环神经网络,每个隐藏状态不断传递至当前层的…

(pytorch-深度学习)双向循环神经网络

双向循环神经网络 一般,我们认为循环神经网络模型都是假设当前时间步是由前面的较早时间步的序列决定的,因此它们都将信息通过隐藏状态从前往后传递。 有时候,当前时间步也可能由后面时间步决定。 例如,当我们写下一个句子时&…

pytorch实现梯度下降、随机梯度下降-图像直观展示

深度学习与优化算法原理 优化函数与深度学习 在一个深度学习问题中,通常需要预先定义一个损失函数。有了损失函数以后,使用优化算法试图将其最小化。 在优化中,这样的损失函数通常被称作优化问题的目标函数(objective function…

小批量随机梯度下降

小批量随机梯度下降 在每一次迭代中,梯度下降使用整个训练数据集来计算梯度,因此它有时也被称为批量梯度下降(batch gradient descent)。 随机梯度下降在每次迭代中只随机采样一个样本来计算梯度。可以在每轮迭代中随机均匀采样…

动量法解决梯度下降的一些问题

动量法 目标函数有关自变量的梯度代表了目标函数在自变量当前位置下降最快的方向,因此,梯度下降也叫作最陡下降(steepest descent)。在每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变…

深度学习AdaGrad算法

AdaGrad算法 在一般的优化算法中,目标函数自变量的每一个元素在相同时间步都使用同一个学习率来自我迭代。 例如,假设目标函数为fff,自变量为一个二维向量[x1,x2]⊤[x_1, x_2]^\top[x1​,x2​]⊤,该向量中每一个元素在迭代时都使…

深度学习优化算法:RMSProp算法

RMSProp算法 在AdaGrad算法中,因为调整学习率时分母上的变量st\boldsymbol{s}_tst​一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。因此,当学习率在迭代…

深度学习-参数与超参数

参数(parameters)/模型参数 由模型通过学习得到的变量比如权重、偏置 超参数(hyperparameters)/算法参数 根据经验进行设定,影响到权重和偏置的大小比如迭代次数、隐藏层的层数、每层神经元的个数、学习速率等

深度学习优化算法-AdaDelta算法

AdaDelta算法 除了RMSProp算法以外,另一个常用优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进 [1]。 不一样的是,AdaDelta算法没有学习率这个超参数。 它通过使用有关自变量更新量平方的指数加权移动平均的项来替代…

深度学习优化算法-Adam算法

Adam算法 Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均。Adam算法可以看做是RMSProp算法与动量法的结合。 算法内容 Adam算法使用了动量变量vt\boldsymbol{v}_tvt​和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量st\boldsymbol{s}_…