有趣的卷積神經網絡 頂 原 薦

一、前言

    最近一直在研究深度學習,聯想起之前所學,感嘆數學是一門樸素而神奇的科學。F=G*m1*m2/r²萬有引力描述了宇宙星河運轉的規律,E=mc²描述了恆星發光的奧祕,V=H*d哈勃定律描述了宇宙膨脹的奧祕,自然界的大部分現象和規律都可以用數學函數來描述,也就是可以求得一個函數。

    神經網絡(《簡單又複雜的人工神經網絡》)可以逼近任何連續的函數,那麼神經網絡就有無限的泛化能力。對於大部分分類問題而言,本質就是求得一個函數y=f(x),例如:對於圖像識別而言就是求得一個以像素張量爲自變量的函數y=F(像素張量),其中y=貓、狗、花、汽車等等;對於文本情感分析而言,就是爲了求得一個以詞向量或者段落向量爲自變量的函數y=F(詞向量),其中y=正面、負面等等……

二、導讀

    本篇博客包括以下內容:

    1、卷積神經網絡的原理

    2、基於dl4j定型一個卷積神經網絡來進行手寫數字識別

三、卷積神經網絡原理

    下面左邊有個9*9的網格,紅色填充的部分構成了數字7,把紅色部分填上1,空白部分填上0,就構成了一個二維矩陣,傳統做法可以用求向量距離,如果數字全部都標準的寫在網格中相同的位置,那麼肯定是準確的,但是,實際上數字7在書寫的過程中,可能偏左一點、偏右一點,變形扭曲一點,這時候就難以識別。另外,一幅圖片的像素點的數量是巨大的,例如一幅50*50的圖片將有2500個像素點,每個像素點有R、G、B三個維度的顏色,那麼輸入參數的個數有7500個,這個運算量是巨大的。

                  

    那麼就需要有一個抽象特徵、降低數據維度的方法,這就說到了卷積運算,用一個小於圖片的卷積核掃過整幅圖片求點積。卷積的過程看下圖。圖片來源於https://my.oschina.net/u/876354/blog/1620906

    

    卷積運算的過程在於尋找圖片中的顯著特徵,並達到降維的目的,整個過程相當於一個函數掃過另一個函數,掃過時兩個函數的積分重疊部分並沒改變圖片的特徵形狀,並可以降低維度,另外還可以分區塊來提取特徵,並且拼接特徵。

convgaus

    爲了進一步降低維度,引入了池化,池化的方式有很多,如最大值,平均值。下圖展示了一個步長爲2的2*2最大池化過程,用一個2*2的方塊掃描過,求Max,總共掃描4次,4次掃描的最大值分別是6、8、3、4。

maxpool

    最後,經過多層卷積和池化之後,會得到一個矩陣,該矩陣作爲一個全連接網絡的輸入,在逼近一個函數,就識別出數字了,以上圖得到的6、8、3、4爲例,全連接網絡求一個函數。

四、deeplearning4j手寫體識別

    1、先下載mnist數據集,地址如下:

       http://github.com/myleott/mnist_png/raw/master/mnist_png.tar.gz

    2、解壓(我解壓在E盤)

    3、訓練網絡,評估(一些比較難的部分都做了註釋)

public class MnistClassifier {

  private static final Logger log = LoggerFactory.getLogger(MnistClassifier.class);
  private static final String basePath = "E:";

