Deeplearning4jへのガイド

1前書き

この記事では、https://deeplearning4j.org[deeplearning4j](dl4j)ライブラリーを使用した単純なニューラルネットワークを作成します。これは、機械学習用の最新かつ強力なツールです。

始める前に、このガイドでは線形代数、統計、機械学習理論、および十分に根拠のあるMLエンジニアに必要なその他のトピックに関する深い知識は必要ありません。

** 2ディープラーニングとは

ニューラルネットワークは、相互接続されたノードの層からなる計算モデルです。

ノードは数値データのニューロンのようなプロセッサです。入力からデータを取得し、これらのデータに重みと関数を適用して結果を出力に送信します。そのようなネットワークは、ソースデータのいくつかの例で訓練することができます。

トレーニングは、本質的には後で計算に影響を与えるノード内のいくつかの数値状態(重み)を保存することです。訓練例は、これらの項目の特徴およびある種の既知のクラスを有するデータ項目を含み得る(例えば、「この16×16ピクセルのセットは手書きの文字「a」を含む)。

トレーニングが終了した後、ニューラルネットワークは、以前にこれらの特定のデータ項目を見たことがなくても、新しいデータから情報を導き出すことができます。よくモデル化され、よく訓練されたネットワークは、画像、手書きの手紙、スピーチを認識し、統計データを処理してビジネスインテリジェンスの結果を生み出すことができます。

高性能ニューラルコンピューティングの進歩により、近年、ディープニューラルネットワークが可能になりました。 そのようなネットワークは それらが複数の中間の(または隠された)レイヤー からなるという点で単純なニューラルネットワークとは異なります。この構造により、ネットワークはデータをはるかに複雑な方法(再帰的、再帰的、畳み込みなど)で処理し、そこからさらに多くの情報を抽出することができます。

3プロジェクトの設定

このライブラリを使用するには、少なくともJava 7が必要です。また、ネイティブコンポーネントによっては、64ビットJVMバージョンでしか動作しません。

ガイドを始める前に、要件が満たされているかどうかを確認しましょう。

