深度学习:从入门到放弃
Posted `早茶月光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习:从入门到放弃相关的知识,希望对你有一定的参考价值。
https://zhuanlan.zhihu.com/p/22976342
FCN学习:Semantic Segmentation
感谢@huangh12 @郑途 @麦田守望者对标签图像生成的研究和讨论,这几天研究了一下,补充如下。
-----------------------------------------------------分割线------------------------------------------------------------
谢谢@潘达的评论,这一篇确实有很多点没有分析完,当时想着后来加上去,但是由于工作的关系,也就不了了之了。最近终于忙完了一个项目,有时间更新一下文章了!
----------------------------------------------------- 分割线------------------------------------------------------------
本来这一篇是想写Faster-RCNN的,但是Faster-RCNN中使用了RPN(Region Proposal Network)替代Selective Search等产生候选区域的方法。RPN是一种全卷积网络,所以为了透彻理解这个网络,首先学习一下FCN(fully convolutional networks)Fully Convolutional Networks for Semantic Segmentation全卷积网络首现于这篇文章。这篇文章是将CNN结构应用到图像语义分割领域并取得突出结果的开山之作,因而拿到了CVPR 2015年的best paper.
图像语义分割,简而言之就是对一张图片上的所有像素点进行分类
如上图就是一个语义分割的例子,不同的颜色代表不同的类别
下面简单介绍一下论文,重点介绍文中的那几个结构:
一.论文解读
1.Introduction
CNN这几年一直在驱动着图像识别领域的进步。无论是整张图片的分类(ILSVRC),还是物体检测,关键点检测都在CNN的帮助下得到了非常大的发展。但是图像语义分割不同于以上任务,这是个空间密集型的预测任务,换言之,这需要预测一幅图像中所有像素点的类别。
以往的用于语义分割的CNN,每个像素点用包围其的对象或区域类别进行标注,但是这种方法不管是在速度上还是精度上都有很大的缺陷。
本文提出了全卷积网络(FCN)的概念,针对语义分割训练一个端到端,点对点的网络,达到了state-of-the-art。这是第一次训练端到端的FCN,用于像素级的预测;也是第一次用监督预训练的方法训练FCN。
2.Fully Convolutional networks & Architecture
下面我们重点看一下FCN所用到的三种技术:
1.卷积化(convolutionalization)
分类所使用的网络通常会在最后连接全连接层,它会将原来二维的矩阵(图片)压缩成一维的,从而丢失了空间信息,最后训练输出一个标量,这就是我们的分类标签。
而图像语义分割的输出则需要是个分割图,且不论尺寸大小,但是至少是二维的。所以,我们丢弃全连接层,换上卷积层,而这就是所谓的卷积化了。
这幅图显示了卷积化的过程,图中显示的是AlexNet的结构,简单来说卷积化就是将其最后三层全连接层全部替换成卷积层
2.上采样(Upsampling)
上采样也就是对应于上图中最后生成heatmap的过程。
在一般的CNN结构中,如AlexNet,VGGNet均是使用池化层来缩小输出图片的size,例如VGG16,五次池化后图片被缩小了32倍;而在ResNet中,某些卷积层也参与到缩小图片size的过程。我们需要得到的是一个与原图像size相同的分割图,因此我们需要对最后一层进行上采样,在caffe中也被称为反卷积(Deconvolution),可能叫做转置卷积(conv_transpose)更为恰当一点。
为理解转置卷积,我们先来看一下Caffe中的卷积操作是怎么进行的
在caffe中计算卷积分为两个步骤:
1)使用im2col操作将图片转换为矩阵
2)调用GEMM计算实际的结果
简单来说就是以下的矩阵相乘操作:
下面使用一下@贾扬清大神在知乎上解释Caffe计算卷积的图来解释一下上面的操作:
这是将输入转换为feature matrix的过程(im2col),这里的feature matrix对应于上图中的矩阵B的转置,k即是卷积核的尺寸,C为输入的维度,矩阵B中的K=C x k x k,当然N就等于H‘ x W‘了,H‘,W‘对应于输出的高和宽,显然H‘=(H-k+2*pad)/stride+1,W‘=(W-k+2*pad)/stride+1,这组公式想必大家很熟悉了,就不加以解释了,接下来我们看看A矩阵,
A矩阵对应于filter matrix,Cout是输出的维度,亦即卷积核的个数,K= C x k x k.
所以在caffe中,先调用im2col将filters和input转换为对应filter matrix(A)和feature matrix(B‘),然后再用filter matrix乘以feature matrix的转置,就得到了C矩阵,亦即输出矩阵,再将C矩阵通过col2im转换为对应的feature map,这就是caffe中完整的卷积的前向传播过程。
那么当反向传播时又会如何呢?首先我们已经有从更深层的网络中得到的.
根据矩阵微分公式,可推得
,
Caffe中的卷积操作简单来说就是这样,那转置卷积呢?
其实转置卷积相对于卷积在神经网络结构的正向和反向传播中做相反的运算。
所以所谓的转置卷积其实就是正向时左乘,而反向时左乘,即的运算。
虽然转置卷积层和卷积层一样,也是可以train参数的,但是实际实验过程中,作者发现,让转置卷积层可学习,并没有带来performance的提升,所以实验中的转置卷积层的lr全部被置零了
注意:在代码中可以看出,为了得到和原图像size一模一样的分割图,FCN中还使用了crop_layer来配合deconvolution层
3.跳跃结构(Skip Architecture)
其实直接使用前两种结构就已经可以得到结果了,但是直接将全卷积后的结果上采样后得到的结果通常是很粗糙的。所以这一结构主要是用来优化最终结果的,思路就是将不同池化层的结果进行上采样,然后结合这些结果来优化输出,具体结构如下:
而不同的结构产生的结果对比如下:
二.实践与代码分析
作者在github上开源了代码:Fully Convolutional Networks
git clone https://github.com/shelhamer/fcn.berkeleyvision.org.git
我们首先将项目克隆到本地
项目文件结构很清晰,如果想train自己的model,只需要修改一些文件路径设置即可,这里我们应用已经train好的model来测试一下自己的图片:
我们下载voc-fcn32s,voc-fcn16s以及voc-fcn8s的caffemodel(根据提供好的caffemodel-url),fcn-16s和fcn32s都是缺少deploy.prototxt的,我们根据train.prototxt稍加修改即可。
-------------------------------------------------标签图像生成--------------------------------------------------------
从VOCdevkit处下载VOC2012的训练/验证集,解压之后,在SegmentationClass文件夹下可以看到标签图像。
在PIL中,图像有很多种模式,如‘L‘模式,’P‘模式,还有常见的‘RGB‘模式,模式‘L‘为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。模式“P”为8位彩色图像,它的每个像素用8个bit表示,其对应的彩色值是按照调色板索引值查询出来的。标签图像的模式正是‘P‘模式,因此测试时要生成对应标签图像的图片的话,构建一个调色板即可。
按照上图,对应修改调色板和infer.py,就可以测试我们自己的图片了
import numpy as np
from PIL import Image
import caffe
# load image, switch to BGR, subtract mean, and make dims C x H x W for Caffe
im = Image.open(‘data/pascal/VOCdevkit/VOC2012/JPEGImages/2007_000129.jpg‘)
in_ = np.array(im, dtype=np.float32)
in_ = in_[:,:,::-1]
in_ -= np.array((104.00698793,116.66876762,122.67891434))
in_ = in_.transpose((2,0,1))
# load net
net = caffe.Net(‘voc-fcn8s/deploy.prototxt‘, ‘voc-fcn8s/fcn8s-heavy-pascal.caffemodel‘, caffe.TEST)
# shape for input (data blob is N x C x H x W), set data
net.blobs[‘data‘].reshape(1, *in_.shape)
net.blobs[‘data‘].data[...] = in_
# run net and take argmax for prediction
net.forward()
out = net.blobs[‘score‘].data[0].argmax(axis=0)
arr=out.astype(np.uint8)
im=Image.fromarray(arr)
palette=[]
for i in range(256):
palette.extend((i,i,i))
palette[:3*21]=np.array([[0, 0, 0],
[128, 0, 0],
[0, 128, 0],
[128, 128, 0],
[0, 0, 128],