takeda_san’s blog

JavaFXと機械学習を頑張る方向。

EclipseでJigsawしてみる その1

Java Day Tokyo 2017に行ってきた。
わかった気になっているうちに、実践。

エディタで書いて、コマンドラインでビルドと実行?
とんでもない、いつものEclipseで書きます。

JDK9-eaダウンロードしてインストールして、Eclipsejreを追加…
あれ、『Target is not a JDK root. System library was not found』。
いやいやいや、あるじゃないですか。

f:id:takeda_san:20170522232309p:plain

と、おもったらNEON.3時点ではJava 9用のモジュールが入ってないんだってさ。
ちぇっ。
念のために、pleiadesも落としてやってみたけど、まぁ同じよね。
『ターゲットは JDK ルートではありません。システム・ライブラリーが見つかりませんでした。』

Bug 494481 – [1.9]Java9 JRE reports "Target is not a JDK root. System library was not found"

このページによるとOxygen落として、プラグイン入れてから出直して来いよ!
とのこと。
Oxygenってなんだろかと、さらに調べると。 Neonの次のバージョン名らしい。(おしゃれな名前じゃないの)
ここから、Eclipse Oxygen M7(6以降ならOKらしい)をもってくる。
しかし、ダウンロード遅い。

https://www.eclipse.org/downloads/packages/release/Oxygen/M7

あら、スプラッシュ画像がスタイリッシュ。

f:id:takeda_san:20170523001218j:plain

解凍後、マーケットプレイスから「Java 9 Support (BETA) for Oxygen」をインストール。

f:id:takeda_san:20170522235327p:plain

んで、下記のページの通りもうちょっと設定が必要。

https://marketplace.eclipse.org/content/java-9-support-beta-oxygen

eclipse.iniに「–add-modules=java.se.ee」を追加。
【5/23追記 5/23のパッチで必要なくなったみたいです】
で、再度jrejdkを検索してみる。

f:id:takeda_san:20170523001824p:plain

とりあえず、認識はしてくれたみたい。
環境設定で2時間。まさかのコードを書くことなく次回へ続く。
(調べてる途中で、IntelliJのcommunity版でjdk9-eaを設定してみたら一瞬で設定完了したのは内緒。Eclipseを信じろ。)

続きの記事はこちら

takeda-san.hatenablog.com

JavaFXのテーブル行の文字色をまとめて設定したい

結論から書くと、次のスタイルを指定すればよい。

-fx-text-background-color

参考にしたのは下記のページ。
やはり、スタイルの設定をしたければ、まずmodenaの定義を見よ。ということに尽きる。
(以前にもtreetableの偶数行の背景色を変更する設定で、はまったときにmodenaを読みに読んで解決しました…)

stackoverflow.com

実際にプログラムを書いてみる。
使うコードはみんな大好き、oracleの解説ページ。

docs.oracle.com

setRowFactoryのところ以外は。 警告が出ているところをちょっと変えただけ。

   public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(450);
        stage.setHeight(500);

        final Label label = new Label("Address Book");
        label.setFont(new Font("Arial", 20));

        table.setEditable(true);

        table.setRowFactory(tableView -> {
            TableRow<Person> row = new TableRow<>();
            row.setStyle("-fx-text-background-color:red");

            return row;
        });

        TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));

        TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));

        TableColumn<Person, String> emailCol = new TableColumn<>("Email");
        emailCol.setMinWidth(200);
        emailCol.setCellValueFactory(new PropertyValueFactory<>("email"));

        table.setItems(data);
        table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);

        final VBox vbox = new VBox();
        vbox.setSpacing(5);
        vbox.setPadding(new Insets(10, 0, 0, 10));
        vbox.getChildren().addAll(label, table);

        ((Group) scene.getRoot()).getChildren().addAll(vbox);

        stage.setScene(scene);
        stage.show();
    }

f:id:takeda_san:20170406222047p:plain

重要なのが、ここ。

       table.setRowFactory(tableView -> {
            TableRow<Person> row = new TableRow<>();
            row.setStyle("-fx-text-background-color:red");

            return row;
        });

行の設定はtableのsetRowFactoryで行う。
カラムの中のセルの設定をcellFactoryで行うのと同じイメージ。
最初は全セルでモデルデータの値を頑張って取得して、文字色を変えようとしていたんだけど、
思えば行で設定してあげればよかったんですよね。
TableRowのsetFillでも、同じことができるかと思いきや、できない。
(-fx-text-fillでも同じ)

