对于FCN模型的搭建以及训练自己的数据集,其实并不是特别难。可以参考FCN训练自己的数据集。可能更多的人想知道FCN每一层输出的结果是什么。这篇博客主要介绍的是对FCN每一层输出进行可视化操作。
-
查看FCN总体框架
先打开工具netcope,如果打不开点击这个netscope然后打开fcn-master2\voc-fcn32s下的train.prototxt文件,将train.prototxt文件内容复制到网页左侧编辑框后,shift+enter,就可以直接显示网络结构。
详解FCN模型中每一层的输出及可视化操作 -
查看每一层输出的结构
打开fcn-master2\voc-fcn32s下infer.py文件,在原代码的最后写上如下代码。
for layer_name, blob in net.blobs.iteritems():
print layer_name + '\t' + str(blob.data.shape)
运行程序,可以看到输出的结果如下
data (1L, 3L, 375L, 500L)
data_input_0_split_0 (1L, 3L, 375L, 500L)
data_input_0_split_1 (1L, 3L, 375L, 500L)
conv1_1 (1L, 64L, 573L, 698L)
conv1_2 (1L, 64L, 573L, 698L)
pool1 (1L, 64L, 287L, 349L)
conv2_1 (1L, 128L, 287L, 349L)
conv2_2 (1L, 128L, 287L, 349L)
pool2 (1L, 128L, 144L, 175L)
conv3_1 (1L, 256L, 144L, 175L)
conv3_2 (1L, 256L, 144L, 175L)
conv3_3 (1L, 256L, 144L, 175L)
pool3 (1L, 256L, 72L, 88L)
conv4_1 (1L, 512L, 72L, 88L)
conv4_2 (1L, 512L, 72L, 88L)
conv4_3 (1L, 512L, 72L, 88L)
pool4 (1L, 512L, 36L, 44L)
conv5_1 (1L, 512L, 36L, 44L)
conv5_2 (1L, 512L, 36L, 44L)
conv5_3 (1L, 512L, 36L, 44L)
pool5 (1L, 512L, 18L, 22L)
fc6 (1L, 4096L, 12L, 16L)
fc7 (1L, 4096L, 12L, 16L)
score_fr (1L, 5L, 12L, 16L)
upscore (1L, 5L, 416L, 544L)
score (1L, 5L, 375L, 500L)
从输出结果可以看出输入的是500 x 375 x 3的图片,第一层卷积后输出的是698 x 573 x 64的特征图。可能有很多人疑惑为什么第一层卷积层分为conv1_1和conv1_2。因为FCN是基于VGG16的结构,VGG16总共是有16层,第一层分为conv1_1和conv1_2因为没有改变图像的大小以及卷积核数,所以两个层合并称为第一层。后面的层数是同样的道理,具体的可以参考博客详解VGG16模型。
-
可视化数据函数
在fcn-master2\voc-fcn32s下新建vis_square.py文件,复制代码
# -*- coding: utf-8 -*
####定义辅助函数来进行可视化中间层特征
import numpy as np
from PIL import Image
import scipy.misc
import pylab
import matplotlib.pyplot as plt
def vis_square(data):
"""Take an array of shape (n, height, width) or (n, height, width, 3)
and visualize each (height, width) thing in a grid of size approx. sqrt(n) by sqrt(n)"""
# normalize data for display # 数据正则化
data = (data - data.min()) / (data.max() - data.min())
# force the number of filters to be square
# 此处目的是将一个个滤波器按照正方形的样子排列
# 先对shape[0]也就是滤波器数量取平方根,然后取大于等于该结果的正整数
# 比如40个卷积核,则需要7*7的正方形格子(虽然填不满)
n = int(np.ceil(np.sqrt(data.shape[0])))
padding = (((0, n ** 2 - data.shape[0]),
(0, 1), (0, 1))
# add some space between filters # 在相邻的卷积核之间加入0进行padding
+ ((0, 0),) * (data.ndim - 3))
# don't pad the last dimension (if there is one) 如果最后最后一维是1不进行扩充
data = np.pad(data, padding, mode='constant', constant_values=1)
# pad函数声明:pad(array, pad_width, mode, **kwargs),作用是把list在原维度上进行扩展;
# pad_width是扩充参数,例如参数((3,2),(2,3));
# 其中(3,2)为水平方向上,上面加3行,下面加2行;
# (2,3)为垂直方向上,上面加2行,下面加3行;
# constant是常数填充的意思。# pad with ones (white)
# tile the filters into an image # 将卷积核平铺成图片
data = data.reshape((n, n) + data.shape[1:]).transpose((0, 2, 1, 3) + tuple(range(4, data.ndim + 1)))
data = data.reshape((n * data.shape[1], n * data.shape[3]) + data.shape[4:])
plt.imshow(data);pylab.show(); plt.axis('off')
保存退出,这代码的功能是用来显示每一层输出的图像。
然后在infer.py文件上添加模块
import vis_square
import pylab
- 可视化第一层卷积核图像
在代码的最后写上如下代码
filters = net.params['conv1_1'][0].data
vis_square.vis_square(filters.transpose(0, 2, 3, 1))
输出的是第一层卷积核模板的图像,这卷积核图像是通过训练模型得出来的。FCN的第一层共有64个卷积核,所以输出的是64个卷积核图像。
-
可视化第一层图像输出
先将第4步的代码注释,然后在写上如下代码
feat = net.blobs['conv1_1'].data[0, :]
vis_square.vis_square(feat)
输出的结果为下图所示。因为有64个卷积核,所以输出的是64张特征图。
-
可视化第一层池化后的数据图像
同样先注释第5步的代码,然后在写上如下代码。
feat = net.blobs['pool1'].data[0]
vis_square.vis_square(feat)
-
输出第一层图像的数据
图像就是一组数据组成的,如果想要得出第一层数据的话,可以下上如下代码
print('conv1_1')
print net.blobs['conv1_1'].data[0]
输出的结果为如下图所示。
-
总结
以上介绍的是第一层图像的可视化操作。如果想要其它层图像可视化操作,只需要修改net.blobs[‘ ’]中每层网络结构的名称即可。如查看第5层数据。代码为
feat = net.blobs['conv5_1'].data[0, :]
vis_square.vis_square(feat)
到此所有的可视化操作已经介绍完了,相信通过对每一层数据的可视化操作,能够更加深刻的理解FCN模型。