- 目标分割经过了FCN->U-net->U-net++等等的迭代之后,出现了一个通过改良VGG网络融合机器学习方法CRF得到的全新网络DeepLab。然而,为了让网络能够更加简洁,使用推广起来更加方便,作者又放弃了可解释性强的CRF方法,把整个网络变成了一个end2end的全深度学习网络。目标分割网络的迭代过程也是令人心潮澎湃,值得好好的学习和整理,为将来的论文改进提供思想上的指导。
- 在进行DeepLab的介绍之前,我想先讲一下空洞卷积和感受野的计算方法,在这两个概念明确了之后,我们能够更好的理解DeepLab的运算流程。
膨胀卷积(相当于空洞卷积):
-
直接上图片比较清晰直观。我们可以看到,7x7的feature map经过3x3kernel (dilation = 2),我们可以代入计算公式:Hout = (7 - 2 x (3-1) -1 + 1) = 3。当然stride = 2的话可以自己另外计算。
-
虽然空洞卷积能够增加感受野,增加在下采样过程中空间信息的保留。但是它也存在一些缺点:
-
由于空洞卷积是对feature map离散的像素进行处理,处理的并不是临近的像素,很可能在预测的时候会出现一些栅格化的效应。
-
对于大物体比较友好,小物体就不好说了
-
-
空洞卷积和转置卷积的区别是,空洞卷积是卷积核裂开,转置卷积是feature map裂开,加padding。这个原来已经整理过,在FCN大总结中。
-
空洞卷积能够在计算量不变的情况下增加感受野,我们可以从这一张图片看到空洞卷积在每一层的感受野变化
-
为了防止出现栅格化的现象,一般在使用block的第一个卷积层的dilation rate是1,就像图a所示,这样能够让经过空洞卷积的feature map的感受野有连续性。否则,从第一个卷积层是2的话,感受野就会不连续,出现栅格化效应也就不奇怪了。
感受野的计算公式:
- R n = R n − 1 + ( k n − 1 ) ∗ ∏ i = 1 n − 1 s i R_{n}=R_{n-1}+\left(k_{n}-1\right) * \prod_{i=1}^{n-1} s_{i} Rn=Rn−1+(kn−1)∗∏i=1n−1si k n − 1 k_{n}-1 kn−1$ = dilation * (kernel size - 1)$.
- R指的是层数。具体感受野公式推导如下链接:https://zhuanlan.zhihu.com/p/375100687
- 相对感受野的公式是: R n , m = k m + ( R n , m + 1 − 1 ) ∗ s m + 1 R_{n, m}=k_{m}+\left(R_{n, m+1}-1\right) * s_{m+1} Rn,m=km+(Rn,m+1−1)∗sm+1 如果是膨胀卷积的话,用
- 笔记还是上面的知乎专栏比较完整。
DeepLabV1:
-
这个网络是从VGG-16网络改变而来的。其实主要做了以下的一些改变:
- 全连接层变成卷积层
- 把最后两个pooling层给丢掉,变成空洞卷积(通过stride = 2进行下采样),在代码上是把max pooling的stride从2变成1,简直就是为了用VGG的所有参数,省的写代码定制的。
- 把全连接层的4096维度变成1024,效果没有降低,让速度提升
- 最开始的FC全卷积层从是7 x 7的kernel size(膨胀卷积),但是作者把这里的卷积核变成了3 x 3(或者4 x 4).VGG直接有的膨胀卷积,这个计算量会太大,当时也就是因为计算量太大之后才开始优化计算量的。
- 具体的改进已经在图上表明
- 最后的变成28 x 28,之后直接用双线性上采样8倍之后再用crf
-
在这里再总结一下V1的流程:
- 首先采用VGG的backbone进行特征提取。但是这个是经过修改之后的backbone,VGG一共有5次pooling,在第四次和第五次pooling中,作者把pooling的stride变成1,使得之后的feature map的size都是28 x 28.最后的VGG有两个4096的FC层,作者把4096变成1024,减少计算量。这些是backbone的比较大的改动总结
- 在分类的时候,首先对28 x 28 x 21的feature map进行8倍上采样到224 x 224 x21大小,连接CRF,对最后的数据进行处理
- 接入损失函数,用的是mIOU指标,通过它进行反向传播计算。
-
至于CRF,觉得不知道它为什么能够提高分辨的精确率
-
Multi-scale 就是把相同图片,变成不懂大小一起输入,之后把结果平均一下。
-
CRF:觉得就是用这个当损失函数?或者是用机器学习方法求解参数
-
E ( x ) = ∑ i θ i ( x i ) + ∑ i j θ i j ( x i , x j ) E(\boldsymbol{x})=\sum_{i} \theta_{i}\left(x_{i}\right)+\sum_{i j} \theta_{i j}\left(x_{i}, x_{j}\right) E(x)=∑iθi(xi)+∑ijθij(xi,xj) x表示每一个像素的类别, x i x_i xi 表示第i个像素的类别。每一个点有21个取值情况。
θ i ( x i ) = − log P ( x i ) \theta_{i}\left(x_{i}\right)=-\log P\left(x_{i}\right) θi(xi)=−logP(xi)
θ i j ( x i , x j ) = μ ( x i , x j ) ∑ m = 1 K w m ⋅ k m ( f i , f i ) \theta_{i j}\left(x_{i}, x_{j}\right)=\mu\left(x_{i}, x_{j}\right) \sum_{m=1}^{K} w_{m} \cdot k^{m}\left(\boldsymbol{f}_{i}, \boldsymbol{f}_{i}\right) θij(xi,xj)=μ(xi,xj)∑m=1Kwm⋅km(fi,fi)
= μ ( x i , x j ) [ w 1 exp ( − ∥ p i − p j ∥ 2 2 σ α 2 − ∥ I i − I j ∥ 2 2 σ β 2 ) + w 2 exp ( − ∥ p i − p j ∥ 2 2 σ γ 2 ) ] μ ( x i , x j ) = 0 \quad=\mu\left(x_{i}, x_{j}\right)\left[w_{1} \exp \left(-\frac{\left\|p_{i}-p_{j}\right\|^{2}}{2 \sigma_{\alpha}^{2}}-\frac{\left\|I_{i}-I_{j}\right\|^{2}}{2 \sigma_{\beta}^{2}}\right)+w_{2} \exp \left(-\frac{\left\|p_{i}-p_{j}\right\|^{2}}{2 \sigma_{\gamma}^{2}}\right)\right] \quad \mu\left(x_{i}, x_{j}\right)=0 =μ(xi,xj)[w1exp(−2σα2∥pi−pj∥2−2σβ2∥Ii−Ij∥2)+w2exp(−2σγ2∥pi−pj∥2)]μ(xi,xj)=0 if x i = x j x_{i}=x_{j} xi=xj -
μ ( x i , x j ) = 1 \mu\left(x_{i}, x_{j}\right)=1 μ(xi,xj)=1 if x i ≠ x j x_{i}≠x_{j} xi=xj,让这个θ变小一些。不过这一部分在DeepLabV3里面就被干掉了。
-
更多的是求解,和梯度下降。
-
large-FOV就是把3x3的空洞卷积stride变成12.让感受野变得更大,甚至是比原图的更大。
DeepLabV2:
- 第二版有以下的改变:首先是用了ResNet作为backbone,然后用了ASPP(空洞空间金字塔池化)模块进行多尺度特征融合,在学习率调整上,这一片论文也提出了自己的一些见解。对于ResNet我就不做更多的解释了,在之前的文章已经写过了。我们先来看看ASPP模块。
ASPP:
-
这个rate的意思就是dilation rate,其实现在看来还是写得比较清楚的。经过这4个卷积块之后,能够实现对不同尺度(感受野)的特征进行融合,提取到更加丰富的信息。
-
论文里面对ASPP还进行了一些说明和实验,在这里就把图片先贴出来。这个r就是rate,里面的列表表示的是每一个卷积层对应的dilation rate。实验中,数据占优的是ASPP-L,它的rate是比较大的,都是6的倍数。有些事情就是人肉搜索出来的,强硬解释为什么这个效果好的方法就只能说,这个卷积的感受野更大,融合的特征感受野也更大,有利于分割任务的完成。
-
在这里加一下ASPP的代码实现,这个还是比较重要的,因为自己对于代码实现还是比较薄弱的。需要增强一下这方面的知识。
import torch
import torch.nn as nn
import torch.nn.functional as F
class ASPP(nn.Module):
def __init__(self, in_chans, out_chans, rate=1):
super(ASPP, self).__init__()
# 1 x 1 无孔洞卷积
# conv2d 参数先是输入输出通道,然后是kernel size,stride padding dilation默认一般是1,
# bias看看能不能要
self.conv_part1 = nn.Sequential(
nn.Conv2d(in_chans,out_chans,1,1,padding=0,dilation=rate,bias=True),
nn.BatchNorm2d(out_chans),
nn.ReLU(inplace=True)
)
# 3 x 3 空洞6卷积
self.conv_part2 = nn.Sequential(
nn.Conv2d(in_chans, out_chans, 3, 1, padding = 6*rate, dilation= 6*rate,bias = True),
nn.BatchNorm2d(out_chans),
nn.ReLU(inplace= True)
)
self.conv_part3 = nn.Sequential(
nn.Conv2d(in_chans,out_chans,3,1,padding=12 * rate,dilation=12 * rate, bias = True),
nn.BatchNorm2d(out_chans),
nn.ReLU(inplace=True)
)
# 为什么没有24的dilation,因为在deeplabV3+中24的dilation被去除了
self.conv_part4 = nn.Sequential(
nn.Conv2d(in_chans,out_chans,3,1,padding=18*rate, dilation=18*rate,bias = True),
nn.BatchNorm2d(out_chans),
nn.ReLU(inplace=True)
)
# 全局池化:里面的参数output size,这部分代码,就是对合并之后的feature map处理的代码作者
# 在论文里面并没有写。
self.conv_part5_avg = nn.AdaptiveAvgPool2d(1)
# 1 x 1的cbr用来处理平均池化所得到的特征图
self.conv_part5_conv = nn.Conv2d(in_chans,out_chans,1,1,bias = True)
self.conv_part5_bn = nn.BatchNorm2d(out_chans)
self.conv_part5_relu = nn.ReLU(inplace=True)
self.conv_cat = nn.Sequential(
nn.Conv2d(out_chans * 5,out_chans, 1,1,padding=0,bias=True),
nn.BatchNorm2d(out_chans),
nn.ReLU(inplace=True)
)
def forward(self, x):
b, c, h, w = x.size()
conv1x1 = self.conv_part1(x)
conv3x3_1 = self.conv_part2(x)
conv3x3_2 = self.conv_part3(x)
conv3x3_3 = self.conv_part4(x)
global_feature = self.conv_part5_avg(x)
# 对全局池化的特征要进行处理
global_feature = self.conv_part5_relu(self.conv_part5_bn(self.conv_part5_conv(global_feature)))
# 上采样到原图的大小,一直到deeplabV3+作者才用了了转置卷积
global_feature = F.interpolate(global_feature,(h,w),None,'bilinear',True)
# 在bchw上contact,所以dim必须是1
feature_cat = torch.cat([conv1x1,conv3x3_1,conv3x3_2,conv3x3_3,global_feature],dim = 1)
result = self.conv_cat(feature_cat)
return result
learning rate policy
-
KaTeX parse error: Expected '}', got '_' at position 42: … }}{\text { max_̲iter }}\right)^… 这个是作者在训练的时候对学习率的调整策略,和原来百度的三角调整策略很相似。
-
在论文中,作者实验的power值是0.9.iter是每一次迭代(不是epoch的意思,max_iter = epoch * iter_per_epoch)。当然这个应该只是一个权重,前面应该还需要乘上一个系数,也就是lr。
-
作者在计算的时候发现,batch_size减小的时候mean_IOU也不会降低,于是就减小了它,加快训练速度。
-
论文的剩下部分就是实验结果部分了。我们可以大踏步来到V3
DeepLabV3:
-
作者一直在想方设法把CRF给去掉,终于在这一个版本,作者成功去掉了CRF这个模块。在这一个版本,作者的改动其实并不太多。用的block都是resnet_block。只不过在使用的时候,作者把block4重复了好几次,做了一个Cascade。在block内部,作者对每一个conv的dilation rate进行了调整。当然作者也不会忘记对ASPP进行调整,作者也做了一个parallel网络,最终选择的网络还是parallel,说明特征融合的强大。
-
我们首先来看看Cascade模块:
- 上面的图是原始的resnet和加了dilation rate 的deeplab的结构比较图。我们看到,从block3之后,feature map就不再减小了。我们在论文里面可以看到OS这个东西,这个的意思是output_stride,就是下采样的倍数。在resnet的conv1和pooling1中,已经下采样了4倍,经过block1,block2再下采样4倍合起来就是16。具体可以看deeplabv3代码。
- 在级联的网络中,我们可以看到Multi_Grid这个东西,其实这个代表了初始block中三个卷积层每一个的dilation rate。最终经过实验证明,初始的Multi_grid = (1,2,1)效果最好。rate就是这一个block中Multi_grid的值是rate x (1,2,1)。具体实验图表如下图所示:
- 作者还对网络结构进行了调整,作者试试如果把ASPP稍微改了一下,把V2中有一个rate = 24的conv给删除了,改成了1 x1的conv层,之后contact。于是出现了这个parallel结构:
-
其中image Pooling的代码如下:
-
global_feature = self.conv_part5_avg(x) # 对全局池化的特征要进行处理 global_feature = self.conv_part5_relu(self.conv_part5_bn(self.conv_part5_conv(global_feature))) global_feature = F.interpolate(global_feature,(h,w),None,'bilinear',True)
-
经过作者不断地人肉搜索结构之后,得到的结果还是比较好的。在之后也成功地删除了CRF,终于不用搞这个麻烦的公式了。
DeepLabV3+:
-
这个论文只是对DeepLabV3的补充,并没有提出太多的东西。主要的创新点在这里:
-
backbone使用了Xception,这个结构之后再看。内容太多了。
-
然后把卷积变成了深度可分离空洞卷积。加上了一个Decoder的模块,终于用上了转置卷积。具体的等到我看完论文再整理吧。