そのほうが、personのどれかの値が変わったときに文字色を変えたい!!!みたいなときに、updateItemをオーバーロードして更新ができる。
セルでやっていると実装の都合上、そのセル(カラム)に表示している値が更新されないとupdateItemが走らないので、実装が大変。

実際に書くとこんな感じだろうか。 イザベラさんの行だけを赤くする。

       table.setRowFactory(tableView -> {
            final TableRow<Person> row = new TableRow<Person>() {
                @Override
                protected void updateItem(Person person, boolean empty) {
                    super.updateItem(person, empty);

                    if (!empty) {
                        if ("Isabella".equals(person.firstName.getValue())) {
                            setStyle("-fx-text-background-color:red");
                        } else {
                            getStyleClass().remove("-fx-text-background-color:red");
                        }
                    }
                }
            };

            return row;
        });

スタイルの追加、削除の処理がちょっと怪しいがとりあえずは動く。

f:id:takeda_san:20170406222051p:plain

なんだかんだで、半日ほど悩んでいたが1行で事足りてしまった。
ちょっと複雑な気持ち。

プログラミングのための線形代数「1.1 ベクトルと空間」

やったこと

プログラミングのための線形代数「1.1 ベクトルと空間」を読む。

表紙の主な対象読者の記述にぐっときたので買ってしまいました。
『研究・仕事で信号処理なりデータ解析なりをやろうとして、その方面の本を読んだら、線形代数が出てきた。どうにもよくわからないから勉強したい(し直したい)けど、証明ばかりの数学の教科書や、わかった気にしかならない入門書しかなくて困る。』
素晴らしい。
空いた時間に少しずつ読んでいきます。

ベクトルって何?おいしいの?から解説してくれるので大変ありがたい。
また、整数と実数も怪しい私のような読者向けにコラムまで用意してくれる親切ぶりであった。
この節の内容は、大学でやった内容でまだ覚えている範囲。

随所で数学派の皆さんとRubyユーザにおびえてる記述があるのは何なんですかね。
聞きつけると肩パットのお兄さん達が、乗り込んでくるんですかね。

平均二乗誤差について少しだけ理解した話

前回のあらすじ

分類とラベルについて、今更理解した。

takeda-san.hatenablog.com

やったこと

統計学の入門書を読んでいる。

www.kyoritsu-pub.co.jp

要するにやる夫で学ぶ統計学的なものである。
半分ぐらいまで読んだが、今のところ躓くことなく読めている。
(題名については、漢字に長々とルビが振ってあるところが素敵だと思った)

以前に誤差関数に平均二乗誤差(MSE)を使ったサンプルプログラムを読んで、中途半端な理解のまま放置していた。

takeda-san.hatenablog.com

ちょっとばかり入門書で自信がついたので再び見てみよう。


\displaystyle MSE( \sum (t-o) ^ 2 )

相変わらず威圧感ある数式である。
サンプルプログラムの解説ページでは上記の表記であったが、わかりやすく書くと次のような数式らしい。


\displaystyle MSE(c) = \frac{1}{n} \sum_{i=1}^{n} (x_i - c) ^ 2

今回の場合、xが学習データの出力値(正解の値)、cがとあるパラメータでの出力値(予測値)と考えて、分散として求める。


\displaystyle \sum_{i=1}^{n} (x_i - c) ^ 2

それをデータの個数で割るとばらつきの平均値が求められる。
これを誤差の度合いとして小さくしていきましょうというのが、誤差関数に平均二乗誤差を使うということなのだろう。
前よりは納得。

しかし、courseraの機械学習コースでも、統計学の入門書でも分散の絶対値ではなく、2乗する意味については
「だって、こっちのほうが便利だろ?な。そうだろ?」
というばかりで一切解説してくれない。
業界のタブー的なものなのだろうか。

次回の予定

未実施

クラスタリングのサンプルプログラムを読む
Mavenのpomの読み方

実施中

統計学の入門書一冊読む
・courseraの機械学習の動画を見る
・Deep Learning Javaプログラミングを読む
・プログラミングのための線形代数を読む

分類とラベルについて理解した話

前回のあらすじ

ようやく、csvからデータを読み込み想定通りの出力を得る。

takeda-san.hatenablog.com

