Python----神经网络(《Deep Residual Learning for Image Recognition》论文和ResNet网络结构)

一、论文

1.1、论文基本信息 

  • 标题:Deep Residual Learning for Image Recognition

  • 作者:Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun

  • 单位:Microsoft Research

  • 会议:CVPR 2016

  • 主要贡献:提出了一种深度残差学习框架(Residual Learning Framework),有效解决了深度神经网络训练中的退化问题(Degradation Problem),使得可以训练更深的神经网络,并在图像识别任务中取得了显著的性能提升。

1.2、主要内容

1.2.1、退化问题(Degradation Problem)

        随着网络深度的增加,模型的训练误差和测试误差都会增加,这种现象被称为退化问题。  退化问题不是由过拟合引起的,而是由于深度网络难以优化所致。

1.2.2、残差学习框架(Residual Learning Framework)

        核心思想:将网络层学习的目标从原始的映射函数改为原始映射函数和输入的差,即残差映射函数。  

        残差块(Residual Block):通过shortcut连接将输入直接加到某些层(通常是两到三层)的输出上。  

1.2.3、网络结构

        提出了一种基于残差学习的深度卷积神经网络——ResNet。  设计了18层、34层、50层、101层、152层等不同深度的ResNet,并通过实验验证了其有效性。  ResNet-152是当时ImageNet上最深的网络结构。  

1.2.4、实验结果

        在ImageNet分类任务上,ResNet取得了显著的成果,获得了ILSVRC 2015分类任务的第一名。  在COCO目标检测数据集上,ResNet也取得了28%的相对提升。  

        实验结果表明,ResNet能够有效解决退化问题,并且能够通过增加网络深度来提高性能。

1.3、作用

        解决了深度神经网络的退化问题,使得训练更深的网络成为可能。  

        提高了图像识别的准确率,在多个基准数据集上取得了state-of-the-art的结果。  

        推动了深度学习的发展,为后续的计算机视觉研究提供了新的思路和方法。  

1.4、影响

        ResNet是深度学习领域的一项重大突破,对后续的深度学习研究产生了深远的影响。

        许多后续的网络结构,如DenseNet、MobileNet等,都借鉴了ResNet的思想。

        ResNet被广泛应用于各种计算机视觉任务,如图像分类、目标检测、语义分割等。

1.5、优点

        解决了退化问题,可以训练非常深的网络。  

        网络性能好,显著提高了图像识别的准确率。  

        结构简洁,易于实现和扩展。  

1.6、缺点

        shortcut连接方式较为简单,可能不是最优的选择。

        计算效率有待进一步优化,虽然比VGG网络计算量小,但仍然较大。  

论文地址:

        [1512.03385] Deep Residual Learning for Image Recognition 

二、ResNet

2.1、网络的基本介绍

        ResNet(“残差网络”的简称)是一种深度神经网络,由Microsoft研究团队于2015年提出。它在当时的ImageNet 比赛获得了图像分类第一名,目标检测第一名,在COCO数据集目标检测第一名,图像分割第一名。

        ResNet的主要特点是采用了残差学习机制。在传统的神经网络中,每一层的输出都是直接通过一个非线性激活函 数得到的。但在ResNet中,每一层的输出是通过一个“残差块”得到的,该残差块包含了一个快捷连接 (shortcut)和几个卷积层。这样,在训练过程中,每一层只需要学习残差(即输入与输出之间的差异),而不 是所有的信息。这有助于防止梯度消失和梯度爆炸的问题,从而使得网络能够训练得更深。

        ResNet的网络结构相对简单,并且它的训练速度也比GoogLeNet快。这使得ResNet成为了在许多计算机视觉任 务中的首选模型。

        ResNet的主要优点是具有非常深的层数,可以达到1000多层,但仍然能够高效地训练。这是通过使用残差连接来 实现的,这种连接允许模型学习跨越多个层的残差,而不是直接学习每一层的输出。这使得ResNet能够更快地收 敛,并且能够更好地泛化到新的数据集,ResNet论文中共提出了五种结构,分别是ResNet-18,ResNet-34, ResNet-50,ResNet-101,ResNet-152。

