DAGNN 是Directed acyclic graph neural network 縮寫,也就有向圖非循環神經網絡。我使用的是由MatConvNet 提供的DAGNN API。選擇這套API作為工具的原因有三點,第一:這是matlab的API,相對其他語言我對Matlab比較熟悉;第二:有向圖非循環的網絡可以實現RPN,Network in Network 等較為復雜的功能,可以隨意的引出各層的輸入和輸出,有利於針對三維視覺任務改造網絡結構。MNIST 是手寫數字的圖片集,也是機器學習網絡最簡單的試金石。
1、定義層
1 conv_layer1 = dagnn.Conv('size',single([5,5,1,30]),'hasBias',true); 2 relu_layer2 = dagnn.ReLU(); 3 4 conv_layer3 = dagnn.Conv('size',single([5,5,30,16]),'hasBias',true); 5 relu_layer4 = dagnn.ReLU(); 6 pooling_layer5 = dagnn.Pooling('poolSize',[2,2],'stride',[2 2]); 7 8 fullConnet_layer6 = dagnn.Conv('size',single([4,4,16,256]),'hasBias',true); 9 relu_layer7 = dagnn.ReLU(); 10 fullConnet_layer8 = dagnn.Conv('size',single([1,1,256,10]),'hasBias',true); 11 SoftMat_layer9 = dagnn.SoftMax(); 12 Loss_layer = dagnn.Loss();
首先是利用API構造各層網絡,定義網絡結構類型。所有的Layer 都繼承自dagnn.Layer類,子類中定義了輸入輸出,前向傳播,反向傳播的行為。
其中包括卷積層,激活層,池化層,Softmax 分類層,以及計算Loss層。值得注意的是全連接層是通過大卷積層來實現的。本質上全連接就是“輸入的等尺寸卷積”。全連接層的作用是將卷積層提取的特征進行高度非線性的映射,將其映射到輸出空間中。
2、定義網絡
1 mynet = dagnn.DagNN(); 2 mynet.addLayer('conv1',conv_layer1,{'input'},{'x2'},{'filters_conv1','bias_conv1'}); 3 mynet.addLayer('relu1',relu_layer2,{'x2'},{'x3'}); 4 mynet.addLayer('pool1',pooling_layer5,{'x3'},{'x4'}); 5 6 mynet.addLayer('conv2',conv_layer3,{'x4'},{'x5'},{'filters_conv2','bias_conv2'}); 7 mynet.addLayer('relu2',relu_layer4,{'x5'},{'x6'}); 8 mynet.addLayer('pool2',pooling_layer5,{'x6'},{'x7'}); 9 10 mynet.addLayer('full1',fullConnet_layer6,{'x7'},{'x8'},{'filters_fc1','bias_fc1'}); 11 mynet.addLayer('relu3',relu_layer7,{'x8'},{'x9'}); 12 mynet.addLayer('full2',fullConnet_layer8,{'x9'},{'x10'},{'filters_fc2','bias_fc2'}); 13 mynet.addLayer('Cls1',SoftMat_layer9,{'x10'},{'pred'}); 14 mynet.addLayer('Loss',Loss_layer,{'pred','label'},{'loss'}); 15 mynet.initParams(); 16 mynet.meta.inputs = {'data',[28,28,1,1]}; 17 mynet.meta.classes.name = {1,2,3,4,5,6,7,8,9,10}; 18 mynet.meta.normalization.imageSize = [28,28,1,1]; 19 mynet.meta.interpolation = 'bicubic';
定義網絡調用了addLayer方法,與其他API的網絡構建方法不同的是,DAGNN的API需要針對每層定義輸入和輸出,以及網絡中的待求得參數。當然,作為初學者我先實現了鏈式網絡,在下周的工作中會嘗試實現Faster R-CNN。
net.addLayer('full1',fullConnet_layer6,{'x7'},{'x8'},{'filters_fc1','bias_fc1'});
以此為例,代表該層的名字是full1 , 該層的結構是fullConnect_layer6,輸入為x7、輸出x8,參數名為filters_fc1 和 bias_fc1。其中loss 層最為特殊,其具有來自softmax層的pred 和 label (ground truth) 兩種輸入。
最重要的是一定要initParams()!!!!這會生成初始參數。
3、定義數據輸入函數
為了訓練網絡,我們需要定義一個輸入函數。數據量小,可存在內存中,但當數據量大的時候全部存在內存里是不現實的,這就需要一個數據輸入函數來對你定義的數據庫進行操作。本例中我僅使用5000幅圖片進行訓練,所以可以把圖片放在內存中。getBatch函數如下所示:
1 function inputs = getBatch(imdb, batch) 2 % -------------------------------------------------------------------- 3 images = imdb.images.data(:,:,:,batch) ; 4 labels = imdb.images.labels(1,batch) ; 5 6 % images = gpuArray(images) ; 7 8 inputs = {'input', images, 'label', labels} ;
其中 imdb 是image data base. 其中包括:
imdb.images.data 圖片 W*H*C*N 的4-D single Array
imdb.images.label 標簽 N*1 的 single Array
imdb.images.data_mean 圖片平均值 用於預處理時去中心
imdb.images.set 集合號 N*1 的 single Array, 其中1 代表訓練集 2 代表測試集 3 代表驗證集
imdb.meta 存放類型名稱等和訓練關系不太密切的東西
4、開始訓練
直接調用 cnn_train_dag 的API 開始對整個集合進行訓練,注意getBatch 輸入的是函數句柄。
cnn_train_dag(mynet,imdb_sub,@getBatch);

訓練了30個epoch,但是learningRate好像給太高了,掉局部最小里了。。。。。。。不過結果不錯,在驗證集中拿到了4998/5000.
