1. 程式人生 > >深度學習Deeplearning4j 入門實戰(5):基於多層感知機的Mnist壓縮以及在Spark實現

深度學習Deeplearning4j 入門實戰(5):基於多層感知機的Mnist壓縮以及在Spark實現

在上一篇部落格中,我們用基於RBM的的Deep AutoEncoder對Mnist資料集進行壓縮,應該說取得了不錯的效果。這裡,我們將神經網路這塊替換成傳統的全連線的前饋神經網路對Mnist資料集進行壓縮,看看兩者的效果有什麼異同。整個程式碼依然是利用Deeplearning4j進行實現,並且為了方便以後的擴充套件,我們將其與Spark平臺結合。下面,就具體來說一下模型的結構、訓練過程以及最終的壓縮效果。

首先,我們新建Maven工程並加入Deeplearning4j的相關依賴(這一塊內容在之前的文章中多次提及,因此這裡就不再囉嗦了)。接下來,我們新建Spark任務,讀取已經存放在HDFS上的Mnist資料集(和之前文章中提到的一樣,Mnist資料集已經事先以JavaRDD<DataSet>的形式儲存在HDFS上,具體操作可以參考之前的部落格。),並生成訓練資料集JavaRDD。具體程式碼如下:

  1. SparkConf conf = new SparkConf()  
  2.                     .set("spark.kryo.registrator""org.nd4j.Nd4jRegistrator")  
  3.                     .setAppName("MLP AutoEncoder Mnist(Java)");  
  4. JavaSparkContext jsc = new JavaSparkContext(conf);  
  5. //
  6. final String inputPath = args[0];  
  7. final String savePath = args[
    1];  
  8. double lr = Double.parseDouble(args[2]);  
  9. finalint batchSize = Integer.parseInt(args[3]);  
  10. finalint numEpoch = Integer.parseInt(args[4]);  
  11. //
  12. JavaRDD<DataSet> javaRDDMnist = jsc.objectFile(inputPath);//read mnist data from HDFS
  13. JavaRDD<DataSet> javaRDDTrain = javaRDDMnist.map(new Function<DataSet, DataSet>() {  
  14.     @Override
  15.     public DataSet call(DataSet next) throws Exception {  
  16.         returnnew DataSet(next.getFeatureMatrix(),next.getFeatureMatrix());  
  17.     }  
  18. });  

構築完訓練資料集之後,我們就可以定義網路結構並配以相應的超引數:
  1. MultiLayerConfiguration netconf = new NeuralNetConfiguration.Builder()  
  2.         .seed(123)  
  3.         .iterations(1)  
  4.         .learningRate(lr)  
  5.         .learningRateScoreBasedDecayRate(0.5)  
  6.         .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)  
  7.         .updater(Updater.ADAM).adamMeanDecay(0.9).adamVarDecay(0.999)  
  8.         .list()  
  9.         .layer(0new DenseLayer.Builder().nIn(784).nOut(1000).activation("relu").build())  
  10.         .layer(1new DenseLayer.Builder().nIn(1000).nOut(500).activation("relu").build())  
  11.         .layer(2new DenseLayer.Builder().nIn(500).nOut(250).activation("relu").build())  
  12.         .layer(3new DenseLayer.Builder().nIn(250).nOut(500).activation("relu").build())  
  13.         .layer(4new DenseLayer.Builder().nIn(500).nOut(1000).activation("relu").build())  
  14.         .layer(5new OutputLayer.Builder(LossFunctions.LossFunction.MSE)  
  15.                                 .nIn(1000)  
  16.                                 .nOut(784)  
  17.                                 .activation("relu")  
  18.                                 .build())  
  19.         .backprop(true).pretrain(false)  
  20.         .build();  
  21. ParameterAveragingTrainingMaster trainMaster = new ParameterAveragingTrainingMaster.Builder(batchSize)  
  22.                                                     .workerPrefetchNumBatches(0)  
  23.                                                     .saveUpdater(true)  
  24.                                                     .averagingFrequency(5)  
  25.                                                     .batchSizePerWorker(batchSize)  
  26.                                                     .build();  
  27. MultiLayerNetwork net = new MultiLayerNetwork(netconf);  
  28. net.init();  
  29. SparkDl4jMultiLayer sparkNetwork = new SparkDl4jMultiLayer(jsc, net, trainMaster);  
  30. sparkNetwork.setListeners(Collections.<IterationListener>singletonList(new ScoreIterationListener(1)));  
這裡我們做一些簡要的說明:我們一共定義了5層的神經網路,並且每一層都是普通的全連線網路。學習率等超引數可以通過入口引數傳遞進來,損失函式用的是均方誤差。後面的ParameterAveragingTrainingMaster以及Spark網路的定義在之前的文章中有過說明,這裡就略過了。

那麼,接下來就是訓練的程式碼:

  1. forint i = 0; i < numEpoch; ++i ){  
  2.             sparkNetwork.fit(javaRDDTrain);   //train modek
  3.             System.out.println("----- Epoch " + i + " complete -----");  
  4.             MultiLayerNetwork trainnet = sparkNetwork.getNetwork();  
  5.             System.out.println("Epoch " + i + " Score: " + sparkNetwork.getScore());  
  6.             List<DataSet> listDS = javaRDDTrain.takeSample(false50);   
  7.             for( DataSet ds : listDS ){   
  8.                 INDArray testFeature = ds.getFeatureMatrix();   
  9.                 INDArray testRes = trainnet.output(testFeature);   
  10.                 System.out.println("Euclidean Distance: " + testRes.distance2(testFeature));   
  11.             }  
  12.             DataSet first = listDS.get(0);   
  13.             INDArray testFeature = first.getFeatureMatrix();   
  14.             double[] doubleFeature = testFeature.data().asDouble();   
  15.             INDArray testRes = trainnet.output(testFeature);   
  16.             double[] doubleRes = testRes.data().asDouble();   
  17.             forint j = 0; j < doubleFeature.length && j < doubleRes.length; ++j ){   
  18.                 double f = doubleFeature[j]; double t = doubleRes[j];   
  19.                 System.out.print(f + ":" + t + " ");   
  20.             }   
  21.             System.out.println();   
  22.         }  
訓練過程中我們將在每一輪訓練結束後隨機抽取一些資料進行預測,並將預測值和原值進行歐氏距離的計算。同時我們也會隨機抽取一張圖片直接比較每個畫素點值的不同。具體可以看下面的兩張圖:

完整的訓練過程,Spark任務截圖:

隨機抽取的資料的比較:

在經過多輪次的訓練後,我們將模型儲存在HDFS上(具體的程式碼實現可以參考之前的部落格)並且將其拉到本地後,隨機預測/重構一些圖片來看看效果,具體的,我隨機選擇了9張圖進行重構,如下圖:

   

 

 

  

  

最後做下小結。

這裡我們用多層感知機來對Mnsit資料集進行壓縮,並且也取得不錯的壓縮效果。和之前利用Deep AutoEncoder進行資料進行壓縮的不同在於我們將每一層中RBM替換成了FNN。應當說,從肉眼的角度我們沒法分辨兩種網路對Mnist資料集壓縮的好壞程度,但是從理論上,基於RBM的壓縮網路應該會取得更好的效果,在Hinton教授的論文中,也拿兩者做了比較,結論也是基於RBM的Deep AutoEncoder效果更好,實際中,兩者都會應用到。所以還得還情況而定!