2.2、 更深的网络层数

        上图都是直接堆叠神经网络的结果,在左侧图中,黄色线是训练过程中20层网络的训练损失曲线,红色线是训练 过程中56层网络的训练损失曲线,理论上讲,网络深可以带来更小的损失,但是实时恰恰相反,56层的错误率要 高于20层的错误率。

        主要有两个原因:

        1. 梯度消失或梯度爆炸:例如在一个网络中,每一层的损失梯度的值都小于1,那么连续的链式法则之下,每向 前传播一次,都要乘以一个小于1的误差梯度,那么如果网络越深,在经过非常多的前向传播次数之后,那么 梯度越来越小,直到接近于0,这就是梯度消失。但是如果每一层的损失梯度的值都大于1,那么网络越深,在 经过非常多的前向传播次数之后,那么梯度越来越大,导致梯度爆炸。但是误差梯度肯定不会一直是1或者是 和1非常接近的数值,所以这种情况发生是非常普遍的,所以一般通过数据标准化处理,权重初始化等操作进 行抑制,但网络太深依然很难很好的抑制,当然Relu也可以抑制梯度消失问题,但是Relu可能会导致原始特征 不可逆损失,导致下一个问题,即网络退化。

        2. degradation problem:直译就是退化问题,随着网络层数的增多,训练集loss逐渐下降,然后趋于饱和,当 再增加网络深度,训练集loss反而会增大。注意这并不是过拟合,因为在过拟合中训练loss是一直减小的。

        用残差结构(残差结构在下一小节会详细介绍)进行网络组合时,可以很明显的解决这个问题

        在使用残差结构后,从20层,到110层,错误率都是逐步在降低,文章讲残差网络对 degradation problem是有抑制作用的,还有下下小节讲到的Batch Normalization是对解决梯度消失或者梯度爆 炸的抑制起到了作用,但是网络退化的一部分原因也是因为梯度消失训练不动了,在使用残差网络之后,模型内 部得复杂度降低,所以抑制了退化问题。 

2.3、 Residual结构

        Residual结构是残差结构,在文章中给了两种不同的残差结构,在ResNet-18和ResNet-34中,用的如下图中左侧 图的结构,在ResNet-50、ResNet-101和ResNet-152中,用的是下图中右侧图的结构。

        在上图左侧图可以看到输入特征矩阵的channels是64,经过一个3x3的卷积核卷积之后,要进行Relu激活函数的激活,再经过一个3x3的卷积核进行卷积,但是在这之后并没有直接经过激活函数进行激活。并且可以看到,在主 分支上有一个圆弧的线从输入特征矩阵直接连到了一个加号,这个圆弧的线是shortcut(捷径分支),它直接将 输入特征矩阵加到经过第二次3x3的卷积核卷积之后的输出特征矩阵,注意,这里描述的是加,而不是叠加或者拼 接,也就是说是矩阵对应维度位置进行一个和法运算,意味着主分支的输出矩阵和shortcut的输出矩阵的shape必 须相同,这里包括宽、高、channels,在相加之后,再经过Relu激活函数进行激活。

        在上图右侧图可以看到输入特征矩阵的channels是256,要先经过一个1x1的卷积,之前在GoogLeNet提到过, 1x1的卷积是为了维度变换,所以这里也是先用1x1的卷积进行降维到64,然后再使用3x3的卷积进行特征提取, 提取完成后,在通过1x1的卷积进行升维到256,之后得到的输出矩阵再和经过shortcut的输入矩阵进行对应维度 位置的加法运算,在相加之后,再经过Relu激活函数进行激活。

        可以看到上图中shortcut有实线和虚线部分,实现部分就是普通的shortcut,可以看到虚线部分不仅仅有 channels变化,还有特征矩阵的宽和高变化,虚线部分一个处理来让主分支的输出特征矩阵和shortcut的输出特 征矩阵保持一致 。

        从上图左侧图可以看到,当主分支的输入特征矩阵和输出特征矩阵的shape一致时,输入特征矩阵可以经过 shortcut得到输出特征矩阵直接与主分支的输出特征矩阵进行加法运算,但是上图右侧图主分支上由于步长=2, 导致矩阵的宽和高都减半了,同时由于第一个卷积核的个数是128,导致channels从64升到了128,从而 channels也不一样了,所以主分支的输出特征矩阵是[28,28,128],那么如果将shortcut分支上加一个卷积运算, 卷积核个数为128,步长为2,那么经过shortcut分支的输出矩阵也同样为[28,28,128],那么两个输出矩阵又可以 进行相加了。

 2.4、Batch Normalization

        Batch Normalization的作用是将一个批次(Batch)的特征矩阵的每一个channels计算为均值为0,方差为1的分 布规律。

        一般而言,在一个神经网络输入图像之前,会将图像进行预处理,这个预处理可能是标准化处理等手段,由于输 入数据满足某一分布规律,所以会加速网络的收敛。这样在输入第一次卷积的时候满足某一分布规律,但是在输 入第二次卷积时,就不一定满足某一分布规律了,再往后的卷积的输入就更不满足了,那么就需要一个中间商, 让上一层的输出经过它之后能够某一分布规律,Batch Normalization就是这个中间商,它可以让输入的特征矩阵 的每一个channels满足均值为0,方差为1的分布规律。

