resnet50 代码下载 resnet50源码_pycharm

本文目的不在于让你学会各种大小数据的变化,而在于体会resnet执行的流程,大白话解说,读不懂的见谅!

废话少说,直接上最重要的两个图片

resnet50 代码下载 resnet50源码_pycharm_02

resnet50 代码下载 resnet50源码_resnet50 代码下载_03

图:唱跳rap 

 用于和代码debug对照,接下来直接开始 

resnet50 代码下载 resnet50源码_ide_04

 内参数(瓶颈层,[3,4,6,3]对应唱跳rapx3x4x6x3,我个人理解为每个块内的遍历次数,分类数)

从括号里外的顺序开始,先跳转到resnet类

resnet50 代码下载 resnet50源码_ide_05

 inplane 输入通道数,plane 输出通道数

这一部分的操作主要是把nn.module的父类方法继承过来,现在还在定义resnet函数阶段

 为什么inplane等于64,因为唱跳rap stage1的输入是64


super(ResNet, self).__init__()这个很重要也不重要,重要在于必须有,不重要在于不用理睬。


按部就班往下走

 

resnet50 代码下载 resnet50源码_ide_06

 一一和唱跳rap对应,stage4定义后,来到block_list的定义,可以理解为用于记录每轮的遍历次数,就是那个唱跳rap的3,4,6,3。

resnet50 代码下载 resnet50源码_ide_07

 下采样的操作,按部就班走

resnet50 代码下载 resnet50源码_ide_08

resnet50 代码下载 resnet50源码_resnet50 代码下载_09

 Bottleneck的实现,还是往下走

 

resnet50 代码下载 resnet50源码_ide_10

 走到这注意一下,意思为,下一层的输入等于上一层的输出乘以block.extention,这个变量是啥?比如第一个stage,输入64 输出256,那么这个变量就是4啊。这就已经走了一次这个遍历,还要走两次

resnet50 代码下载 resnet50源码_2d_11

 再次走这个循环了昂

 

resnet50 代码下载 resnet50源码_ide_12

resnet50 代码下载 resnet50源码_pycharm_13

 

resnet50 代码下载 resnet50源码_ide_14

resnet50 代码下载 resnet50源码_resnet50 代码下载_15

 一共走了三次,对应于这个x3

resnet50 代码下载 resnet50源码_pycharm_16

 然后,开始走第二个stage

resnet50 代码下载 resnet50源码_resnet50 代码下载_17

 同理,这个需要走4遍对吧?i=3,对上了,走了4遍 

resnet50 代码下载 resnet50源码_2d_18

stage3 6遍

 

resnet50 代码下载 resnet50源码_网络_19

 

resnet50 代码下载 resnet50源码_2d_20

stage4 3遍

resnet50 代码下载 resnet50源码_resnet50 代码下载_21

  

resnet50 代码下载 resnet50源码_resnet50 代码下载_22

 

resnet50 代码下载 resnet50源码_2d_23

这四层都搞完了是吧

resnet50 代码下载 resnet50源码_pycharm_24

 

跳到这里啥意思,没毛病就是这个 

 

resnet50 代码下载 resnet50源码_pycharm_25

 现在,这个网络结构真的已经搭建好了,你说对吗?

该传入数据了!给的是随机大小的固定维度tensor

 

resnet50 代码下载 resnet50源码_网络_26

我们要带着数据走一遍网络进行传播,这次过程中会有计算

 

resnet50 代码下载 resnet50源码_pycharm_27

resnet50 代码下载 resnet50源码_ide_28

 

进入resnet 开始传播了,随手截几个图 

resnet50 代码下载 resnet50源码_ide_29

resnet50 代码下载 resnet50源码_resnet50 代码下载_30

 

特别注意103行这个加法,对应的是resnet最牛的抄近道操作 

resnet50 代码下载 resnet50源码_2d_31

 

resnet50 代码下载 resnet50源码_resnet50 代码下载_32

 走完了,运行一下吧

resnet50 代码下载 resnet50源码_2d_33

 附上全程代码,读不懂的自己操作吧

import torch
from torch import nn
class Bottleneck(nn.Module):
    #每个stage维度中扩展的倍数
    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)

        #是否直连(如果Indentity blobk就是直连;如果Conv2 Block就需要对残差边就行卷积,改变通道数和size
        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):
        #inplane=当前的fm的通道数
        self.inplane=64
        super(ResNet, self).__init__()

        #参数
        self.block=block
        self.layers=layers

        #stem的网络层
        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)

        #64,128,256,512指的是扩大4倍之前的维度,即Identity Block中间的维度
        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):
        #stem部分:conv+bn+maxpool
        out=self.conv1(x)
        out=self.bn1(out)
        out=self.relu(out)
        out=self.maxpool(out)

        #block部分
        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
        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)是不一样的,所以不能连续串联,他的作用是改变网络的维度
        # Identity Block 输入维度和输出(通道数和size)相同,可以直接串联,用于加深网络
        #Conv_block
        conv_block=block(self.inplane,plane,stride=stride,downsample=downsample)
        block_list.append(conv_block)
        self.inplane=plane*block.extention

        #Identity Block
        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)