深度学习_ResNet_5

news2024/9/21 19:07:37
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)))
        # 卷积,BN
        Y = 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卷积,步长为2
        self.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,通道数为64
        self.resnet_block1=ResnetBlock(64,num_blocks[0], first_block=True)
        # 第二个block,通道数为128
        self.resnet_block2=ResnetBlock(128,num_blocks[1])
        # 第三个block,通道数为256
        self.resnet_block3=ResnetBlock(256,num_blocks[2])
        # 第四个block,通道数为512
        self.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)
        # BN
        x=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.coloradmin.cn/o/1527653.html

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

相关文章

网络视频播放器|基于JSP技术+ Mysql+Java+ B/S结构的网络视频播放器设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java,ssm,springboot的平台设计与实现项目系统开发资源(可…

3D模型优化服务+三维可视化+数字孪生+元宇宙=眸瑞科技

眸瑞科技:老子云平台AMRT3D数字孪生引擎 老子云概述 老子云3D可视化快速开发平台,集云压缩、云烘焙、云存储云展示于一体,使3D模型资源自动输出至移动端PC端、Web端,能在多设备、全平台进行展示和交互,是全球领先、自…

MYSQL日志 redo_log更新流程 bin_log以及bin_log数据恢复

Redo_log写入策略 Redo log的Innodb_flush_log_at_trx_commit:: 这个参数有三个取值 取值为0:每次事务提交时,只是把redo_log留在 redo log buffer中,宕机会丢失数据; 取值为1(默认值):每次事…

代码随想录算法训练营第day30|332.重新安排行程 、 51. N皇后 、37. 解数独

目录 332.重新安排行程 思路: 51. N皇后 思路: 37. 解数独 332.重新安排行程 力扣题目链接 (opens new window) 给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进…

Jenkins-pipeline流水线构建完钉钉通知

添加钉钉机器人 在钉钉群设置里添加机器人拿出Webhook地址,设置关键词 Jenkins安装钉钉插件 Dashboard > 系统管理 > 插件管理,搜索构建通知,直接搜索Ding Talk也行 安装DingTalk插件,重启Jenkins 来到Dashboard > 系…

探索编程迷宫:选择你的职业赛道

在现代科技的浪潮中,程序员的职业赛道就像是一座迷宫,充满着前端的美丽花园,后端的黑暗洞穴,以及数据科学的神秘密室。这个迷宫中,每一条通道都充满了挑战和机遇,而每一个行走其中的人都在寻找着属于自己的…

sentinel系统负载自适应流控

系统负载自适应流控 规则配置 规则创建 public class SystemRule extends AbstractRule {private double highestSystemLoad -1;private double highestCpuUsage -1;private double qps -1;private long avgRt -1;private long maxThread -1; }SystemRule类包含了以下几…

设计模式学习笔记 - 设计原则与思想总结:2.运用学过的设计原则和思想完善之前性能计数器项目

概述 在 《设计原则 - 10.实战:针对非业务的通用框架开发,如何做需求分析和设计及如何实现一个支持各种统计规则的性能计数器》中,我们讲解了如何对一个性能计数器框架进行分析、设计与实现,并且实践了一些设计原则和设计思想。当…

如何使用 ArcGIS Pro 生成TIN

三角网是一种常用于表示地表地形的数字地球模型(DEM)方式,我们可以通过 ArcGIS Pro 将等高线和高程点转换为TIN,这里为大家介绍一下转换方法,希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的高…

vue2使用webSocket双向通讯

基于webSocket实现双向通信,使用webworker保持心跳。 由于浏览器的资源管理策略会暂停或限制某些资源的消耗,导致前端心跳包任务时效,后端接收不到webSocket心跳主动断开,因此需要使用webworker保持心跳 引入webworker npm insta…

CSDN 停更通知

CSDN 不再更新,欢迎关注我的微信公众号,分享更多有趣的技术内容。 如果大家有任何疑问,或者感兴趣的话题,都可以通过微信公众号与我交流,相互学习,相互成长。

Trent电源设计那些事儿教学

本课程将深入探讨Trent电源设计的关键概念与技术。学生将学习功率电子器件和拓扑、电路保护、稳压技术以及EMI滤波等内容。通过理论和实践相结合的教学方式,帮助学员掌握Trent电源设计的原理与应用。 课程大小:12.5G 课程下载:https://down…

2024年发布jar到国外maven中央仓库最新教程

2024年发布jar到国外maven中央仓库最新教程 文章目录 1.国外sonatype仓库的版本1.1老OSSHR账号注册说明1.2新账号注册说明 2.新账号注册(必选)3.新账号登录创建Namespace3.1创建Namespace的名字的格式要求(必选)3.2发布一个静态网站(可选&…

科技云报道:第五次工业革命,中国AI企业如何打造新质生产力?

科技云报道原创。 人类历史的叙述与技术进步的影响深深交织在一起。 迄今为止,每一次工业革命都彻底改变了我们社会的轮廓,引入了机械化、大规模生产和数字化,并重新定义了人类生存的规范。 自2022年11月30日OpenAI发布ChatGPT以来&#x…

webpack5零基础入门-11处理html资源

1.目的 主要是为了自动引入打包后的js与css资源,避免手动引入 2.安装相关包 npm install --save-dev html-webpack-plugin 3.引入插件 const HtmlWebpackPlugin require(html-webpack-plugin); 4.添加插件(通过new方法调用) /**插件 *…

c语言,联合体

一.什么是联合体: 像结构体一样,联合体也是由一个或多个成员变量组成的这些成员变量可以是不同的类型,但编译器只给最大成员分配足够的内存,联合体体内的成员都是公用一块空间的,因此联合体也叫做共同体 二.联合体类…

聚类分析 | Matlab实现基于PCA+DBO+K-means的数据聚类可视化

聚类分析 | Matlab实现基于PCADBOK-means的数据聚类可视化 目录 聚类分析 | Matlab实现基于PCADBOK-means的数据聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 PCA(主成分分析)、DBO(蜣螂优化算法)和K-means聚类…

【观察】紫光云:紫鸾5.0云平台“再升级”,为政企客户提供新质生产力

毫无疑问,数字化既是这个时代前进所趋,也是国家战略所指,更是所有企业在未来发展中已达成的高度共识。 在此过程中,千姿百态、复杂多样的应用场景,可以看做是遍布数字中国的“点”;百行百业、各种类型的行业…

阿里云发布 AI 编程助手 “通义灵码”——VSCode更强了 !!

文章目录 什么是 通义灵码(TONGYI Lingma) 快速体验“通义灵码” 什么是“通义灵码”(TONGYI Lingma) 通义灵码(TONGYI Lingma),是阿里云出品的一款基于通义大模型的智能编码辅助工具&#xff…

idea 开发serlvet班级通讯录管理系统idea开发mysql数据库web结构计算机java编程layUI框架开发

一、源码特点 idea开发 java servlet 班级通讯录管理系统是一套完善的web设计系统mysql数据库 系统采用serlvetdaobean mvc 模式开发,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 servlet 班…