前回のあらすじ
そもそも、教師データの想定が間違っていたのだな。
やったこと
まずは、教師データの見直し。
今使っている株価のデータはこんなデータが入っている。
日付,始値,高値,安値,終値,出来高,終値調整値 "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
データの正規化を行っているらしい。
データの正規化とは、「学習に関係ないデータ間の差異を,学習に影響を及ぼさないように取り除くこと」らしい。
具体的な手法など、今は深入りせず、そういうものだと思っておこう。
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メソッド内ではないぞ…。
どうやら、出力層以外の層の活性化関数は各層でも、その前の段階どちらでも定義できるらしい。
損失関数に負の対数尤度関数を使っている。
対数尤度関数って何だろうか。
そもそも読めない…
ゆうどって読むのね。
こ、これは高度な知能が求められそう…
後ほどゆっくり読もう…な。
出力層の活性化関数にはsoftmax関数を使用している。
後ほどゆっくり読もう…な。
あとは今までと同じかな。
前回のプログラムでは、活性化関数を文字列で指定していたが、非推奨になっていたようで警告が出ていた。 「.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の読み方