系列文章目录(共五章33节已完结)
第一章deeplabv3+源码之慢慢解析 根目录(1)main.py–get_argparser函数
第一章deeplabv3+源码之慢慢解析 根目录(2)main.py–get_dataset函数
第一章deeplabv3+源码之慢慢解析 根目录(3)main.py–validate函数
第一章deeplabv3+源码之慢慢解析 根目录(4)main.py–main函数
第一章deeplabv3+源码之慢慢解析 根目录(5)predict.py–get_argparser函数和main函数
第二章deeplabv3+源码之慢慢解析 datasets文件夹(1)voc.py–voc_cmap函数和download_extract函数
第二章deeplabv3+源码之慢慢解析 datasets文件夹(2)voc.py–VOCSegmentation类
第二章deeplabv3+源码之慢慢解析 datasets文件夹(3)cityscapes.py–Cityscapes类
第二章deeplabv3+源码之慢慢解析 datasets文件夹(4)utils.py–6个小函数
第三章deeplabv3+源码之慢慢解析 metrics文件夹stream_metrics.py–StreamSegMetrics类和AverageMeter类
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(a1)hrnetv2.py–4个函数和可执行代码
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(a2)hrnetv2.py–Bottleneck类和BasicBlock类
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(a3)hrnetv2.py–StageModule类
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(a4)hrnetv2.py–HRNet类
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(b1)mobilenetv2.py–2个类和2个函数
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(b2)mobilenetv2.py–MobileNetV2类和mobilenet_v2函数
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(c1)resnet.py–2个基础函数,BasicBlock类和Bottleneck类
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(c2)resnet.py–ResNet类和10个不同结构的调用函数
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(d1)xception.py–SeparableConv2d类和Block类
第四章deeplabv3+源码之慢慢解析 network文件夹(1)backbone文件夹(d2)xception.py–Xception类和xception函数
第四章deeplabv3+源码之慢慢解析 network文件夹(2)_deeplab.py–ASPP相关的4个类和1个函数
第四章deeplabv3+源码之慢慢解析 network文件夹(3)_deeplab.py–DeepLabV3类,DeepLabHeadV3Plus类和DeepLabHead类
第四章deeplabv3+源码之慢慢解析 network文件夹(4)modeling.py–5个私有函数(4个骨干网,1个模型载入)
第四章deeplabv3+源码之慢慢解析 network文件夹(5)modeling.py–12个调用函数
第四章deeplabv3+源码之慢慢解析 network文件夹(6)utils.py–_SimpleSegmentationModel类和IntermediateLayerGetter类
第五章deeplabv3+源码之慢慢解析 utils文件夹(1)ext_transforms.py.py–2个翻转类和ExtCompose类
第五章deeplabv3+源码之慢慢解析 utils文件夹(2)ext_transforms.py.py–2个裁剪类和2个缩放类
第五章deeplabv3+源码之慢慢解析 utils文件夹(3)ext_transforms.py.py–旋转类,填充类,张量转化类和标准化类
第五章deeplabv3+源码之慢慢解析 utils文件夹(4)ext_transforms.py.py–ExtResize类,ExtColorJitter类,Lambda类和Compose类
第五章deeplabv3+源码之慢慢解析 utils文件夹(5)loss.py–FocalLoss类
第五章deeplabv3+源码之慢慢解析 utils文件夹(6)scheduler.py–PolyLR类
第五章deeplabv3+源码之慢慢解析 utils文件夹(7)utils.py–去标准化,momentum设定,标准化层锁定和路径创建
第五章deeplabv3+源码之慢慢解析 utils文件夹(8)visualizer.py–Visualizer类(完结)
Xception类
提示:Xception类是xception.py中的核心,整合各个功能。
class Xception(nn.Module):
"""
Xception optimized for the ImageNet dataset, as specified in
https://arxiv.org/pdf/1610.02357.pdf
"""
def __init__(self, num_classes=1000, replace_stride_with_dilation=None):
""" Constructor
Args:
num_classes: number of classes
"""
super(Xception, self).__init__()
self.num_classes = num_classes
self.dilation = 1
if replace_stride_with_dilation is None:
# each element in the tuple indicates if we should replace
# the 2x2 stride with a dilated convolution instead
replace_stride_with_dilation = [False, False, False, False]
if len(replace_stride_with_dilation) != 4:
raise ValueError("replace_stride_with_dilation should be None "
"or a 4-element tuple, got {}".format(replace_stride_with_dilation))
self.conv1 = nn.Conv2d(3, 32, 3,2, 0, bias=False) # 1 / 2#此处strde=2,长宽减半,基本的数据处理。把它理解为stage1的开始。
self.bn1 = nn.BatchNorm2d(32)
self.relu1 = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(32,64,3,bias=False)
self.bn2 = nn.BatchNorm2d(64)
self.relu2 = nn.ReLU(inplace=True) #还是基本卷积,stage1的结束。
#do relu here
#Entry Flow就是stage1-4
self.block1=self._make_block(64,128,2,2,start_with_relu=False,grow_first=True, dilate=replace_stride_with_dilation[0]) # 1 / 4 #stage2,唯一的start_with_relu=False
self.block2=self._make_block(128,256,2,2,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[1]) # 1 / 8 #stage3
self.block3=self._make_block(256,728,2,2,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2]) # 1 / 16 #stage4
#Middle Flow就是stage5-12,对应8个block的循环,即此处的block4-11,此处stride=1,没有maxpooling。
self.block4=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
self.block5=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
self.block6=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
self.block7=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
self.block8=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
self.block9=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
self.block10=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
self.block11=self._make_block(728,728,3,1,start_with_relu=True,grow_first=True, dilate=replace_stride_with_dilation[2])
#Exit Flow就是stage13-14
self.block12=self._make_block(728,1024,2,2,start_with_relu=True,grow_first=False, dilate=replace_stride_with_dilation[3]) # 1 / 32 #stage13,唯一的grow_first=False
#stage14,原文的stage14=2个深度卷积层+全局平均池化层+全连接层。此代码的实现方式,在最后有点绕。
self.conv3 = SeparableConv2d(1024,1536,3,1,1, dilation=self.dilation) #stage14的深度卷积层1
self.bn3 = nn.BatchNorm2d(1536)
self.relu3 = nn.ReLU(inplace=True)
#do relu here #从这个注释看,这个代码的作者也对他引用的原码中不足提出了意见。
self.conv4 = SeparableConv2d(1536,2048,3,1,1, dilation=self.dilation)#stage14的深度卷积层2
self.bn4 = nn.BatchNorm2d(2048)
#self.relu4 = nn.ReLU(inplace=True) #本文自行补充的,按原论文此处应该补一个relu。
self.fc = nn.Linear(2048, num_classes) #stage14的全连接层
# #------- init weights -------- #权重初始化
# for m in self.modules():
# if isinstance(m, nn.Conv2d):
# n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
# m.weight.data.normal_(0, math.sqrt(2. / n))
# elif isinstance(m, nn.BatchNorm2d):
# m.weight.data.fill_(1)
# m.bias.data.zero_()
# #-----------------------------
def _make_block(self, in_filters,out_filters,reps,strides=1,start_with_relu=True,grow_first=True, dilate=False):
if dilate: #是否使用空洞卷积代替strides
self.dilation *= strides
strides = 1
return Block(in_filters,out_filters,reps,strides,start_with_relu=start_with_relu,grow_first=grow_first, dilation=self.dilation)
def features(self, input):
x = self.conv1(input)
x = self.bn1(x)
x = self.relu1(x)
x = self.conv2(x)
x = self.bn2(x)
x = self.relu2(x) #以上为stage1
x = self.block1(x)#stage2
x = self.block2(x)#stage3
x = self.block3(x)#stage4 以上为Entry flow
x = self.block4(x)
x = self.block5(x)
x = self.block6(x)
x = self.block7(x)
x = self.block8(x)
x = self.block9(x)
x = self.block10(x)
x = self.block11(x)#以上是Middle Flow,stage5-12,即此处的block4-11。
x = self.block12(x)#stage13.以下是Exit Flow。
x = self.conv3(x)#stage14,2个深度卷积层+全局平均池化层+全连接层。
x = self.bn3(x)
x = self.relu3(x) #stage14的深度卷积层1
x = self.conv4(x) #stage14的深度卷积层2
x = self.bn4(x)
#x = self.relu4(x) #本文自补,应该有这个relu。这段代码把此处的relu放在了logits函数的开头,不贴和原文的‘深度卷积+relu’结构的表达,不建议这样写。
return x
def logits(self, features):
x = nn.ReLU(inplace=True)(features)#这个relu应该写在stage14的深度卷积层2的后面。建议在上面函数增加self.relu4。此句可以注释掉。
x = F.adaptive_avg_pool2d(x, (1, 1))#stage14的全局平均池化层
x = x.view(x.size(0), -1)#扁平化处理,view(h,w),h代表行(想要变为几行),w代表的是列(想要变为几列)。当确定h行,却不知道几列时,则w=-1,自动按行计算列。
x = self.last_linear(x)#stage14的全局平均池化层。如果一定这么写的话,其实直接用self.fc即可。
return x
def forward(self, input):
x = self.features(input) #此处用self.features+self.logits连接了,感觉太绕了。
x = self.logits(x)
return x
本节建议的修改
提示:如果是我,可能这样写,features+logits+forward变为下面的forward。
def forward(self, input):
x = self.conv1(input)
x = self.bn1(x)
x = self.relu1(x)
x = self.conv2(x)
x = self.bn2(x)
x = self.relu2(x) #以上为stage1
x = self.block1(x) #stage2
x = self.block2(x) #stage3
x = self.block3(x) #stage4 以上为Entry flow
x = self.block4(x)
x = self.block5(x)
x = self.block6(x)
x = self.block7(x)
x = self.block8(x)
x = self.block9(x)
x = self.block10(x)
x = self.block11(x)#以上是Middle Flow,stage5-12,即此处的block4-11。
x = self.block12(x) #stage13.以下是Exit Flow。
x = self.conv3(x) #stage14,2个深度卷积层+全局平均池化层+全连接层+逻辑回归。
x = self.bn3(x) #stage14的深度卷积层1
x = self.relu3(x)
x = self.conv4(x) #stage14的深度卷积层2
x = self.bn4(x)
x = self.relu4(x) #在__init__函数里,去掉self.relu4的注释。
x = F.adaptive_avg_pool2d(x, (1, 1)) #stage14的全局平均池化层
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
xception函数
def xception(num_classes=1000, pretrained='imagenet', replace_stride_with_dilation=None):
model = Xception(num_classes=num_classes, replace_stride_with_dilation=replace_stride_with_dilation)#新建Xception模型
if pretrained: #使用预训练则导入参数
settings = pretrained_settings['xception'][pretrained]
assert num_classes == settings['num_classes'], \
"num_classes should be {}, but is {}".format(settings['num_classes'], num_classes)
model = Xception(num_classes=num_classes, replace_stride_with_dilation=replace_stride_with_dilation)
model.load_state_dict(model_zoo.load_url(settings['url']))#载入模型
# TODO: ugly #哈哈,这个注释言简意赅,同感。
model.last_linear = model.fc #补了forward函数中self.last_linear没有对应self.fc的硬伤
del model.fc #补完之后,再应删掉self.fc,保持使用self.last_linear。确实ugly。
return model
Tips
- 至此,xception.py结束。
- 通过各个主干网络的学习和对比,大家应该会发现,本代码中的主干网络大体结构类似,块结构中都有DW和PW,shortcut等。通道数和分辨率如天下大势,分分合合。
- 下一节回到了network文件夹下,讲一下_deeplab.py文件(终于看到deeplab字样了)。