$ java -version
java version "1.8.0__131"
Java(TM) SE Runtime Environment (build 1.8.0__131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

まず、必要なライブラリをMavenの pom.xml ファイルに追加しましょう。

ライブラリのバージョンをプロパティエントリに抽出します(最新バージョンのライブラリについては、https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22orgを確認してください。 deeplearning4j%22[Maven Central]リポジトリ):

<properties>
    <dl4j.version>0.9.1</dl4j.version>
</properties>

<dependencies>

    <dependency>
        <groupId>org.nd4j</groupId>
        <artifactId>nd4j-native-platform</artifactId>
        <version>${dl4j.version}</version>
    </dependency>

    <dependency>
        <groupId>org.deeplearning4j</groupId>
        <artifactId>deeplearning4j-core</artifactId>
        <version>${dl4j.version}</version>
    </dependency>
</dependencies>

nd4j-native-platform 依存関係は、いくつかある利用可能な実装のうちの1つです。

それは多くの異なるプラットフォーム(macOS、Windows、Linux、Androidなど)で利用可能なネイティブライブラリに依存しています。 CUDAプログラミングモデルをサポートするグラフィックスカードで計算を実行したい場合は、バックエンドを nd4j-cuda-8.0-platform に切り替えることもできます。

4データを準備する

4.1. DataSetファイルの準備

機械学習の「Hello World」 - https://en.wikipedia.org/wiki/Iris flower data set[iris flower data set]の分類]を作成します。これは、さまざまな種の花( Iris setosa Iris versicolor 、および Iris virginica__)から収集されたデータのセットです。

これらの種は花弁とseの長さと幅が異なります。入力データ項目を分類する(つまり、特定の花がどの種に属しているのかを判断する)正確なアルゴリズムを書くのは難しいでしょう。しかし、よく訓練されたニューラルネットワークは、少しの間違いでそれを素早く分類することができます。

ここでは、このデータのCSVバージョンを使用します。ここで、列0..3には種のさまざまな特徴が、列4にはレコードのクラス、つまり値0、1、または2でコード化された種が含まれます。

5.1,3.5,1.4,0.2,0
4.9,3.0,1.4,0.2,0
4.7,3.2,1.3,0.2,0
...
7.0,3.2,4.7,1.4,1
6.4,3.2,4.5,1.5,1
6.9,3.1,4.9,1.5,1
...

4.2. データのベクトル化と読み取り

ニューラルネットワークは数字を扱うので、クラスを数字でエンコードします。 実世界のデータ項目を一連の数(ベクトル)に変換することはベクトル化と呼ばれます - deeplearning4jはhttps://github.com/deeplearning4j/DataVec[datavec]ライブラリを使用してこれを行います。

まず、このライブラリを使用して、ベクトル化データを含むファイルを入力しましょう。 CSVRecordReader を作成するときに、スキップする行数(ファイルにヘッダー行がある場合など)と区切り記号(この場合はコンマ)を指定できます。

try (RecordReader recordReader = new CSVRecordReader(0, ',')) {
    recordReader.initialize(new FileSplit(
      new ClassPathResource("iris.txt").getFile()));

   //...
}

レコードを反復処理するために、 DataSetIterator インターフェースの複数の実装のいずれかを使用できます。データセットは非常に大規模になる可能性があり、値をページングまたはキャッシュする機能は便利です。

しかし、小さなデータセットには150レコードしか含まれていないので、 iterator.next() を呼び出して、一度にすべてのデータをメモリに読み込んでみましょう。

  • また、クラス列のインデックスも指定しています この場合、これはfeature count(4) およびクラスの総数** (3)と同じです。

また、元のファイルのクラスの順序を削除するには、データセットをシャッフルする必要があります。

シャッフリングの結果が常に同じになるように、デフォルトの System.currentTimeMillis() コールの代わりに定数random seed(42)を指定します。これにより、プログラムを実行するたびに安定した結果が得られます。

DataSetIterator iterator = new RecordReaderDataSetIterator(
  recordReader, 150, FEATURES__COUNT, CLASSES__COUNT);
DataSet allData = iterator.next();
allData.shuffle(42);

4.3. 正規化と分割

トレーニングの前にデータに対して行うべきもう1つのことは、データを正規化することです。正規化は2段階のプロセスです。

  • データに関するいくつかの統計の収集(fit)

  • データを一様にするための何らかの方法でのデータの変更

正規化はデータの種類によって異なります。

たとえば、さまざまなサイズの画像を処理したい場合は、まずサイズ統計を収集してから、その画像を一定のサイズに拡大縮小する必要があります。

しかし、数の場合、正規化とは通常、それらをいわゆる正規分布に変換することを意味します。 NormalizerStandardize クラスはそれを手助けすることができます。

DataNormalization normalizer = new NormalizerStandardize();
normalizer.fit(allData);
normalizer.transform(allData);

データが準備できたので、セットを2つの部分に分割する必要があります。

最初の部分はトレーニングセッションで使用されます。私たちは訓練されたネットワークをテストするためにデータの2番目の部分(ネットワークにはまったく見えないでしょう)を使います。

これにより、分類が正しく機能していることを確認できます。

トレーニングのためにデータの65%(0.65)を取り、テストのために残りの35%を残します。

SplitTestAndTrain testAndTrain = allData.splitTestAndTrain(0.65);
DataSet trainingData = testAndTrain.getTrain();
DataSet testData = testAndTrain.getTest();

5ネットワーク設定の準備

5.1. 流暢な設定ビルダー

これで、流暢な流暢なビルダーを使ってネットワークの構成を構築できます。

MultiLayerConfiguration configuration
  = new NeuralNetConfiguration.Builder()
    .iterations(1000)
    .activation(Activation.TANH)
    .weightInit(WeightInit.XAVIER)
    .learningRate(0.1)
    .regularization(true).l2(0.0001)
    .list()
    .layer(0, new DenseLayer.Builder().nIn(FEATURES__COUNT).nOut(3).build())
    .layer(1, new DenseLayer.Builder().nIn(3).nOut(3).build())
    .layer(2, new OutputLayer.Builder(
      LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
        .activation(Activation.SOFTMAX)
        .nIn(3).nOut(CLASSES__COUNT).build())
    .backprop(true).pretrain(false)
    .build();

ネットワークモデルを構築するためのこの単純化された流暢な方法でも、ダイジェストすることと調整するための多くのパラメータがあります。このモデルを分解しましょう。

5.2. ネットワークパラメータの設定

  • iterations() ビルダーメソッドは、最適化の反復回数を指定します。

反復最適化は、ネットワークが良好な結果に収束するまでトレーニングセットに対して複数のパスを実行することを意味します。

通常、実際のデータセットと大規模なデータセットをトレーニングするときは、複数のエポック(ネットワークを介したデータの完全なパス)と各エポックに対して1回の反復を使用します。ただし、最初のデータセットは最小限であるため、1回のエポックと複数回の反復を使用します。

  • activation() はノードの内部で実行されてその出力を決定する関数です。

最も単純な活性化関数は線形__f(x)= xです。しかし、非線形関数だけがネットワークが少数のノードを使用することによって複雑なタスクを解くことを可能にすることがわかります。

org.nd4j.linalg.activations.Activation enumで調べることができるさまざまなアクティベーション機能があります。必要に応じてアクティベーション関数を書くこともできます。ただし、提供されている双曲線正接(tanh)関数を使用します。

  • weightInit() メソッドは、ネットワークの初期重みを設定するためのさまざまな方法の1つを指定します。** 正しい初期重みは、トレーニングの結果に大きな影響を与える可能性があります。あまり計算に入らずに、ガウス分布( WeightInit.XAVIER )の形式に設定しましょう。これは通常、最初の選択として適しています。

他のすべてのウェイト初期化メソッドは org.deeplearning4j.nn.weights.WeightInit enumで調べることができます。

  • 学習率** はネットワークの学習能力に大きく影響する重要なパラメータです。

もっと複雑な場合には、このパラメーターを微調整するのに多くの時間を費やすことがあります。しかし、私たちの簡単な作業のために、かなり重要な0.1の値を使い、 learningRate() ビルダーメソッドで設定します。

  • ニューラルネットワークをトレーニングする際の問題の1つは、ネットワークがトレーニングデータを「記憶」するときの過剰適合** の場合です。

これは、ネットワークがトレーニングデータに過度に高い重みを設定し、他のデータに悪い結果をもたらす場合に起こります。

  • この問題を解決するために、 .regularization(true).l2(0.0001) という行を使用してl2正規化を設定します。正則化は、大きすぎるウェイトに対してネットワークを「不利にし」、過剰適合を防ぎます。

5.3. ネットワーク層の構築

次に、密(完全接続)層のネットワークを作成します。

最初の層は、トレーニングデータの列と同じ数のノードを含む必要があります(4)。

2番目の高密度層には3つのノードが含まれます。これは私達が変えることができる値ですが、前の層の出力の数は同じである必要があります。

最終的な出力層はクラスの数(3)と一致するノードの数を含むべきです。ネットワークの構造は図に示されています。

リンク:/uploads/Untitled-Diagram.png%20447w[]

トレーニングが成功すると、入力を介して4つの値を受け取り、3つの出力のうちの1つに信号を送信するネットワークができます。

これは単純な分類子です。

最後に、ネットワークの構築を完了するために、伝播を設定し(最も効果的なトレーニング方法の1つ)、 .backprop(true).pretrain(false) という行で事前トレーニングを無効にします。

6. ネットワークの作成とトレーニング

それでは、設定からニューラルネットワークを作成し、初期化して実行しましょう。

MultiLayerNetwork model = new MultiLayerNetwork(configuration);
model.init();
model.fit(trainingData);

これで、残りのデータセットを使用して学習済みモデルをテストし、3つのクラスの評価指標で結果を検証できます。

INDArray output = model.output(testData.getFeatureMatrix());
Evaluation eval = new Evaluation(3);
eval.eval(testData.getLabels(), output);

eval.stats() を印刷すると、クラス1とクラス2を3回間違えたにもかかわらず、私たちのネットワークはアイリスの花の分類にかなり適していることがわかります。

Examples labeled as 0 classified by model as 0: 19 times
Examples labeled as 1 classified by model as 1: 16 times
Examples labeled as 1 classified by model as 2: 3 times
Examples labeled as 2 classified by model as 2: 15 times

==========================Scores========================================
# of classes: 3
Accuracy: 0.9434
Precision: 0.9444
Recall: 0.9474
F1 Score: 0.9411
Precision, recall & F1: macro-averaged (equally weighted avg. of 3 classes)
========================================================================

Fluent Configuration Builderを使用すると、ネットワークのレイヤをすばやく追加または変更したり、他のパラメータを調整してモデルを改善できるかどうかを確認できます。

7. 結論

この記事では、deeplearning4jライブラリを使用して、シンプルで強力なニューラルネットワークを構築しました。

いつものように、この記事のソースコードはhttps://github.com/eugenp/tutorials/tree/master/deeplearning4j[over on GitHub]にあります。