导语: 前段时间学习了一下全卷积神经网络fcn,现以笔记的形式总结学习的过程。主要包括四个部分: (1)caffe框架的搭建;(2)fcn原理介绍;(3)分析具体fcn网络;(4)使用caffe框架进行fcn的训练和预测
可以选择阿里源
sudo apt-get update
sudo dpkg -i cuda-repo-ubuntu1404-7-5-local_7.5-18_amd64.deb
sudo apt-get update
sudo apt-get install cuda
sudo gedit ~/.bashrc
将以下内容写入到~/.bashrc尾部:
export PATH=/usr/local/cuda-8.0/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda8.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
cd /usr/local/cuda-8.0/samples/1_Utilities/deviceQuerymake
sudo make
sudo ./deviceQuery
如果出现以下输出,则说明cuda安装成功。一般需要在重启后运行sudo ./deviceQuery 才会有正确的输出。
sudo tar -zxvf ./cudnn-8.0-linux-x64-v5.1.tgz
cd cuda
sudo cp lib64/lib* /usr/local/cuda/lib64/
sudo cp include/cudnn.h /usr/local/cuda/include/
cd /usr/local/cuda/lib64/
sudo rm -rf libcudnn.so libcudnn.so.5 #删除原有动态文件
sudo ln -s libcudnn.so.5.1.5 libcudnn.so.5 #生成软衔接
sudo ln -s libcudnn.so.5 libcudnn.so #生成软链接
sudo ldconfig
最后一句代码出现错误
sudo mv /usr/lib/nvidia-375/libEGL.so.1 /usr/lib/nvidia-375/libEGL.so.1.org
sudo mv /usr/lib32/nvidia-375/libEGL.so.1 /usr/lib32/nvidia-375/libEGL.so.1.org
sudo ln -s /usr/lib/nvidia-375/libEGL.so.375.39 /usr/lib/nvidia-375/libEGL.so.1
sudo ln -s /usr/lib32/nvidia-375/libEGL.so.375.39 /usr/lib32/nvidia-375/libEGL.so.1
sudo ldconfig
unzip opencv-3.1.0.zip
sudo cp ./opencv-3.1.0 /home
sudo mv opencv-3.1.0 opencv
cd ~/opencv
mkdir build
cd build
sudo apt install cmake
sudo cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
因为opecv3.0与cuda8.0不兼容导致的。解决办法:修改 ~/opencv/modules/cudalegacy/src/graphcuts.cpp文件内容,如图:
代码:
#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER)||(CUDART_VERSION>=8000)
sudo make -j8
以上只是将opencv编译成功,还没将opencv安装,需要运行下面指令进行安装:
sudo make install
sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install libatlas-base-dev
sudo apt-get install libgflags-dev libgoogle-glog-dev liblmdb-dev
因为make指令只能make Makefile.config文件,而Makefile.config.example是caffe给出的makefile例子,因此,首先将Makefile.config.example的内容复制到Makefile.config:
sudo cp Makefile.config.example Makefile.config
修改为:
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include /usr/include/hdf5/serial
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/hdf5/serial
这是因为ubuntu16.04的文件包含位置发生了变化,尤其是需要用到的hdf5的位置,所以需要更改这一路径.
打开makefile文件,做如下修改: 将:
NVCCFLAGS +=-ccbin=$(CXX) -Xcompiler-fPIC $(COMMON_FLAGS)
替换为:
NVCCFLAGS += -D_FORCE_INLINES -ccbin=$(CXX) -Xcompiler -fPIC $(COMMON_FLAGS)
将其中的第115行注释掉:将
#error-- unsupported GNU version! gcc versions later than 4.9 are not supported!
改为
//#error-- unsupported GNU version! gcc versions later than 4.9 are not supported!
将一些文件复制到/usr/local/lib文件夹下:
注意自己CUDA的版本号!
sudo cp /usr/local/cuda-8.0/lib64/libcudart.so.8.0 /usr/local/lib/libcudart.so.8.0 && sudo ldconfig
sudo cp /usr/local/cuda-8.0/lib64/libcublas.so.8.0 /usr/local/lib/libcublas.so.8.0 && sudo ldconfig
sudo cp /usr/local/cuda-8.0/lib64/libcurand.so.8.0 /usr/local/lib/libcurand.so.8.0 && sudo ldconfig
sudo cp /usr/local/cuda-8.0/lib64/libcudnn.so.5 /usr/local/lib/libcudnn.so.5 && sudo ldconfig
make all -j8 #-j根据自己电脑配置决定
sudo make runtest
如果运行之后出现下图,说明caffe配置成功。
在caffe根目录的python文件夹下,有一个requirements.txt的清单文件,上面列出了需要的依赖库,按照这个清单安装就可以了。
就会看到,安装成功的,都会显示Requirement already satisfied, 没有安装成功的,也可以按照requirements.txt所罗列的一个一个安装
简单的说,FCN与CNN的区别在于FCN把CNN最后的全连接层换成卷积层,输出一张已经label好的图。
对于FC6(4096),使用4096个filter,filter的大小是7*7,做完以后大小为1*1*4096
对于FC7(4096),使用4096个filter,filter的大小是1*1,做完以后大小为1*1*4096
对于FC8(1000),使用1000个filter,filter的大小是1*1,做完以后大小为1*1*1000
所有的层都是卷积层,故称为全卷积网络。
下图是32倍,16倍和8倍上采样得到的结果的对比,可以看到它们得到的结果越来越精确:
在不同层级的赤化层分别上采样,然后叠加在一起,这样就产生了很好的结果。
我们主要分析voc-fcn32s文件夹下train.prototxt文件,这是网络训练模型,以此模型为例,分析fcn网络。
layer {
name: "data"
type: "Python"
top: "data"
top: "label"
python_param {
module: "voc_layers"
layer: "SBDDSegDataLayer"
param_str: "{\'sbdd_dir\': \'../data/sbdd/dataset\', \'seed\': 1337, \'split\': \'train\', \'mean\': (104.00699, 116.66877, 122.67892)}"
}
}
==表示允许使用python来编写layer==
函数名 | 作用 |
---|---|
setup() | 数据初始化 |
reshape() | 取数据然后把它规范化为四维的矩阵 |
forward() | 把取到的数据往前传递 |
backward() | 数据输入层没有反向传播,所以直接pass |
load_image() | 将输入转化为caffe的标准输入数据 |
load_label() | 加载输入数据对应的label,是一个二维的图 |
激活层不改变特征图的大小。
没有改变特征图的大小
特征图的大小变为输入图像的1/2
第二到第五个阶段的卷积层k=3,p=1,s=1,根据公式
h' = (h-3+2*1)/1+1 = h
卷基层并没有改变特征图的大小,所以特征图都是在每个卷积阶段的pooling层较小的。
即在第二到第五个卷积阶段,特征图分别为第一个卷积阶段之后的1/4,1/8,1/16,1/32.
到这里就解释了为什么要在第一个卷积层设置padding=100,如果设为1,那么fc6之后特征图的大小变为(h-192)/32,那么h小于192的图像都无法处理。
反卷积就是卷积计算的逆过程
h' = (h-k+2*p)/s+1
===>所以在做反卷积时,
h = (h'+1)*s-2*p+k
所以经过upscore,k=64,s=32,特征图的大小变为
h_u = (h_6+1)*32+64 = [(h+6)/32+1]*32+64 = h+38
可以看到,经过upscore的上采样之后,特征图的大小为 h+38,这显然与输入图像大小不一致,所以为了与输入图像大小一致,在upscore之后,又添加了一层score(Crop)层
Crop层有两个输入,一个是要裁剪的特征图A,一个是作为参考的输入B。
很显然crop的作用就是参考B裁剪A
我们把voc-fcn32s网络的各层都进行了分析,其他的网络结构可以参考这个总结进行分析。
这篇总结主要介绍使用caffe框架来进行fcn的训练,caffe里面包含了卷积层等各种网络层的实现。caffe框架使用比较简单,只需要在prototxt中以文本的形式定义好网络就可以进行训练,但配置比较麻烦,我在上篇总结中已经给出了完整的配置步骤。
github下载地址https://github.com/shelhamer/fcn.berkeleyvision.org 这是根据论文CVPR FCN实现的fcn模型和代码,兼容BVLC/caffe:master。下载下来之后,使用比较简单,无需配置和编译,只要满足以下条件:
因为该开源代码中使用了python来编写layer。
下载之后,直接解压就可以使用,其中有多个经典的fcn模型。
因为train用的是SBDDdata,而val用的是voc中的data,所以要下载两套数据集
在data文件夹下新建sbdd文件夹,将下载的dataset解压到该文件夹下
将下载好的测试数据集在data/pascal文件下解压。
下载VGG-16的预训练模型放至/fcn.berkeleyvision.org/ilsvrc- nets/目录下,并重命名为vgg16-fcn.caffemodel
下载地址 http://dl.caffe.berkeleyvision.org/siftflow-fcn16s-heavy.caffemodel
下载这个预训练模型的目的就是在训练时以该模型为基础进行fintune,而不用从头开始训练。
在使用fcn进行图像语义分割时,需要进行三次训练,分别是对pool5后得到的特征图以32为步长进行上采样(32s)、对pool4之后得到的特征图以16为步长进行上采样(16s)和对pool3之后得到的特征图以8为步长进行上采样(8s)。首先进行32s的训练。
#solver = caffe.SGDSolver(‘solver.prototxt’) #solver.net.copy_from(weights) solver = caffe.SGDSolver(‘solver.prototxt’) vgg_net = caffe.Net(vgg_proto, vgg_weights, caffe.TRAIN) surgery.transplant(solver.net, vgg_net) del vgg_net
原先的方法仅仅是从vgg-16模型中拷贝参数,但是并没有改造原先的网络,造成网络不收敛。可以看一下transplant函数的作用
sudo python solve.py
如果报outofmemory错误,说明显卡大小不够,可以减小输入图像的大小或者增大显卡。也可以换成cpu计算模式。在solve.py中更改网络的计算模式为cpu
#caffe.set_device(0)
#caffe.set_mode_gpu()
caffe.set_mode_cpu()
在训练好32s后之后,我们需要依次以32s训练得到的模型训练16s,以16s训练得到的模型训练8s,由于在16s和8s中,不需要全连接层到卷积层的转换,所以在solve.py中直接使用代码
solver = caffe.SGDSolver('solver.prototxt')
solver.net.copy_from(weights)
即可,不需要上述训练32s时使用transplant函数。
对于对单张图片,这个开源实现也提供了脚本利用训练好的模型对其进行语义分割,就是根目录下的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('pascal/VOC2010/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)
为了保存分割图像,添加几句代码
plt.imshow(out, cmap='gray')
plt.axis('off')
plt.savaflg(test.jpg)
输入测试图像
分割后的图像
或者在在终端或者bashrc中将接口加入到PYTHONPATH中:
export PYTHONPATH=caffe根目录/python:$PYTHONPATH