對於FCN模型的搭建以及訓練自己的數據集,其實並不是特別難。可以參考FCN訓練自己的數據集。可能更多的人想知道FCN每一層輸出的結果是什麼。這篇博客主要介紹的是對FCN每一層輸出進行可視化操作。
- 查看FCN總體框架
先打開工具netcope,然後打開fcn-master2\voc-fcn32s下的train.prototxt文件,將train.prototxt文件內容複製到網頁左側編輯框後,shift+enter,就可以直接顯示網絡結構。
- 查看每一層輸出的結構
打開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模型。