bilinear
双线性插值是目前在语义分割中用的比较多的一种方式,比如FCN中就是用的这种方法。
这种方法特点是不需要进行学习,运行速度快,操作简单。只需要设置好固定的参数值即可,设置的参数就是中心值需要乘以的系数。
一个简单的例子可以参考如下(来自互联网):
接着,同样的道理,使用f(R1), f(R2)进行插值,推导出来f( P )就可以了.
具体的实现方式,可以直接参考fcn.berkerlyvision.org中的surgery.py如下:
def upsample_filt(size):
"""
Make a 2D bilinear kernel suitable for upsampling of the given (h, w) size.
"""
factor = (size + 1) // 2
if size % 2 == 1:
center = factor - 1
else:
center = factor - 0.5
og = np.ogrid[:size, :size]
return (1 - abs(og[0] - center) / factor) * \
(1 - abs(og[1] - center) / factor)
如果看了上面的简单实例和具体代码还不会使用bilinear,就看最后一个例子,这里以FCN举例,
在FCN的berkerly官方caffe实现,以voc-fcn32/train.prototxt举例,bilinear部分代码如下:
layer {
name: "score_fr"
type: "Convolution"
bottom: "fc7"
top: "score_fr"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 21
pad: 0
kernel_size: 1
}
}
layer {
name: "upscore"
type: "Deconvolution"
bottom: "score_fr"
top: "upscore"
param {
lr_mult: 0
}
convolution_param {
num_output: 21
bias_term: false
kernel_size: 64
stride: 32
}
}
其中,upscore层使用surgery.py中的upsample_filt函数进行实现(本文第一段代码),相当于设置参数type: ‘bilinear’,这个很容易实现。通过测试可以发现,在forward-propagation过程中,score_fr层的blob shape是[1, 21, 16, 16],经过bilinear后变为[1, 21, 544, 544]。依照代码简单分析就可以发现bilinear的具体计算流程如下:
(1) 64%2 == 0,所以center的坐标为(31.5, 31.5),note在python中下标从0开始
(2) 计算出一个64x64的矩阵,每个位置根据代码所示填数,其实就是线性规则
(3) 二维来看,将score_fr层的16x16的每一个位置的坐标放在31.5位置,也就是在相邻元素之间插入32个位置,周边补充32个位置,不同元素对插入的不同值有影响,则相互叠加,最后将原来的16x16的元素移除,也就形成了32x17 = 544,也就是具体upscore层算出来的值
放点东西,形象表示一下:
32个元素 [16x16中的(0,0)元素] 32个元素 [16x16中的(0,1)元素] 32个元素 …
其中,中括号中的元素不可见
Deconvolution
Deconvolution是目前争议比较多的方法,主要是名字上的争议,由于实现上采用转置卷积核的方法,所以有人说应该叫(transposed convolution),但是思想上是为了还原原有特征图,类似消除原有卷积的某种效果,所以叫反卷积(deconvolution). Caffe中叫deconvolution,这里就继续沿用这个名字.
要理解deconv,要先了解conv的具体是实现方式,在实现过程中,为了使卷积运算更快的执行,通常转化为矩阵乘法进行处理(因为矩阵乘法有一些加速计算库)。卷积计算通常的两种实现方式是:在caffe中使用im2col的方法,在其他的地方使用toeplitz matrix(托普利兹矩阵)进行实现。
具体例子参看知乎https://www.zhihu.com/question/43609045?sort=created
为了更容易地实现deconvolution,直接使deconv的前向传播模拟conv的反向传播,当然,这里只是为了保证尺寸大小互逆,并不能保证值的还原。具体思路可以参考这篇博客http://blog.csdn.net/zsz_shsf/article/details/53201669。
做几个小例子帮助理解:
(1) 关于第一节中bilinear的在caffe中使用deconvolution进行实现,上节的bilinear论述过程中使用固定值计算的方法,本节从deconv可视化计算的角度进行理解,https://github.com/vdumoulin/conv_arithmetic
63个元素 [16x16中的(0,0)元素] 31个元素 [16x16中的(0,1)元素] 31个元素
其中,中括号中的元素可见
对以上过程进行卷积运算,注意这里stride 使用1, 最终输出大小为(63+32x15+64-64)/1 + 1 = 544
这里可以脑补一下计算过程,相当于第一节中的手算叠加
(2) 对bilinear使用转置运算进行实现,先将64x64的卷积核转化为toeplitz matrix,然后转置,得到544x544,256的矩阵,然后将score_fr转化为1, 256的矩阵,两者矩阵乘法,得到544x544的最终结果,具体过程脑补吧
unpooling
也就是反池化,不需要学习,用的不是太多,参考论文Visualizing and Understanding Convolutional Networks,还有SegNet和DeconvNet
简单原理:在池化过程中,记录下max-pooling在对应kernel中的坐标,在反池化过程中,将一个元素根据kernel进行放大,根据之前的坐标将元素填写进去,其他位置补0
实现代码可以看SegNet的实现
end
简单捋了一遍,帮助理解