【caffe-matlab】权重以及特征图的可视化
前言
移植了各種caffe,是時候進行下一步操作了,先拿可視化下手吧。大部分內容可能跟網上的方法不一樣,大家看完我的博客最好去網上看看大牛們的博客,萬一被我誤導了,就罪過了o(╯□╰)o,開更.............
環境:微軟caffe+wind7+matlab2013a
參考:http://caffe.berkeleyvision.org/tutorial/interfaces.html
? ? ? ? ? ? ?http://nbviewer.jupyter.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb
一、模型讀取
讀取bvlc_reference_caffenet 的模型結構以及訓練好的參數,注意此處的模型結構為deploy,而非train時候的。
addpath('..') %加入+caffe路徑 caffe.set_mode_cpu();%設置CPU模式 model = '../../models/bvlc_reference_caffenet/deploy.prototxt';%模型 weights = '../../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel';%參數 net=caffe.Net(model,'test');%測試 net.copy_from(weights); %得到訓練好的權重參數 net %顯示net的結構然后我們就可以看到模型的結構了: net = Net with properties:layer_vec: [1x24 caffe.Layer]blob_vec: [1x15 caffe.Blob]inputs: {'data'}outputs: {'prob'}name2layer_index: [24x1 containers.Map]name2blob_index: [15x1 containers.Map]layer_names: {24x1 cell}blob_names: {15x1 cell} 這里額外提一下,net 通過"." 能顯示的東西,除了上面輸出的這些properties外,還有Net.m中定義的函數function self = Net(varargin) function layer = layers(self, layer_name) function blob = blobs(self, blob_name) function blob = params(self, layer_name, blob_index) function forward_prefilled(self) function backward_prefilled(self) function res = forward(self, input_data) function res = backward(self, output_diff) function copy_from(self, weights_file) function reshape(self) function save(self, weights_file)我們在matlab中進行操作的基礎也就是這些函數,當然還有其它的,以后慢慢接觸吧。
二、輸入數據整理
嗯,還是拿這只貓開刀,這只貓一般藏在E:\CaffeDev\caffe-master\examples\images\cat.jpg,沒找到的話直接右鍵保存下方圖片
先說一下過程:①先把均值讀進來
設置在輸入網絡之前需要將圖片resize的大小,一般我們會取圖片長寬最小的那個,其次需要設置的是輸入網絡的圖片的大小,注意與deploy.prototxt的輸入一致,比如 name: "CaffeNet" layer {name: "data"type: "Input"top: "data"input_param { shape: { dim: 10 dim: 3 dim: 227 dim: 227 } } }這里關注一下input_param,代表一次輸入十張圖片,每張圖片三通道,每張圖片大小是227*227。此外注意一下,在opencv中,彩色圖像按照BGR存儲,而matlab中讀取的順序一般是RGB。所以對這只貓需要進行如下處理: im_data = im(:, :, [3, 2, 1]); %matlab按照RGB讀取圖片,opencv是BGR,所以需要轉換順序為opencv處理格式 im_data = permute(im_data, [2, 1, 3]); % 原始圖像m*n*channels,現在permute為n*m*channels大小 im_data = single(im_data); % 強制轉換數據為single類型 im_data = imresize(im_data, [IMAGE_DIM IMAGE_DIM], 'bilinear'); % 線性插值resize圖像 注意一下你在訓練的train.prototxt中的預處理部分
transform_param {mirror: truecrop_size: 227mean_file: "data/ilsvrc12/imagenet_mean.binaryproto"}這里最后一行代表進行了零均值處理,關于這一部分參數,可以看我前面用classification,exe手寫識別時候提到的那個博客,介紹了如何看這一部分進行了怎樣的預處理。
先零均值化一下,然后按照deploy和train的prototxt,將這只貓crop(分成)十塊,采用的是classification.demo的分割方法,分別取貓的上下左右四個角以及中心的大小為deploy中提到的227*227大小。這是五個,然后再對圖片翻轉180°;合起來就是代表這只貓的十張圖片:
im_data = im_data - mean_data; % 零均值 crops_data = zeros(CROPPED_DIM, CROPPED_DIM, 3, 10, 'single');%注意此處是因為prototxt的輸入大小為寬*高*通道數*10 indices = [0 IMAGE_DIM-CROPPED_DIM] + 1;%獲得十塊每一塊大小與原始圖像大小差距,便于crops %下面就是如何將一張圖片crops成十塊 n = 1; %此處兩個for循環并非是1:indices,而是第一次取indices(1),然后是indices(2),每一層循環兩次 %分別讀取圖片四個角大小為CROPPED_DIM*CROPPED_DIM的圖片 for i = indicesfor j = indicescrops_data(:, :, :, n) = im_data(i:i+CROPPED_DIM-1, j:j+CROPPED_DIM-1, :);%產生四個角的cropdata,1 2 3 4crops_data(:, :, :, n+5) = crops_data(end:-1:1, :, :, n);%翻轉180°來一次,產生四個角的翻轉cropdata,6 7 8 9n = n + 1;end end center = floor(indices(2) / 2) + 1; %以中心為crop_data左上角頂點,讀取CROPPED_DIM*CROPPED_DIM的塊 crops_data(:,:,:,5) = ...im_data(center:center+CROPPED_DIM-1,center:center+CROPPED_DIM-1,:); %與for循環里面一樣,翻轉180°,繞左邊界翻轉 crops_data(:,:,:,10) = crops_data(end:-1:1, :, :, 5);可視化看看長啥樣: clear clc close all %caffenet的解讀:http://www.2cto.com/kf/201606/515700.html %% 設置網絡 addpath('..') %加入+caffe路徑 caffe.set_mode_cpu();%設置CPU模式 model = '../../models/bvlc_reference_caffenet/deploy.prototxt';%模型 weights = '../../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel';%參數 net=caffe.Net(model,'test');%測試 net.copy_from(weights); %得到訓練好的權重參數 net %顯示net的結構%% 預處理 d = load('../+caffe/imagenet/ilsvrc_2012_mean.mat'); mean_data = d.mean_data;%讀取均值 im = imread('../../examples/images/cat.jpg');%讀取圖片 IMAGE_DIM = 256;%圖像將要resize的大小,建議resize為圖像最小的那個維度 CROPPED_DIM = 227;%最終需要把一張圖片crops成十塊,最終求出每一塊可能的標簽 im_data=im; im_data = im(:, :, [3, 2, 1]); %matlab按照RGB讀取圖片,opencv是BGR,所以需要轉換順序為opencv處理格式 im_data = permute(im_data, [2, 1, 3]); % 原始圖像m*n*channels,現在permute為n*m*channels大小 im_data = single(im_data); % 強制轉換數據為single類型 im_data = imresize(im_data, [IMAGE_DIM IMAGE_DIM], 'bilinear'); % 線性插值resize圖像 % im_data = im_data - mean_data; % 零均值 crops_data = zeros(CROPPED_DIM, CROPPED_DIM, 3, 10, 'single');%注意此處是因為prototxt的輸入大小為寬*高*通道數*10 indices = [0 IMAGE_DIM-CROPPED_DIM] + 1;%獲得十塊每一塊大小與原始圖像大小差距,便于crops %下面就是如何將一張圖片crops成十塊 n = 1; %此處兩個for循環并非是1:indices,而是第一次取indices(1),然后是indices(2),每一層循環兩次 %分別讀取圖片四個角大小為CROPPED_DIM*CROPPED_DIM的圖片 for i = indicesfor j = indicescrops_data(:, :, :, n) = im_data(i:i+CROPPED_DIM-1, j:j+CROPPED_DIM-1, :);%產生四個角的cropdata,1 2 3 4crops_data(:, :, :, n+5) = crops_data(end:-1:1, :, :, n);%翻轉180°來一次,產生四個角的翻轉cropdata,6 7 8 9n = n + 1;end end center = floor(indices(2) / 2) + 1; %以中心為crop_data左上角頂點,讀取CROPPED_DIM*CROPPED_DIM的塊 crops_data(:,:,:,5) = ...im_data(center:center+CROPPED_DIM-1,center:center+CROPPED_DIM-1,:); %與for循環里面一樣,翻轉180°,繞左邊界翻轉 crops_data(:,:,:,10) = crops_data(end:-1:1, :, :, 5);cat_map=zeros(CROPPED_DIM*2,CROPPED_DIM*5,3);%兩行五列展示 cat_num=0; for i=0:1for j=0:4cat_num=cat_num+1cat_map(CROPPED_DIM*i+1:(i+1)*CROPPED_DIM,CROPPED_DIM*j+1:(j+1)*CROPPED_DIM,:)=crops_data(:,:,:,cat_num);end end imshow(uint8(cat_map))看著比較怪的原因在于,中間經過了matlab處理方式到opencv處理方式的轉變,但是我們依舊用matlab輸出了。
三、前向計算
res=net.forward({crops_data}); prob=res{1}; prob1 = mean(prob, 2); [~, maxlabel] = max(prob1);這一步完畢以后,整個網絡就會充滿參數了,權重,特征圖均生成完畢,接下來可視化它們。四、特征圖可視化
4.1、特征圖提取方法
說一下步驟,首先利用net 中的blob_name函數取出與deploy.prototxt對應的?top 名字,顯示一下看看
names=net.blob_namesnames = 'data''conv1''pool1''norm1''conv2''pool2''norm2''conv3''conv4''conv5''pool5''fc6''fc7''fc8''prob'然后利用blob調用get_data()函數獲取我們需要的特征圖的值。注意,每一層的特征圖是四維,看看前三層的特征圖大小:
size(featuremap{1})=227 227 3 10 size(featuremap{2})= 55 55 96 10 size(featuremap{3})= 27 27 96 10結合deploy中每一層的卷積核大小以及步長,利用 (當前層特征圖大小 - 卷積核大小) / 步長+1=下一層特征圖大小,可以推導出每一個featuremap 的前兩維,第三個維度代表的是卷積核個數,featuremap {2}到featuremap {3}是池化了。第四個維度代表最開始輸入了十張圖4.2 部分可視化方法:
這一部分針對指定的第crop_num張圖像在第map_num層進行可視化。注意,這一部分的可視化包含池化層等。
function [ ] = feature_partvisual( net,mapnum,crop_num ) names=net.blob_names; featuremap=net.blobs(names{mapnum}).get_data();%獲取指定層的特征圖 [m_size,n_size,num,crop]=size(featuremap);%獲取特征圖大小,長*寬*卷積核個數*通道數 row=ceil(sqrt(num));%行數 col=row;%列數 feature_map=zeros(n_size*col,m_size*row);%特征圖的寬實際是matlab讀取圖像的高 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調用方法: mapnum=1;%第幾層的feature☆☆☆☆☆☆☆☆ crop_num=1;%第幾個crop的特征圖☆☆☆☆☆☆☆☆ feature_partvisual( net,mapnum,crop_num )
讀者可以更改"☆"標志的行中的數值去提取不同crop圖像的不同層特征圖。
第一層特征圖:
第二層featuremap:
4.3、全部可視化
這一部分可視化每一張輸入圖片在指定卷積層的特征圖,按照每一行為存儲圖片的特征圖為圖例。
function [ ] = feature_fullvisual( net,mapnum ) names=net.blob_names; featuremap=net.blobs(names{mapnum}).get_data();%獲取指定層的特征圖 [m_size,n_size,num,crop]=size(featuremap)%獲取特征圖大小,長*寬*卷積核個數*圖片個數 row=crop;%行數 col=num;%列數 feature_map=zeros(n_size*colm_size*row); for i=0:row-1for j=0:col-1feature_map(j*n_size+1:(j+1)*n_size,i*m_size+1:(i+1)*m_size)=(mapminmax(featuremap(:,:,j+1,i+1),0,1)*255)';end end figure imshow(uint8(feature_map)) str=strcat('feature map num:',num2str(row*col)); title(str) end調用方法 mapnum=2;%第幾層的feature☆☆☆☆☆☆☆☆ feature_fullvisual( net,mapnum )第一層:
第二層:基本看不清楚了,十張輸入圖片,每一張都有96個特征圖,不好顯示
五、卷積核可視化
【注】卷積核可視化中,采用的像素放大方法與特征圖的不一樣。特征圖中采用歸一化mapminmax到(0,1)范圍,然后乘以255,;在下面卷積核的可視化中采用(x-最小值)/最大值*255的方法去放大像素。讀者可根據自己喜好決定。
5.1、權重提取方法
先建議讀者去看看多通道卷積的概念:http://blog.csdn.net/u014114990/article/details/51125776,不看也行,注意這句話,每一個通道的卷積核是不一樣的,同一個卷積核只在同一個特征圖中共享,應該理解的沒錯吧o(╯□╰)o。
通過net 的layer_names 函數能夠獲取deploy.txt 對應的name 的名稱,每一個name的blob對應兩個值,分別是權重和偏置,提取方法如下:
【注】貌似僅僅卷積核能夠獲取到權重,池化層倒是沒有權重,全連接部分也是有權重的,但是沒什么意義
layers=net.layer_names; convlayer=[]; for i=1:length(layers)if strcmp(layers{i}(1:3),'con')%僅僅卷積核能獲取到權重convlayer=[convlayer;layers{i}];end end w=cell(1,length(convlayer));%存儲權重 b=cell(1,length(convlayer));%存儲偏置 for i=1:length(convlayer)w{i}=net.layers(convlayer(i,:)).params(1).get_data();b{i}=net.layers(convlayer(i,:)).params(2).get_data(); end提取完畢以后觀察一下每一層的權重維度,發現也是四維,顯示一下前三個卷積核的維度:size(w{1})= 11 11 3 96 size(w{2})= 5 5 48 256 size(w{3})= 3 3 256 384
前兩個維度不說了,卷積核的大小,第三個維度代表卷積核的左邊,也就是上一層的特征圖的個數(對應前面說的每一個通道對應不同卷積核),第四個維度代表每一個通道對應的卷積核個數(也就是卷積核右邊下一層的特征圖的個數)。
【注】稍微說一下為什么`conv1`的輸出大小是96, 而到了`conv2`的輸入時大小為48呢?因為`conv2`有個參數叫`group`, 相當于兩兩合一起了, 你看`conv3`肯定就沒有設置`group', 所以輸入才是和`conv2`的輸出大小一樣都是256
5.2、部分可視化方法
那么我們可視化也是可選的,需要選擇哪一個特征圖對應的卷積核,可視化方法如下:
function [ ] = weight_partvisual( net,layer_num ,channels_num ) layers=net.layer_names; convlayer=[]; for i=1:length(layers)if strcmp(layers{i}(1:3),'con')convlayer=[convlayer;layers{i}];end end w=net.layers(convlayer(layer_num,:)).params(1).get_data(); b=net.layers(convlayer(layer_num,:)).params(2).get_data(); minval=min(min(min(min(w)))); maxval=max(max(max(max(w)))); w=(w-minval)/maxval*255;weight=w(:,:,channels_num,:);%四維,核長*核寬*核左邊輸入*核右邊輸出(核個數) [kernel_r,kernel_c,input_num,kernel_num]=size(w); map_row=ceil(sqrt(kernel_num));%行數 map_col=map_row;%列數 weight_map=zeros(kernel_r*map_row,kernel_c*map_col); kernelcout_map=1; for i=0:map_row-1for j=0:map_col-1if kernelcout_map<=kernel_numweight_map(i*kernel_r+1+i:(i+1)*kernel_r+i,j*kernel_c+1+j:(j+1)*kernel_c+j)=weight(:,:,:,kernelcout_map);kernelcout_map=kernelcout_map+1;endend end figure imshow(uint8(weight_map)) str1=strcat('weight num:',num2str(kernelcout_map-1)); title(str1)end調用方法
layer_num=1;%想看哪一個卷積核對應的權重☆☆☆☆☆☆☆☆☆☆ channels_num=1;%想看第幾個通道對應的卷積核 weight_partvisual( net,layer_num ,channels_num )看看效果:
第一個卷積層的第一個通道對應的卷積核:
第二個卷積層的第一個通道對應的卷積核:
2017.3.4更新日志:
謝謝?TensorSense指出的代碼錯誤,上面說過避免像素值過小的計算方法是通過減去最最小值除以最大值以后乘以255,但是代碼卻寫成了
w=w-min(min(min(min(w)))); w=w/max(max(max(max(w))))*255; 在此做一下更正,應該是 minval=min(min(min(min(w)))); maxval=max(max(max(max(w)))); w=(w-minval)/maxval*255;當然也可以用全部可視化中提到的MATLAB自帶函數mapminmanx函數進行歸一化。
5.3、全部可視化
將指定卷積層對應的每一個特征圖的全部卷積核畫出
function [ ] = weight_fullvisual( net,layer_num ) layers=net.layer_names; convlayer=[]; for i=1:length(layers)if strcmp(layers{i}(1:3),'con')convlayer=[convlayer;layers{i}];end endweight=net.layers(convlayer(layer_num,:)).params(1).get_data();%四維,核長*核寬*核左邊輸入*核右邊輸出(核個數) b=net.layers(convlayer(layer_num,:)).params(2).get_data(); minval=min(min(min(min(w)))); maxval=max(max(max(max(w)))); w=(w-minval)/maxval*255;[kernel_r,kernel_c,input_num,kernel_num]=size(weight); map_row=input_num;%行數 map_col=kernel_num;%列數 weight_map=zeros(kernel_r*map_row,kernel_c*map_col); for i=0:map_row-1for j=0:map_col-1weight_map(i*kernel_r+1+i:(i+1)*kernel_r+i,j*kernel_c+1+j:(j+1)*kernel_c+j)=weight(:,:,i+1,j+1);end end figure imshow(uint8(weight_map)) str1=strcat('weight num:',num2str(map_row*map_col)); title(str1)end第一層:
第二層:
附上所有代碼:
主函數
<pre name="code" class="cpp">clear clc close all %caffenet的解讀:http://www.2cto.com/kf/201606/515700.html %% 設置網絡 addpath('..') %加入+caffe路徑 caffe.set_mode_cpu();%設置CPU模式 model = '../../models/bvlc_reference_caffenet/deploy.prototxt';%模型 weights = '../../models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel';%參數 net=caffe.Net(model,'test');%測試 net.copy_from(weights); %得到訓練好的權重參數 net %顯示net的結構%% 預處理 d = load('../+caffe/imagenet/ilsvrc_2012_mean.mat'); mean_data = d.mean_data;%讀取均值 im = imread('../../examples/images/cat.jpg');%讀取圖片 IMAGE_DIM = 256;%圖像將要resize的大小,建議resize為圖像最小的那個維度 CROPPED_DIM = 227;%最終需要把一張圖片crops成十塊,最終求出每一塊可能的標簽 im_data=im; im_data = im(:, :, [3, 2, 1]); %matlab按照RGB讀取圖片,opencv是BGR,所以需要轉換順序為opencv處理格式 im_data = permute(im_data, [2, 1, 3]); % 原始圖像m*n*channels,現在permute為n*m*channels大小 im_data = single(im_data); % 強制轉換數據為single類型 im_data = imresize(im_data, [IMAGE_DIM IMAGE_DIM], 'bilinear'); % 線性插值resize圖像 % im_data = im_data - mean_data; % 零均值 crops_data = zeros(CROPPED_DIM, CROPPED_DIM, 3, 10, 'single');%注意此處是因為prototxt的輸入大小為寬*高*通道數*10 indices = [0 IMAGE_DIM-CROPPED_DIM] + 1;%獲得十塊每一塊大小與原始圖像大小差距,便于crops %下面就是如何將一張圖片crops成十塊 n = 1; %此處兩個for循環并非是1:indices,而是第一次取indices(1),然后是indices(2),每一層循環兩次 %分別讀取圖片四個角大小為CROPPED_DIM*CROPPED_DIM的圖片 for i = indicesfor j = indicescrops_data(:, :, :, n) = im_data(i:i+CROPPED_DIM-1, j:j+CROPPED_DIM-1, :);%產生四個角的cropdata,1 2 3 4crops_data(:, :, :, n+5) = crops_data(end:-1:1, :, :, n);%翻轉180°來一次,產生四個角的翻轉cropdata,6 7 8 9n = n + 1;end end center = floor(indices(2) / 2) + 1; %以中心為crop_data左上角頂點,讀取CROPPED_DIM*CROPPED_DIM的塊 crops_data(:,:,:,5) = ...im_data(center:center+CROPPED_DIM-1,center:center+CROPPED_DIM-1,:); %與for循環里面一樣,翻轉180°,繞左邊界翻轉 crops_data(:,:,:,10) = crops_data(end:-1:1, :, :, 5);%% 展示被crop的圖需要im_data = im_data - mean_data注釋 cat_map=zeros(CROPPED_DIM*2,CROPPED_DIM*5,3);%兩行五列展示 cat_num=0; for i=0:1for j=0:4cat_num=cat_num+1cat_map(CROPPED_DIM*i+1:(i+1)*CROPPED_DIM,CROPPED_DIM*j+1:(j+1)*CROPPED_DIM,:)=crops_data(:,:,:,cat_num);end end figure imshow(uint8(cat_map)) %% 前向計算 res=net.forward({crops_data}); prob=res{1}; prob1 = mean(prob, 2); [~, maxlabel] = max(prob1); %% 觀察網絡結構,獲取特征圖 %注意blob_names和layer_names的區別 %獲取特征圖,特征圖就是prototxt的每一層top名字 mapnum=2;%第幾層的feature☆☆☆☆☆☆☆☆ crop_num=1;%第幾個crop的特征圖☆☆☆☆☆☆☆☆ feature_partvisual( net,mapnum,crop_num ) feature_fullvisual( net,mapnum )%% 觀察網絡結構,獲取權重 %看多通道卷積解釋http://blog.csdn.net/u014114990/article/details/51125776 %中間group過一次,96分兩兩group變成48,第56行 %獲取權重,每一層的layer_names存儲了權重和偏置,第一個參數是權重,第二個參數是偏置 layer_num=2;%想看哪一層的權重☆☆☆☆☆☆☆☆☆☆ channels_num=1;%想看第幾個通道對應的卷積核 weight_partvisual( net,layer_num ,channels_num ) weight_fullvisual( net,layer_num )部分特征圖可視化: function [ ] = feature_partvisual( net,mapnum,crop_num ) names=net.blob_names; featuremap=net.blobs(names{mapnum}).get_data();%獲取指定層的特征圖 [m_size,n_size,num,crop]=size(featuremap);%獲取特征圖大小,長*寬*卷積核個數*通道數 row=ceil(sqrt(num));%行數 col=row;%列數 feature_map=zeros(m_size*row,n_size*col); cout_map=1; for i=0:row-1for j=0:col-1if cout_map<=numfeature_map(i*m_size+1:(i+1)*m_size,j*n_size+1:(j+1)*n_size)=(mapminmax(featuremap(:,:,cout_map,crop_num),0,1)*255)';cout_map=cout_map+1;endend end figure;imshow(uint8(feature_map)) str=strcat('feature map num:',num2str(cout_map-1)); title(str) end全部特征圖可視化: function [ ] = feature_fullvisual( net,mapnum ) names=net.blob_names; featuremap=net.blobs(names{mapnum}).get_data();%獲取指定層的特征圖 [m_size,n_size,num,crop]=size(featuremap)%獲取特征圖大小,長*寬*卷積核個數*圖片個數 row=crop;%行數 col=num;%列數 feature_map=zeros(m_size*row,n_size*col); for i=0:row-1for j=0:col-1feature_map(i*m_size+1:(i+1)*m_size,j*n_size+1:(j+1)*n_size)=(mapminmax(featuremap(:,:,j+1,i+1),0,1)*255)';end end figure imshow(uint8(feature_map)) str=strcat('feature map num:',num2str(row*col)); title(str) end部分卷積核可視化: function [ ] = weight_visual( net,layer_num ,channels_num ) layers=net.layer_names; convlayer=[]; for i=1:length(layers)if strcmp(layers{i}(1:3),'con')convlayer=[convlayer;layers{i}];end end w=net.layers(convlayer(layer_num,:)).params(1).get_data(); b=net.layers(convlayer(layer_num,:)).params(2).get_data(); minval=min(min(min(min(w)))); maxval=max(max(max(max(w)))); w=(w-minval)/maxval*255; [kernel_r,kernel_c,input_num,kernel_num]=size(w); map_row=ceil(sqrt(kernel_num));%行數 map_col=map_row;%列數 weight_map=zeros(kernel_r*map_row,kernel_c*map_col); kernelcout_map=1; for i=0:map_row-1for j=0:map_col-1if kernelcout_map<=kernel_numweight_map(i*kernel_r+1+i:(i+1)*kernel_r+i,j*kernel_c+1+j:(j+1)*kernel_c+j)=weight(:,:,:,kernelcout_map);kernelcout_map=kernelcout_map+1;endend end figure imshow(uint8(weight_map)) str1=strcat('weight num:',num2str(kernelcout_map-1)); title(str1)end全部卷積核可視化: function [ ] = weight_fullvisual( net,layer_num ) layers=net.layer_names; convlayer=[]; for i=1:length(layers)if strcmp(layers{i}(1:3),'con')convlayer=[convlayer;layers{i}];end endweight=net.layers(convlayer(layer_num,:)).params(1).get_data();%四維,核長*核寬*核左邊輸入*核右邊輸出(核個數) b=net.layers(convlayer(layer_num,:)).params(2).get_data(); minval=min(min(min(min(weight)))); maxval=max(max(max(max(weight)))); weight=(weight-minval)/maxval*255;[kernel_r,kernel_c,input_num,kernel_num]=size(weight); map_row=input_num;%行數 map_col=kernel_num;%列數 weight_map=zeros(kernel_r*map_row,kernel_c*map_col); for i=0:map_row-1for j=0:map_col-1weight_map(i*kernel_r+1+i:(i+1)*kernel_r+i,j*kernel_c+1+j:(j+1)*kernel_c+j)=weight(:,:,i+1,j+1);end end figure imshow(uint8(weight_map)) str1=strcat('weight num:',num2str(map_row*map_col)); title(str1)end
六、全連接探討
這里初步探索一下CaffeNet 的最后一個池化層pool5到第一個全連接層fc6的連接,我最開始的理解是直接把pool層所有的單元拉成一個列向量,不過分析以后,感覺應該是類似BP,pool5先被拉成一個一維向量,然后利用權重連接到fc6層的所有單元上,類似二部圖的連接方法。
實驗過程如下:
①首先提取出pool5的特征圖大小
K>> A=net.blobs('pool5').get_data(); K>> size(A)ans =6 6 256 10可以發現對于每一個輸入圖片(總共十張)都有256個6*6大小的特征圖。預先計算一下256*6*6=9216
②然后提取出fc6的特征圖大小 K>> B=net.blobs('fc6').get_data(); K>> size(B)ans =4096 10然后發現pool5到fc6的連接并不是簡單的拉成一維向量,而是利用了一個9216*4096的權重去將pool5的特征映射到fc6的單元中③驗證一下是否如所想的映射方法, 只需要看看pool5到fc6的權重大小即可
K>> C=net.layers('fc6').params(1).get_data(); K>> size(C)ans =9216 4096發現果真如此,所以池化層到全連接層的確是用了一次映射而非簡單的拉成向量最后貼三個關于softmax分類器的地址:
http://blog.csdn.net/hlx371240/article/details/40201499
https://wugh.github.io/posts/2016/02/cs224d-notes2-softmax-classification-and-window-classification/?utm_source=tuicool&utm_medium=referral
http://deeplearning.stanford.edu/wiki/index.php/Exercise:Softmax_Regression
【特別提示】記得搜一些大牛的代碼看看,我自己理解可能部分有問題哦~~~~如果讀者發現錯誤,謝謝指正
總結
以上是生活随笔為你收集整理的【caffe-matlab】权重以及特征图的可视化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中国银行信用卡制卡中可以查额度吗?卡片没
- 下一篇: 【caffe-matlab】使用matl