系列文章目录(共五章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类(完结)
前期准备和说明
- 还是建议:有足够的耐心看完本章推荐的全部补充链接。
- 虽然在mobilenet中说过深度可分离卷积,但形象不深的同学还是建议再看看理论部分。(老规矩,看文后补充链接)。
- Xception是Inception的改进版,Inception v1就是当年的GoogleNet。Xception在Inception的基础上,使用深度可分离卷积(depthwise separate convolution)替代传统的Inception块,实现跨通道相关性和空间相关性的完全解耦。此外,还引入了残差连接,最终提出了Xception的网络结构。
深度可分离卷积主要分为两个过程:
(1)逐通道卷积(Depthwise Convolution, DW)。其中一个通道只被一个卷积核卷积,输入和输出通道数不变。简单来说,DW就是单通道卷积计算,目的在于降低参数数量。
(2)逐点卷积(Pointwise Convolution, PW)。这个部分与常规卷积运算非常相似,卷积核的尺寸为 1×1×M,M为上一层的通道数。简单来说就是改变通道数的操作。在各种网络的改进中,PW如果放在DW后面,就是整合各个通道的信息;如果放在DW前面就是为了改变通道数(即升维或者降维)。
Xception的具体结构:共包括36层卷积,分为14个stage。
(1)Entry flow:stage1(2卷积),stage2(2分离卷积),stage3(2分离卷积),stage4(2分离卷积)。
(2)Middle flow:stage5-12,同一stage循环8次(3分离卷积)。
(3)Exit flow:stage13(2分离卷积),stage14(2分离卷积)。
xception.py的导入部分
from __future__ import print_function, division, absolute_import #__future__模块是为了在当前版本python中使用新python3.x特性而加入的语法。
import math
import torch #还是简简单单,torch的各种基本功能
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.model_zoo as model_zoo
from torch.nn import init
xception.py的非函数非类代码
提示:提示:xception.py包含1个函数和3个类。先介绍非函数非类的代码。
__all__ = ['xception'] #xception函数
pretrained_settings = { #预训练模型设置
'xception': {
'imagenet': {
'url': 'http://data.lip6.fr/cadene/pretrainedmodels/xception-43020ad28.pth',
'input_space': 'RGB',
'input_size': [3, 299, 299],
'input_range': [0, 1],
'mean': [0.5, 0.5, 0.5],
'std': [0.5, 0.5, 0.5],
'num_classes': 1000,
'scale': 0.8975 # The resize parameter of the validation transform should be 333, and make sure to center crop at 299x299
}
}
}
SeparableConv2d类
提示:即DW+PW阶段。
class SeparableConv2d(nn.Module):
def __init__(self,in_channels,out_channels,kernel_size=1,stride=1,padding=0,dilation=1,bias=False):
super(SeparableConv2d,self).__init__()
self.conv1 = nn.Conv2d(in_channels,in_channels,kernel_size,stride,padding,dilation,groups=in_channels,bias=bias) #通道数不变的DW卷积。
self.pointwise = nn.Conv2d(in_channels,out_channels,1,1,0,1,1,bias=bias) #改变通道数的PW卷积。
def forward(self,x):
x = self.conv1(x)#DW
x = self.pointwise(x)#PW
return x
Block类
提示:基础块,实际上就是:分离卷积层+relu+池化层。如果按stage分的话,输入和输出各算1个stage,下面的块,每一个块算一个stage。
(1)Entry flow:输入stage+3个块(2个(relu(第一个块没有relu)+分离卷积)+maxpooling)。
(2)Middle flow:循环8个块(3个(relu+分离卷积),无池化)。
(3)Exit flow:2个块(2个(relu+分离卷积)+maxpooling)+输出stage。
class Block(nn.Module):#此块是指原文中的ReLU+3*3SeparableConv2d的小组合。主要参数即是否以relu开头start_with_relu,是否是这个部分的第一个小组合grow_first。
def __init__(self,in_filters,out_filters,reps,strides=1,start_with_relu=True,grow_first=True, dilation=1):
super(Block, self).__init__()
#filters过滤器的个数就是卷积核的组数,实际上对应的就是输出通道的个数。后补链接,新手同学建议看看。
if out_filters != in_filters or strides!=1: #通道数量变化或者尺寸变化时
self.skip = nn.Conv2d(in_filters,out_filters,1,stride=strides, bias=False)#此处的skip connection(跳连)其实就是之前提到过的shorcut(中文译为捷径比较合适),就是如果无变化则与输入相加,如果变化了则把输入进行这个层的操作再相加。
self.skipbn = nn.BatchNorm2d(out_filters)#标准化。
else:
self.skip=None #无变化则不需要这个处理,后面直接使用输入数据即可。
rep=[] #别的主干网,往往用layers=[]这个写法,就是重复的层。
filters=in_filters #先按输入通道设置卷积核数量
if grow_first: #关于参数grow_first,从程序看,先增长,即在第一个分离卷积层把输出通道增大为out_filters的意思。而如果值为false,即在最后一个分离卷积层再把输出通道增大为out_filters。
rep.append(nn.ReLU(inplace=True)) #inplace = True ,会改变输入数据的值,节省反复申请与释放内存的空间与时间。inplace = False 时,不会修改输入对象的值,而是返回一个新创建的对象。
rep.append(SeparableConv2d(in_filters,out_filters,3,stride=1,padding=dilation, dilation=dilation, bias=False)) #第一个分离卷积,通道数对应in_filters和out_filters。
rep.append(nn.BatchNorm2d(out_filters))
filters = out_filters #第一个小组合之后,对应的输入通道数就是第一次的输出通道数,即out_filters。
for i in range(reps-1): #第1个小组合后,按循环次数reps,再加入reps-1个小组合,输入和输出通道数为out_filters。如果grow_first=False,则先按输入和输出通道数为in_filters做reps-1个小组合,后面的代码再接一个out_filters的输出小组合。
rep.append(nn.ReLU(inplace=True))
rep.append(SeparableConv2d(filters,filters,3,stride=1,padding=dilation,dilation=dilation,bias=False))
rep.append(nn.BatchNorm2d(filters))
if not grow_first: #如果grow_first=False,最后接一个通道数为in_filters和out_filters的分离卷积层。
rep.append(nn.ReLU(inplace=True))
rep.append(SeparableConv2d(in_filters,out_filters,3,stride=1,padding=dilation,dilation=dilation,bias=False))
rep.append(nn.BatchNorm2d(out_filters))
if not start_with_relu:
rep = rep[1:] #去掉第一个relu层。
else:
rep[0] = nn.ReLU(inplace=False) #第一个relu层,inplace=False不改变输入数据。
if strides != 1:
rep.append(nn.MaxPool2d(3,strides,1)) #如改变尺寸,按原文strides=2,则块的最后加入3*3的MaxPooling。
self.rep = nn.Sequential(*rep)
def forward(self,inp):
x = self.rep(inp) #inp就是输入数据。
if self.skip is not None:
skip = self.skip(inp) #如skip不为空则按这个shortcut进行相加。
skip = self.skipbn(skip)
else:
skip = inp #如skip为空则直接按input进行相加。
x+=skip
return x
Tips
- 补充:深度可分离卷积。
- 补充:Xception理论。
- 补充:卷积中的filters。
- 下一节是xception.py剩余的部分,即Xception类和xception函数。