深度学习_ResNet_5

ResNet学习目标
  • 什么是ResNet
  • 为什么要引入ResNet?
  • ResNet网络结构的特点
  • 利用ResNet完成图像分类

什么是ResNet?

ResNet(Residual Network)是一种深度残差网络,由何凯明等人在2015年提出,是深度学习领域中一项突破性的进展,尤其在计算机视觉任务中表现突出。ResNet 解决了随着神经网络加深而导致的梯度消失和训练困难的问题,这一问题在之前阻碍了构建更深的网络模型以提高模型性能。

ResNet 的核心创新是引入了残差连接(Residual Connections)的概念。在传统的网络结构中,每一层网络试图直接学习输入到输出的复杂映射。而在ResNet中,每一层并不直接学习原始输入到输出的映射,而是学习输入与前面某一层输出之间的残差(或称“shortcut”)。也就是说,一个残差块(Residual Block)包含至少两层卷积层,其输出不是直接提供给下一层,而是与原始输入相加,这样网络可以更容易地学习深层次网络中微小的残差变化。

具体来说,ResNet 的基本结构单元如下:

H(x) = F(x, {W_i}) + x

这里,F(x, {W_i}) 表示多层卷积层组成的残差函数,x 是输入特征图,H(x) 是该残差块的输出。通过引入残差连接,即使深层网络的输出只是恒等映射(Identity Mapping),网络也能轻易地学习到这一点,避免了梯度消失问题,使得训练数百乃至上千层的网络成为可能。

ResNet 不仅在图像分类任务上取得了出色的效果,还在许多其他视觉任务,如目标检测、图像分割等上展示了优越性,并在当年的ImageNet大规模视觉识别挑战赛中获得了优异的成绩。此外,ResNet 的设计理念对后来的深度学习模型架构产生了深远影响,成为了现代深度学习模型设计的基础组成部分。

为什么要引入ResNet?

网络越深,获取的信息就越多,特征也越丰富。但是在实践中,随着网络的加深,优化效果反而越差,测试数据和训练数据的准确率反而降低了

针对这一问题,何恺明等人提出了残差网络(ResNet)在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经网络的设计。

ResNet网络结构的特点

1 残差块

假设 F(x) 代表某个只包含有两层的映射函数, x 是输入, F(x)是输出。假设他们具有相同的维度。在训练的过程中我们希望能够通过修改网络中的 w和b去拟合一个理想的 H(x)(从输入到输出的一个理想的映射函数)。也就是我们的目标是修改F(x) 中的 w和b逼近 H(x) 。如果我们改变思路,用F(x) 来逼近 H(x)-x ,那么我们最终得到的输出就变为 F(x)+x(这里的加指的是对应位置上的元素相加,也就是element-wise addition),这里将直接从输入连接到输出的结构也称为shortcut,那整个结构就是残差块,ResNet的基础模块。

ResNet沿用了VGG全3×33×3卷积层的设计。残差块里首先有2个有相同输出通道数的3×33×3卷积层。每个卷积层后接BN层和ReLU激活函数,然后将输入直接加在最后的ReLU激活函数前,这种结构用于层数较少的神经网络中,比如ResNet34。若输入通道数比较多,就需要引入1×11×1卷积层来调整输入的通道数,这种结构也叫作瓶颈模块,通常用于网络层数较多的结构中。如下图所示:

上图左中的残差块的实现如下,可以设定输出通道数,是否使用1*1的卷积及卷积层的步幅。

# 导入相关的工具包
import tensorflow as tf
from tensorflow.keras import layers, activations# 定义ResNet的残差块
class Residual(tf.keras.Model):# 指明残差块的通道数,是否使用1*1卷积,步长def __init__(self, num_channels, use_1x1conv=False, strides=1):super(Residual, self).__init__()# 卷积层:指明卷积核个数,padding,卷积核大小,步长self.conv1 = layers.Conv2D(num_channels,padding='same',kernel_size=3,strides=strides)# 卷积层:指明卷积核个数,padding,卷积核大小,步长self.conv2 = layers.Conv2D(num_channels, kernel_size=3, padding='same')if use_1x1conv:self.conv3 = layers.Conv2D(num_channels,kernel_size=1,strides=strides)else:self.conv3 = None# 指明BN层self.bn1 = layers.BatchNormalization()self.bn2 = layers.BatchNormalization()# 定义前向传播过程def call(self, X):# 卷积,BN,激活Y = activations.relu(self.bn1(self.conv1(X)))# 卷积,BNY = self.bn2(self.conv2(Y))# 对输入数据进行1*1卷积保证通道数相同if self.conv3:X = self.conv3(X)# 返回与输入相加后激活的结果return activations.relu(Y + X)

1*1卷积用来调整通道数。

2 ResNet模型

