ResNet网络结构的搭建(一)
目录
1.基本结构:BasicBlock和BottleNeck
2.构建ResNet网络
3.完整代码
代码部分参考b站视频
1.基本结构:BasicBlock和BottleNeck
ResNet中最基本也是最重要的两个结构(图片来自:ResNet论文):BasicBlock(左)和BottleNeck(右),这两个结构分别用在轻量级的(ResNet18,ResNet34)及基础版(ResNet50,ResNet101,ResNet152)中。
首先是BasicBlock,经过两个3×3卷积后,与输入端的特征图进行相加的操作,这就是残差网络的基本思想。由于要进行残差连接,所以可能会遇到特征图分辨率或者通道数不匹配的情况,所以要对残差边的特征图进行判断,如果需要调整特征图分辨率或者通道数,则进行downsample操作。
然后是BottleNeck,经过1×1、3×3、1×1卷积后,与输入端的特征图进行相加的操作。
使用例子增加downsample说明:
1°首先要了解downsample的功能:a.调整特征图的分辨率(观察ResNet的设计可知,只有layer2/3/4第一个block块才需要进行调整分辨率,进行下采样)b.调整特征图的通道数(观察ResNet的设计可知,在不同的layer之间需要进行通道数转换,分辨率)
2°其次了解哪里需要进行下采样:a.首先是layer2/3/4层的第一个Block,通过stride=2调整输入特征图的宽高和通道数。b.其次是layer2/3/4层的第一个Block中的downsample结构,需要进行特征图分辨率和通道数的调整。
3°最后layer1的第一个block中的downsample作用:是用于调整通道数的,为输入通道数的四倍。
import torch
from torch import nn as nnclass BasicBlock(nn.Module):expansion=1#in_channel表示输入通道数,out_channel表示第二层的通道数,不是指最后一层的通道数def __init__(self,in_channel,out_channel,stride=1,downsample=None):super(BasicBlock, self).__init__()#通过传入stride,方便对特征图的分辨率进行调整self.conv1=nn.Conv2d(in_channel,out_channel,kernel_size=3,stride=stride,padding=1,bias=False)self.bn1=nn.BatchNorm2d(out_channel)self.conv2=nn.Conv2d(out_channel,out_channel*self.expansion,kernel_size=3,stride=1,padding=1,bias=False)self.bn2=nn.BatchNorm2d(out_channel*self.expansion)self.relu=nn.ReLU(inplace=True)self.downsample=downsampledef forward(self,x):residual=xx=self.conv1(x)x=self.bn1(x)x=self.conv2(x)x=self.bn2(x)#如果需要进行下采样,则将残差边加入下采样操作if self.downsample:residual=self.downsample(residual)x+=residualx=self.relu(x)return xclass BottleNeck(nn.Module):#第一个卷积的通道数和第三个卷积的通道数倍数是4expansion=4#in_channel表示输入通道数,out_channel表示第二个卷积的通道数def __init__(self,in_channel,out_channel,stride=1,downsample=None):super(BottleNeck, self).__init__()self.conv1=nn.Conv2d(in_channel,out_channel,kernel_size=1,stride=1,bias=False)self.bn1=nn.BatchNorm2d(out_channel)#通过传入stride,方便对特征图的分辨率进行调整self.conv2=nn.Conv2d(out_channel,out_channel,kernel_size=3,stride=stride,padding=1,bias=False)self.bn2=nn.BatchNorm2d(out_channel)self.conv3=nn.Conv2d(out_channel,out_channel*self.expansion,kernel_size=1,stride=1,bias=False)self.bn3=nn.BatchNorm2d(out_channel*self.expansion)self.relu=nn.ReLU(inplace=True)self.downsample=downsampledef forward(self,x):residual=xx=self.conv1(x)x=self.bn1(x)x = self.conv2(x)x = self.bn2(x)x = self.conv3(x)x = self.bn3(x)if self.downsample:residual=self.downsample(residual)x+=residualx=self.relu(x)return x
2.构建ResNet网络
首先输入一个224x224x3的图像到网络中,经过一个7x7的卷积后得到112x112x64的特征图,然后经过最大池化得到56x56x64的特征图。
然后经过layer1/2/3/4层。
然后经过平均池化层,刚好转换为1x1大小的图,最后经过全连接层得到(batch_size,num_class)大小的特征图。
class ResNet(nn.Module):"""观察ResNet网络结构:1.需要定义的以上两个基本结构block,2.需要layer1/2/3/4层的数量(num_layers[2,2,2,2]),3.需要输入通道数init_channels=3,4.需要类别数num_class=10(手写)"""def __init__(self,block_layer,num_layers,init_channels=3,num_class=10):super(ResNet, self).__init__()self.conv1=nn.Conv2d(init_channels,64,kernel_size=7,stride=2,padding=3,bias=False)self.bn1=nn.BatchNorm2d(64)self.max_pool=nn.MaxPool2d(kernel_size=3,stride=2)#输入四个layer层的通道数均为64self.in_channel=64"""定义4个layer1.第一个layer不需要进行下采样,只需要给第一个block_layer的downsample进行调整通道数2.第二三四个layer的第一个block_layer的downsample需要下采样和调整通道数"""self.layer1=self._make_layer(block_layer,64,stride=1,num_layer=num_layers[0])self.layer2 = self._make_layer(block_layer, 128, stride=2, num_layer=num_layers[1])self.layer3 = self._make_layer(block_layer, 256, stride=2, num_layer=num_layers[2])self.layer4 = self._make_layer(block_layer, 512, stride=2, num_layer=num_layers[3])#平均池化层,刚好转换为1x1大小的图self.avg_pool=nn.AvgPool2d(kernel_size=7,stride=1)#全连接层,输入通道数为512*expansionself.fc=nn.Linear(512*block_layer.expansion,num_class)def forward(self,x):x=self.conv1(x)x=self.bn1(x)x=self.max_pool(x)x=self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x=self.avg_pool(x)#进行全连接层之前进行转换,[batch_size,num_class]x=x.view(x.size(0),-1)x=self.fc(x)return x"""1.需要传入block构建网络,2.每个block的第二个卷积的通道数,3.一般不需要进行下采样,所以步长stride=1,4.每个layer的层数num_layer"""def _make_layer(self,block_layer,out_channel,num_layer,stride=1):downsample = None"""1.每一个layer的第一个block比较特殊,先构建第一个block2.如果stride!=1,即当stride=2需要进行下采样时候(位于两个layer交界处时),需要对残差边进行通道数与分辨率调整3.如果in_channel!=out_channel*block_layer.expansion,表示如果处于每个layer的第一层时候,需要进行通道数调整"""if stride!=1 or self.in_channel!=out_channel*block_layer.expansion:downsample=nn.Sequential(#注意,此处传入的stride可以调节,即可以根据是否需要分辨率调整进行传值,卷积大小为1x1nn.Conv2d(self.in_channel,out_channel*block_layer.expansion,kernel_size=1,stride=stride,bias=False),nn.BatchNorm2d(out_channel*block_layer.expansion))layer=[]#构建每一个layer的第一层,其是否需要进行进行下采样,可以由stride来调节,layer.append(block_layer(self.in_channel,out_channel,stride=stride,downsample=downsample))#layer的第二层输入通道数即为第一层的输出通道数,输出通道数也为第一层的输出通道数,注意此处的outchannel为第二个卷积的通道数self.in_channel=out_channel*block_layer.expansionfor i in range(1,num_layer):layer.append(block_layer(self.in_channel,out_channel,stride=1,downsample=None))return nn.Sequential(*layer)def resnet18():model=ResNet(BasicBlock,[2,2,2,2],num_class=10)return modeldef resnet34():model=ResNet(BasicBlock,[3,4,6,3],num_class=10)return modeldef resnet50():model=ResNet(BottleNeck,[3,4,6,3],num_class=10)return modeldef resnet101():model=ResNet(BasicBlock,[3,4,23,3],num_class=10)return modeldef resnet152():model=ResNet(BasicBlock,[3,8,36,3],num_class=10)return model
3.完整代码
以下是搭建ResNet网络的完整代码。后面第二Part用作网络训练和验证。
#-*- coding = utf-8 -*-
#@time:2022/6/7 16:32
#@Author:iceimport torch
from torch import nn as nnclass BasicBlock(nn.Module):expansion=1#in_channel表示输入通道数,out_channel表示第二层的通道数,不是指最后一层的通道数def __init__(self,in_channel,out_channel,stride=1,downsample=None):super(BasicBlock, self).__init__()#通过传入stride,方便对特征图的分辨率进行调整self.conv1=nn.Conv2d(in_channel,out_channel,kernel_size=3,stride=stride,padding=1,bias=False)self.bn1=nn.BatchNorm2d(out_channel)self.conv2=nn.Conv2d(out_channel,out_channel*self.expansion,kernel_size=3,stride=1,padding=1,bias=False)self.bn2=nn.BatchNorm2d(out_channel*self.expansion)self.relu=nn.ReLU(inplace=True)self.downsample=downsampledef forward(self,x):residual=xx=self.conv1(x)x=self.bn1(x)x=self.conv2(x)x=self.bn2(x)#如果需要进行下采样,则将残差边加入下采样操作if self.downsample:residual=self.downsample(residual)x+=residualx=self.relu(x)return xclass BottleNeck(nn.Module):#第一个卷积的通道数和第三个卷积的通道数倍数是4expansion=4#in_channel表示输入通道数,out_channel表示第二个卷积的通道数def __init__(self,in_channel,out_channel,stride=1,downsample=None):super(BottleNeck, self).__init__()self.conv1=nn.Conv2d(in_channel,out_channel,kernel_size=1,stride=1,bias=False)self.bn1=nn.BatchNorm2d(out_channel)#通过传入stride,方便对特征图的分辨率进行调整self.conv2=nn.Conv2d(out_channel,out_channel,kernel_size=3,stride=stride,padding=1,bias=False)self.bn2=nn.BatchNorm2d(out_channel)self.conv3=nn.Conv2d(out_channel,out_channel*self.expansion,kernel_size=1,stride=1,bias=False)self.bn3=nn.BatchNorm2d(out_channel*self.expansion)self.relu=nn.ReLU(inplace=True)self.downsample=downsampledef forward(self,x):residual=xx=self.conv1(x)x=self.bn1(x)x = self.conv2(x)x = self.bn2(x)x = self.conv3(x)x = self.bn3(x)if self.downsample:residual=self.downsample(residual)x+=residualx=self.relu(x)return xclass ResNet(nn.Module):"""观察ResNet网络结构:1.需要定义的以上两个基本结构block,2.需要layer1/2/3/4层的数量(num_layers[2,2,2,2]),3.需要输入通道数init_channels=3,4.需要类别数num_class=10(手写)"""def __init__(self,block_layer,num_layers,init_channels=3,num_class=10):super(ResNet, self).__init__()self.conv1=nn.Conv2d(init_channels,64,kernel_size=7,stride=2,padding=3,bias=False)self.bn1=nn.BatchNorm2d(64)self.max_pool=nn.MaxPool2d(kernel_size=3,stride=2)#输入四个layer层的通道数均为64self.in_channel=64"""定义4个layer1.第一个layer不需要进行下采样,只需要给第一个block_layer的downsample进行调整通道数2.第二三四个layer的第一个block_layer的downsample需要下采样和调整通道数"""self.layer1=self._make_layer(block_layer,64,stride=1,num_layer=num_layers[0])self.layer2 = self._make_layer(block_layer, 128, stride=2, num_layer=num_layers[1])self.layer3 = self._make_layer(block_layer, 256, stride=2, num_layer=num_layers[2])self.layer4 = self._make_layer(block_layer, 512, stride=2, num_layer=num_layers[3])#平均池化层,刚好转换为1x1大小的图self.avg_pool=nn.AvgPool2d(kernel_size=7,stride=1)#全连接层,输入通道数为512*expansionself.fc=nn.Linear(512*block_layer.expansion,num_class)def forward(self,x):x=self.conv1(x)x=self.bn1(x)x=self.max_pool(x)x=self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x=self.avg_pool(x)#进行全连接层之前进行转换,[batch_size,num_class]x=x.view(x.size(0),-1)x=self.fc(x)return x"""1.需要传入block构建网络,2.每个block的第二个卷积的通道数,3.一般不需要进行下采样,所以步长stride=1,4.每个layer的层数num_layer"""def _make_layer(self,block_layer,out_channel,num_layer,stride=1):downsample = None"""1.每一个layer的第一个block比较特殊,先构建第一个block2.如果stride!=1,即当stride=2需要进行下采样时候(位于两个layer交界处时),需要对残差边进行通道数与分辨率调整3.如果in_channel!=out_channel*block_layer.expansion,表示如果处于每个layer的第一层时候,需要进行通道数调整"""if stride!=1 or self.in_channel!=out_channel*block_layer.expansion:downsample=nn.Sequential(#注意,此处传入的stride可以调节,即可以根据是否需要分辨率调整进行传值,卷积大小为1x1nn.Conv2d(self.in_channel,out_channel*block_layer.expansion,kernel_size=1,stride=stride,bias=False),nn.BatchNorm2d(out_channel*block_layer.expansion))layer=[]#构建每一个layer的第一层,其是否需要进行进行下采样,可以由stride来调节,layer.append(block_layer(self.in_channel,out_channel,stride=stride,downsample=downsample))#layer的第二层输入通道数即为第一层的输出通道数,输出通道数也为第一层的输出通道数,注意此处的outchannel为第二个卷积的通道数self.in_channel=out_channel*block_layer.expansionfor i in range(1,num_layer):layer.append(block_layer(self.in_channel,out_channel,stride=1,downsample=None))return nn.Sequential(*layer)def resnet18():model=ResNet(BasicBlock,[2,2,2,2],num_class=10)return modeldef resnet34():model=ResNet(BasicBlock,[3,4,6,3],num_class=10)return modeldef resnet50():model=ResNet(BottleNeck,[3,4,6,3],num_class=10)return modeldef resnet101():model=ResNet(BasicBlock,[3,4,23,3],num_class=10)return modeldef resnet152():model=ResNet(BasicBlock,[3,8,36,3],num_class=10)return modelif __name__ == '__main__':a=torch.randn(5,3,224,224)m=resnet18()o=m(a)print(o.size())
ResNet网络结构的搭建(一)相关推荐
- ResNet网络结构详解,网络搭建,迁移学习
前言: 参考内容来自up:6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili up的代码和ppt:https://github.com/WZMIAOMIAO/deep-le ...
- pytorch图像分类篇:6. ResNet网络结构详解与迁移学习简介
前言 最近在b站发现了一个非常好的 计算机视觉 + pytorch 的教程,相见恨晚,能让初学者少走很多弯路. 因此决定按着up给的教程路线:图像分类→目标检测→-一步步学习用pytorch实现深度学 ...
- ResNet网络结构,BN以及迁移学习详解
看b站up主霹雳吧啦Wz视频,以及一些文章参考,所做笔计 链接: 6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili 深度学习之残差神经网络(ResNet) - 知乎 (z ...
- cnn卷积过程,通道数,《6.1 ResNet网络结构,BN以及迁移学习详解》
6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili CNN科普讲解:李宏毅讲解卷积神经网络(带字幕)_哔哩哔哩_bilibili 1.图像中的RGBpython rgb 图 ...
- resnet网络结构_深度学习之16——残差网络(ResNet)
残差网络在设计之初,主要是服务于卷积神经网络(CNN),在计算机视觉领域应用较多,但是随着CNN结构的发展,在很多文本处理,文本分类里面(n-gram),也同样展现出来很好的效果. 首先先明确一下几个 ...
- resnet网络结构_ResNet网络详细解析(超详细哦)
1.RestNet网络 1.1.RestNet网络结构 ResNet在2015年被提出,在ImageNet比赛classification任务上获得第一名,因为它"简单与实用"并存 ...
- resnet50网络结构_学习笔记(一):分析resnet源码理解resnet网络结构
最近在跑实验的过程中一直在使用resnet50和resnet34,为了弄清楚网络的结构和原理的实现,打开resnet的源码进行了学习. 残差网络学习的原理 针对神经网络过深而导致的学习准确率饱和甚至是 ...
- ResNet网络的搭建
文章目录 亮点 残差结构 计算量 虚线残差结构 代码解析 resnet18/34的残差结构 resnet50/101/152的残差结构Bottleneck 一层layer的结构(_make_layer ...
- VGG网络结构的搭建(pytorch以及百度飞桨)
对应百度飞桨页面 VGG网络是在2014年由牛津大学著名研究组VGG (Visual Geometry Group) 提出. 下载花分类数据集 import requests import os im ...
最新文章
- 计算机组成原理形考任务五答案,计算机组成原理形考任务5
- 计算机安装操作系统的目的是什么,安装计算机操作系统.doc
- 【转】WPF从我炫系列4---装饰控件的用法
- .NET程序员应掌握的常用类库
- linux tomcat catalina.out 乱码,Tomcat输出日志乱码解决
- 本人见过的最有用的日志!不来转藏肯定后悔
- 计算机编程英语词汇大全
- 零基础 Java 学习笔记
- 联想笔记本重装系统声卡驱动未安装报错代码28,声音图标显示红叉没有声音
- 单层感知器python_深度学习之单层感知器(一)
- 吉林大学超星MOOC学习通高级语言程序设计 C++ 实验04 数组及其在程序设计中的应用(2021级)(3)
- 宇宙飞船大战html5游戏,GitHub - WildDogTeam/demo-js-starwars: 基于WildDog的太空大战游戏...
- 杨百翰大学计算机科学专业,杨百翰大学研究生什么专业好
- 写html和css怎么分屏,如何使用CSS创建分屏(50/50)?
- 什么是蓝牙(Bluetooth)
- yocto FILES_${PN}
- php如何连接数据库 甲骨文,Windows PHP/phpStudy 连接 甲骨文Oracle 数据库 oci8
- 1.2开发小程序的准备工作
- Airship再下一城,因为戴尔和AtT的合作
- html控制图的宽,【公差的验收控制图】 过程稳定与控制图傻傻分不清
热门文章
- The driver has not received any packets from the server.
- Redis主从复制的讲解
- Mysql迁移到Oracle
- 不知道同声传译软件哪个好?这篇文章分享给你三个同声传译软件
- ChatGPT老板撒钱救难:百万美元帮硅谷银行受害公司,不要借条不用承诺,能还时再还...
- 「自动化测试」新一代 Web 前端自动化测试框架 —— playwright 快速上手,轻松带入项目
- 品牌电脑Windows8.1中文版想改英文版的问题
- 原创--公司年会搞笑新春祝福
- python数组维度变换
- 这些酷酷的文字特效图片,制作真的很简单!