[caffe解读] caffe从数学公式到代码实现5-caffe中的卷积
今天要講的就是跟卷積相關的一些layer了
im2col_layer.cpp
base_conv_layer.cpp
conv_layer.cpp
deconv_layer.cpp
inner_product_layer.cpp
01
im2col_layer.cpp
這是caffe里面的重要操作,caffe為什么這么耗顯存,跟這個有很大關系。im2col的目的,就是把要滑動卷積的圖像,先一次性存起來,然后再進行矩陣乘操作。簡單來說,它的輸入是一個C*H*W的blob,經過im2col操作會變成K' x (H x W) 的矩陣,其中K' =C*kernel_r*kernel_r,kernel_r就是卷積核的大小,這里只看正方形的卷積核。
如果不用這樣的操作,賈揚清有一個吐槽,對于輸入大小為W*H,維度為D的blob,卷積核為M*K*K,那么如果利用for循環,會是這樣的一個操作,6層for循環,計算效率是極其低下的。
for w in 1..W
具體im2col是什么原理呢?先貼出賈揚清的回答,https://www.zhihu.com/question/28385679
上面說了,要把C*H*W的blob,變成K' x (H x W)或者 (H x W) xK' 的矩陣,把filters也復制成一個大矩陣,這樣兩者直接相乘就得到結果,下面看一個簡單小例子。
借用網友一張圖,雖然和caffe細節上不同,但是還是有助于理解。http://blog.csdn.net/mrhiuser/article/details/52672824
4*4的原始數據,進行stride=1的3*3操作,其中im2col的操作就是:
也就是說4*4的矩陣,經過了im2col后,變成了9*4的矩陣,卷積核可以做同樣擴展,卷積操作就變成了兩個矩陣相乘。
下面看im2col的代碼;
template <typename Dtype>
相關注釋已經放在了上面,col2im的操作非常類似,可以自行看源碼,這一段要自己寫出來怕是需要調試一些時間。
有了上面的核心代碼后,Forward只需要調用im2col,輸入為bottom_data,輸出為top_data,Backward只需要調用col2im,輸入為top_diff,輸出為bottom_diff即可,代碼就不貼出了。
02
conv_layer.cpp,base_conv_layer.cpp
數學定義不用說,我們直接看代碼,這次要兩個一起看。由于conv_layer.cpp依賴于base_conv_layer.cpp,我們先來看看base_conv_layer.hpp中包含了什么東西,非常多。
base_conv_layer.hpp變量:
/// @brief The spatial dimensions of a filter kernel.
非常之多,因為卷積發展到現在,已經有很多的參數需要控制。無法一一解釋了,stride_,pad_,dilation是和卷積步長有關參數,kernel_shape_是卷積核大小,conv_input_shape_是輸入大小,output_shape是輸出大小,其他都是以后遇到了再說,現在我們先繞過。更具體的解答,有一篇博客可以參考
http://blog.csdn.net/lanxuecc/article/details/53188738
下面直接看conv_layer.cpp。既然是卷積,輸出的大小就取決于很多參數,所以先要計算輸出的大小。
void ConvolutionLayer<Dtype>::compute_output_shape() {
然后,在forward函數中,
template <typename Dtype>
我們知道卷積層的輸入,是一個blob,輸出是一個blob,從上面代碼知道卷積核的權重存在了this->blobs_[0]->cpu_data()中, this->blobs_[1]->cpu_data()則是bias,當然不一定有值。外層循環大小為bottom.size(),可見其實可以有多個輸入。
看看里面最核心的函數,this>forward_cpu_gemm。
輸入input,輸出col_buff,關于這個函數的解析,https://tangxman.github.io/2015/12/07/caffe-conv/解釋地挺詳細,我大概總結一下。
首先,按照調用順序,對于3*3等正常的卷積,forward_cpu_gemm會調用conv_im2col_cpu函數(在base_conv_layer.hpp中),它的作用看名字就知道,將圖像先轉換為一個大矩陣,將卷積核也按列復制成大矩陣;
然后利用caffe_cpu_gemm計算矩陣相乘得到卷積后的結果。
template <typename Dtype>
反向傳播:
template <typename Dtype>
略去bias,從上面源碼可以看出,有this->weight_cpu_gemm和this->backward_cpu_gemm兩項。
this->backward_cpu_gemm是計算bottom_data的反向傳播的,也就是feature map的反向傳播。
template <typename Dtype>
}
weight_cpu_gemm是計算權重的反向傳播的;
template <typename Dtype>
其中諸多細節,看不懂就再去看源碼,一次看不懂就看多次。
03
?deconv_layer.cpp
卷積,就是將下圖轉換為上圖,一個輸出像素,和9個輸入像素有關。反卷積則反之,計算反卷積的時候,就是把上圖輸入的像素乘以卷積核,然后放在下圖對應的輸出各個位置,移動輸入像素,最后把所有相同位置的輸出相加。
template <typename Dtype>
forward直接調用了backward_cpu_gemm函數,反向的時候就直接調用forward函數,這里肯定是需要反復去理解的,一次不懂就多次。
template <typename Dtype>
04
inner_product_layerfilter.hpp
既然卷積層已經讀過了,現在該讀一讀全連接層了。
全連接層和卷積層的區別是什么?就是沒有局部連接,每一個輸出都跟所有輸入有關,如果輸入feature map是H*W,那么去卷積它的核也是這么大,得到的輸出是一個1*1的值。
它在setup函數里面要做一些事情,其中最重要的就是設定weights的尺寸,下面就是關鍵代碼。num_output是一個輸出標量數,比如imagenet1000類,最終輸出一個1000維的向量。
K是一個樣本的大小,當axis=1,實際上就是把每一個輸入樣本壓縮成一個數,C*H*W經過全連接變成1個數。
const int num_output = this->layer_param_.inner_product_param().num_output();
所以,weight的大小就是N*K_。
有了這個之后,forward就跟conv_layer是一樣的了。
好了,這一節雖然沒有復雜的公式,但是很多東西夠大家喝一壺了,得仔細推敲才能好好理解的。caffe_cpu_gemm是整節計算的核心,感興趣的去看吧!
同時,在我的知乎專欄也會開始同步更新這個模塊,歡迎來交流
https://zhuanlan.zhihu.com/c_151876233
注:部分圖片來自網絡
—END—
打一個小廣告,我的攝影中的圖像基礎技術公開課程《AI 程序員碼說攝影圖像基礎》上線了,主要從一個圖像處理工程師的角度來說說攝影中的基礎技術和概念,歡迎大家訂閱交流。
加入我們做點趣事
微信
Longlongtogo
公眾號內容
1 心路分享|2 攝影知識|3 深度學習
往期精彩
?
[caffe解讀] caffe從數學公式到代碼實現4-認識caffe自帶的7大loss。
?
如何步入深度學習刷榜第一重境界。
總結
以上是生活随笔為你收集整理的[caffe解读] caffe从数学公式到代码实现5-caffe中的卷积的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [caffe解读] caffe从数学公式
- 下一篇: 【从caffe到Tensorflow 1