ResNet模型的构成如下图所示:

ResNet网络中按照残差块的通道数分为不同的模块。第一个模块前使用了步幅为2的最大池化层,所以无须减小高和宽。之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。

下面我们来实现这些模块。注意,这里对第一个模块做了特别处理。

# ResNet网络中模块的构成
class ResnetBlock(tf.keras.layers.Layer):# 网络层的定义:输出通道数(卷积核个数),模块中包含的残差块个数,是否为第一个模块def __init__(self,num_channels, num_residuals, first_block=False):super(ResnetBlock, self).__init__()# 模块中的网络层self.listLayers=[]# 遍历模块中所有的层for i in range(num_residuals):# 若为第一个残差块并且不是第一个模块,则使用1*1卷积,步长为2(目的是减小特征图,并增大通道数)if i == 0 and not first_block:self.listLayers.append(Residual(num_channels, use_1x1conv=True, strides=2))# 否则不使用1*1卷积,步长为1 else:self.listLayers.append(Residual(num_channels))      # 定义前向传播过程def call(self, X):# 所有层依次向前传播即可for layer in self.listLayers.layers:X = layer(X)return X

ResNet的前两层跟之前介绍的GoogLeNet中的一样:在输出通道数为64、步幅为2的7×77×7卷积层后接步幅为2的3×33×3的最大池化层。不同之处在于ResNet每个卷积层后增加了BN层,接着是所有残差模块,最后,与GoogLeNet一样,加入全局平均池化层(GAP)后接上全连接层输出。

# 构建ResNet网络
class ResNet(tf.keras.Model):# 初始化:指定每个模块中的残差快的个数def __init__(self,num_blocks):super(ResNet, self).__init__()# 输入层:7*7卷积,步长为2self.conv=layers.Conv2D(64, kernel_size=7, strides=2, padding='same')# BN层self.bn=layers.BatchNormalization()# 激活层self.relu=layers.Activation('relu')# 最大池化层self.mp=layers.MaxPool2D(pool_size=3, strides=2, padding='same')# 第一个block,通道数为64self.resnet_block1=ResnetBlock(64,num_blocks[0], first_block=True)# 第二个block,通道数为128self.resnet_block2=ResnetBlock(128,num_blocks[1])# 第三个block,通道数为256self.resnet_block3=ResnetBlock(256,num_blocks[2])# 第四个block,通道数为512self.resnet_block4=ResnetBlock(512,num_blocks[3])# 全局平均池化self.gap=layers.GlobalAvgPool2D()# 全连接层:分类self.fc=layers.Dense(units=10,activation=tf.keras.activations.softmax)# 前向传播过程def call(self, x):# 卷积x=self.conv(x)# BNx=self.bn(x)# 激活x=self.relu(x)# 最大池化x=self.mp(x)# 残差模块x=self.resnet_block1(x)x=self.resnet_block2(x)x=self.resnet_block3(x)x=self.resnet_block4(x)# 全局平均池化x=self.gap(x)# 全链接层x=self.fc(x)return x
# 模型实例化:指定每个block中的残差块个数 
mynet=ResNet([2,2,2,2])

这里每个模块里有4个卷积层(不计算 1×1卷积层),加上最开始的卷积层和最后的全连接层,共计18层。这个模型被称为ResNet-18。通过配置不同的通道数和模块里的残差块数可以得到不同的ResNet模型,例如更深的含152层的ResNet-152。虽然ResNet的主体架构跟GoogLeNet的类似,但ResNet结构更简单,修改也更方便。这些因素都导致了ResNet迅速被广泛使用。 在训练ResNet之前,我们来观察一下输入形状在ResNe的架构:

X = tf.random.uniform(shape=(1,  224, 224 , 1))
y = mynet(X)
mynet.summary()

