庖丁解牛-Resnet50 深度剖析,细致讲解,深入理解

article/2025/9/18 5:37:33

背景介绍

ResNet-50侧边输出形状
假设输入为352,则
output2 = 256x88x88
output3 = 512x44x44
output4 = 1024x22x22
output5 = 2048x11x11

VGG-16侧边输出形状
假设输入为352,则
output1 = 64x320x320

output2 = 128x160x160
output3 = 256x88x88
output4 = 512x44x44
output5 = 512x22x22

请看50-layer这一列
是本文介绍的层的结构:

resnet-50
有四组大block
每组分别是3, 4, 6, 3个小block
每个小block里面有三个卷积
另外这个网络的最开始有一个单独的卷积层,
因此是:(3+4+6+3)*3+1=49
最后又一个全连接层,因而一共50层

如下图,每个大block里面的
第一个都是IN !==OUT情况,侧支线,命名为:Conv Block
其他都是    IN  ==OUT情况,侧支线,   命名为:ID      Block
3 = +右+右
4 = +右+右+右
6 = +右+右+右+右+右
3 = +右+右

0 特征图尺寸计算

1 卷积层计算: N = (W-F+2P)/2 + 1
F 卷积核
S 步长
P Padding

2 池化层计算: N = (W-F)/s + 1
F 卷积核
S 步长
P Padding


3 当尺寸不被整除时
卷积向下取整,池化向上取整。
本题中 (200-5+2*1)/2+1 为99.5,取99
(99-3)/1+1 为97
(97-3+2*1)/1+1 为97
 
卷积前后尺寸不变情况: 当stride为1的时候,当kernel为 3 padding为1或者kernel为5 padding为2 ,卷积前后尺寸不变。

1 resnet用skip-connection规避梯度消失问题

梯度消失:反向传递到浅层的时候,gradient会小到接近0,
导致学习效率低,parameters更新越来越慢

多个Resnet Blocks累积起来能解决梯度消失问题。
Resnet Block = main path  +  skip connection

2 ResNet有2个基本的block:

Identity Block: 输入和输出的dimension是一样的,    所以可以串联多个;
串联多个,
可直接相加,
维度不变(input shape = output shape)
Conv Block:      输入和输出的dimension是不一样的,所以不能连续串联,它的作用本来就是为了改变特征向量的dimension
不能连续串联,
在skip connection里面加入了conv2d layer, 以让维度相等然后相加
改变维度 (input shape != output shape)

因为CNN最后都是要把输入图像一点点的转换成很小但是depth很深的feature map,
一般的套路是用统一的比较小的kernel(比如VGG都是用3*3),
但是随着网络深度的增加,output的channel也增大(学到的东西越来越复杂),
所以有必要在进入Identity Block之前,用Conv Block转换一下维度,这样后面就可以连续接Identity Block.

Identity Block:

Conv Block: 

Conv Block 的不同之处在于:
其实就是在shortcut path的地方加上一个conv2D layer(1*1 filter size),
然后在main path改变dimension,并与shortcut path对应起来.

3 如何搭建一个跨越三层的Conv Block

1 main path
第一: Conv-BatchNorm-ReLU block
conv2d: filter=F1, kernel_size=1, stride=s, padding=valid
output shape变小
起名,random seed=0, BatchNorm axis=3 貌似是tf keras的

第二: Conv-BatchNorm-ReLU block
conv2d: filter=F2, kernel_size=f, stride=1, padding=same
output shape不变
起名,random seed=0, BatchNorm axis=3 貌似是tf keras的

第三: Conv-BatchNorm-ReLU block
conv2d: filter=F3, kernel_size=1, stride=1, padding=valid
output shape不变
得到最终的X_output

2 skip-connection
Conv-BatchNorm block
conv2d: filter=F3, kernel_size=1, stride=s, padding=valid 
shape与X_output一致
axis=3
返回X_skip

3 X_skip + X_output 通过ReLU函数

4 如何搭建一个跨越三层Identity Block


