Caffe中的卷積計算是將卷積核矩陣和輸入圖像矩陣變換為兩個大的矩陣A與B,然后A與B進行矩陣相乘得到結果C(利用GPU進行矩陣相乘的高效性),三個矩陣的說明如下:
(1)在矩陣A中
M為卷積核個數,K=k*k,等於卷積核大小,即第一個矩陣每行為一個卷積核向量(是將二維的卷積核轉化為一維),總共有M行,表示有M個卷積核。
(2)在矩陣B中
N=((image_h + 2*pad_h – kernel_h)/stride_h+ 1)*((image_w +2*pad_w – kernel_w)/stride_w + 1)
image_h:輸入圖像的高度
image_w:輸入圖像的寬度
pad_h:在輸入圖像的高度方向兩邊各增加pad_h個單位長度(因為有兩邊,所以乘以2)
pad_w:在輸入圖像的寬度方向兩邊各增加pad_w個單位長度(因為有兩邊,所以乘以2)
kernel_h:卷積核的高度
kernel_w:卷積核的寬度
stride_h:高度方向的滑動步長;
stride_w:寬度方向的滑動步長。
因此,N為輸出圖像大小的長寬乘積,也是卷積核在輸入圖像上滑動可截取的最大特征數。
K=k*k,表示利用卷積核大小的框在輸入圖像上滑動所截取的數據大小,與卷積核大小一樣大。
(3)在矩陣C中
矩陣C為矩陣A和矩陣B相乘的結果,得到一個M*N的矩陣,其中每行表示一個輸出圖像即feature map,共有M個輸出圖像(輸出圖像數目等於卷積核數目)
(在Caffe中是使用src/caffe/util/im2col.cu中的im2col和col2im來完成矩陣的變形和還原操作)
舉個例子(方便理解):
假設有兩個卷積核為與
,因此M=2,kernel_h=2,kernel_w=2,K= kernel_h * kernel_w=4
輸入圖像矩陣為,因此image_h=3,image_w=3,令邊界擴展為0即pad_h=0,pad_w=0,滑動步長為1,即stride_h=1,stride_w=1
故N=[(3+2*0-2)/1+1]*[ (3+2*0-2)/1+1]=2*2=4
A矩陣(M*K)為,B矩陣(K*N)為
C=A*B=*
=
C中的與
分別為兩個輸出特征圖像即featuremap。
在Caffe源碼中,src/caffe/util/math_functions.cu(如果使用CPU則是src/util/math_functions.cpp)中的caffe_gpu_gemm()函數,其中有兩個矩陣A(M*K)
與矩陣 B(K*N),大家可以通過輸出M、K、N的值即相應的矩陣內容來驗證上述的原理,代碼中的C矩陣與上述的C矩陣不一樣,代碼中的C矩陣存儲的是偏置bias,
是A 與B相乘后得到M*N大小的矩陣,然后再跟這個存儲偏置的矩陣C相加完成卷積過程。如果是跑Mnist訓練網絡的話,可以看到第一個卷積層卷積過程中,
M=20,K=25,N=24*24=576。
(caffe中涉及卷積具體過程的文件主要有:src/caffe/layers/conv_layer.cu、src/caffe/layers/base_conv_layer.cpp、 src/caffe/util/math_functions.cu、src/caffe/util/im2col.cu)
另外大家也可以參考知乎上賈揚清大神的回答,幫助理解http://www.zhihu.com/question/28385679
(對於他給出的ppt上的C表示圖像通道個數,如果是RGB圖像則通道數為3,對應於caffe代碼中的變量為src/caffe/layers/base_conv_layer.cpp中
函數forward_gpu_gemm中的group_)
注:如果本人理解有誤,歡迎大家指出。
想象一副圖像尺寸為MxM,卷積核mxm。在計算時,卷積核與圖像中每個mxm大小的圖像塊做element-wise相乘,相當於把該mxm圖像塊提取出來,表示成一個長度為m^2的列,共有多少個這種圖像塊?在不考慮pad和stride的情況下,一共有(M-m+1)^2個,把這么些個圖像塊均表示為m^2的列,然后組合為一個大矩陣(m^2 x (M-m+1)^2)。這里的操作就是img2col中做的事,matlab中就有這種函數。然后把卷積核也表示為m^2向量,並按列復制為同尺寸矩陣(m^2 x (M-m+1)^2)。倆矩陣按列做點積即得結果。
粗略介紹,細節不包完全正確。