现代卷积神经网络(ResNet)

这篇具有很好参考价值的文章主要介绍了现代卷积神经网络(ResNet)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

专栏:神经网络复现目录


本章介绍的是现代神经网络的结构和复现,包括深度卷积神经网络(AlexNet),VGG,NiN,GoogleNet,残差网络(ResNet),稠密连接网络(DenseNet)。
文章部分文字和代码来自《动手学深度学习》


残差网络(ResNet)

残差网络(Residual Network,简称 ResNet)是由微软研究院于 2015 年提出的一种深度卷积神经网络。它的主要特点是在网络中添加了“残差块”(Residual Block),有效地解决了深层网络的梯度消失和梯度爆炸问题,从而使得更深的网络结构可以训练得更好。

ResNet 的核心思想是学习残差,即在训练过程中让神经网络学习一个残差映射,该映射将输入直接映射到输出,即 F ( x ) = H ( x ) − x F(x) = H(x) - x F(x)=H(x)x。这里 x x x 表示残差块的输入, H ( x ) H(x) H(x) 表示残差块的输出。在传统的网络结构中,网络的每一层都需要学习一个映射函数,即 H ( x ) H(x) H(x),但这种方法会出现梯度消失和梯度爆炸问题。而使用残差块可以使网络只学习残差部分,即 F ( x ) F(x) F(x),这使得训练更加容易,并且可以构建更深的网络结构。

ResNet 的基本结构是残差块,其中包含了跨层连接(skip connection)的机制。跨层连接可以将输入直接传递到输出端,从而避免了梯度消失问题,同时也减轻了梯度爆炸问题。除了跨层连接之外,ResNet 还采用了批量归一化(Batch Normalization)和池化操作等技巧来提高网络的训练效率和泛化能力。

总体上来说,ResNet 是一种十分有效的深度神经网络结构,其在许多计算机视觉任务上都取得了优异的表现,例如图像分类、物体检测和语义分割等

恒等变换

恒等变换(Identity Transformation)指的是一种变换,使得输入和输出完全相同。在数学上,恒等变换可以用一个函数f(x) = x来表示。

在深度学习中,恒等变换通常用于残差块(Residual Block)中。在残差块中,恒等变换被用作跳跃连接(Shortcut Connection),将输入直接传递给输出,这样可以加速梯度的传播和网络的训练。

举个例子,假设有一个残差块的输入x和输出y,它们的维度都为d。那么该残差块可以表示为:

y = f(x) + x

其中,f(x)是残差块的变换,它会对输入进行处理。而x则是恒等变换,它使得输入可以直接传递给输出。通过这样的设计,残差块可以保留输入中的有用信息,同时仍然能够对输入进行一定程度的处理,从而提高网络的性能。

跳跃连接

跳跃连接(Skip Connection),也称为残差连接(Residual Connection),是深度神经网络中的一种连接方式,用于解决网络训练过程中梯度消失问题。

在跳跃连接中,网络的某一层的输出不仅会传递给下一层进行计算,还会直接传递到距离当前层较远的层。这样可以使得网络中的信息能够更快地传递和共享,同时也可以减轻梯度消失的问题,使得训练过程更加稳定。

在ResNet网络中,跳跃连接被用于将网络的输入直接连接到卷积层的输出上,形成一个残差块。这样,网络的前向传播就变成了从输入到输出的“路径”加上一个残差块的“跳跃”,即“shortcut”。跳跃连接的使用大大改善了ResNet的性能,使得它在ImageNet图像分类等任务中取得了非常优秀的表现。

残差块

残差块(Residual Block)是深度学习中常用的一种模块,可以用来构建深度神经网络。残差块的主要作用是使得神经网络的训练更加容易,并且能够加速神经网络的收敛。