1 main path
第一: Conv-BatchNorm-ReLU block
conv2d: kernel_size=1, stride=1, padding=valid
output shape不变
起名,random seed=0, BatchNorm axis=3 貌似是tf keras的

第二: Conv-BatchNorm-ReLU block
conv2d: kernel_size=f, stride=1, padding=same
output shape不变
起名,random seed=0, BatchNorm axis=3 貌似是tf keras的

第三: Conv-BatchNorm block
conv2d: 同第一
output shape不变
得到最终的X_output

2 skip-connection

3 X_identity = X + X_output 通过ReLU函数

4 整体结构:


zera-padding:
(3x3)上下左右各添加3像素

stage1:
Conv:             filters=64, kernel_size=7x7, stride=2x2
BatchNorm: 
RELU:
MaxPooling:  windows=3x3, stride=2x2

stage2:
1xConv Block: named a

3set: [64, 64, 256], k_s=3x3, stride=1x1
2xID Block: named b,c
3set: [64, 64, 256], k_s=3x3,

stage3:
1xConv Block: named a

3set: [128, 128, 512], k_s=3x3, stride=2x2
3xID Block: named b,c,d
3set: [128, 128, 512], k_s=3x3

stage4:
1xConv Block: named a

3set: [256, 256, 1024], k_s=3x3, stride=2x2
5xID Block: named b,c,d,e,f
3set: [256, 256, 1024], k_s=3x3

stage5:
1xConv Block: named a

3set: [512, 512, 2048], k_s=3x3, stride=2x2
2xID Block: named b,c
3set: [512, 512, 2048], k_s=3x3

Average Pooling: named avg_pool
windows=(2x2)
Flatten:
Fully Connected(Dense) layer:
named 'fc'

5 resnet50文字详解

