【轉】數據挖掘系列(10)——卷積神經網絡算法的一個實現

前言

  從理解卷積神經到實現它,前後花了一個月時間,現在也還有一些地方沒有理解透徹,CNN還是有一定難度的,不是看哪個的博客和一兩篇論文就明白了,主要還是靠自己去專研,閱讀推薦列表在末尾的參考文獻。目前實現的CNN在MINIT數據集上效果還不錯,但是還有一些bug,因爲最近比較忙,先把之前做的總結一下,以後再繼續優化。

  卷積神經網絡CNN是Deep Learning的一個重要算法,在很多應用上表現出卓越的效果,[1]中對比多重算法在文檔字符識別的效果,結論是CNN優於其他所有的算法。CNN在手寫體識別取得最好的效果,[2]將CNN應用在基於人臉的性別識別,效果也非常不錯。前段時間我用BP神經網絡對手機拍照圖片的數字進行識別,效果還算不錯,接近98%,但在漢字識別上表現不佳,於是想試試卷積神經網絡。

1、CNN的整體網絡結構

  卷積神經網絡是在BP神經網絡的改進,與BP類似,都採用了前向傳播計算輸出值,反向傳播調整權重和偏置;CNN與標準的BP最大的不同是:CNN中相鄰層之間的神經單元並不是全連接,而是部分連接,也就是某個神經單元的感知區域來自於上層的部分神經單元,而不是像BP那樣與所有的神經單元相連接。CNN的有三個重要的思想架構:

  • 局部區域感知
  • 權重共享
  • 空間或時間上的採樣

  局部區域感知能夠發現數據的一些局部特徵,比如圖片上的一個角,一段弧,這些基本特徵是構成動物視覺的基礎[3];而BP中,所有的像素點是一堆混亂的點,相互之間的關係沒有被挖掘。

  CNN中每一層的由多個map組成,每個map由多個神經單元組成,同一個map的所有神經單元共用一個卷積核(即權重),卷積核往往代表一個特徵,比如某個卷積和代表一段弧,那麼把這個卷積核在整個圖片上滾一下,卷積值較大的區域就很有可能是一段弧。注意卷積核其實就是權重,我們並不需要單獨去計算一個卷積,而是一個固定大小的權重矩陣去圖像上匹配時,這個操作與卷積類似,因此我們稱爲卷積神經網絡,實際上,BP也可以看做一種特殊的卷積神經網絡,只是這個卷積核就是某層的所有權重,即感知區域是整個圖像。權重共享策略減少了需要訓練的參數,使得訓練出來的模型的泛華能力更強。

  採樣的目的主要是混淆特徵的具體位置,因爲某個特徵找出來後,它的具體位置已經不重要了,我們只需要這個特徵與其他的相對位置,比如一個“8”,當我們得到了上面一個"o"時,我們不需要知道它在圖像的具體位置,只需要知道它下面又是一個“o”我們就可以知道是一個'8'了,因爲圖片中"8"在圖片中偏左或者偏右都不影響我們認識它,這種混淆具體位置的策略能對變形和扭曲的圖片進行識別。

  CNN的這三個特點是其對輸入數據在空間(主要針對圖像數據)上和時間(主要針對時間序列數據,參考TDNN)上的扭曲有很強的魯棒性。CNN一般採用卷積層與採樣層交替設置,即一層卷積層接一層採樣層,採樣層後接一層卷積...這樣卷積層提取出特徵,再進行組合形成更抽象的特徵,最後形成對圖片對象的描述特徵,CNN後面還可以跟全連接層,全連接層跟BP一樣。下面是一個卷積神經網絡的示例:

