代码如下:
- # import torch.nn as nn
- # import torch
- # import torch.nn.functional as F
- # import math
- #
- # #1.resNet的基本BottleBlock类
- #
- # class Bottleneck(nn.Module):
- # expension=4
- # def __init__(self,in_planes,planes,stride=1,downsample=None):
- # super(Bottleneck,self).__init__()
- #
- # self.bottleneck=nn.Sequential(
- # #1.对输入变量x进行卷积、归一化、RELU激活
- # nn.Conv2d(in_planes,planes,1,bias=False),
- # nn.BatchNorm2d(planes),
- # nn.ReLU(inplace=True),
- #
- # #2.构造f(x)输出
- # nn.Conv2d(planes,planes,3,stride,1,bias=False),
- # nn.BatchNorm2d(planes),
- # nn.ReLU(inplace=True),
- #
- # nn.Conv2d(planes,self.expension*planes,1,bias=False),
- # nn.BatchNorm2d(self.expension*planes)
- # )
- #
- # #3.构造激活函数,构造下采样函数为None
- # self.relu=nn.ReLU(inplace=True)
- # #self.downsample=downsample
- # self.downsample =nn.Sequential(
- # nn.Conv2d(in_planes,self.expension*planes,1,1),
- # nn.BatchNorm2d(self.expension*planes)
- # )
- #
- #
- # def forward(self,x):
- #
- # identity=x
- #
- # #4.构造f(x)
- # out=self.bottleneck(x)
- #
- # #5.如果有下采样,则进行下采样
- # if self.downsample is not None:
- # identity=self.downsample(x)
- #
- # #6.构造 x+f(x)
- # out+=identity
- #
- # #7.对 x+f(x) 进行激活
- # out=self.relu(out)
- #
- # #8.返回激活后的 x+f(x)
- # return out
- #
- #
- # if __name__ == '__main__':
- #
- #
- # input=torch.randn(1,64,56,56)
- #
- # ss=Bottleneck(64,256)
- #
- # hh=ss(input)
- # print(hh.shape)
-
-
- import torch.nn as nn
- import torch
- import torch.nn.functional as F
-
- #1.resNet的基本BottleBlock类
-
- class Bottleneck(nn.Module):
- expension=4
- def __init__(self,in_planes,planes,stride=1,downsample=None):
- super(Bottleneck,self).__init__()
-
- self.bottleneck=nn.Sequential(
- #1.对输入变量x进行卷积、归一化、RELU激活
- nn.Conv2d(in_planes,planes,1,bias=False),
- nn.BatchNorm2d(planes),
- nn.ReLU(inplace=True),
-
- #2.构造f(x)输出
- nn.Conv2d(planes,planes,3,stride,1,bias=False),
- nn.BatchNorm2d(planes),
- nn.ReLU(inplace=True),
-
- nn.Conv2d(planes,self.expension*planes,1,bias=False),
- nn.BatchNorm2d(self.expension*planes)
- )
-
- #3.构造激活函数,构造下采样函数为None
- self.relu=nn.ReLU(inplace=True)
- self.downsample=downsample
- # self.downsample =nn.Sequential(
- # nn.Conv2d(in_planes,self.expension*planes,1,1),
- # nn.BatchNorm2d(self.expension*planes)
- # )
-
-
- def forward(self,x):
-
- identity=x
-
- #4.构造f(x)
- out=self.bottleneck(x)
-
- #5.如果有下采样,则进行下采样
- if self.downsample is not None:
- identity=self.downsample(x)
-
- #6.构造 x+f(x)
- out+=identity
-
- #7.对 x+f(x) 进行激活
- out=self.relu(out)
-
- #8.返回激活后的 x+f(x)
- return out
-
- #2.FPN的类,初始化需要一个list,代表ResNet的每一个阶段的Bottleneck数量
- class FPN(nn.Module):
- def __init__(self,layers):
- super(FPN,self).__init__()
- self.inplanes=64
-
- #1.处理输入的C1模块
- self.conv1=nn.Conv2d(3,64,7,2,3,bias=False)
- self.bn1=nn.BatchNorm2d(64)
- self.relu=nn.ReLU(inplace=True)
- self.maxpool=nn.MaxPool2d(3,2,1)
-
- #2.搭建自上而下的C2,C3,C4,C5
- self.layer1=self.make_layer(64,layers[0])
- self.layer2=self.make_layer(128,layers[1],2)
- self.layer3 = self.make_layer(256, layers[2],2)
- self.layer4 = self.make_layer(512, layers[3],2)
-
- #3.对C5减少通道数,得到P5 输入通道数2048,输出通道数256,卷积核大小1,步长为1,0填充
- self.toplayer=nn.Conv2d(2048,256,1,1,0)
-
- #4.3X3卷积融合特征
- self.smooth1=nn.Conv2d(256,256,3,1,1)
- self.smooth2 = nn.Conv2d(256, 256, 3, 1, 1)
- self.smooth3 = nn.Conv2d(256, 256, 3, 1, 1)
-
- #5.横向连接,保证通道数相同
- self.latlayer1=nn.Conv2d(1024,256,1,1,0)
- self.latlayer2= nn.Conv2d(512, 256, 1, 1, 0)
- self.latlayer3= nn.Conv2d(256, 256, 1, 1, 0)
-
-
-
- #2.构造C2到C5,注意区分stride为1和2的情况
- def make_layer(self,planes,blocks,stride=1):
- downsample=None
- if stride!=1 or self.inplanes!=Bottleneck.expension*planes:
- #下采样的卷积核大小为1,目的是使输入x和输出的f(x)的通道数相同,使x的通道数是输入的4倍,这样残差网络才能做 x+f(x) 运算
- downsample=nn.Sequential(
- nn.Conv2d(self.inplanes,Bottleneck.expension*planes,1,stride,bias=False),
- nn.BatchNorm2d(Bottleneck.expension*planes)
- )
-
- layers=[]
- #加入第一层
- layers.append(Bottleneck(self.inplanes,planes,stride,downsample))
- self.inplanes=planes*Bottleneck.expension
-
- # C2,C3,C4,C5的bottleneck个数不同,其个数分别是[3,4,6,3]
- for i in range(1,blocks):
- layers.append(Bottleneck(self.inplanes,planes))
- return nn.Sequential(*layers)
-
- #3.自上而下的上采样模块
- def _upsample(self,x,y):
- _,_,H,W=y.shape
- #用最近邻插值法进行下采样
- return F.upsample(x,size=(H,W),mode="bilinear")+y
-
- def forward(self,x):
- #1.自下而上的C1,C2,C3,C4,C5
- c1=self.maxpool(self.relu(self.bn1(self.conv1(x))))
- c2=self.layer1(c1)
- c3=self.layer2(c2)
- c4=self.layer3(c3)
- c5=self.layer4(c4)
-
- #2.自上而下的上采样
- p5=self.toplayer(c5)
- p4=self._upsample(p5,self.latlayer1(c4))
- p3 = self._upsample(p5, self.latlayer2(c3))
- p2 = self._upsample(p5, self.latlayer3(c2))
-
- #3.卷积融合,平滑处理
- p4=self.smooth1(p4)
- p3=self.smooth2(p3)
- p2=self.smooth3(p2)
-
- return p2,p3,p4,p5
-
-
- if __name__ == '__main__':
-
- net_fpn=FPN([3,4,6,3])
- # print(net_fpn.conv1)
- # print(net_fpn.layer1)
- input=torch.randn(1,3,224,224)
- output=net_fpn(input)
-
- print(len(output),output[0].shape)#print(len(output),output[0].shape)#
- print( output[1].shape)#[1, 256, 28, 28]
- print(output[2].shape)#[1, 256, 14, 14]
- print(output[3].shape)#[1, 256, 7, 7]