block_sizes=[3, 4, 6, 3]指的是stage1(first pool)之后的4个layer的block数, 分别对应res2,res3,res4,res5,每一个layer的第一个block在shortcut上做conv+BN, 即Conv Block
inputs: (1, 720, 1280, 3)
initial_conv:conv2d_fixed_padding()1. kernel_size=7, 先做padding(1, 720, 1280, 3) -> (1, 726, 1286, 3)2. conv2d kernels=[7, 7, 3, 64], stride=2, VALID 卷积. 7x7的kernel, padding都为3, 为了保证左上角和卷积核中心点对其(1, 726, 1286, 3) -> (1, 360, 640, 64)3. BN, Relu (只有resnetv1在第一次conv后面做BN和Relu)
initial_max_pool:k=3, s=2, padding='SAME', (1, 360, 640, 64) -> (1, 180, 320, 64)
以下均为不使用bottleneck的building_block
block_layer1:(有3个block, layer间stride=1(上一层做pool了), 64个filter, 不使用bottleneck(若使用bottleneck 卷积核数量需乘4))1. 第一个block:Conv Block有projection_shortcut, 且strides可以等于1或者2Identity Block没有projection_shortcut, 且strides只能等于1`inputs = block_fn(inputs, filters, training, projection_shortcut, strides, data_format)`shortcut做[1, 1, 64, 64], stride=1的conv和BN, shape不变然后和主要分支里input做3次卷积后的结果相加, 一起Relu, 注意block里最后一次卷积后只有BN没有Reluinput:    conv-bn-relu-conv-bn-relu-conv-bn  和shortcut相加后再做relushortcut: conv-bn                            shortcut: [1, 1, 64, 64], s=1, (1, 180, 320, 64) -> (1, 180, 320, 64)input做两次[3, 3, 64, 64], s=1的卷积, shape不变(1, 180, 320, 64) -> (1, 180, 320, 64) -> (1, 180, 320, 64)inputs += shortcut, 再relu2. 对剩下的2个block, 每个block操作相同:`inputs = block_fn(inputs, filters, training, None, 1, data_format)`shortcut直接和input卷积结果相加, 不做conv-bninput做两次[3, 3, 64, 64], s=1的卷积, shape不变(1, 180, 320, 64) -> (1, 180, 320, 64) -> (1, 180, 320, 64)inputs += shortcut, 再relu
block_layer2/3/4同block_layer1, 只是每个layer的identity block数量不同, 卷积核数量和layer间stride也不同, 不过仍然只有第一个conv block的shortcut做conv-bn
block_layer2: 4个block, 128个filter, layer间stride=2 (因为上一层出来后没有pool)1. 第一个block:对shortcut做kernel=[1, 1, 64, 128], s=2的conv和BN, (1, 180, 320, 64) -> (1, 90, 160, 128)对主要分支先做kernel=[3, 3, 64, 128], s=2的卷积, padding='VALID', (1, 180, 320, 64) -> (1, 90, 160, 128)再做kernel=[3, 3, 128, 128], s=1的卷积, padding='SAME', (1, 90, 160, 128) -> (1, 90, 160, 128)2. 剩下的3个block, 每个block操作相同:shortcut不操作直接和结果相加做Relu对主要分支做两次[3, 3, 128, 128], s=1的卷积, padding='SAME', (1, 90, 160, 128) -> (1, 90, 160, 128) -> (1, 90, 160, 128)
block_layer3: 6个block, 256个filter, layer间stride=21. 第一个block:对shortcut做kernel=[1, 1, 128, 256], s=2的conv和BN, (1, 90, 160, 128) -> (1, 45, 80, 256)对主要分支先做kernel=[3, 3, 128, 256], s=2的卷积, padding='VALID', (1, 90, 160, 128) -> (1, 45, 80, 256)再做kernel=[3, 3, 256, 256], s=1的卷积, padding='SAME', (1, 45, 80, 256) -> (1, 45, 80, 256)2. 剩下的5个block, 每个block操作相同:shortcut不操作直接和结果相加做Relu对主要分支做两次[3, 3, 256, 256], s=1的卷积, padding='SAME', (1, 45, 80, 256) -> (1, 45, 80, 256) -> (1, 45, 80, 256)
block_layer4: 3个block, 512个filter, layer间stride=21. 第一个block:对shortcut做kernel=[1, 1, 256, 512], s=2的conv和BN, (1, 45, 80, 256) -> (1, 23, 40, 512)对主要分支先做kernel=[3, 3, 256, 512], s=2的卷积, padding='VALID', (1, 45, 80, 256) -> (1, 23, 40, 512)再做kernel=[3, 3, 512, 512], s=1的卷积, padding='SAME', (1, 23, 40, 512) -> (1, 23, 40, 512)2. 剩下的2个block, 每个block操作相同:shortcut不操作直接和结果相加做Relu对主要分支做两次[3, 3, 512, 512], s=1的卷积, padding='SAME', (1, 23, 40, 512) -> (1, 23, 40, 512)
avg_pool, 7*7
FC, output1000
softmax
输出prediction

6 resnet50图解


 

这里写图片描述


http://chatgpt.dhexx.cn/article/9Px5vNEm.shtml

相关文章

Resnet-50网络结构详解

