ResNet _make_layer代码理解
ResNet _make_layer代码理解ResNet构建过程BasicBlock理解Bottleneck理解ResNet上图为ResNet的5个 基本结构,为了方便理解,此处以最简单的18-layer为例来展开:首先我们知道ResNet中对于50层以下的构建块采用的是BasicBlock,而大于50的深层则采用的是Bottleneck,BasicBlock的构建代码如下:class Basic
ResNet _make_layer代码理解
ResNet
上图为ResNet的5个 基本结构,为了方便理解,此处以最简单的18-layer为例来展开:
首先我们知道ResNet中对于50层以下的构建块采用的是BasicBlock,而大于50的深层则采用的是Bottleneck,BasicBlock的构建代码如下:
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = nn.BatchNorm2d(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
首先我想解释下 BasicBlock 在ResNet 里代表的是什么,我们知道在图1所示,ResNet18由以下几部分组成,conv1,conv2_x,conv3_x,conv4_x,conv5_x,以及最后的全连接层,BasicBlock指的是 conv2_x,conv3_x,conv4_x,conv5_x里的这些具有重复结构的块,如Conv3_x层中具有两个([33,128]+[33,128])块,BasicBlock就指的是这些块,在ResNet中,通过重复调用BasicBlock方法就能方便的构建出这些块。
构建过程
在ResNet中通过BasicBlock构建一个完整网络利用的方法是_make-layer(通过循环创建块来创建层),其代码如下:
# block:基础块的类型,是BasicBlock,还是Bottleneck
# planes:当前块的输入输入通道数
# blocks:块的数目
def _make_layer(self, block, planes, blocks, stride=1):
downsample = None
# downSample的作用于在残差连接时 将输入的图像的通道数变成和卷积操作的尺寸一致
# 根据ResNet的结构特点,一般只在每层开始时进行判断
if stride != 1 or self.inplanes != planes * block.expansion:
# 通道数恢复成一致
downsample = nn.Sequential(
conv1x1(self.inplanes, planes * block.expansion, stride),
nn.BatchNorm2d(planes * block.expansion),
)
layers = []
layers.append(block(self.inplanes, planes, stride, downsample))
self.inplanes = planes * block.expansion
for _ in range(1, blocks):
layers.append(block(self.inplanes, planes))
return nn.Sequential(*layers)
为了方便理解,此处我分别对BasicBlock和Bottleneck根据结构进行推导下:
BasicBlock理解
Layer18–Conv2_x:假设我们的初始输入大小为2242243在conv1和max pool后尺寸变成:565664
则此时的self.inplanes=64,Conv2_x的创建代码为
# layer = [2,2,2,2]
self.layer1 = self._make_layer(Basicblock, 64, block_num=layer[0], stride=1)
stride=1 && 64 = 641(也就是self.inplanes = planes * block.expansion),无需downsample
在执行完成_make_layer后len(layers) = 2,结构均为(33,64)
然后是Conv3_x:此时的self.inplanes = 64,Conv3_x的代码为:
self.layer2 = self._make_layer(Basicblock, 128, block_num=layers[1], stride=2)
stride=2 需要downsample,此时downsample的主要修改是将通道由上层的64,变成本层的128,所以第一个BasicBlock的结构是:
64—>128,128—<128,第二个结构是,128—>128.128—>128,此阶段后的 self.inplanes=128,往后类似。也就是说,下一层的self.inplanes是上一层的最后的输出通道数。
Bottleneck理解
Layer50–Conv2_x:假设我们的初始输入大小为2242243在conv1和max pool后尺寸变成:565664
则此时的self.inplanes=64,Conv2_x的创建代码为
# layer = [3,4,6,3]
self.layer1 = self._make_layer(block,64,layer[0])
64 <> 644(也就是self.inplanes <> planes * block.expansion),需downsample
通道数 64—>644=256
第一个块的结构为:64—>64,64—>64,64—>256然后在残差连接时,使用downsample对上一层的输出进行调整,以方便进行连接,后续则无需downsample,然后进入下一层,与此类似。
更多推荐
所有评论(0)