【caffe-windows】全卷积网络特征图分析
前言
突然就想分析一下全卷積網絡的轉置卷積部分了, 就是這么猝不及防的想法, 而且這個網絡對圖片的輸入大小無要求,這么神奇的網絡是時候分析一波了,我個人的學習方法調試代碼,然后對照論文看理論
本次分析主要針對每層的權重大小和特征圖大小的推導分析, 最最重要的是轉置卷積部分是如何將特征圖慢慢擴張到原始圖片大小的.
國際慣例, 參考博客:
FCN的caffe實現戳這里
FCN學習:Semantic Segmentation
全卷積網絡(FCN)與圖像分割
如何理解深度學習中的deconvolution networks?
如何理解空洞卷積(dilated convolution)?
本文所需文件戳這里,一個模型, 一個網絡結構,兩張測試圖片
鏈接: https://pan.baidu.com/s/1hseRZ5i 密碼: e94k
運行代碼
python
官方提供的代碼是python文件, 我直接貼出來了, 因為我討厭python調試代碼
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') im = Image.open('2007_000175.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)matlab
clear clc close all addpath('../..') caffe.reset_all %讀取圖片 im=imread('2007_000129.jpg'); im=double(im); im=im(:,:,[3 2 1]);%RGB->BGR im(:,:,1)=im(:,:,1)-104.00698793;%B im(:,:,2)=im(:,:,2)-116.66876762;%G im(:,:,3)=im(:,:,3)-122.67891434;%R im=permute(im,[2,1,3]);%(width,height,channel)->(C,H,W) %讀取模型 model_dir = './voc-fcn8s/'; net_model = [model_dir 'deploy.prototxt']; net_weights = [model_dir 'fcn8s-heavy-pascal.caffemodel']; net=caffe.Net(net_model,net_weights,'test') %計算 net.blobs('data').reshape([size(im) 1]); net.reshape(); out=net.forward({im}); [max_val,max_ind]=max(out{1},[],3); %顯示特征圖 max_ind=max_ind-1; max_ind=permute(max_ind,[2,1]); max_ind=max_ind(end:-1:1,:); contourf(max_ind)直接貼個結果圖,就是那個騎摩托車的jpg對應的分割圖
【PS】關于matlab的代碼是我自己瞎寫的,如果有問題,歡迎在評論區指出
卷積特征圖推導
直入主題,先看看網絡結構(自己去NetScope把prototxt粘貼過去看,太大了), 然后我們一層一層推特征圖大小, 看看轉置卷積到底是何方神圣, 不過開始之前記住一個東東: 關于卷積的運算, 假設圖像高度是M,卷積核高度是N, 對圖像的填充(padding)是P, 卷積核移動步長為S, 則卷積后的特征圖的高度是:
對于特征圖的寬度計算一樣, 這個公式應該沒問題吧
對于池化, 除不盡向上取整即可
對于轉置卷積的高寬就是
好了,開始計算,假設輸入圖像就是這個騎摩托的 2007_000129.jpg圖像, 大小為 (334,500,3), 此時你應該用 notepad++打開 deploy.prototxt了
先看看都有哪些層:
net.blob_names輸出
'data''data_input_0_split_0''data_input_0_split_1''conv1_1''conv1_2''pool1''conv2_1''conv2_2''pool2''conv3_1''conv3_2''conv3_3''pool3''pool3_pool3_0_split_0''pool3_pool3_0_split_1''conv4_1''conv4_2''conv4_3''pool4''pool4_pool4_0_split_0''pool4_pool4_0_split_1''conv5_1''conv5_2''conv5_3''pool5''fc6''fc7''score_fr''upscore2''upscore2_upscore2_0_split_0''upscore2_upscore2_0_split_1''score_pool4''score_pool4c''fuse_pool4''upscore_pool4''upscore_pool4_upscore_pool4_0_split_0''upscore_pool4_upscore_pool4_0_split_1''score_pool3''score_pool3c''fuse_pool3''upscore8''score'先看看輸入數據大小:
size(net.blobs('data').get_data())ans =334 500 3| data | (334,500,3) | - | - | 輸入 |
| conv1_1 | (334,500,3) | (64,100,3,1) | (64,532,698) | 卷積 |
| conv1_2 | (64,532,698) | (64,1,3,1) | (64,532,698) | 卷積 |
| pool1 | (64,532,698) | (2,2) | (64,266,349) | 池化 |
| conv2_1 | (64,266,349) | (128,1,3,1) | (128,266,349) | 卷積 |
| conv2_2 | (128,266,349) | (128,1,3,1) | (128,266,349) | 卷積 |
| pool2 | (128,266,349) | (2,2) | (128,133,175) | 池化 |
| conv3_1 | (128,133,175) | (256,1,3,1) | (256,133,175) | 卷積 |
| conv3_2 | (256,133,175) | (256,1,3,1) | (256,133,175) | 卷積 |
| conv3_3 | (256,133,175) | (256,1,3,1) | (256,133,175) | 卷積 |
| pool3 | (256,133,175) | (2,2) | (256,67,88) | 池化 |
| conv4_1 | (256,67,88) | (512,1,3,1) | (512,67,88) | 卷積 |
| conv4_2 | (512,67,88) | (512,1,3,1) | (512,67,88) | 卷積 |
| conv4_3 | (512,67,88) | (512,1,3,1) | (512,67,88) | 卷積 |
| pool4 | (512,67,88) | (2,2) | (512,34,44) | 池化 |
| conv5_1 | (512,34,44) | (512,1,3,1,) | (512,34,44) | 卷積 |
| conv5_2 | (512,34,44) | (512,1,3,1) | (512,34,44) | 卷積 |
| conv5_3 | (512,34,44) | (512,1,3,1) | (512,34,44) | 卷積 |
| pool5 | (512,34,44) | (2,2) | (512,17,22) | 池化 |
| fc6 | (512,17,22) | (4096,0,7,1) | (4096,11,16) | 卷積 |
| fc7 | (4096,11,16) | (4096,0,1,1) | (4096,11,16) | 卷積 |
| score_fr | (4096,11,16) | (21,0,1,-) | (21,11,16) | 卷積 |
| upscore2 | (21,11,16) | (21,-,4,2) | (21,24,34) | 轉置卷積 |
| score_pool4 | pool4 | (21,0,1,-) | (21,34,44) | 卷積 |
| score_pool4c | upscore2,score_pool4 | - | (21,24,34) | 裁切 |
| fuse_pool4 | score_pool4c, upscore2 | - | (21,24,34) | 元素級加和 |
| upscore_pool4 | fuse_pool4 | (21,-,4,2) | (21,50,70) | 轉置卷積 |
| score_pool3 | pool3 | (21,0,1,-) | (21,67,88) | 卷積 |
| score_pool3c | fuse_pool4,pool3 | - | (21,50,70) | 裁剪 |
| fuse_pool3 | upscore_pool4, score_pool3c | - | (21,50,70) | 元素級相加 |
| upscore8 | fuse_pool3 | (21,-,16,8) | (21,408,568) | 轉置卷積 |
| score | input, upscore8 | - | (21,334,500) | 裁剪 |
如果有興趣的話, 可以使用我前面提供的可視化方法將特征圖可視化看看, 貼一個部分可視化代碼
function [ ] = feature_partvisual( net,mapnum,crop_num ) names=net.blob_names; names{mapnum} featuremap=net.blobs(names{mapnum}).get_data();%獲取指定層的特征圖 [m_size,n_size,num,~]=size(featuremap);%獲取特征圖大小,長*寬*卷積核個數*通道數 row=ceil(sqrt(num));%行數 col=row;%列數 feature_map=zeros(n_size*col,m_size*row);%因為opencv讀取的圖像高剛好是特征圖的寬 cout_map=1; for i=0:row-1for j=0:col-1if cout_map<=numfeature_map(j*n_size+1:(j+1)*n_size,i*m_size+1:(i+1)*m_size)=(mapminmax(featuremap(:,:,cout_map,crop_num),0,1)*255)';cout_map=cout_map+1;endend end imshow(uint8(feature_map)) str=strcat('feature map num:',num2str(cout_map-1)); title(str) end參數分別是:①net已經進行過一次前向計算的網絡,這個網絡現在已經充滿了參數和特征圖, ②mapnum是第幾層的特征圖, ③ crop_num是第幾個圖片的特征圖需要被顯示出來, 比如我丟進去十張圖片, 只想顯示第五張圖片對應的特征圖, 就把這個參數設置成5. 本例中, 如果想顯示conv1_1的特征圖可以這樣調用feature_partvisual(net,4,1), 結果是
重要的層
接下來分析一下哪些層對網絡結構挺重要的
轉置卷積層
調用方法
layer {name: "upscore2"type: "Deconvolution"bottom: "score_fr"top: "upscore2"param {lr_mult: 0}convolution_param {num_output: 21bias_term: falsekernel_size: 4stride: 2} }直接貼一下相關的參考文獻吧, 后面學習theano的轉置卷積再詳細看看這部分的理論推導
如何理解深度學習中的deconvolution networks?
What are deconvolutional layers?
A technical report on convolution arithmetic in the context of deep learning
簡單看看兩個圖, 第一個是對于步長為1,沒有padding填充的卷積核的轉置卷積過程,也就是(4*4)的圖像經過(3*3)的卷積核得到(2*2)的操作的逆過程
第二個是對于步長為2, padding填充為1的卷積核對應的轉置卷積過程:也就是(5*5)的圖像被padding填充1次以后, 經過(3*3)的卷積核步長為2以后得到(3*3)的操作的逆過程
裁剪
調用方法
layer {name: "score_pool4c"type: "Crop"bottom: "score_pool4"bottom: "upscore2"top: "score_pool4c"crop_param {axis: 2offset: 5} }作用可以戳這里看, 其實就是以第二個bottom的大小為參考,將第一個bottom裁剪成同樣大小, axis=2表示從第二個軸開始裁剪(軸的編號是從1開始的), offset=5代表從當前軸的第五個元素開始取值,長度為第二個bottom的對應維度大小
元素級相加
將兩個同樣大小的矩陣的對應位置相加
調用方法為
layer {name: "fuse_pool4"type: "Eltwise"bottom: "upscore2"bottom: "score_pool4c"top: "fuse_pool4"eltwise_param {operation: SUM} }后記
這個網絡的優勢在于后面的全連接變成了使用卷積核, 這樣對圖片大小的輸入就沒有嚴格要求了, 因為全連接參數固定, 而卷積核只需要在同一張圖片滑動即可,重構過程的網絡設計還是很嚴格的, 一定要嚴格注意各種裁剪, 各種轉置卷積的參數(包含padding和stride等).
這里網絡設計幾個技巧就是
- 一個特征圖經過(個數,填充padding=1,卷積核大小=3,步長stride=1)的卷積核卷積以后, 特征圖大小是不變的. 然后通過轉置卷積就可以越卷越大了.
- 一個pool和另一個特征圖拼接起來的方法就是按照最小的那個特征圖大小對另一個進行裁剪, 然后再把這兩個同樣大小的特征圖相加就行了
后續可能就要關注轉置卷積的具體推導過程了, 如果有興趣請繼續關注博客后續
如果有錯誤地方,謝謝在評論區指正.
總結
以上是生活随笔為你收集整理的【caffe-windows】全卷积网络特征图分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信用卡到期换卡要激活吗?这些事项要注意!
- 下一篇: 【theano-windows】学习笔记