Model: "res_net" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_2 (Conv2D) multiple 3200 _________________________________________________________________ batch_normalization_2 (Batch multiple 256 _________________________________________________________________ activation (Activation) multiple 0 _________________________________________________________________ max_pooling2d (MaxPooling2D) multiple 0 _________________________________________________________________ resnet_block (ResnetBlock) multiple 148736 _________________________________________________________________ resnet_block_1 (ResnetBlock) multiple 526976 _________________________________________________________________ resnet_block_2 (ResnetBlock) multiple 2102528 _________________________________________________________________ resnet_block_3 (ResnetBlock) multiple 8399360 _________________________________________________________________ global_average_pooling2d (Gl multiple 0 _________________________________________________________________ dense (Dense) multiple 5130 ================================================================= Total params: 11,186,186 Trainable params: 11,178,378 Non-trainable params: 7,808 _________________________________________________________________

利用ResNet完成图像分类

2.手写数字势识别

因为ImageNet数据集较大训练时间较长,我们仍用前面的MNIST数据集来演示resNet。读取数据的时将图像高和宽扩大到ResNet使用的图像高和宽224。这个通过tf.image.resize_with_pad来实现。

2.1 数据读取

首先获取数据,并进行维度调整:

import numpy as np
# 获取手写数字数据集
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# 训练集数据维度的调整:N H W C
train_images = np.reshape(train_images,(train_images.shape[0],train_images.shape[1],train_images.shape[2],1))
# 测试集数据维度的调整:N H W C
test_images = np.reshape(test_images,(test_images.shape[0],test_images.shape[1],test_images.shape[2],1))

由于使用全部数据训练时间较长,我们定义两个方法获取部分数据,并将图像调整为224*224大小,进行模型训练:

# 定义两个方法随机抽取部分样本演示
# 获取训练集数据
def get_train(size):# 随机生成要抽样的样本的索引index = np.random.randint(0, np.shape(train_images)[0], size)# 将这些数据resize成22*227大小resized_images = tf.image.resize_with_pad(train_images[index],224,224,)# 返回抽取的return resized_images.numpy(), train_labels[index]
# 获取测试集数据 
def get_test(size):# 随机生成要抽样的样本的索引index = np.random.randint(0, np.shape(test_images)[0], size)# 将这些数据resize成224*224大小resized_images = tf.image.resize_with_pad(test_images[index],224,224,)# 返回抽样的测试样本return resized_images.numpy(), test_labels[index]

调用上述两个方法,获取参与模型训练和测试的数据集:

# 获取训练样本和测试样本
train_images,train_labels = get_train(256)
test_images,test_labels = get_test(128)

2.2 模型编译

# 指定优化器,损失函数和评价指标
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.0)mynet.compile(optimizer=optimizer,loss='sparse_categorical_crossentropy',metrics=['accuracy'])

2.3 模型训练

# 模型训练:指定训练数据,batchsize,epoch,验证集
mynet.fit(train_images,train_labels,batch_size=128,epochs=3,verbose=1,validation_split=0.1)

训练输出为:

Epoch 1/3
2/2 [==============================] - 10s 5s/step - loss: 2.7811 - accuracy: 0.1391 - val_loss: 4.7931 - val_accuracy: 0.1923
Epoch 2/3
2/2 [==============================] - 8s 4s/step - loss: 2.2579 - accuracy: 0.2478 - val_loss: 2.9262 - val_accuracy: 0.2692
Epoch 3/3
2/2 [==============================] - 15s 7s/step - loss: 2.0874 - accuracy: 0.2609 - val_loss: 2.5882 - val_accuracy: 0.2692

2.4 模型评估

# 指定测试数据
mynet.evaluate(test_images,test_labels,verbose=1)

输出为:

4/4 [==============================] - 1s 370ms/step - loss: 3.4343 - accuracy: 0.1016 [3.4342570304870605, 0.1015625]

参考文章:ResNet详解——通俗易懂版-CSDN博客

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

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

相关文章

Leetcode 31. 删除无效的括号

心路历程: 一开始看到有点懵,后来发现有点像按照一定规则穷举所有可能情况,想到了排列组合问题,再结合问题长度不固定,无法用已知个for循环表示,从而想到了回溯。这个题相当于需要在一定规则下枚举。 按照…

力扣题目训练(22)

2024年2月15日力扣题目训练 2024年2月15日力扣题目训练563. 二叉树的坡度637. 二叉树的层平均值643. 子数组最大平均数 I304. 二维区域和检索 - 矩阵不可变154. 寻找旋转排序数组中的最小值 II 2024年2月15日力扣题目训练 2024年2月15日第二十二天编程训练,今天主要…

Redis-2 Redis基础数据类型与基本使用

高级Redis应用进阶 一站式Redis解决方案-Redis-2 Redis基础数据类型与基本使用 源代码在GitHub - 629y/food-social-contact-parent: redis项目-美食社交APP 1. Redis基本数据类型 1.字符串(strings) set username zhangsan get username mset age 18 …

ECMAscript6学习

ECMAscript6介绍 ECMA是一个浏览器脚本标准制定的公司,Netscape 创造了 JavaScript 由于商标原因, 后面ECMA公司取名ECMAscript 1 发布,JavaScript 也就是 ECMAscript.到现在最新的版本是6,简称es6. 新增let 与const let 与const …

【数据分析可视化】动态生成柱状图

import pandas as pd import matplotlib.pyplot as plt import matplotlib.ticker as ticker from matplotlib.animation import FuncAnimation import matplotlib.patches as mpatches from matplotlib.animation import FFMpegWriter# 定义一个函数,用于生成…

探秘酒店业黑科技:3D可视化引领管理新风尚

在信息化飞速发展的今天,酒店管理已不再是传统的模式所能满足。 想象一下,你站在一个巨大的3D地图上,轻轻一点,就能瞬间进入酒店的任何一个角落。你可以看到客房的布置、餐厅的摆设、会议室的布局……一切都如同身临其境&#xff…

shell编程中 for while until循环的使用方法及案例

CSDN 成就一亿技术人! 作者主页:点击! Shell编程专栏:点击! CSDN 成就一亿技术人 ————前言———— Shell脚本编程中,有几种常见的循环结构,包括for循环、while循环和until循环&#xf…

C goto 语句

C 语言中的 goto 语句允许把控制无条件转移到同一函数内的被标记的语句。 注意:在任何编程语言中,都不建议使用 goto 语句。因为它使得程序的控制流难以跟踪,使程序难以理解和难以修改。任何使用 goto 语句的程序可以改写成不需要使用 goto 语…

PWM驱动舵机

PWM驱动舵机 接线图 程序结构图: pwm.c部分代码 #include "stm32f10x.h" // Device headervoid PWM_Init(void){// 开启时钟,这里TIM2是通用寄存器RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);// GPIO初始化代…

2.27线性模型

可以利用无监督学习解决的问题,大致分为两类: 关联分析:发现不同事物之间同时出现的概率。在购物篮分析中被广泛地应用。如果发现买面包的客户有百分之八十的概率买鸡蛋,那么商家就会把鸡蛋和面包放在相邻的货架上。 聚类问题&a…

onnx 格式模型可视化工具

onnx 格式模型可视化工具 0. 引言1. 可视化工具2. 安装 Netron: Viewer for ONNX models 0. 引言 ONNX 是一种开放格式,用于表示机器学习模型。ONNX 定义了一组通用运算符(机器学习和深度学习模型的构建基块)和通用文件格式,使 A…

100天精通Python(实用脚本篇)——第118天:基于selenium和ddddocr库实现反反爬策略之验证码识别

文章目录 专栏导读一、前言二、ddddocr库使用说明1. 介绍2. 算法步骤3. 安装4. 参数说明5. 纯数字验证码识别6. 纯英文验证码识别7. 英文数字验证码识别8. 带干扰的验证码识别 三、验证码识别登录代码实战1. 输入账号密码2. 下载验证码3. 识别验证码并登录 书籍推荐 专栏导读 …

SAR ADC教程系列5——FFT频谱泄露以及相干采样

频谱泄露的出现以及如何规避? 为什么要相干采样? 1.分析ADC输出信号的频谱工具:DFT(Discrete Fourier Transform) 重点:DFT相邻频谱频率间隔为fs/N 如何规避频谱泄露? 对于DFT,它对于接收到的信…

前端项目部署后,如何提示用户版本更新

目录 前言解决方案1、public目录下新建manifest.json2、写入当前时间戳到manifest.json3、检查版本更新4、woker线程5、入口文件引入 可能出现的问题 前言 项目部署上线后,特别是网页项目,提示正在操作系统的用户去更新版本非常 important。一般我们都会…

osg执行opengl4.3的shader报错

运行案例:运行这篇博客的opengl4.3的例子,OSG使用GLSL各个版本例子,报如下错误: Warning: detected OpenGL error invalid operation at after pcp->apply(Unfiorm&) in GLObjectsVisitor::apply(osg::StateSet& stat…

C++ 笛卡尔树

目录 一、性质二、构建笛卡尔树三、应用四、源码 一、性质 堆性质: 笛卡尔树是一种满足堆性质的树。每个节点包含两个值:键值(key)和优先级值(priority)。在笛卡尔树中,根节点的优先级值最大&am…

Prompt提示工程上手指南:基础原理及实践(三)-Prompt个性知识库引导

前言 Prompt系列的第二期文章已经将所有的Prompt工程主流策略讲解完毕,共涉及到六种Prompt类别模型以及具体生产内容详解。再结合系列第一篇文章具体对Prompt工程的详细介绍,也就可以达到Prompt工程师的初步入门,现在如果掌握了这些基础技能…

在pharmit里匹配药效团

我把400个无活性的小分子(decoys)提交到pharmit里。 命名为decoyset00~decoyset08,查找时,按这个找。 1、导入药效团配体: 进入药效团筛选界面: 导入代表药效团模型的活性肽构象: 2、选择预先…

MATLAB环境下基于可调Q因子小波变换的滚动轴承故障诊断(MATLAB R2021B)

小波变换是一种时频局域化方法,它的窗口面积固定但形状可以发生改变(时间窗与频率窗均可变化)。小波变换在时间域与频率域都能够表示信号的局部特征,并具有多分辨率分析的特点,是机械故障诊断中常用的方法。小波变换故…

【C++】手撕红黑树

> 作者简介:დ旧言~,目前大二,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 目标:能直接手撕红黑树。 > 毒鸡汤:行到…