让我们聚焦于神经网络局部:如图所示,假设我们的原始输入为x,而希望学出的理想映射为f(x)作为上方激活函数的输入,左图虚线框中的部分需要直接拟合出该映射f(x),而右图虚线框中的部分则需要拟合出残差映射f(x)-x。残差映射在现实中往往更容易优化。 以本节开头提到的恒等映射作为我们希望学出的理想映射f(x),我们只需将图中右图虚线框内上方的加权运算(如仿射)的权重和偏置参数设成0,那么f(x)即为恒等映射,实际中,当理想映射f(x)极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。
现代卷积神经网络(ResNet)
在深度神经网络中,很容易出现梯度消失或梯度爆炸的问题,这会导致深度神经网络的训练非常困难。残差块的设计可以缓解这个问题。残差块的主要思想是在网络中增加一条跳跃连接(Shortcut Connection),这条连接可以让输入直接跳过一些层,从而更加容易地传递梯度。具体来说,残差块可以分为如下几个步骤:

  1. 输入x经过一个卷积层,得到特征图y1。
  2. 将y1经过一个Batch Normalization层和ReLU激活函数。
  3. 将y1再次经过一个卷积层,得到特征图y2。
  4. 将y2经过一个Batch Normalization层。
  5. 将输入x和y2相加,得到残差特征图y3。
  6. 将y3再经过一个ReLU激活函数。

在这个过程中,输入x可以看做是一种残差,因为它会直接和特征图y2相加。这个残差块的设计可以让网络更容易地学习残差,从而更好地拟合训练数据。此外,残差块也可以增加网络的深度,从而提升网络的效果。

ResNet模型

结构

ResNet原文中给出了几种基本的网络结构配置,本文以ResNet50为例。

现代卷积神经网络(ResNet)
ResNet50结构详解:

  1. 输入层(Input Layer):输入图像的大小为224 x 224 x 3。

  2. 卷积层(Convolution Layer):7x7的卷积核,步长为2,输出通道为64,padding为3。

  3. 标准化层(Batch Normalization Layer):对每个通道的输出做标准化处理,包括均值和方差。

  4. 激活函数(Activation Layer):使用ReLU激活函数。

  5. 最大池化层(Max Pooling Layer):3x3的池化核,步长为2,padding为1,对每个通道的输出取最大值。

  6. 残差块(Residual Block)1:包含3个卷积层和标准化层。第一个卷积层的卷积核为1x1,输出通道为64;第二个卷积层的卷积核为3x3,输出通道为64;第三个卷积层的卷积核为1x1,输出通道为256(因为残差块的输入和输出通道数不同,需要用1x1的卷积核进行通道变换)。

  7. 残差块(Residual Block)2:包含4个卷积层和标准化层。第一个卷积层的卷积核为1x1,输出通道为128;第二个卷积层的卷积核为3x3,输出通道为128;第三个卷积层的卷积核为1x1,输出通道为512。

  8. 残差块(Residual Block)3:包含6个卷积层和标准化层。第一个卷积层的卷积核为1x1,输出通道为256;第二个卷积层的卷积核为3x3,输出通道为256;第三个卷积层的卷积核为1x1,输出通道为1024。

  9. 残差块(Residual Block)4:包含3个卷积层和标准化层。第一个卷积层的卷积核为1x1,输出通道为512;第二个卷积层的卷积核为3x3,输出通道为512;第三个卷积层的卷积核为1x1,输出通道为2048。

  10. 平均池化层(Average Pooling Layer):使用全局平均池化,将输出的特征图转化为向量。

  11. 全连接层(Fully Connected Layer):将向量连接到最终的输出层,该层包含1000个神经元,每个神经元对应于一个类别,表示图像属于该类别的概率。

  12. Softmax层(Softmax Layer):使用softmax函数将全连接层的输出转化为概率分布,每个类别的概率为0到1之间的实数,概率之和为1。

总结:ResNet50网络结构由多个残差块组成,每个残差块内部包含多个卷积层和标准化层。通过使用残差学习的方法,ResNet50网络能够在训练深度神经网络时解决梯度消失和梯度爆炸的问题,同时在图像分类等任务中表现出色。文章来源地址https://www.toymoban.com/news/detail-465776.html

实现

残差块

import torch.nn as nn
import torch.onnx