2.5、网络的结构

2.6、设计思路

import torch
import torch.nn as nn
from torch import Tensor
from torchsummary import summaryclass BasicBlock(nn.Module):expansion = 1  # 扩张因子,用于调整输入和输出通道数def __init__(self, inplanes, planes, stride=1, downsample=None):super().__init__()# 定义第一个卷积层self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=3, stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(planes)  # 批归一化self.relu = nn.ReLU(inplace=True)  # 激活函数# 定义第二个卷积层self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)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  # 返回输出class Bottleneck(nn.Module):"""注意:原论文中,在虚线残差结构的主分支上,第一个1x1卷积层的步距是2,第二个3x3卷积层步距是1。但在pytorch官方实现过程中是第一个1x1卷积层的步距是1,第二个3x3卷积层步距是2,这么做的好处是能够在top1上提升大概0.5%的准确率。可参考Resnet v1.5 https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch"""expansion: int = 4  # 扩张因子,用于调整输入和输出通道数def __init__(self, inplanes, planes, stride=1, downsample=None, groups=1, base_width=64, dilation=1):super().__init__()width = int(planes * (base_width / 64.0)) * groups  # 计算卷积的宽度self.conv1 = nn.Conv2d(inplanes, width, kernel_size=1, stride=1, bias=False)self.bn1 = nn.BatchNorm2d(width)  # 批归一化self.conv2 = nn.Conv2d(width, width, kernel_size=3, stride=stride, padding=dilation, bias=False)self.bn2 = nn.BatchNorm2d(width)  # 批归一化self.conv3 = nn.Conv2d(width, planes * self.expansion, kernel_size=1, stride=1, bias=False)self.bn3 = nn.BatchNorm2d(planes * self.expansion)  # 批归一化self.relu = nn.ReLU(inplace=True)  # 激活函数self.downsample = downsample  # 可能的降采样操作self.stride = stride  # 步幅def forward(self, x: Tensor) -> Tensor: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  # 返回输出class ResNet(nn.Module):def __init__(self, block, layers, num_classes=1000):super().__init__()self.inplanes = 64  # 初始通道数# 定义初始卷积层self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3, bias=False)self.bn1 = nn.BatchNorm2d(self.inplanes)  # 批归一化self.relu = nn.ReLU(inplace=True)  # 激活函数self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)  # 最大池化层self.layer1 = self._make_layer(block, 64, layers[0])  # 第一层self.layer2 = self._make_layer(block, 128, layers[1], stride=2)  # 第二层self.layer3 = self._make_layer(block, 256, layers[2], stride=2)  # 第三层self.layer4 = self._make_layer(block, 512, layers[3], stride=2)  # 第四层self.avgpool = nn.AdaptiveAvgPool2d((1, 1))  # 自适应平均池化self.fc = nn.Linear(512 * block.expansion, num_classes)  # 全连接层def _make_layer(self, block, planes, blocks, stride=1):downsample = None  # 初始化降采样层# 如果需要降采样if stride != 1 or self.inplanes != planes * block.expansion:downsample = nn.Sequential(nn.Conv2d(self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(planes * block.expansion),)layers = []layers.append(block(self.inplanes, planes, stride, downsample)  # 添加块)self.inplanes = planes * block.expansion  # 更新输入通道数for _ in range(1, blocks):  # 添加后续的块layers.append(block(self.inplanes, planes))return nn.Sequential(*layers)  # 返回层的序列def forward(self, x):x = self.conv1(x)  # 通过初始卷积层x = self.bn1(x)  # 批归一化x = self.relu(x)  # 激活x = self.maxpool(x)  # 池化x = self.layer1(x)  # 通过第一层x = self.layer2(x)  # 通过第二层x = self.layer3(x)  # 通过第三层x = self.layer4(x)  # 通过第四层x = self.avgpool(x)  # 通过自适应平均池化x = torch.flatten(x, 1)  # 展平张量x = self.fc(x)  # 通过全连接层return x  # 返回输出# 定义不同版本的ResNet
def resnet18(num_classes=1000):# https://download.pytorch.org/models/resnet18-f37072fd.pthreturn ResNet(BasicBlock, [2, 2, 2, 2], num_classes=num_classes)def resnet34(num_classes=1000):# https://download.pytorch.org/models/resnet34-333f7ec4.pthreturn ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes)def resnet50(num_classes=1000):# https://download.pytorch.org/models/resnet50-19c8e357.pthreturn ResNet(Bottleneck, [3, 4, 6, 3], num_classes=num_classes)def resnet101(num_classes=1000):# https://download.pytorch.org/models/resnet101-63fe2227.pthreturn ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes)def resnet152(num_classes=1000):# https://download.pytorch.org/models/resnet152-394f9c45.pthreturn ResNet(Bottleneck, [3, 8, 26, 3], num_classes=num_classes)if __name__ == '__main__':# model = resnet18(num_classes=3)# model = resnet34(num_classes=3)# model = resnet50(num_classes=3)# model = resnet101(num_classes=3)model = resnet152(num_classes=3)  # 创建ResNet152模型print(summary(model, (3, 224, 224)))  # 打印模型总结
from torchvision import models
from torchsummary import  summaryresnet_models = {"resnet18": models.resnet18(pretrained=False),"resnet34": models.resnet34(pretrained=False),"resnet50": models.resnet50(pretrained=False),"resnet101": models.resnet101(pretrained=False),"resnet152": models.resnet152(pretrained=False),
}
'''
当pretrained=True是会自动下载预训练模型
'''for name, model in resnet_models.items():print(summary(model,(3,244,244)))

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

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