  public static void main(String[] args) throws Exception {
    int height = 28;
    int width = 28;
    int channels = 1; // 這裏有沒有複雜的識別,沒有分成紅綠藍三個通道
    int outputNum = 10; // 有十個數字,所以輸出爲10
    int batchSize = 54;//每次迭代取54張小批量來訓練,可以查閱神經網絡的mini batch相關優化,也就是小批量求平均梯度
    int nEpochs = 1;//整個樣本集只訓練一次
    int iterations = 1;

    int seed = 1234;
    Random randNumGen = new Random(seed);

    File trainData = new File(basePath + "/mnist_png/training");
    FileSplit trainSplit = new FileSplit(trainData, NativeImageLoader.ALLOWED_FORMATS, randNumGen);
    ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator(); //以父級目錄名作爲分類的標籤名
    ImageRecordReader trainRR = new ImageRecordReader(height, width, channels, labelMaker);//構造圖片讀取類
    trainRR.initialize(trainSplit);
    DataSetIterator trainIter = new RecordReaderDataSetIterator(trainRR, batchSize, 1, outputNum);

    // 把像素值區間 0-255 壓縮到0-1 區間
    DataNormalization scaler = new ImagePreProcessingScaler(0, 1);
    scaler.fit(trainIter);
    trainIter.setPreProcessor(scaler);
    

    // 向量化測試集
    File testData = new File(basePath + "/mnist_png/testing");
    FileSplit testSplit = new FileSplit(testData, NativeImageLoader.ALLOWED_FORMATS, randNumGen);
    ImageRecordReader testRR = new ImageRecordReader(height, width, channels, labelMaker);
    testRR.initialize(testSplit);
    DataSetIterator testIter = new RecordReaderDataSetIterator(testRR, batchSize, 1, outputNum);
    testIter.setPreProcessor(scaler); // same normalization for better results

    log.info("Network configuration and training...");
    Map<Integer, Double> lrSchedule = new HashMap<>();//設定動態改變學習速率的策略,key表示小批量迭代到幾次
    lrSchedule.put(0, 0.06); 
    lrSchedule.put(200, 0.05);
    lrSchedule.put(600, 0.028);
    lrSchedule.put(800, 0.0060);
    lrSchedule.put(1000, 0.001);

    MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
        .seed(seed)
        .iterations(iterations)
        .regularization(true).l2(0.0005)
        .learningRate(.01)
        .learningRateDecayPolicy(LearningRatePolicy.Schedule)
        .learningRateSchedule(lrSchedule) 
        .weightInit(WeightInit.XAVIER)
        .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
        .updater(Updater.NESTEROVS)
        .list()
        .layer(0, new ConvolutionLayer.Builder(5, 5)
            .nIn(channels)
            .stride(1, 1)
            .nOut(20)
            .activation(Activation.IDENTITY)
            .build())
        .layer(1, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX)
            .kernelSize(2, 2)
            .stride(2, 2)
            .build())
        .layer(2, new ConvolutionLayer.Builder(5, 5)
            .stride(1, 1) 
            .nOut(50)
            .activation(Activation.IDENTITY)
            .build())
        .layer(3, new SubsamplingLayer.Builder(SubsamplingLayer.PoolingType.MAX)
            .kernelSize(2, 2)
            .stride(2, 2)
            .build())
        .layer(4, new DenseLayer.Builder().activation(Activation.RELU)
            .nOut(500).build())
        .layer(5, new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
            .nOut(outputNum)
            .activation(Activation.SOFTMAX)
            .build())
        .setInputType(InputType.convolutionalFlat(28, 28, 1)) 
        .backprop(true).pretrain(false).build();

    MultiLayerNetwork net = new MultiLayerNetwork(conf);
    net.init();
    net.setListeners(new ScoreIterationListener(10));
    log.debug("Total num of params: {}", net.numParams());

    // 評估測試集
    for (int i = 0; i < nEpochs; i++) {
      net.fit(trainIter);
      Evaluation eval = net.evaluate(testIter);
      log.info(eval.stats());
      trainIter.reset();
      testIter.reset();
    }
    ModelSerializer.writeModel(net, new File(basePath + "/minist-model.zip"), true);//保存訓練好的網絡
  }
}

運行main方法,得到如下評估結果:

 # of classes:    10
 Accuracy:        0.9897
 Precision:       0.9897
 Recall:          0.9897
 F1 Score:        0.9896

    整個效果還比較好,保存好訓練的網絡,便可以用於手寫體數據的識別了,下一篇博客將介紹怎麼加載定型的網絡,配合springMVC來開發一個手寫體識別的應用。

 

 

 

    

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