class Residual(nn.Module):
    def __init__(self, in_channels, channels, stride, downsample=None):
        super(Residual, self).__init__()
        # 1x1的卷积降维操作
        self.conv1 = nn.Conv2d(in_channels=in_channels, out_channels=channels, kernel_size=(1, 1),
                               bias=False)
        self.bn1 = nn.BatchNorm2d(channels)
        # 3x3的卷积提取特征操作
        self.conv2 = nn.Conv2d(in_channels=channels, out_channels=channels, kernel_size=(3, 3),
                               stride=stride,
                               padding=1,
                               bias=False)
        self.bn2 = nn.BatchNorm2d(channels)
        # 1x1的卷积升维操作
        self.conv3 = nn.Conv2d(in_channels=channels, out_channels=channels * 4, kernel_size=(1, 1),
                               bias=False)
        self.bn3 = nn.BatchNorm2d(channels * 4)
        self.relu = nn.ReLU(inplace=True)
        self.downsample = downsample
        # 4组卷积层的头一层网络会做一次降采样,目的是使out和identity维度一致可以做加法
        if self.downsample is not None:
            self.dconv = nn.Conv2d(in_channels, channels * 4, stride=stride, kernel_size=(1, 1), bias=False)
            self.dbn = nn.BatchNorm2d(channels * 4)

    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.dconv(identity)
            identity = self.dbn(identity)

        out += identity
        out = self.relu(out)

        return out

ResNet

