Deep learning on Spark with DL4J

이 문서는 Spark Advent Calendar 9일차 문서로 작성되었습니다.

Spark에서 Deep Learning 알고리즘을 실행하는 방법에는 여러 가지가 있습니다.
  • MLlib에서 구현되는 MultilayerPerceptronClassifier

  • Sparkling Water 사용

  • deeplearning4j 사용

  • 이번에는 세 번째 deeplearning4j를 Spark에서 이용하는 방법을 소개하고 싶습니다.

    deeplearning4j란?



    Skymind 가 중심이 되어 개발을 하고 있는 JVM상에서 움직이는 Deep Learning의 프레임워크입니다. Deep Learning의 알고리즘을 구현한 것은 Caffe 이나 Torch , Chainer 등이 있습니다만, 이것들은 메인에서는 Lua나 C/C++로 구현되고 있습니다. deeplearning4j는 Java, Scala로 구현되어 원래 Hadoop이나 Spark 등 이미 있는 Big Data를 위한 프레임워크와의 친화성에 중점을 두어 만들어지고 있습니다.

    image
    Architecture from deeplearning4j.org

    본체인 deeplearning4j 외에도
  • 벡터화를위한 도구입니다 Canova
  • JVM에서 ndarray와 같은 벡터를 처리 할 수 ​​있습니다 nd4j
  • Scala 랩퍼 deeplearning4s , nd4s

  • 등 원래 개발되었습니다.

    Spark에서 사용하려면



    Spark에서 이용하기 위해서는 이 deeplearning4j를 스파크 패키지화한 dl4j-spark-ml이라는 패키지를 사용합니다.
  • dl4j-spark-ml

  • 벌써 Spark가 이용할 수 있는 환경이 갖추어져 있으면 다음의 커멘드를 두드릴 뿐입니다.
    $ $SPARK_HOME/bin/spark-shell \
        --packages deeplearning4j:dl4j-spark-ml:0.4-rc3.4
    

    이제 classpath에 deeplearning4j의 클래스들이 통과하여 Spark에서 사용할 수 있습니다. 그렇다고해도 어떻게 알고리즘을 써야할지 모르기 때문에 dl4j-spark-ml-examples 라는 이름으로 샘플 코드도 만들어지고 있습니다.

    여기에 포함된 Iris 데이터 세트를 이용한 샘플을 살펴보겠습니다.

    ml.JavaIrisClassification



    이 코드에서는 Spark의 DaraFrame을 사용하여 Iris 데이터 세트를 읽습니다. deeplearning4j 쪽에서 그것 용 커넥터 (Relation)가 용이합니다.
            String path = args.length == 1 ? args[0]
                    : "file://" + System.getProperty("user.dir") + "/data/svmLight/iris_svmLight_0.txt";
            DataFrame data = jsql.read()
                    .format(DefaultSource.class.getName())
                    .load(path);
    

    이 DataFrame에 대해 ML Pipeline을 구축합니다. Spark ML/MLlib의 자산을 이용할 수 있는 것은 매우 편리합니다.
            // 訓練用のデータを抜き出す
            DataFrame trainingData = data.sample(false, 0.6, 11L);
            // テスト用のデータを抜き出す
            DataFrame testData = data.except(trainingData);
    
            // 特徴量をscalingさせる
            StandardScaler scaler = new StandardScaler()
                    .setWithMean(true).setWithStd(true)
                    .setInputCol("features")
                    .setOutputCol("scaledFeatures");
    
            // deeplearning4jが用意したEstimatorを作成する
            NeuralNetworkClassification classification 
              = new NeuralNetworkClassification()
                    .setFeaturesCol("scaledFeatures")
                    .setConf(getConfiguration());
    
            // Pipelineを作る
            Pipeline pipeline = new Pipeline()
              .setStages(new PipelineStage[] { scaler, classification });
    
    NeuralNetworkClassification 라는 Estimator를 만들기 위한 설정은 다음과 같습니다.
    
    new NeuralNetConfiguration.Builder()
      .seed(11L) // 初期重み行列を生成するためのseed
      .iterations(100) // backpropagtionを行うiterationの回数
      .learningRate(1e-3f) // 重み更新を行うときの学習率
      .optimizationAlgo(OptimizationAlgorithm.LINE_GRADIENT_DESCENT) // backpropagationの際の勾配の計算方法
      .momentum(0.9) // 勾配更新の際に利用される値
      .constrainGradientToUnitNorm(true)
      .useDropConnect(true) // 過学習などを避ける学習速度を早めるためにDrop outするかどうか
      .list(2) // 層の数。入力層(データそのもの)は含まなない
      // 第一層 制約付きボルツマンマシン 隠れ層と可視層のユニットのタイプを設定
      .layer(0, new RBM.Builder(RBM.HiddenUnit.RECTIFIED, RBM.VisibleUnit.GAUSSIAN)
                  .nIn(4) // 入力の大きさ Irisのfeatureの数
                  .nOut(3) // 出力層。今回はそのままカテゴリ数になっている
                  .weightInit(WeightInit.XAVIER)
                  .activation("relu") // 活性化関数のタイプ Rectified Linear Unit
                  .lossFunction(LossFunctions.LossFunction.RMSE_XENT)
                  .updater(Updater.ADAGRAD) // RBMの勾配更新はAdaGradを使う
                  .k(1) // # Contrastive Divergenceのiterationの数。RBM特有パラメタ
                  .dropOut(0.5) // Drop Outをこの層で行う割合
                  .build()
      ) 
      // 第二層 出力層
      .layer(1, new OutputLayer.Builder(LossFunctions.LossFunction.MCXENT)
                  .nIn(3) // 入力の数
                  .nOut(3) // 出力の数
                  .activation("softmax") // 活性化関数。Classificationなのでsoftmax
                  .weightInit(WeightInit.XAVIER)
                  .updater(Updater.ADAGRAD) // Full connection layerの更新の方法
                  .dropOut(0.5) // Drop Outをこの層で行う割合
                  .build()
      ) 
      .build();
    

    Deep Learning은 설정해야 할 Hyper Parameter가 많고 tuning이나 모델의 설정이 어려워져 버리는 경우가 많다고 합니다. 바로 그것을 구현한 것 같은 Configuration의 생성입니다.
    뭔가 좋은 모델의 프리셋 등이 몇 가지 있으면 충분하다는 경우도 있다고 생각하기 때문에 그러한 경우에는 사용하기 어려울지도 모릅니다만, 반대로 생각하면 이만큼의 파라미터의 자유도가 주어지고 있으므로 모델을 선택 가능한 폭은 퍼질 것 같습니다.

    이 Iris Dataset에 대한 example을 움직이고 싶다면 script가 준비되어 있기 때문에 그것을 두드리면 괜찮습니다.
    $ ./bin/run-example org.deeplearning4j.ml.JavaIrisClassification 
    

    단지 데이터에 따라서는 (Mnist라든지) 필요한 용량이 환경에 대해서 큰 경우가 있기 때문에 run-example 에 기재되어 있는 --driver-memory--executor-memory 를 늘릴 필요가 있을지도 모릅니다.

    좋은 웹페이지 즐겨찾기