目录

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网络结构的搭建(一)相关推荐

  1. ResNet网络结构详解,网络搭建,迁移学习

    前言: 参考内容来自up:6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili up的代码和ppt:https://github.com/WZMIAOMIAO/deep-le ...

  2. pytorch图像分类篇:6. ResNet网络结构详解与迁移学习简介

    前言 最近在b站发现了一个非常好的 计算机视觉 + pytorch 的教程,相见恨晚,能让初学者少走很多弯路. 因此决定按着up给的教程路线:图像分类→目标检测→-一步步学习用pytorch实现深度学 ...

  3. ResNet网络结构,BN以及迁移学习详解

    看b站up主霹雳吧啦Wz视频,以及一些文章参考,所做笔计 链接: 6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili 深度学习之残差神经网络(ResNet) - 知乎 (z ...

  4. cnn卷积过程,通道数,《6.1 ResNet网络结构,BN以及迁移学习详解》

    6.1 ResNet网络结构,BN以及迁移学习详解_哔哩哔哩_bilibili CNN科普讲解:李宏毅讲解卷积神经网络(带字幕)_哔哩哔哩_bilibili 1.图像中的RGBpython rgb 图 ...

  5. resnet网络结构_深度学习之16——残差网络(ResNet)

    残差网络在设计之初,主要是服务于卷积神经网络(CNN),在计算机视觉领域应用较多,但是随着CNN结构的发展,在很多文本处理,文本分类里面(n-gram),也同样展现出来很好的效果. 首先先明确一下几个 ...

  6. resnet网络结构_ResNet网络详细解析(超详细哦)

    1.RestNet网络 1.1.RestNet网络结构 ResNet在2015年被提出,在ImageNet比赛classification任务上获得第一名,因为它"简单与实用"并存 ...

  7. resnet50网络结构_学习笔记(一):分析resnet源码理解resnet网络结构

    最近在跑实验的过程中一直在使用resnet50和resnet34,为了弄清楚网络的结构和原理的实现,打开resnet的源码进行了学习. 残差网络学习的原理 针对神经网络过深而导致的学习准确率饱和甚至是 ...

  8. ResNet网络的搭建

    文章目录 亮点 残差结构 计算量 虚线残差结构 代码解析 resnet18/34的残差结构 resnet50/101/152的残差结构Bottleneck 一层layer的结构(_make_layer ...

  9. VGG网络结构的搭建(pytorch以及百度飞桨)

    对应百度飞桨页面 VGG网络是在2014年由牛津大学著名研究组VGG (Visual Geometry Group) 提出. 下载花分类数据集 import requests import os im ...

最新文章

  1. 计算机组成原理形考任务五答案,计算机组成原理形考任务5
  2. 计算机安装操作系统的目的是什么,安装计算机操作系统.doc
  3. 【转】WPF从我炫系列4---装饰控件的用法
  4. .NET程序员应掌握的常用类库
  5. linux tomcat catalina.out 乱码,Tomcat输出日志乱码解决
  6. 本人见过的最有用的日志!不来转藏肯定后悔
  7. 计算机编程英语词汇大全
  8. 零基础 Java 学习笔记
  9. 联想笔记本重装系统声卡驱动未安装报错代码28,声音图标显示红叉没有声音
  10. 单层感知器python_深度学习之单层感知器(一)
  11. 吉林大学超星MOOC学习通高级语言程序设计 C++ 实验04 数组及其在程序设计中的应用(2021级)(3)
  12. 宇宙飞船大战html5游戏,GitHub - WildDogTeam/demo-js-starwars: 基于WildDog的太空大战游戏...
  13. 杨百翰大学计算机科学专业,杨百翰大学研究生什么专业好
  14. 写html和css怎么分屏,如何使用CSS创建分屏(50/50)?
  15. 什么是蓝牙(Bluetooth)
  16. yocto FILES_${PN}
  17. php如何连接数据库 甲骨文,Windows PHP/phpStudy 连接 甲骨文Oracle 数据库 oci8
  18. 1.2开发小程序的准备工作
  19. Airship再下一城,因为戴尔和AtT的合作
  20. html控制图的宽,【公差的验收控制图】 过程稳定与控制图傻傻分不清

热门文章

  1. The driver has not received any packets from the server.
  2. Redis主从复制的讲解
  3. Mysql迁移到Oracle
  4. 不知道同声传译软件哪个好?这篇文章分享给你三个同声传译软件
  5. ChatGPT老板撒钱救难:百万美元帮硅谷银行受害公司,不要借条不用承诺,能还时再还...
  6. 「自动化测试」新一代 Web 前端自动化测试框架 —— playwright 快速上手,轻松带入项目
  7. 品牌电脑Windows8.1中文版想改英文版的问题
  8. 原创--公司年会搞笑新春祝福
  9. python数组维度变换
  10. 这些酷酷的文字特效图片,制作真的很简单!