圖1(圖片來源

  卷積神經網絡的基本思想是這樣,但具體實現有多重版本,我參考了matlab的Deep Learning的工具箱DeepLearnToolbox,這裏實現的CNN與其他最大的差別是採樣層沒有權重和偏置,僅僅只對卷積層進行一個採樣過程,這個工具箱的測試數據集是MINIST,每張圖像是28*28大小,它實現的是下面這樣一個CNN:

圖2

2、網絡初始化

  CNN的初始化主要是初始化卷積層和輸出層的卷積核(權重)和偏置,DeepLearnToolbox裏面對卷積核和權重進行隨機初始化,而對偏置進行全0初始化。

3、前向傳輸計算

  前向計算時,輸入層、卷積層、採樣層、輸出層的計算方式不相同。

  3.1 輸入層:輸入層沒有輸入值,只有一個輸出向量,這個向量的大小就是圖片的大小,即一個28*28矩陣;

  3.2 卷積層:卷積層的輸入要麼來源於輸入層,要麼來源於採樣層,如上圖紅色部分。卷積層的每一個map都有一個大小相同的卷積核,Toolbox裏面是5*5的卷積核。下面是一個示例,爲了簡單起見,卷積核大小爲2*2,上一層的特徵map大小爲4*4,用這個卷積在圖片上滾一遍,得到一個一個(4-2+1)*(4-2+1)=3*3的特徵map,卷積核每次移動一步,因此。在Toolbox的實現中,卷積層的一個map與上層的所有map都關聯,如上圖的S2和C3,即C3共有6*12個卷積核,卷積層的每一個特徵map是不同的卷積核在前一層所有map上作卷積並將對應元素累加後加一個偏置,再求sigmod得到的。還有需要注意的是,卷積層的map個數是在網絡初始化指定的,而卷積層的map的大小是由卷積核和上一層輸入map的大小決定的,假設上一層的map大小是n*n、卷積核的大小是k*k,則該層的map大小是(n-k+1)*(n-k+1),比如上圖的24*24的map大小24=(28-5+1)。 斯坦福的深度學習教程更加詳細的介紹了卷積特徵提取的計算過程。                                                                        

  圖3

  3.3 採樣層(subsampling,Pooling:採樣層是對上一層map的一個採樣處理,這裏的採樣方式是對上一層map的相鄰小區域進行聚合統計,區域大小爲scale*scale,有些實現是取小區域的最大值,而ToolBox裏面的實現是採用2*2小區域的均值。注意,卷積的計算窗口是有重疊的,而採用的計算窗口沒有重疊,ToolBox裏面計算採樣也是用卷積(conv2(A,K,'valid'))來實現的,卷積核是2*2,每個元素都是1/4,去掉計算得到的卷積結果中有重疊的部分,即:                                                                

圖4

4、反向傳輸調整權重

  反向傳輸過程是CNN最複雜的地方,雖然從宏觀上來看基本思想跟BP一樣,都是通過最小化殘差來調整權重和偏置,但CNN的網絡結構並不像BP那樣單一,對不同的結構處理方式不一樣,而且因爲權重共享,使得計算殘差變得很困難,很多論文[1][5]和文章[4]都進行了詳細的講述,但我發現還是有一些細節沒有講明白,特別是採樣層的殘差計算,我會在這裏詳細講述。

  4.1輸出層的殘差

  和BP一樣,CNN的輸出層的殘差與中間層的殘差計算方式不同,輸出層的殘差是輸出值與類標值得誤差值,而中間各層的殘差來源於下一層的殘差的加權和。輸出層的殘差計算如下:

公式來源

  這個公式不做解釋,可以查看公式來源,看斯坦福的深度學習教程的解釋。

  4.2 下一層爲採樣層(subsampling)的卷積層的殘差

  當一個卷積層L的下一層(L+1)爲採樣層,並假設我們已經計算得到了採樣層的殘差,現在計算該卷積層的殘差。從最上面的網絡結構圖我們知道,採樣層(L+1)的map大小是卷積層L的1/(scale*scale),ToolBox裏面,scale取2,但這兩層的map個數是一樣的,卷積層L的某個map中的4個單元與L+1層對應map的一個單元關聯,可以對採樣層的殘差與一個scale*scale的全1矩陣進行克羅內克積進行擴充,使得采樣層的殘差的維度與上一層的輸出map的維度一致,Toolbox的代碼如下,其中d表示殘差,a表示輸出值:

net.layers{l}.d{j} = net.layers{l}.a{j} .* (1 - net.layers{l}.a{j}) .* expand(net.layers{l + 1}.d{j}, [net.layers{l + 1}.scale net.layers{l + 1}.scale 1])

  擴展過程:

圖5

  利用卷積計算卷積層的殘差:

圖6

  4.3 下一層爲卷積層(subsampling)的採樣層的殘差

  當某個採樣層L的下一層是卷積層(L+1),並假設我們已經計算出L+1層的殘差,現在計算L層的殘差。採樣層到卷積層直接的連接是有權重和偏置參數的,因此不像卷積層到採樣層那樣簡單。現再假設L層第j個map Mj與L+1層的M2j關聯,按照BP的原理,L層的殘差Dj是L+1層殘差D2j的加權和,但是這裏的困難在於,我們很難理清M2j的那些單元通過哪些權重與Mj的哪些單元關聯,Toolbox裏面還是採用卷積(稍作變形)巧妙的解決了這個問題,其代碼爲:

convn(net.layers{l + 1}.d{j}, rot180(net.layers{l + 1}.k{i}{j}), 'full');
rot180表示對矩陣進行180度旋轉(可通過行對稱交換和列對稱交換完成),爲什麼這裏要對卷積核進行旋轉,答案是:通過這個旋轉,'full'模式下得卷積的正好抓住了前向傳輸計算上層map單元與卷積和及當期層map的關聯關係,需要注意的是matlab的內置函數convn在計算卷積前,會對卷積核進行一次旋轉,因此我們之前的所有卷積的計算都對卷積核進行了旋轉:
複製代碼
a =
     1     1     1
     1     1     1
     1     1     1
k =
     1     2     3
     4     5     6
     7     8     9

>> convn(a,k,'full')
ans =

     1     3     6     5     3
     5    12    21    16     9
    12    27    45    33    18
    11    24    39    28    15
     7    15    24    17     9
複製代碼

   convn在計算前還會對待卷積矩陣進行0擴展,如果卷積核爲k*k,待卷積矩陣爲n*n,需要以n*n原矩陣爲中心擴展到(n+2(k-1))*(n+2(k-1)),所有上面convn(a,k,'full')的計算過程如下:

圖7

實際上convn內部是否旋轉對網絡訓練沒有影響,只要內部保持一致(即都要麼旋轉,要麼都不旋轉),所有我的卷積實現裏面沒有對卷積核旋轉。如果在convn計算前,先對卷積核旋轉180度,然後convn內部又對其旋轉180度,相當於卷積核沒有變。

  爲了描述清楚對卷積核旋轉180與卷積層的殘差的卷積所關聯的權重與單元,正是前向計算所關聯的權重與單元,我們選一個稍微大一點的卷積核,即假設卷積層採用用3*3的卷積核,其上一層採樣層的輸出map的大小是5*5,那麼前向傳輸由採樣層得到卷積層的過程如下:

圖8

  這裏我們採用自己實現的convn(即內部不會對卷積核旋轉),並假定上面的矩陣A、B下標都從1開始,那麼有:

複製代碼
B11 = A11*K11 + A12*K12 + A13*K13 + A21*K21 + A22*K22 + A23*K23 + A31*K31 + A32*K32 + A33*K33
B12 = A12*K11 + A13*K12 + A14*K13 + A22*K21 + A23*K22 + A24*K23 + A32*K31 + A33*K32 + A34*K33
B13 = A13*K11 + A14*K12 + A15*K13 + A23*K21 + A24*K22 + A25*K23 + A33*K31 + A34*K32 + A35*K33
B21 = A21*K11 + A22*K12 + A23*K13 + A31*K21 + A32*K22 + A33*K23 + A41*K31 + A42*K32 + A43*K33
B22 = A22*K11 + A23*K12 + A24*K13 + A32*K21 + A33*K22 + A34*K23 + A42*K31 + A43*K32 + A44*K33
B23 = A23*K11 + A24*K12 + A25*K13 + A33*K21 + A34*K22 + A35*K23 + A43*K31 + A44*K32 + A45*K33
B31 = A31*K11 + A32*K12 + A33*K13 + A41*K21 + A42*K22 + A43*K23 + A51*K31 + A52*K32 + A53*K33
B32 = A32*K11 + A33*K12 + A34*K13 + A42*K21 + A43*K22 + A44*K23 + A52*K31 + A53*K32 + A54*K33
B33 = A33*K11 + A34*K12 + A35*K13 + A43*K21 + A44*K22 + A45*K23 + A53*K31 + A54*K32 + A55*K33
複製代碼

  我們可以得到B矩陣每個單元與哪些卷積核單元和哪些A矩陣的單元之間有關聯:

複製代碼
A11 [K11] [B11]
A12 [K12, K11] [B12, B11]
A13 [K13, K12, K11] [B12, B13, B11]
A14 [K13, K12] [B12, B13]
A15 [K13] [B13]
A21 [K21, K11] [B21, B11]
A22 [K22, K21, K12, K11] [B12, B22, B21, B11]
A23 [K23, K22, K21, K13, K12, K11] [B23, B22, B21, B12, B13, B11]
A24 [K23, K22, K13, K12] [B23, B12, B13, B22]
A25 [K23, K13] [B23, B13]
A31 [K31, K21, K11] [B31, B21, B11]
A32 [K32, K31, K22, K21, K12, K11] [B31, B32, B22, B12, B21, B11]
A33 [K33, K32, K31, K23, K22, K21, K13, K12, K11] [B23, B22, B21, B31, B12, B13, B11, B33, B32]
A34 [K33, K32, K23, K22, K13, K12] [B23, B22, B32, B33, B12, B13]
A35 [K33, K23, K13] [B23, B13, B33]
A41 [K31, K21] [B31, B21]
A42 [K32, K31, K22, K21] [B32, B22, B21, B31]
A43 [K33, K32, K31, K23, K22, K21] [B31, B23, B22, B32, B33, B21]
A44 [K33, K32, K23, K22] [B23, B22, B32, B33]
A45 [K33, K23] [B23, B33]
A51 [K31] [B31]
A52 [K32, K31] [B31, B32]
A53 [K33, K32, K31] [B31, B32, B33]
A54 [K33, K32] [B32, B33]
A55 [K33] [B33]
複製代碼

  然後再用matlab的convn(內部會對卷積核進行180度旋轉)進行一次convn(B,K,'full'),結合圖7,看紅色部分,除去0,A11=B'33*K'33=B11*K11,發現A11正好與K11、B11關聯對不對;我們再看一個A24=B'34*K'21+B'35*K'22+B'44*K'31+B'45*K'32=B12*K23+B13*K22+B22*K13+B23*K12,發現參與A24計算的卷積核單元與B矩陣單元,正好是前向計算時關聯的單元,所以我們可以通過旋轉卷積核後進行卷積而得到採樣層的殘差。

  殘差計算出來後,剩下的就是用更新權重和偏置,這和BP是一樣的,因此不再細究,有問題歡迎交流。

5、代碼實現

  詳細的代碼不再這裏貼了,我依舊放在了github,歡迎參考和指正。我又是在重造車輪了,沒有使用任何第三方的庫類,這裏貼一下調用代碼:

複製代碼
    public static void runCnn() {
        //創建一個卷積神經網絡
        LayerBuilder builder = new LayerBuilder();
        builder.addLayer(Layer.buildInputLayer(new Size(28, 28)));
        builder.addLayer(Layer.buildConvLayer(6, new Size(5, 5)));
        builder.addLayer(Layer.buildSampLayer(new Size(2, 2)));
        builder.addLayer(Layer.buildConvLayer(12, new Size(5, 5)));
        builder.addLayer(Layer.buildSampLayer(new Size(2, 2)));
        builder.addLayer(Layer.buildOutputLayer(10));
        CNN cnn = new CNN(builder, 50);
        
        //導入數據集
        String fileName = "dataset/train.format";
        Dataset dataset = Dataset.load(fileName, ",", 784);
        cnn.train(dataset, 3);//
        String modelName = "model/model.cnn";
        cnn.saveModel(modelName);        
        dataset.clear();
        dataset = null;
        
        //預測
        // CNN cnn = CNN.loadModel(modelName);    
        Dataset testset = Dataset.load("dataset/test.format", ",", -1);
        cnn.predict(testset, "dataset/test.predict");
    }
複製代碼

6、參考文獻

[1].YANN LECUN. Gradient-Based Learning Applied to Document Recognition.

[2].Shan Sung LIEW. Gender classification: A convolutional neural network approach.

[3] D. H. Hubel and T. N. Wiesel, “Receptive fields, binocular interaction teraction,and functional architecture in the cat’s visual cortex,”

[4] tornadomeet. http://www.cnblogs.com/tornadomeet/p/3468450.html.

[5] Jake Bouvrie. Notes on Convolutional Neural Networks.

[6] C++實現的詳細介紹. http://www.codeproject.com/Articles/16650/Neural-Network-for-Recognition-of-Handwritten-Digi

[7] matlab DeepLearnToolbox https://github.com/rasmusbergpalm/DeepLearnToolbox

 

 轉載請註明出處:http://www.cnblogs.com/fengfenggirl

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章