對于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模型。