Oracleから公開されたTribuoを気を取り直してやってみた。Tribuo - A Java prediction library (v4.0)


追記　2020/09/28
この記事は古いです。こちらにまとめなおしました。
https://qiita.com/jashika/items/d7c86dd8053379fd909f

##前置き
[いろいろ思うところもあるが、文句だけ言っていても始まらない。](https://qiita.com/jashika/items/1ba0cad613ec919adaa7)
むしろ俺がこれ作るからOracleが俺を雇え。ください。

##資料について
[元ネタはこちらを参照してください。](https://tribuo.org/learn/4.0/docs/)
[日本語が必要ならこちらを参照してください。](https://qiita.com/jashika/items/bc03dbbca92ba38f4502)
[データのダウンロードはこちらを参照してください。](https://archive.ics.uci.edu/ml/machine-learning-databases/iris/)

##どうにかして動かす
[bezdekIris.data](https://archive.ics.uci.edu/ml/machine-learning-databases/iris/bezdekIris.data)をダウンロードします。

[マニュアルGatting Startedに記載のサンプルコードをJavaファイルに転記します。](https://tribuo.org/learn/4.0/docs/)
[今に始まったことではないので](https://qiita.com/jashika/items/1ba0cad613ec919adaa7)、何も驚きません。そんな変数は定義されていないとエラーで怒られます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/070caf40-a5d9-3906-9a0d-86206f7024c8.png)

DataSource<Label>をirisDataと変数定義したのに、引数で渡すときには、irisesSourceに名称が変わってしまったのでしょう。おそらくirisDataのことでしょう。~~絶対テストやってない。~~irisDataを引数に渡します。ついでにそんなところにあるコメントは邪魔なので削除します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/020ed0b5-8d04-b850-391d-14312e9a3f69.png)
MutableDataset(testData)には、MutableDataset#get()メソッドなど無いと怒られます。~~Oracleクラスともなれば、メソッドなど無くても呼び出せるのでしょう。絶対にテストry~~

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/f43b67f1-16d4-6da5-6c12-8f120f530700.png)
testData#hoge#get(0)とつながりそうなのは、#getData()あたりでしょうか。とりあえずgetData()を使ってみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/617b1a8a-1a63-abda-06b6-313490a0ab1f.png)

LabelEvaluation#evaluate(Model<Label>,MutableDataset<Label>)メソッドなど無いと怒られます。~~今に始まったことではry~~

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/43ab05e8-e1ff-a9a0-db90-3154325618c3.png)
しかし、引数でModel,　MutableDatasetを受け取り、Evaluationを返却する似た名前のメソッドすらありません。~~これで呼び出せるとか天才だと思うわ。さすがオラクry~~おそらくですが、LabelEvaluationのインスタンスを生成するのではなく、LabelEvaluatorのインスタンスを生成しないといけないですね。きっと。勘ですけど。引数もメソッド名も返却値の型も合いますしおすし。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/89423d76-0df2-77a8-0e04-a84cafc91608.png)

うーん。キャストしておけ~~適当~~。
これで一応エラーは消えました。

```java:TribuoSample
/**
 *
 */
package org.project.eden.adam;

import java.io.IOException;
import java.nio.file.Paths;

import org.tribuo.DataSource;
import org.tribuo.Model;
import org.tribuo.MutableDataset;
import org.tribuo.Prediction;
import org.tribuo.classification.Label;
import org.tribuo.classification.LabelFactory;
import org.tribuo.classification.dtree.CARTClassificationTrainer;
import org.tribuo.classification.evaluation.LabelEvaluation;
import org.tribuo.classification.evaluation.LabelEvaluator;
import org.tribuo.classification.sgd.linear.LogisticRegressionTrainer;
import org.tribuo.data.csv.CSVLoader;
import org.tribuo.evaluation.Evaluation;
import org.tribuo.evaluation.TrainTestSplitter;

/**
 * @author jashika
 *
 */
public class TribuoSample {

    /**
     * @param args mainメソッドの引数。
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {

        // ラベル付きアヤメ(アイリス)データを読み込む
        var irisHeaders = new String[] { "sepalLength", "sepalWidth", "petalLength", "petalWidth", "species" };
        DataSource<Label> irisData = new CSVLoader<>(new LabelFactory()).loadDataSource(Paths.get("/Users/admin/Downloads/bezdekIris.data"),irisHeaders[4],irisHeaders);

        // アヤメ(アイリス)データをトレーニングセット(70%)とテストセット(30%)に分割
        var splitIrisData = new TrainTestSplitter<>(irisData, 0.7, 1L);

        var trainData = new MutableDataset<>(splitIrisData.getTrain());
        var testData = new MutableDataset<>(splitIrisData.getTest());

        // 決定木を学習する
        var cartTrainer = new CARTClassificationTrainer();
        Model<Label> tree = cartTrainer.train(trainData);

        // ロジスティック回帰
        var linearTrainer = new LogisticRegressionTrainer();
        Model<Label> linear = linearTrainer.train(trainData);

        // 最終的には、目に見えないデータから予測を行う
        // 各予測は、出力名（ラベル）からスコア/確率へのマップ
        Prediction<Label> prediction = linear.predict(testData.getData().get(0));

        // 完全なテストデータセットを評価して、精度、F1などを計算してもよい。
        Evaluation<Label> evaluation = new LabelEvaluator().evaluate(linear, testData);

        // 手動での評価を検査する。
        double acc = LabelEvaluation.class.cast(evaluation).accuracy();

        // フォーマットされた評価文字列を表示する。
        System.out.println(evaluation.toString());
    }
}
```
実行してみます。
151行目が1エレメントしかない？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/bacfda49-6cb1-c7eb-683f-a2d85d1c2e29.png)

空行・・・。空行無視じゃないの?LF改行だから、何か恨みがあるの？別にこれは無視する対応でよくね？CSVの規約違反でもないし。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/a16742dc-8370-cce9-bde0-9b9974782ce7.png)

データファイルから消したよ。手動で。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/beddf213-46c5-65cf-0aa7-8a40acce0dde.png)

再度実行。それっぽいのが出た。もう少し突っ込んだところは、後で追記します。~~多分。~~
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/732297/e400bb56-2b11-9e9a-ae8f-9733672970df.png)