相关文章

Qt/C++开发监控GB28181系统/录像文件查询/录像回放/倍速播放/录像文件下载

一、前言 搞定了实时预览后,另一个功能就是录像回放,录像回放和视频点播功能完全一致,唯一的区别就是发送点播的sdp信息中携带了开始时间和结束时间,因为是录像文件,所以有这个时间,而实时视频预览这个对应…

在Spark搭建YARN

(一)什么是SparkONYarn模式 Spark on YARN(Yet Another Resource Negotiator)是 Spark 框架在 Hadoop 集群中运行的一种部署模式,它借助 Hadoop YARN 来管理资源和调度任务。 架构组成 ResourceManager:作…

SpringAI

机器学习: 定义:人工智能的子领域,通过数据驱动的方法让计算机学习规律,进行预测或决策。 核心方法: 监督学习(如线性回归、SVM)。 无监督学习(如聚类、降维)。 强化学…

如何用Redis实现分布式锁?RedLock算法的核心思想?Redisson的看门狗机制原理?

一、Redis分布式锁基础实现 public class RedisDistributedLock {private JedisPool jedisPool;private String lockKey;private String clientId;private int expireTime 30; // 默认30秒public boolean tryLock() {try (Jedis jedis jedisPool.getResource()) {// NX表示不…

前端面试宝典---js垃圾回收机制

什么是垃圾回收 垃圾回收是指一种自动内存管理机制,当声明一个变量时,会在内存中开辟一块内存空间用于存放这个变量。当这个变量被使用过后,可能再也不需要它了,此时垃圾回收器会自动检测并回收这些不再使用的内存空间。垃圾回收…

阿里妈妈LMA2新进展:集成大语言模型与电商知识的通用召回大模型URM

近日,阿里妈妈在国际顶级学术会议 —— 国际万维网大会(International World Wide Web Conference, 简称WWW)上共同主持了计算广告算法技术相关的Tutorial(讲座),介绍了计算广告领域的技术发展脉络&#xf…

数字孪生实时监控汽车零部件工厂智能化巡检新范式

在汽车制造业面临数字化转型时,汽车零部件工厂也面临着提升生产效率、降低运营成本和增强市场竞争力的多重挑战。传统的巡检方式已经难以满足现代工厂对高效、精准管理和实时决策的需求。数字孪生系统的出现,为汽车零部件工厂提供了一种创新的智能化巡检…

【计算机网络】3数据链路层②

1. 数据链路层所处的地位 数据链路层使用的信道主要有两种: ①点对点信道:PPP协议 ②广播信道:有线局域网,CSMA/CD协议;无线局域网,CSMA/CA协议 对比项点对点信道 vs 单播广播信道 vs 广播核心是否一致✅ 一致(一对一传输)✅ 一致(一对所有传输)差异点前者是物理层…

c++中的函数(默认参数,占位参数,重载)

1&#xff0c;函数默认参数 在c中&#xff0c;函数的形参列表中的形参是可以有默认值得 语法&#xff1a;返回值类型 函数名 &#xff08;参数 默认值&#xff09;{} 示例&#xff1a; #include<iostream> using namespace std;//函数默认参数 // 就是如果传了就…

【原创】使用阿里云存放一个临时共享的文件

在某些场合&#xff0c;需要临时将一个文件存储到一个可被公网访问的地方&#xff0c;某个服务需要访问一下这个文件。这个文件基本上就是一次寿命&#xff0c;也就是你上传一下&#xff0c;然后被访问一下&#xff0c;这个文件的寿命就结束了。 对于这种需求&#xff0c;自建…

Python中列表(list)知识详解(2)和注意事项以及应用示例

在 Python 中列表&#xff08;list&#xff09; 的包括其结构、常见操作&#xff08;更新、添加、删除、查找、队列栈行为等&#xff09;&#xff0c;下面将逐一的进行讲解并附相关的示例。 一、列表的基础知识 1. 定义与特点 定义方式&#xff1a;用 [] 包裹的有序可变集合 …

vscode extention踩坑记

# npx vsce package --allow-missing-repository --no-dependencies #耗时且不稳定 npx vsce package --allow-missing-repository #用这行 code --install-extension $vsixFileName --force我问ai&#xff1a;为什么我的.vsix文件大了那么多 ai答&#xff1a;因为你没有用 --n…

移动端巡检点检,让设备管理更便捷高效

在企业设备管理的日常工作中&#xff0c;巡检点检是保障设备正常运行的重要环节。传统的巡检方式依赖纸质记录、人工操作&#xff0c;效率低、易出错&#xff0c;已难以满足现代企业的管理需求。随着技术发展&#xff0c;越来越多设备管理系统引入移动端功能&#xff0c;为设备…

laravel 中使用的pdf 扩展包 laravel-snappy(已解决中文乱码)

Centos7 安装 wkhtmltopdf 1、先查看系统是 32 位的还是 64 位的 uname -a2、通过 composer 安装 wkhtmltopdf 32位: $ composer require h4cc / wkhtmltopdf-i386 0.12.x $ composer require h4cc / wkhtmltoimage-i386 0.12.x 64位: $ composer require h4cc/wkhtmltopdf-…

Rust:重新定义系统编程的安全与效率边界

在软件工程领域&#xff0c;内存安全漏洞每年造成数千亿美元损失&#xff0c;而C/C生态中60%的漏洞源于指针误用。正是在这样的背景下&#xff0c;Rust凭借其革命性的内存安全机制异军突起。作为一门现代系统级编程语言&#xff0c;Rust不仅解决了困扰开发者数十年的内存管理难…

C++学习细节回顾(汇总二)

一.初始化列表相关 1.初始化顺序受申明顺序影响 2.在必要时可以部分不采用初始化列表&#xff0c;避免受特性1影响 二.非类型模板参数 template< class T , size_t N 10 > 三.特化–特殊化处理 template< class T > bool less(T left , T right) { return left&…

勾选某一行的勾选框,更改当前行的颜色,ALV数据发生变化的事件

文章目录 屏幕ALV的创建定义变量注册事件方法定义方法实现frm_data_change 效果 屏幕 ALV的创建 DATA: g_gui_custom_container TYPE REF TO cl_gui_custom_container. DATA: g_gui_alv_grid TYPE REF TO cl_gui_alv_grid.DATA: gt_listheader TYPE slis_t_listheader, &quo…

AI-02a5a6.神经网络-与学习相关的技巧-批量归一化

批量归一化 Batch Normalization 设置合适的权重初始值&#xff0c;则各层的激活值分布会有适当的广度&#xff0c;从而可以顺利的进行学习。那么&#xff0c;更进一步&#xff0c;强制性的调整激活值的分布&#xff0c;是的各层拥有适当的广度呢&#xff1f;批量归一化&#…

解决SQL Server SQL语句性能问题(9)——合理使用表分区

9.2. 合理使用表分区 本专栏4.1.4节中,我们对表分区相关的概念和机制等基础理论进行了较为详细的介绍和论述,读者可以参考该节中内容,或者,读者也可以参考官方或其他相关资料。与其他关系库类似,SQL Server 2005版本中引进的真正意义上的表分区技术,绝对是解决海量数据环…

C语言学习之文件操作

经过前面的学习&#xff0c;我们已经基本掌握了如何去写一个C语言的代码了。但是在实际的项目中&#xff0c;我们不可能不需要文件去操作。因为如果没有文件&#xff0c;我们写的程序是存储在电脑的内存中的。如果程序推出&#xff0c;内存回收数据就随之丢失了。如果我们要对数…