解决的问题: 梯度消失,深层网络难训练。 因为梯度反向传播到前面的层,重复相乘可能使梯度无穷小。结果就是,随着网络的层数更深,其性能趋于饱和,甚至迅速下降。 关于为什么残差结构(即多了一…

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

前言一、Resnet论文精读引入残差残差块ResNet50模型基本构成BN层Resnet50总体结构 二、Resnet50代码复现完整代码 前言 如果说在CNN领域一定要学习一个卷积神经网络,那一定非Resnet莫属了。 接下来我将按照:Resnet论文解读、Pytorch实现ResNet50模型两部…

Java类加载器介绍

1.类加载器介绍 类加载器负责将class文件加载到内存中,并为之生成对应的java.lang.Class对象。对于任意一个类,都需要加载它的类加载器和这个类本身来确定该类在JVM中唯一性,也就是说,同一个class文件用两个不同的类加载器加载并…

类加载与类加载器概述

目录 一、类加载 类的加载: 类的连接: 类的初始化: 类初始化步骤: 类的初始化时机: 二、类加载器 类加载器的作用 JVM的类加载机制 Java运行时具有以下内置类加载器: 一、类加载 当程序要使用某…

十一、类加载器的作用

狂神说Java:https://www.bilibili.com/video/BV1p4411P7V3 1、类加载的作用 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据…

2.类加载器

回顾 上一节我们学习了JVM类加载机制,我们学习到大概的过程:通过类加载器将编译好的class文件加载到JVM进程中,通过字节码执行引擎去执行代码。这只是一个整体的过程,具体的细节我们从本节开始分析。 通过本节我们将掌握以下知识&…

Java类加载器

一.类的生命周期 1. 加载(Loading):找 Class 文件 1. 通过一个类的全限定名来获取定义此类的二进制字节流。 2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 3.在内存中生成一个代表这个类的java.lang.Class对象&#xf…

Java类加载器的使用

Java类加载器 classloader顾名思义,即是类加载。虚拟机把描述类的数据从class字节码文件加载到内存,并对数据进行检验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。 先认识一下类加载…

JVM 类加载器

什么是类加载器 类加载器负责在运行时将Java类动态加载到Java虚拟机,他们也是JRE(Java运行时环境)的一部分。因此,借助类加载器,JVM无需了解底层文件或文件系统即可运行Java程序。此外,这些Java类不会一次…

类加载器深入理解

虚拟机设计团队把类加载阶段中“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的模块称为“类加载器”。 类加载器在类层次划分、OSGI、热部署、代码加密等领域…

java获取类加载器

获取类加载器的方法: //扩展类加载器MainClassLoader classLoader MainTest.class.getClassLoader();//表示当前线程的类加载器——应用程序类加载器ClassLoader contextClassLoader Thread.currentThread().getContextClassLoader();//—启动类加载器ClassLoader systemClas…

类加载器的种类

类加载器的种类有四种,如下图所示: 1.启动类加载器(引导类加载器,Bootstrap ClassLoader) 这个类加载使用C/C语言实现的,嵌套在JVM内部它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、res…

Java类加载器详解

1 特点 双亲委派: 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载…

【类加载器】java类加载器

类装载器ClassLoader(一个抽象类) 描述一下JVM加载class文件的原理机制 类装载器就是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件,在java中类装载器把一个类装入JVM,经过以下步骤: 1、装载&#xff…

什么是类加载器?

类加载器 什么是类加载器,作用是什么? 类加载器就是加载字节码文件(.class)的类 Java语言是一种具有动态性的解释语言,类(CLASS) 只有被加载到 JVM 中后才能运行。当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规…

类加载器

类加载过程 加载->连接->初始化。连接过程又可分为三步:验证->准备->解析。 类加载器分类 JVM 中内置了三个重要的 ClassLoader,除了 BootstrapClassLoader 其他类加载器均由 Java 实现且全部继承自java.lang.ClassLoader: 启动类加载器&…

类加载器作用

深入探讨 Java 类加载器 成 富, 软件工程师, IBM 中国软件开发中心 成富任职于 IBM 中国软件开发中心,目前在 Lotus 部门从事 IBM Mashup Center 的开发工作。他毕业于北京大学信息科学技术学院,获得计算机软件与理论专业硕士学位。他的个人网站是 http:…

java中的类加载器

文章目录 前言,一、加载器的作用是什么二、详解类加载器1.不得不说的双亲委派机制2.各个加载器加载的内容3.线程上下文类加载器4.类加载器的庐山真面目 总结 前言, java中一般来说有三种类加载器,分别为: 引导加载器,扩展加载器,应用加载器,还有一个线程上下文类加载器 一、加…

JVM类加载器

文章目录 一、类加载器二、类与类加载器三、双亲委派模型四、破坏双亲委派模型4.1、Tomcat4.1.1、WebApp类加载器4.1.2、Shared类加载器4.1.3、Catalina类加载器4.1.4、Common类加载器4.1.5、Jsp类加载器 4.2、JDBC 一、类加载器 从Java虚拟机的角度来讲,只存在两种…

自定义类加载器

目录 一、为什么要自定义类加载器? 二、常见的场景 三、实现方式 四、自定义类加载器示例 五、Java9新特性 一、为什么要自定义类加载器? 隔离加载类 在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:…