参考资料
作为新手学习难免会有很多不懂的地方,以下是我参考的一些资料:
ResNet源码:https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py
源码讲解:https://www.jianshu.com/p/ec0967460d08
ResNet论文:https://arxiv.org/pdf/1512.03385.pdf
ResNet50复现:https://note.youdao.com/ynoteshare1/index.html?id=5a7dbe1a71713c317062ddeedd97d98e&type=note
ResNet50复现讲解:https://www.bilibili.com/video/BV1154y1S7WC?from=search&seid=8328821625196427671
代码实现
import torch
from torch import nn
class Bottleneck(nn.Module):
extention=4
def __init__(self,inplanes,planes,stride,downsample=None):
'''
:param inplanes: 输入block的之前的通道数
:param planes: 在block中间处理的时候的通道数
planes*self.extention:输出的维度
:param stride:
:param downsample:
'''
super(Bottleneck, self).__init__()
self.conv1=nn.Conv2d(inplanes,planes,kernel_size=1,stride=stride,bias=False)
self.bn1=nn.BatchNorm2d(planes)
self.conv2=nn.Conv2d(planes,planes,kernel_size=3,stride=1,padding=1,bias=False)
self.bn2=nn.BatchNorm2d(planes)
self.conv3=nn.Conv2d(planes,planes*self.extention,kernel_size=1,stride=1,bias=False)
self.bn3=nn.BatchNorm2d(planes*self.extention)
self.relu=nn.ReLU(inplace=True)
self.downsample=downsample
self.stride=stride
def forward(self,x):
residual=x
out=self.conv1(x)
out=self.bn1(out)
out=self.relu(out)
out=self.conv2(out)
out=self.bn2(out)
out=self.relu(out)
out=self.conv3(out)
out=self.bn3(out)
out=self.relu(out)
if self.downsample is not None:
residual=self.downsample(x)
out+=residual
out=self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self,block,layers,num_class):
self.inplane=64
super(ResNet, self).__init__()
self.block=block
self.layers=layers
self.conv1=nn.Conv2d(3,self.inplane,kernel_size=7,stride=2,padding=3,bias=False)
self.bn1=nn.BatchNorm2d(self.inplane)
self.relu=nn.ReLU()
self.maxpool=nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
self.stage1=self.make_layer(self.block,64,layers[0],stride=1)
self.stage2=self.make_layer(self.block,128,layers[1],stride=2)
self.stage3=self.make_layer(self.block,256,layers[2],stride=2)
self.stage4=self.make_layer(self.block,512,layers[3],stride=2)
self.avgpool=nn.AvgPool2d(7)
self.fc=nn.Linear(512*block.extention,num_class)
def forward(self,x):
out=self.conv1(x)
out=self.bn1(out)
out=self.relu(out)
out=self.maxpool(out)
out=self.stage1(out)
out=self.stage2(out)
out=self.stage3(out)
out=self.stage4(out)
out=self.avgpool(out)
out=torch.flatten(out,1)
out=self.fc(out)
return out
def make_layer(self,block,plane,block_num,stride=1):
'''
:param block: block模板
:param plane: 每个模块中间运算的维度,一般等于输出维度/4
:param block_num: 重复次数
:param stride: 步长
:return:
'''
block_list=[]
downsample=None
if(stride!=1 or self.inplane!=plane*block.extention):
downsample=nn.Sequential(
nn.Conv2d(self.inplane,plane*block.extention,stride=stride,kernel_size=1,bias=False),
nn.BatchNorm2d(plane*block.extention)
)
conv_block=block(self.inplane,plane,stride=stride,downsample=downsample)
block_list.append(conv_block)
self.inplane=plane*block.extention
for i in range(1,block_num):
block_list.append(block(self.inplane,plane,stride=1))
return nn.Sequential(*block_list)
resnet=ResNet(Bottleneck,[3,4,6,3],1000)
x=torch.randn(64,3,224,224)
X=resnet(x)
print(X.shape)
输出结果
torch.Size([64, 1000])
首先我们需要了解ResNet的原理和ResNet50的构造,如果参考我所上传的资料,完全可以搞懂。
代码讲解
这段代码是这个结构的复现。
self.conv1=nn.Conv2d(inplanes,planes,kernel_size=1,stride=stride,bias=False)
self.bn1=nn.BatchNorm2d(planes)
self.conv2=nn.Conv2d(planes,planes,kernel_size=3,stride=1,padding=1,bias=False)
elf.bn2=nn.BatchNorm2d(planes)
self.conv3=nn.Conv2d(planes,planes*self.extention,kernel_size=1,stride=1,bias=False)
self.bn3=nn.BatchNorm2d(planes*self.extention)
self.relu=nn.ReLU(inplace=True)
这是ResNet最核心的地方:
downsample是用来将残差数据和卷积数据的shape变的相同,可以直接进行相加操作。
if self.downsample is not None:
residual=self.downsample(x)
out+=residual
out=self.relu(out)
ResNet
class ResNet(nn.Module):
def __init__(self,block,layers,num_class):
self.inplane=64
super(ResNet, self).__init__()
self.block=block
self.layers=layers
self.conv1=nn.Conv2d(3,self.inplane,kernel_size=7,stride=2,padding=3,bias=False)
self.bn1=nn.BatchNorm2d(self.inplane)
self.relu=nn.ReLU()
self.maxpool=nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
self.stage1=self.make_layer(self.block,64,layers[0],stride=1)
self.stage2=self.make_layer(self.block,128,layers[1],stride=2)
self.stage3=self.make_layer(self.block,256,layers[2],stride=2)
self.stage4=self.make_layer(self.block,512,layers[3],stride=2)
self.avgpool=nn.AvgPool2d(7)
self.fc=nn.Linear(512*block.extention,num_class)
def forward(self,x):
out=self.conv1(x)
out=self.bn1(out)
out=self.relu(out)
out=self.maxpool(out)
out=self.stage1(out)
out=self.stage2(out)
out=self.stage3(out)
out=self.stage4(out)
out=self.avgpool(out)
out=torch.flatten(out,1)
out=self.fc(out)
return out
def make_layer(self,block,plane,block_num,stride=1):
'''
:param block: block模板
:param plane: 每个模块中间运算的维度,一般等于输出维度/4
:param block_num: 重复次数
:param stride: 步长
:return:
'''
block_list=[]
downsample=None
if(stride!=1 or self.inplane!=plane*block.extention):
downsample=nn.Sequential(
nn.Conv2d(self.inplane,plane*block.extention,stride=stride,kernel_size=1,bias=False),
nn.BatchNorm2d(plane*block.extention)
)
conv_block=block(self.inplane,plane,stride=stride,downsample=downsample)
block_list.append(conv_block)
self.inplane=plane*block.extention
for i in range(1,block_num):
block_list.append(block(self.inplane,plane,stride=1))
return nn.Sequential(*block_list)
这段代码实现了
self.conv1=nn.Conv2d(3,self.inplane,kernel_size=7,stride=2,padding=3,bias=False)
self.bn1=nn.BatchNorm2d(self.inplane)
self.relu=nn.ReLU()
self.maxpool=nn.MaxPool2d(kernel_size=3,stride=2,padding=1)
这段代码实现了
self.stage1=self.make_layer(self.block,64,layers[0],stride=1)
self.stage2=self.make_layer(self.block,128,layers[1],stride=2)
self.stage3=self.make_layer(self.block,256,layers[2],stride=2)
self.stage4=self.make_layer(self.block,512,layers[3],stride=2)
这段代码实现了
self.avgpool=nn.AvgPool2d(7)
self.fc=nn.Linear(512*block.extention,num_class)
make_layer
downsample是残差是否进行卷积的标识。
downsample=None
if(stride!=1 or self.inplane!=plane*block.extention):
downsample=nn.Sequential(
nn.Conv2d(self.inplane,plane*block.extention,stride=stride,kernel_size=1,bias=False),
nn.BatchNorm2d(plane*block.extention)
)
Conv Block输入和输出的维度(通道数和size)是不一样的,所以不能连续串联,他的作用是改变网络的维度
conv_block=block(self.inplane,plane,stride=stride,downsample=downsample)
Identity Block 输入维度和输出(通道数和size)相同,可以直接串联,用于加深网络
for i in range(1,block_num):
block_list.append(block(self.inplane,plane,stride=1))
网络结构
Original: https://blog.csdn.net/m0_50127633/article/details/117200212
Author: 一个小猴子`
Title: pytorch实现ResNet50模型(小白学习,详细讲解)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/626292/
转载文章受原作者版权保护。转载请注明原作者出处!