class Resnet50(nn.Module):
    def __init__(self, num_classes):
        super(Resnet50, self).__init__()
        self.conv1 = nn.Conv2d(3, 64, kernel_size=(7, 7), stride=2, padding=3, bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu1 = nn.ReLU(inplace=True)
        self.maxpool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
         # 对应第1组网络层,3*Resnet的基本结构
        self.conv64_1 = Residual(64, 64, stride=1, downsample=True)
        self.conv64_2 = Residual(256, 64, stride=1)
        self.conv64_3 = Residual(256, 64, stride=1)
        # 对应第2组网络层,4*Resnet的基本结构
        self.conv128_1 = Residual(256, 128, stride=2, downsample=True)
        self.conv128_2 = Residual(128 * 4, 128, stride=1)
        self.conv128_3 = Residual(128 * 4, 128, stride=1)
        self.conv128_4 = Residual(128 * 4, 128, stride=1)
        # 对应第3组网络层,6*Resnet的基本结构
        self.conv256_1 = Residual(512, 256, stride=2, downsample=True)
        self.conv256_2 = Residual(256 * 4, 256, stride=1)
        self.conv256_3 = Residual(256 * 4, 256, stride=1)
        self.conv256_4 = Residual(256 * 4, 256, stride=1)
        self.conv256_5 = Residual(256 * 4, 256, stride=1)
        self.conv256_6 = Residual(256 * 4, 256, stride=1)
        # 对应第4组网络层,3*Resnet的基本结构
        self.conv512_1 = Residual(1024, 512, stride=2, downsample=True)
        self.conv512_2 = Residual(512 * 4, 512, stride=1)
        self.conv512_3 = Residual(512 * 4, 512, stride=1)
        self.avgpool = nn.AdaptiveAvgPool2d((1,1))
        self.fc = nn.Linear(2048, num_classes)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu1(x)
        x = self.maxpool1(x)
        x = self.conv64_1(x)
        x = self.conv64_2(x)
        x = self.conv64_3(x)
        x = self.conv128_1(x)
        x = self.conv128_2(x)
        x = self.conv128_3(x)
        x = self.conv128_4(x)
        x = self.conv256_1(x)
        x = self.conv256_2(x)
        x = self.conv256_3(x)
        x = self.conv256_4(x)
        x = self.conv256_5(x)
        x = self.conv256_6(x)
        x = self.conv512_1(x)
        x = self.conv512_2(x)
        x = self.conv512_3(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.fc(x)
        return x

利用ResNet50进行CIFAR10分类

数据集

# 导入数据集
from torchvision import datasets
import torch
import torchvision.transforms as transforms
transform = transforms.Compose([
    transforms.Resize((224,224)),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.ToTensor(),
    transforms.Normalize((0.485,0.456,0.406),(0.229,0.224,0.225))
])

classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'trunk')
cifar_train = datasets.CIFAR10(root="/data",train=True, download=True, transform=transform)
cifar_test = datasets.CIFAR10(root="/data",train=False, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(cifar_train, batch_size=16, shuffle=True)
test_loader = torch.utils.data.DataLoader(cifar_test, batch_size=16, shuffle=False)

损失函数优化器

# 定义损失函数和优化器
net=Resnet50(10);
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(net.parameters(), lr=0.001)
epoch = 5
net = net.to(device)
total_step = len(train_loader)
train_all_loss = []
val_all_loss = []

训练

import numpy as np

for i in range(epoch):
    net.train()
    train_total_loss = 0
    train_total_num = 0
    train_total_correct = 0

    for iter, (images,labels) in enumerate(train_loader):
        images = images.to(device)
        labels = labels.to(device)

        outputs = net(images)
        loss = criterion(outputs,labels)
        train_total_correct += (outputs.argmax(1) == labels).sum().item()

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        train_total_num += labels.shape[0]
        train_total_loss += loss.item()
        print("Epoch [{}/{}], Iter [{}/{}], train_loss:{:4f}".format(i+1,epoch,iter+1,total_step,loss.item()/labels.shape[0]))
    net.eval()
    test_total_loss = 0
    test_total_correct = 0
    test_total_num = 0
    for iter,(images,labels) in enumerate(test_loader):
        images = images.to(device)
        labels = labels.to(device)

        outputs = net(images)
        loss = criterion(outputs,labels)
        test_total_correct += (outputs.argmax(1) == labels).sum().item()
        test_total_loss += loss.item()
        test_total_num += labels.shape[0]
    print("Epoch [{}/{}], train_loss:{:.4f}, train_acc:{:.4f}%, test_loss:{:.4f}, test_acc:{:.4f}%".format(
        i+1, epoch, train_total_loss / train_total_num, train_total_correct / train_total_num * 100, test_total_loss / test_total_num, test_total_correct / test_total_num * 100

    ))
    train_all_loss.append(np.round(train_total_loss / train_total_num,4))
    val_all_loss.append(np.round(test_total_loss / test_total_num,4))

可视化

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

plt.figure()
plt.title("Train Loss and Test Loss Curve")
plt.xlabel('plot_epoch')
plt.ylabel('loss')
plt.plot(train_all_loss)
plt.plot(val_all_loss)
plt.legend(['train loss', 'test loss'])

到了这里,关于现代卷积神经网络(ResNet)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 卷积神经网络学习—Resnet50(论文精读+pytorch代码复现)

    如果说在CNN领域一定要学习一个卷积神经网络,那一定非Resnet莫属了。 接下来我将按照:Resnet论文解读、Pytorch实现ResNet50模型两部分,进行讲解,博主也是初学者,不足之处欢迎大家批评指正。 预备知识 :卷积网络的深度越深,提取的特征越高级,性能越好,但传统的卷积

    2024年01月19日
    浏览(32)
  • 【动手学深度学习】现代卷积神经网络汇总

    本文为作者阅读学习李沐老师《动手学深度学习》一书的阶段性读书总结,原书地址为:Dive into Deep Learning。 网络结构 实现代码 网络特征 最早发布的卷积神经网络之一。 每个卷积块中的基本单元是一个卷积层、一个sigmoid激活函数和平均汇聚层。 网络结构 实现代码 网络特

    2024年02月07日
    浏览(33)
  • 人工智能(Pytorch)搭建模型6-使用Pytorch搭建卷积神经网络ResNet模型

    大家好,我是微学AI,今天给大家介绍一下人工智能(Pytorch)搭建模型6-使用Pytorch搭建卷积神经网络ResNet模型,在本文中,我们将学习如何使用PyTorch搭建卷积神经网络ResNet模型,并在生成的假数据上进行训练和测试。本文将涵盖这些内容:ResNet模型简介、ResNet模型结构、生成假

    2024年02月06日
    浏览(41)
  • d2l_第八章学习_现代卷积神经网络

    参考: d2l 研究人员认为: 更大更干净的 数据集 或是稍加改进的特征提取方法,比任何学习算法带来的进步大得多。 认为特征本身应该被学习,即卷积核参数应该是可学习的。 创新点在于GPU与更深的网络,使用ReLU激活函数,Dropout层。 可参考: AlexNet https://blog.csdn.net/qq_4

    2024年02月11日
    浏览(36)
  • 【AI】《动手学-深度学习-PyTorch版》笔记(十九):卷积神经网络模型(GoogLeNet、ResNet、DenseNet)

    发布时间:2014年 GoogLeNet的贡献是如何选择合适大小的卷积核,并将不同大小的卷积核组合使用。 之前介绍的网络结构都是串行的,GoogLeNet使用并行的网络块,称为“Inception块” “Inception块”前后进化了四次,论文链接: [1]https://arxiv.org/pdf/1409.4842.pdf [2]https://arxiv.org/pdf/150

    2024年02月12日
    浏览(38)
  • 深度学习图像分类实战——pytorch搭建卷积神经网络(AlexNet, LeNet, ResNet50)进行场景图像分类(详细)

    目录 1  一、实验过程 1.1  实验目的 1.2  实验简介 1.3  数据集的介绍 1.4  一、LeNet5网络模型 1.5  二、AlexNet网络模型 1.6  三、ResNet50(残差网络)网络模型  二、实验代码 导入实验所需要的库  参数配置 数据预处理 重新DataSet 加载数据转为DataLoader函数 可视化一批训练

    2024年02月05日
    浏览(48)
  • Python基于深度学习机器学习卷积神经网络实现垃圾分类垃圾识别系统(GoogLeNet,Resnet,DenseNet,MobileNet,EfficientNet,Shufflent)

    文章目录 1 前言+ 2 卷积神经网络(CNN)详解+ 2.1 CNN架构概述+ 2.1.1 卷积层+ 2.1.2 池化层+ 2.1.3 全连接层 2.2 CNN训练过程+ 2.3 CNN在垃圾图片分类中的应用 3 代码详解+ 3.1 导入必要的库+ 3.2 加载数据集+ 3.3 可视化随机样本+ 3.4 数据预处理与生成器+ 3.5 构建、编译和训练CNN模型+ 3.5.

    2024年02月04日
    浏览(35)
  • 62、python - 全手写搭建 resnet50 神经网络

    如果将上篇文章中涉及到的算法都自己手写完一遍后,我们开始尝试利用自己手写的算法,搭建一个完整的 resnet50 神经网络。 网络结构就参考这个链接中的网络结构,是在之前下载模型的章节中,下载的模型。 为了搭建一个完整的神经网络,定义一个 Resnet 的类,这个类就

    2024年01月24日
    浏览(33)
  • 【神经网络】(10) Resnet18、34 残差网络复现,附python完整代码

    各位同学好,今天和大家分享一下 TensorFlow 深度学习 中如何搭载 Resnet18 和 Resnet34 残差神经网络,残差网络 利用 shotcut 的方法成功解决了网络退化的问题 ,在训练集和校验集上,都证明了的更深的网络错误率越小。 论文中给出的具体的网络结构如下: Resnet50 网络结构 我已

    2023年04月08日
    浏览(30)
  • 9.现代循环神经网络

    #pic_center R 1 R_1 R 1 ​ R 2 R^2 R 2 1、门控循环单元GRU 首先,让我们看一下叫做门控循环单元的GRU。GRU(Gated Recurrent Unit)是近年来提出的一种神经网络结构。它在我们接下来要讨论的LSTM(Long Short-Term Memory)之后提出。选择先介绍GRU的原因是因为相对来说,它更为简单。相比之下

    2024年02月08日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包