第一篇博文,就是想记录一下学习过程,最终目的是实现faster rcnn,本科生跟着老师学习目标检测。
我是参考了b站up主霹雳吧啦Wz的利用pytorch搭建resnet网络的视频,这里附上链接6.2 使用pytorch搭建ResNet并基于迁移学习训练
第一首先无论是resnet几层的网络,它的conv1和conv2_x的maxpool都是一样的
import torch.nn as nn
class resnet(nn.Module):
def __init__(self):
super(resnet,self).__init__()
#假设输入图片大小为600x600x3
#600x600x3-->300x300x64
self.conv1=nn.Conv2d(3,64,kernel_size=7,stride=2,padding=3,bias=False)
self.bn=nn.BatchNorm2d(64)
self.relu=nn.ReLU(inplace=True)
#ceil_mode向上取整
self.maxpooling=nn.MaxPool2d(kernel_size=3,stride=2,ceil_mode=True)
def forward(self, x):
x = self.conv1(x)
x = self.bn(x)
x = self.relu(x)
x = self.maxpooling(x)
return x
net=resnet()
print(net)
可以打印看下
resnet(
(conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
(bn): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(maxpooling): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=True)
)
然后我们就要进入到残差结构中了,这里conv2_x到conv5_x简称第一层到第四层,可以看到resnet18、34层中第一层到第四层内的层结构内它们的卷积核维度是不变的,但是resnet50、101、152的层内的层结构就不一样了,每一层层结构内的卷积核三的维度都是卷积核一的三倍,所以对于这两类不同的层结构,我们需要定义两类结构来区别18、34层和50、101、152层。
对于resnet18、34层它每一个层结构中有两个卷积核,且维度不变,但是它每一个层结构的第一层需要对上一层的图片宽高减半,维度乘2,除了第一层有一个maxpooling层是特殊的,这里的downsample内容尚未定义,代码如下
import torch.nn as nn
import torch
#定义18和34层的瓶颈结构,也就是每一层里的结构
#如果维度改变则需要将输出加上downsample
class bottleneck1(nn.Module):
#层结构中卷积核的维度是一样的
expansion=1
def __init__(self,in_channels,out_channels,stride=1,downsample=None):
super(bottleneck1, self).__init__()
self.conv1=nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=3,stride=stride,padding=1,bias=False)
self.bn1=nn.BatchNorm2d(out_channels)
self.relu=nn.ReLU(inplace=True)
self.conv2=nn.Conv2d(in_channels=out_channels,out_channels=out_channels,kernel_size=3,stride=1,padding=1,bias=False)
self.bn2=nn.BatchNorm2d(out_channels)
self.downsample=downsample
def forward(self,x):
#判断是否需要加上downsample,是否需要对图片宽高减半
a=x
if self.downsample is True:
a=self.downsample(x)
x=self.conv1(x)
x=self.bn1(x)
x=self.relu(x)
x=self.conv2(x)
x=self.bn2(x)
#如果有downsample则需要加上
x+=a
#将合运用激活函数
x=self.relu(x)
return x
对于resnet50、101、152,它每一层内的第三个卷积核会将维度*4,代码如下
#定义50,101,152的层结构
class bottleneck2(nn.Module):
expansion=4
def __init__(self,in_channels,out_channels,stride=1,downsample=None):
super(bottleneck2,self).__init__()
self.conv1=nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=1,stride=1,padding=1,bias=False)
self.bn1=nn.BatchNorm2d(out_channels)
self.conv2=nn.Conv2d(in_channels=out_channels,out_channels=out_channels,kernel_size=3,stride=stride,padding=1,bias=False)
self.bn2=nn.BatchNorm2d(out_channels)
#第三层的维度要阔张4倍
self.conv3=nn.Conv2d(in_channels=out_channels,out_channels=out_channels*self.expansion,kernel_size=1,stride=1,bias=False)
self.bn3=nn.BatchNorm2d(out_channels*self.expansion)
self.downsample=downsample
self.relu=nn.ReLU(inplace=True)
def forward(self,x):
a=x
if self.downsample is True:
a=self.downsample(x)
x=self.conv1(x)
x=self.bn1(x)
x=self.relu(x)
x=self.conv2(x)
x=self.bn2(x)
x=self.relu(x)
x=self.conv3(x)
x=self.bn3(x)
x+=a
x=self.relu(x)
net=bottleneck2(in_channels=64,out_channels=128,stride=1)
print(net)
看一下输出结果,每一层的第三层需要维度*4
bottleneck2(
(conv1): Conv2d(64, 128, kernel_size=(1, 1), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
将这两个瓶颈结构定义完后,需要在resnet类中定义一个函数来包含每一层结构内的所有操作
#block代表使用bottleneck1 or bottleneck2
#channel代表每一层残差结构中第一层的通道数
#block_num代表每一层有多少个残差结构,resnet50为【3,4,6,3】
def makelayer(self,block,channel,block_num,stride=1):
downsample=None
#如果步距不为1则代表有残差结构或者expension不为1也有
if stride!=1 or self.in_channel!=channel*block.expansion:
downsample=nn.Sequential(nn.Conv2d(in_channels=self.in_channel,out_channels=channel*block.expansion,kernel_size=1,stride=stride,bias=False),
nn.BatchNorm2d(channel*block.expansion))
#把第一层的结构放到列表里
layers=[]
layers.append(block(self.in_channel,channel,stride,downsample))
#第二层的输入是第一层的输出
self.in_channel=channel*block.expansion
for i in range(1,block_num):
layers.append(block(self.in_channel,channel))
return nn.Sequential(*layers)
定义完了makelayer之后我们就可以开始前向传播了
class resnet(nn.Module):
in_channel = 64
def __init__(self,block,block_num,num_classes=1000):
super(resnet,self).__init__()
#假设输入图片大小为600x600x3
#600x600x3-->300x300x64
self.conv1=nn.Conv2d(3,64,kernel_size=7,stride=2,padding=3,bias=False)
self.bn=nn.BatchNorm2d(64)
self.relu=nn.ReLU(inplace=True)
#ceil_mode向上取整
self.maxpooling=nn.MaxPool2d(kernel_size=3,stride=2,ceil_mode=True)
#第一层步距为1
self.layer1=self.makelayer(block=block,channel=64,block_num=block_num[0])
#从第二层开始,每一层都要downsamole
self.layer2=self.makelayer(block=block,channel=128,block_num=block_num[1],stride=2)
self.layer3=self.makelayer(block=block,channel=256,block_num=block_num[2],stride=2)
self.layer4=self.makelayer(block=block,channel=512,block_num=block_num[3],stride=2)
#自适应平均池化下采样,无论输入图片的高宽是多少,都变成1,1
self.avgpool=nn.AdaptiveAvgPool2d((1,1))
self.fc=nn.Linear(512*block.expansion,num_classes)
#卷积层初始化
for m in self.modules():
if isinstance(m,nn.Conv2d):
nn.init.kaiming_normal(m.weight,mode="fan_out",nonlinearity='relu')
# self.layer1=
def forward(self,x):
x=self.conv1(x)
x=self.bn(x)
x=self.relu(x)
x=self.maxpooling(x)
x=self.layer1(x)
x=self.layer2(x)
x=self.layer3(x)
x=self.avgpool(x)
x=torch.flatten(x,dims=1)
x=self.fc(x)
return x
最后是平均池化和全连接层,到此resnet网络就定义完毕了,以下附上全部代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/5/12 16:37
# @Author : 半岛铁盒
# @File : resnet 50.py
# @Software: win10 python3.6
import torch.nn as nn
import torch
#定义18和34层的瓶颈结构,也就是每一层里的结构
#如果维度改变则需要将输出加上downsample
class bottleneck1(nn.Module):
#层结构中卷积核的维度是一样的
expansion=1
def __init__(self,in_channels,out_channels,stride=1,downsample=None):
super(bottleneck1, self).__init__()
self.conv1=nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=3,stride=stride,padding=1,bias=False)
self.bn1=nn.BatchNorm2d(out_channels)
self.relu=nn.ReLU(inplace=True)
self.conv2=nn.Conv2d(in_channels=out_channels,out_channels=out_channels,kernel_size=3,stride=1,padding=1,bias=False)
self.bn2=nn.BatchNorm2d(out_channels)
self.downsample=downsample
def forward(self,x):
#判断是否需要加上downsample
a=x
if self.downsample is True:
a=self.downsample(x)
x=self.conv1(x)
x=self.bn1(x)
x=self.relu(x)
x=self.conv2(x)
x=self.bn2(x)
#如果有downsample则需要加上
x+=a
#将合运用激活函数
x=self.relu(x)
return x
#定义50,101,152的层结构
class bottleneck2(nn.Module):
expansion=4
def __init__(self,in_channels,out_channels,stride=1,downsample=None):
super(bottleneck2,self).__init__()
self.conv1=nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=1,stride=1,bias=False)
self.bn1=nn.BatchNorm2d(out_channels)
self.conv2=nn.Conv2d(in_channels=out_channels,out_channels=out_channels,kernel_size=3,stride=stride,padding=1,bias=False)
self.bn2=nn.BatchNorm2d(out_channels)
#第三层的维度要阔张4倍
self.conv3=nn.Conv2d(in_channels=out_channels,out_channels=out_channels*self.expansion,kernel_size=1,stride=1,bias=False)
self.bn3=nn.BatchNorm2d(out_channels*self.expansion)
self.downsample=downsample
self.relu=nn.ReLU(inplace=True)
def forward(self,x):
a=x
if self.downsample is True:
a=self.downsample(x)
x=self.conv1(x)
x=self.bn1(x)
x=self.relu(x)
x=self.conv2(x)
x=self.bn2(x)
x=self.relu(x)
x=self.conv3(x)
x=self.bn3(x)
x+=a
x=self.relu(x)
class resnet(nn.Module):
in_channel = 64
def __init__(self,block,block_num,num_classes=1000):
super(resnet,self).__init__()
#假设输入图片大小为600x600x3
#600x600x3-->300x300x64
self.conv1=nn.Conv2d(3,self.in_channel,kernel_size=7,stride=2,padding=3,bias=False)
self.bn=nn.BatchNorm2d(self.in_channel)
self.relu=nn.ReLU(inplace=True)
#ceil_mode向上取整
self.maxpooling=nn.MaxPool2d(kernel_size=3,stride=2,ceil_mode=True)
#第一层步距为1
self.layer1=self.makelayer(block=block,channel=64,block_num=block_num[0])
#从第二层开始,每一层都要downsamole
self.layer2=self.makelayer(block=block,channel=128,block_num=block_num[1],stride=2)
self.layer3=self.makelayer(block=block,channel=256,block_num=block_num[2],stride=2)
self.layer4=self.makelayer(block=block,channel=512,block_num=block_num[3],stride=2)
#自适应平均池化下采样,无论输入图片的高宽是多少,都变成1,1
self.avgpool=nn.AdaptiveAvgPool2d((1,1))
self.fc=nn.Linear(512*block.expansion,num_classes)
#卷积层初始化
for m in self.modules():
if isinstance(m,nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
#block代表使用bottleneck1 or bottleneck2
#channel代表每一层残差结构中第一层的通道数
#block_num代表每一层有多少个残差结构,resnet50为【3,4,6,3】
def makelayer(self,block,channel,block_num,stride=1):
downsample=None
#如果步距不为1则代表有残差结构或者expension不为1也有
if stride!=1 or self.in_channel!=channel*block.expansion:
downsample=nn.Sequential(nn.Conv2d(in_channels=self.in_channel,out_channels=channel*block.expansion,kernel_size=1,stride=stride,bias=False),
nn.BatchNorm2d(channel*block.expansion))
#把第一层的结构放到列表里
layers=[]
layers.append(block(self.in_channel,channel,stride,downsample))
#第二层的输入是第一层的输出
self.in_channel=channel*block.expansion
for i in range(1,block_num):
layers.append(block(self.in_channel,channel))
return nn.Sequential(*layers)
def forward(self,x):
x=self.conv1(x)
x=self.bn(x)
x=self.relu(x)
x=self.maxpooling(x)
x=self.layer1(x)
x=self.layer2(x)
x=self.layer3(x)
x=self.avgpool(x)
x=torch.flatten(x,dims=1)
x=self.fc(x)
return x
net=resnet(block=bottleneck2,block_num=[3,4,6,3])
print(net)
打印一下最终结果看一下resnet的网络结构
resnet(
(conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
(bn): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(maxpooling): MaxPool2d(kernel_size=3, stride=2, padding=0, dilation=1, ceil_mode=True)
(layer1): Sequential(
(0): bottleneck2(
(conv1): Conv2d(64, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(relu): ReLU(inplace=True)
)
(1): bottleneck2(
(conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(2): bottleneck2(
(conv1): Conv2d(256, 64, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(64, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
)
(layer2): Sequential(
(0): bottleneck2(
(conv1): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(relu): ReLU(inplace=True)
)
(1): bottleneck2(
(conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(2): bottleneck2(
(conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(3): bottleneck2(
(conv1): Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(128, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
)
(layer3): Sequential(
(0): bottleneck2(
(conv1): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(512, 1024, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(relu): ReLU(inplace=True)
)
(1): bottleneck2(
(conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(2): bottleneck2(
(conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(3): bottleneck2(
(conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(4): bottleneck2(
(conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(5): bottleneck2(
(conv1): Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(256, 1024, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
)
(layer4): Sequential(
(0): bottleneck2(
(conv1): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(1024, 2048, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(relu): ReLU(inplace=True)
)
(1): bottleneck2(
(conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
(2): bottleneck2(
(conv1): Conv2d(2048, 512, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(conv3): Conv2d(512, 2048, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn3): BatchNorm2d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
)
)
(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
(fc): Linear(in_features=2048, out_features=1000, bias=True)
)