やったこと

前々回、ハマったラベルとクラスの概念。
下記のページが非常にわかりやすかった。

やる夫で学ぶ機械学習 - 序章 - · けんごのお屋敷

クラス(分類:Classification)とは、判別の種類がいくつあるのかその種類数を表す。
メールが「スパムである」、「スパムでない」の2種類に分けられるときに、Classificationは2。
前回のプログラムでは、株価が翌日…「上がる」、「下がる」、「そのまま」の3種類にしたのでClassificationは3ということだった。
最初は翌日の終値を予想しようとしていたが、これを分類として扱うと1円刻みで分類しないといけないので大変だっただろうな。

ラベル(ラベル:Label)とは、教師データの正解データのこと。
株価が翌日…「上がる」、「下がる」、「そのまま」がラベルだ。
最初は日付データ(入力データ)をラベルとしていたので、これは間違い。

今回作成したプログラムは教師データありの分類について、触れた。
次は教師データなしのクラスタリングについて、サンプルプログラムの理解から始めていこうと思う。

統計わからん

損失関数と活性化関数に使われる各種関数を理解するのに、そもそもの前提知識が足りていない気がする。
前回でてきて後回しにした対数尤度関数、softmax関数だが、優しく書いてあるであろうページを読んでも、まったくわからん。
そもそも、ご存じない記号が出てきてて、歯が立たない。
(平均。偏差の概念もおぼつかないのに、その先を理解するというのは無理なのかも)

引き続きプログラムも書いていくが、統計学の入門書を読んだほうがよさそう。
すでにcourseraの機械学習の動画、線形代数の本で手いっぱいだが、趣味のプログラムなのでゆっくり少しずつ進めていこうと思う。

次回の予定

未実施

クラスタリングのサンプルプログラムを読む
統計学の入門書一冊読む
Mavenのpomの読み方

実施中

・courseraの機械学習の動画を見る
・Deep Learning Javaプログラミングを読む
・プログラミングのための線形代数を読む

setResizableをfalseにすると、不要な余白が生じる問題

唐突に始まるJavaFX記事。

事象

Scene Builder上でのFXMLプレビューと実際にコントローラーを実装して起動したときの画面サイズが違う気がする。

もともと、この事象に気が付いたのはStageが保持しているSceneを別のSceneで差し替えて画面を切り替えるような処理を書いているとき。
なぜか差し替え処理の後に画面サイズが一回り小さくなる。
デバッグしていくうちに原因が分かった。

原因

Stageが持つ、resizablePropertyをfalseにしているとSceneの外側に余白ができるっぽい。

Stage (JavaFX 8)

問題が再現するコードは以下の通り。
ボタンを押したときに、ステージのサイズをSceneの大きさに合わせるメソッドを走らせる。
不要な余白(マージン)があればステージ幅が小さくなるはずだ。

わかりやすくするためにボタンの大きさとSceneの大きさは同じにしている。

package application;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            HBox root = new HBox();

            // Sceneと同じ大きさのボタンを作る
            Button button = new Button("sizeToScene");
            button.setPrefWidth(400); // 幅400px
            button.setPrefHeight(400); // 高さ400px

            // ステージのサイズをシーンの大きさに合わせる
            button.setOnAction(event -> {
                primaryStage.sizeToScene();
            });
            root.getChildren().add(button);

            // シーンとステージの表示
            Scene scene = new Scene(root, 400, 400);
            primaryStage.setScene(scene);
            primaryStage.setResizable(false);
            primaryStage.show();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

実行結果がこちら。

sizeToScene実行前

f:id:takeda_san:20170227222120p:plain

sizeToScene実行後

f:id:takeda_san:20170227222125p:plain

起動時に右下に12~3pxぐらいの余白があるが、sizeToSceneをすると消えている。
ちなみに「primaryStage.setResizable(false);」をtrueにすると余白はできない。

対応策

ここにもある通り、面倒だが毎回、sizeToSceneで余白を消すしかないようだ。
しかもこの件、バグらしく前から要望が出ているものの直されてないとのこと。
(この質問自体、結構前のものだ)

stackoverflow.com

レイアウトの都合上、リサイズされたくない画面って結構あるとおもうんですけどね。
Java 9では直っていることを祈っています。
ちなみに実行環境はWindows + JRE 1.8.0_121でした。

サンプルプログラムをいじろう その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の読み方