卷積神經網絡中卷積的OpenCL實現
==============================================================
目錄
==============================================================
本文主要介紹深度學習中卷積,以及卷積核的OpenCL實現。
另外:新版對齊方式都沒了,莫名其妙。
1、卷積
在CNN(Convolutional Neural Network, 卷積神經網絡)中,卷積是個必不可少的部件,該網絡也因卷積得名。CNN的卷積層中,有許多濾波器,這些濾波器也稱爲卷積核,用於卷積運算,提取數據特徵,這些濾波器的係數由訓練得到。
在數字信號處理中,信號x(n)經過系統,相當於信號與系統的衝激響應函數h(n)進行卷積,得到輸出y(n),其定義如下式。
上式中的符號“*”是卷積的意思。
可以看出,信號x(n)經過系統,與衝擊響應函數序列經過縱軸翻折過的序列對應相乘。
而在卷積神經網絡中卷積操作的原理即卷積核與對應的點對應相乘,核心即是乘累加,
卷積的方式比較多,假定濾波器的滑動步長stride(用s表示),以及濾波器的填充方式(填充長度用p表示),原始特徵圖W*H(寬W,高H),濾波器的尺寸用K*K表示,則輸出特徵圖的尺寸爲
上式中括號爲下取整。
這個關係暫時沒想通也沒關係,可以搜索卷積神經網絡卷積瞭解一下,卷積的核心即乘累加,如下爲卷積圖解。
圖1. 卷積示例
上圖中左側爲特徵圖與卷積核卷積,右側爲輸出,其中卷積的步長stride爲1,濾波器滑動方向先進行行處理,再進行列處理。
如右側的最左上角的數 3= 2*1 + 3*(-1) + 4 + 6*0;
隨後濾波器向右滑動1個單位,此時濾波器對應的數據爲[3,7,6,3],所以3*1 + 7*(-1) + 6*1 +3*0 = 2,其他同理。
行處理完成,濾波器向下移動一個單位,接着處理下一行,此時與之對應的數爲[4,6,12,0],所以4*1 + 6*(-1) + 12*1 + 0*0 = 10。其他同理。
圖2. 特徵圖填充,卷積示例
圖2中的卷積與圖1不同在於,是否對原始特徵圖進行填充,填充可以保留特徵圖的邊緣信息,不至於在卷積過程中丟失邊緣信息。以上是其中一種填充方式,在特徵圖的周圍環繞填充0,本例中忽略頂部及左側的0值,保留右側以及底層的邊緣信息,使輸出特徵圖大小與原始的特徵圖大小相等。
卷積以及更多填充方式的詳細介紹可以參看如下鏈接:
Convolution arithmetic tutorial:
http://deeplearning.net/software/theano/tutorial/conv_arithmetic.html
A guide to convolution arithmetic for deep learning:
https://arxiv.org/pdf/1603.07285v1.pdf
2、卷積與圖像處理
瞭解了卷積之後,如何在OpenCL中實現它呢?首先,一個單進程程序,使用一個卷積覈對一張特徵圖進行處理,與多個並行進程,使用共享的卷積覈對一張特徵圖進行處理相對比,顯然後者計算速度快些。這就是加速,正是時代潮流上的加速。
假設有一個已經訓練好的CNN網絡用於識別著名的Lena,原始圖片如下,寬高分別爲W和H:
圖 3. 名模Lena
卷積核如下,寬高均爲K:
圖 4. K*K卷積核
現對圖片進行周圍零填充,保持經過卷積後的輸出圖像與原始圖像大小一致,假設保留右側和底側邊緣信息,只對這兩側進行填充。(爲什麼保留右側及底側,其實如果你保留頂部和左側邊緣,又想輸出圖像大小一致,可能右側和底側的邊緣信息也保留不了,在這我單純只是爲了好運算)。
圖 5. 填充後的圖大小(W+K-1)*(W+K-1)
如果使用多個進程處理卷積操作,每個進程輸入對應的濾波器係數和圖像數據,對於每個進程,濾波器係數一致,可以共享;數據來自圖像的不同位置,數據塊大小爲K*K。在深度學習中,訓練完成的網絡抽象了數據特徵,得到不同特徵的卷積核(濾波器),所以爲了直觀,本文將使用Sobel算子作爲卷積核,K=3,可以用於圖像邊緣檢測,生成邊緣特徵圖。
Sobel算子的原理參考:
https://blog.csdn.net/qq_29540745/article/details/51918004
圖 6. 水平梯度和垂直梯度卷積因子
3、卷積的OpenCL實現
Sobel算子與圖像卷積的Matlab程序,我已放入Github中,如下:
該代碼與OpenCL中需要實現的卷積邏輯一致:
(1)圖像數據與卷積核在存儲器中是連續的,一維數組;
(2)輸出特徵圖也是連續的一維數組。
圖 7. Matlab輸出水平梯度特徵圖
圖 8. Matlab輸出垂直梯度特徵圖
圖 9. Matlab輸出圖像卷積圖
在OpenCL中將使用W*H個工作項來完成卷積操作,工作項ID爲(x, y),x和y取值分別爲[0, W-1]和[0, H-1]。
濾波器的座標值爲(kj, ki),kj和ki取值範圍均爲[0, 1, 2]。
座標爲(kj, ki)卷積覈對應的數據座標示意圖如下:
圖 10. 濾波器座標與圖像數據座標
所以,濾波器座標(kj, ki)對應的存儲索引值爲[kj + K*ki];
圖像數據對應的存儲索引值爲[( y + ki ) * Wn + x + kj],其中Wn=W+K-1;因爲Matlab的首個數據索引爲1,但在C語言中首個索引值爲0,所以該處與上文Matlab代碼中索引值略微不同。
以下爲OpenCL的核函數Conv2D.cl:
https://github.com/yywyz/OpenCL-Programming-Examples/blob/master/CNN%20Convolution/Conv2D.cl
/**********************************************
function: 2D Convolution of CNN
date : 2018/07/21
**********************************************/
__kernel void Conv2D( __global int * image_in, //image input
__global int * filter_in, //filter input
int K, //filter kernel size
__global int * image_out) //feature map output
{
int W; //work group global size
int Wn; //padded image width
int x; //global id x
int y; //global id y
int ki, kj; //filter coordinate,(kj, ki)
int sum = 0; //multiply and sum of filter and data
W = get_global_size(0);
x = get_global_id(0);
y = get_global_id(1);
Wn = W + (K - 1);
for(ki=0; ki<K; ki++)
for(kj=0; kj<K; kj++)
{
sum = sum + filter_in[ki*K + kj] * image_in[Wn*(y+ki) + x + kj];
}
image_out[y*W + x] = sum;
}
OpenCL主函數main.cpp:
https://github.com/yywyz/OpenCL-Programming-Examples/blob/master/CNN%20Convolution/main.cpp
圖 11. 隨機數Sobel水平梯度卷積的結果顯示
將輸出圖像導入到Matlab中顯示即可作爲驗證,本代碼中沒有讀取和生成圖片功能,鑑於圖片太大,存入內存太大,且只進行了水平梯度特徵圖對隨機數的濾波,但已足以瞭解卷積的並行操作。如需處理文件,可將圖片數據導入,輸出文件已存於程序當前運行目錄的image_out.txt中。
4、總結
本文介紹了卷積,卷積與圖像處理,使用Sobel算子展示lena的水平梯度與垂直梯度的特徵圖,並通過OpenCL實現了卷積核,展示了使用Sobel水平梯度卷積覈對隨機數的卷積操作結果。
如有不當,請您斧正,謝謝。
途次客
2018年7月21日