系列文章目录(共五章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和mobilenetV2的理论部分。(老规矩,看文后补充链接)。
- MobileNetV2中最主要的3特点: 整个结构按照维度来看,类似一个中间宽,两边窄的结构。整个过程是扩张(维度增加),卷积(长和宽降低),压缩(通道数减少)。)
(1)Inverted Residuals (倒残差结构 )。详见InvertedResidual类。通常的residuals block是先经过一个1*1的Conv layer,把feature map的通道数“压”下来,再经过3*3 Conv layer,最后经过一个1*1 的Conv layer,将feature map 通道数再“扩张”回去。即先“压缩”,最后“扩张”回去。而 inverted residuals就是 先“扩张”,最后“压缩”。
(2)Linear Bottlenecks(结构的最后一层采用线性层)。详见MobileNetV2类。Linear bottlenecks,为了避免Relu对特征的破坏,在residual block的Eltwise sum之前的那个 1*1 Conv 不再采用Relu。
(3)在MobileNet结构中,采用了新的激活函数:ReLU6(x)=min(max(0,x),6)。见ConvBNReLU类。
mobilenetv2.py的导入部分
#还是简简单单,torch的各种基本功能
from torch import nn
try: # for torchvision<0.4
from torchvision.models.utils import load_state_dict_from_url
except: # for torchvision>=0.4
from torch.hub import load_state_dict_from_url
import torch.nn.functional as F
mobilenetv2.py的非函数非类代码
提示:提示:mobilenetv2.py包含3个函数和3个类。先介绍非函数非类的代码。
__all__ = ['MobileNetV2', 'mobilenet_v2'] #MobileNetV2类, mobilenet_v2函数。
model_urls = {
'mobilenet_v2': 'https://download.pytorch.org/models/mobilenet_v2-b0353104.pth', #预训练模型下载地址。
}
确保整除 _make_divisible函数
def _make_divisible(v, divisor, min_value=None):
"""
This function is taken from the original tf repo.
It ensures that all layers have a channel number that is divisible by 8
It can be seen here:
https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py
:param v:
:param divisor:
:param min_value: #此函数中要注意,如果保证结果是divisor的整数倍,则min_value最好用默认值,否则可能出现意外,如_make_divisible(5,8,31)==31。
:return:
"""
if min_value is None:
min_value = divisor
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor) #//是商取整数部分的运算符,此处即int(v + divisor / 2) // divisor再* divisor,得到的就是divisor的整数倍。divisor==8就是8的整数倍。
# Make sure that round down does not go down by more than 10%.
if new_v < 0.9 * v: #如果小于v的90%,则加一个divisor,相当于加了1倍。
new_v += divisor
return new_v
基本结构块 ConvBNReLU类
提示:基本结构块Conv2d+BatchNorm2d+ReLU6,在InvertedResidual类和MobileNetV2类中使用。
class ConvBNReLU(nn.Sequential):
def __init__(self, in_planes, out_planes, kernel_size=3, stride=1, dilation=1, groups=1):
#padding = (kernel_size - 1) // 2
super(ConvBNReLU, self).__init__(
nn.Conv2d(in_planes, out_planes, kernel_size, stride, 0, dilation=dilation, groups=groups, bias=False),
nn.BatchNorm2d(out_planes),
nn.ReLU6(inplace=True) #基本序列Conv2d+BatchNorm2d+ReLU6。
)
计算填充 fixed_padding函数
def fixed_padding(kernel_size, dilation): #特别注意:在Pytorch中,空洞卷积(膨胀卷积)dilation = 1等同于没有dilation的标准卷积。dilation = 2才是间隔1个,扩大感受野。
kernel_size_effective = kernel_size + (kernel_size - 1) * (dilation - 1) #插入空洞后,新的卷积核尺寸。
pad_total = kernel_size_effective - 1 #在保持高和宽不变的情况下,卷积减少的尺寸为k-1,有padding填充的为2p,即k-1=2p。则p=(k-1)//2。
pad_beg = pad_total // 2
pad_end = pad_total - pad_beg
return (pad_beg, pad_end, pad_beg, pad_end)
InvertedResidual类
class InvertedResidual(nn.Module): #论文中把这个整体叫做bottleneck。
def __init__(self, inp, oup, stride, dilation, expand_ratio):
super(InvertedResidual, self).__init__()
self.stride = stride
assert stride in [1, 2] #测试用,stride=1则分辨率不变,stride=2则分辨率降一半。
hidden_dim = int(round(inp * expand_ratio)) #输入*扩展系数=hidden_dim ,即中间的输出数量。
self.use_res_connect = self.stride == 1 and inp == oup #stride==1且输入输出数量相等,则use_res_connect=True。
layers = []
if expand_ratio != 1:#如果扩展系数不为1,即需要扩大中间的输出通道数,则进行kernel=1的升维。即论文中的前期的pw升维部分。
# pw
layers.append(ConvBNReLU(inp, hidden_dim, kernel_size=1))
layers.extend([
# dw
ConvBNReLU(hidden_dim, hidden_dim, stride=stride, dilation=dilation, groups=hidden_dim),#正常的DW部分。
# pw-linear #pw线性降维部分,不用激活函数。
nn.Conv2d(hidden_dim, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
])
self.conv = nn.Sequential(*layers) #整合网络各层,即pw+dw+pw-linear,论文中把这个整体叫做bottleneck,本代码没有单独写出这个名字。
self.input_padding = fixed_padding( 3, dilation )#计算填充的尺寸。
def forward(self, x):
x_pad = F.pad(x, self.input_padding) #把输入x按填充尺寸填充。
if self.use_res_connect: #特别的,针对stride=1 和stride=2,在block上有稍微不同,主要是为了与shortcut的维度匹配,因此,stride=2时,不采用shortcut。
return x + self.conv(x_pad) #stride==1且输入输出数量相等时,输入x与输出结果整合。
else:
return self.conv(x_pad)
Tips
- 补充:mobilenet V2原文。
- 补充:mobilenet,mobilenet V2。
- 补充:mobilenet,mobilenet V2,Resnet对比。
- 补充:dalition详解,padding,stride,dalition关系。
- 每个骨干网的第一节,重在看原理,其次是部分代码。
- 下一节是mobilenetv2.py剩余的部分,即MobileNetV2类和mobilenet_v2函数。