takeda_san’s blog

KotlinとVRを頑張っていく方向。

サンプルプログラムをいじろう その4

前回のあらすじ

そもそも、教師データの想定が間違っていたのだな。

takeda-san.hatenablog.com

やったこと

まずは、教師データの見直し。
今使っている株価のデータはこんなデータが入っている。

日付,始値,高値,安値,終値,出来高,終値調整値
"2015-01-05","2507","2538","2491","2512","37300","2512"

これにラベルを付けて教師データとして使えるようにしたい。 結果、こうなった。

営業日,始値,高値,安値,終値,出来高,終値調整値,Label
1,2507,2538,2491,2512,37300,2512,0

日付は入力値が数値しか受け付けないので、営業日にした。
Labelと書いてあるところは、翌日株価が上がったら2、下がったら0、変化なしなら1を入れるようにしている。

営業日,始値,高値,安値,終値,出来高,終値調整値 … 入力
Label … 出力

にしようという考えだ。
もちろん普通に考えて前日の取引データで翌日の株価が決まるわけがないが、今回は実験だ。
そういうもんだということにする。

そしてプログラムを書きからコピペしてくる。

dl4j-examples/CSVExample.java at master · deeplearning4j/dl4j-examples · GitHub

書き換えるのはこの部分。

int labelIndex = 7;
int numClasses = 3;
int batchSize = 244; 

ラベルは文字通りLabel列。0から数えて7番目。
クラスの数は、上がった場合の2、下がった場合の0、変化なしの1の3種類。
バッチサイズは1回ですべて読み込むので、ファイル行数の244。

とりあえず、実行してみる。

==========================Scores========================================
 Accuracy:        0.3837
 Precision:       0.4039
 Recall:          0.2851
 F1 Score:        0.3343
========================================================================

お、結果が出力された。
ちなみに、
 Acuracy … 正解率
 Preision … 精度
 Recall … 再現率
 F1 Score … 精度と再現率の調和平均 とのこと。
つまり、38%しか正解してないということは大損ですね。
数値的には非常に残念な結果だが、どうやら正しく動いているらしい。

せっかくなので出力値を見てみよう。

21:36:52.547 [main] INFO  sample.d4j.Gyudon - actual [0.00, 0.00, 1.00]vs predited [0.45, 0.02, 0.52]
21:36:52.548 [main] INFO  sample.d4j.Gyudon - actual [0.00, 0.00, 1.00]vs predited [0.36, 0.03, 0.61]
21:36:52.548 [main] INFO  sample.d4j.Gyudon - actual [1.00, 0.00, 0.00]vs predited [0.57, 0.02, 0.41]
21:36:52.556 [main] INFO  sample.d4j.Gyudon - actual [0.00, 0.00, 1.00]vs predited [0.57, 0.02, 0.41]
21:36:52.557 [main] INFO  sample.d4j.Gyudon - actual [0.00, 0.00, 1.00]vs predited [0.55, 0.02, 0.43]

actual [0.00, 0.00, 1.00]が、テストのラベル。
つまりは正解の値。
predited [0.45, 0.02, 0.52]が、予測のラベル。
つまりは学習した結果の値。

うーん、正解しているものあるが差が微妙すぎる。
興味本位で、学習回数を10倍に。

==========================Scores========================================
 Accuracy:        0.5233
 Precision:       0.5356
 Recall:          0.3702
 F1 Score:        0.4378
========================================================================

お、50%超えた。
これで、億万長者やん。

冗談はさておき、前回まで使っていたプログラムとの差異を見ていく。

allData.shuffle();
SplitTestAndTrain testAndTrain = allData.splitTestAndTrain(0.65);  //Use 65% of data for training

DataSet trainingData = testAndTrain.getTrain();
DataSet testData = testAndTrain.getTest();

allData.shuffle(); csvから読み込んだデータセットのレコードの並び順をシャッフルする。
SplitTestAndTrain testAndTrain = allData.splitTestAndTrain(0.65); データセットをトレーニングデータと学習後に使うテストデータに分ける。
コメントの通り、トレーニングデータに65%割り振られる。
DataSet trainingData = testAndTrain.getTrain(); トレーニングデータとして割り振られたデータセットを取得する。
DataSet testData = testAndTrain.getTest(); テストデータとして割り振られたデータセットを取得する。

//We need to normalize our data. We'll use NormalizeStandardize (which gives us mean 0, unit variance):
DataNormalization normalizer = new NormalizerStandardize();
normalizer.fit(trainingData);           //Collect the statistics (mean/stdev) from the training data. This does not modify the input data
normalizer.transform(trainingData);     //Apply normalization to the training data
normalizer.transform(testData);         //Apply normalization to the test data. This is using statistics calculated from the *training* set

データの正規化を行っているらしい。
データの正規化とは、「学習に関係ないデータ間の差異を,学習に影響を及ぼさないように取り除くこと」らしい。
具体的な手法など、今は深入りせず、そういうものだと思っておこう。

sig.tsg.ne.jp

MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
            .seed(seed)
            .iterations(iterations)
            .activation(Activation.TANH)
            .weightInit(WeightInit.XAVIER)
            .learningRate(0.1)
            .regularization(true).l2(1e-4)
            .list()
            .layer(0, new DenseLayer.Builder().nIn(numInputs).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(outputNum).build())
            .backprop(true).pretrain(false)
            .build();

ニューラルネットワークの定義部分。
活性化関数にtanhを使っている。
でもlayerメソッド内ではないぞ…。

deeplearning4j.org

どうやら、出力層以外の層の活性化関数は各層でも、その前の段階どちらでも定義できるらしい。

損失関数に負の対数尤度関数を使っている。
対数尤度関数って何だろうか。
そもそも読めない…

qiita.com

ゆうどって読むのね。
こ、これは高度な知能が求められそう…
後ほどゆっくり読もう…な。

出力層の活性化関数にはsoftmax関数を使用している。

hiro2o2.hatenablog.jp

後ほどゆっくり読もう…な。
あとは今までと同じかな。

前回のプログラムでは、活性化関数を文字列で指定していたが、非推奨になっていたようで警告が出ていた。 「.activation(“sigmoid”)」の部分。 ここはActivationというenumクラスを使って指定すると良い。

.layer(0, new DenseLayer.Builder()
                        .nIn(inputNum)
                        .nOut(middleNum)
                        .activation("sigmoid").build())
.layer(0, new DenseLayer.Builder()
                        .nIn(inputNum)
                        .nOut(middleNum)
                        .activation(Activation.SIGMOID).build())

というわけで、後回しになっているもろもろを次回見ていく。
あと、当初目的の2015年データのすべてで学習して、2016年データでテストするのもやりたい。

次回の予定

・対数尤度関数、softmax関数とは
・courseraの動画を見る
・Deep Learning Javaプログラミングを読む
・プログラミングのための線形代数を読む
Mavenのpomの読み方