読者です 読者をやめる 読者になる 読者になる

takeda_san’s blog

JavaFXとDeeplearning4jを頑張る方向。

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の読み方

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

前回のあらすじ

csvからのデータ読み込みができない。

takeda-san.hatenablog.com

やったこと

参考になる情報がないなら公式にいけばいいじゃない。

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

というわけで、csvを読み込んでいる、サンプルコードを見てみる。

//Second: the RecordReaderDataSetIterator handles conversion to DataSet objects, ready for use in neural network
int labelIndex = 4;     //5 values in each row of the iris.txt CSV: 4 input features followed by an integer label (class) index. Labels are the 5th value (index 4) in each row
int numClasses = 3;     //3 classes (types of iris flowers) in the iris data set. Classes have integer values 0, 1 or 2
int batchSize = 150;    //Iris data set: 150 examples total. We are loading all of them into one DataSet (not recommended for large data sets)

DataSetIterator iterator = new RecordReaderDataSetIterator(recordReader,batchSize,labelIndex,numClasses);
DataSet allData = iterator.next();

iris.txtというのはよく使うサンプルデータセットらしい。

https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data

3種類(Iris-setosa、Iris-versicolor、Iris-virginica)のデータが150行格納されている。
サンプルコードと照らし合わせてみよう。

int labelIndex = 4;     //5 values in each row of the iris.txt CSV: 4 input features followed by an integer label (class) index. Labels are the 5th value (index 4) in each row

iris.txtには1行あたり5つの値が含まれている。
5つめの値がラベルなので、0開始の5番目の値である4を指定する。

int numClasses = 3;     //3 classes (types of iris flowers) in the iris data set. Classes have integer values 0, 1 or 2

iris.txtには3種類の値が含まれる。
この種類のことをクラスと呼ぶらしい。
なのでここの値は3。(これは0から数えないんだ…配列じゃないから?)

int batchSize = 150;    //Iris data set: 150 examples total. We are loading all of them into one DataSet (not recommended for large data sets)

iris.txtには150行のデータが格納されている。
batchSizeには一度に読み込むデータを指定できる。
今回はデータの数が少ないので一度にすべて読み込む。
なので、150。

なるほど、公式のサンプルが一番わかりやすい。
次から各種クラスの使い方に困ったら見にこよう。

つづいて、このルールに沿って前回のプログラムを修正してみる。

int labelIndex = 0;  // 1カラム目がラベル
int numClasses = 244;  //    データの種類は244
int batchSize = 244;  // バッチサイズ。一度にすべて読み込む

あれ?バッチサイズ以外は前回と変わらないような。

Exception in thread "main" org.deeplearning4j.exception.DL4JInvalidInputException: Invalid classification data: expect label value (at label index column = 0) to be in range 0 to 243 inclusive (0 to numClasses-1, with numClasses=244); got label value of 244

うーん。問題は別の場所にありそう。
とりあえず、CSVExample.javaを丸コピーで動かしてみよう。

Exception in thread "main" java.lang.NumberFormatException: For input string: "Iris-setosa"

データは数値型しか受け付けないようだ。
リポジトリをあさってみると、文字列を数値に置換したデータがあった。
3種類の文字列を0~2に置き換えているようだ。
これもなぜか0から始めないといけない模様。

deeplearning4j/iris.dat at master · deeplearning4j/deeplearning4j · GitHub

これにデータを置き換えて、再度実行。

===========INPUT===================
[[5.10, 3.50, 1.40, 0.20],
~長いので略~
 [5.90, 3.00, 5.10, 1.80]]
=================OUTPUT==================
[[1.00, 0.00, 0.00],
~長いので略~
 [0.00, 0.00, 1.00]]

お、いけてるっぽい。
次にこの謎の出力の読み方を考えてみる。
まずは、前回のプログラム上にデータセットを書いていたころの出力。

===========INPUT===================
[1.00, 2.00, 3.00, 4.00]
=================OUTPUT==================
[0.25, 0.25, 0.25, 0.25]

値が丸まっていて出力データが区別がつかないが、入力と出力が対応していることがわかる。
つぎに今回のirisデータセット

===========INPUT===================
[[5.10, 3.50, 1.40, 0.20],
 [4.90, 3.00, 1.40, 0.20],
 [4.70, 3.20, 1.30, 0.20],
 [4.60, 3.10, 1.50, 0.20],
 [5.00, 3.60, 1.40, 0.20],
~略~
=================OUTPUT==================
[[1.00, 0.00, 0.00],
 [1.00, 0.00, 0.00],
 [1.00, 0.00, 0.00],
 [1.00, 0.00, 0.00],
 [1.00, 0.00, 0.00],

この5行と対応するcsvの行が次のとおり。

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
4.6,3.1,1.5,0.2,0
5.0,3.6,1.4,0.2,0

なるほど、1~4列目までが入力になっていると。
つまりCSVExample.javaでやりたいことは、1~4列目の4つの入力を使って、どのラベルが出力されるかを見ていたということらしい。
となると、今回の指定は入力と出力が逆となっていたようだ。
csvファイルが、1列目に営業日、2列目に株価なのでラベルは2列目ということになる。

ここまでくると、一度コードを全部捨ててCSVExample.javaを元にやり直したほうが良い気がしてきた。 一からの再出発は、また次回。

次回の予定

・サンプルプログラムをいじる
・courseraの動画を見る
・Deep Learning Javaプログラミングを読む
・プログラミングのための線形代数を読む
Mavenのpomの読み方