JavaとGridDBによるAdaBoost、バギング、スタッキング、およびVotingの実装

AdaBoost、バギング、スタッキング、Votingはすべてアンサンブル学習の手法です。アンサンブル学習は機械学習の一種で、一般に「弱い学習器」や「ベースモデル」と呼ばれる複数のモデルを特定の問題を解決するために学習させ、より良い結果を得るために組み合わせるものです。弱いモデルを正しく組み合わせると、より正確で頑健なモデルが得られるという仮説に基づいています。

ソースコードの全文はこちらをご覧ください。<https://github.com/griddbnet/Blogs/tree/adaboost-bagging>

それでは、今回使用するアンサンブル学習の方法について説明します。

1\. **バギング**

バギングは "Boostrap Aggregation "の略です。ブートストラップとは、特定の学習セットから、置換を用いて異なるデータセットをサンプリングする手法になります。バギングは、1つの学習アルゴリズムを学習データの異なるサブセットに適用し、サブセットのサンプリングを置換（ブートストラップ）を用いて行うアンサンブル学習の手法です。

アルゴリズムがすべてのサブセットを使って学習された後、バギングでは、異なるサブセットを使ってアルゴリズムが行った予測を集約することで予測を行います。ベース学習器の出力を集約するために、アルゴリズムは、分類には多数決を、回帰問題には平均化を使用します。

2\. **AdaBoost**

AdaBoostは弱い学習器を強い学習器に変換するアルゴリズム群です。このアンサンブル手法は、あらゆる学習アルゴリズムのモデル予測を向上させます。AdaBoostでは、弱い学習器を順次学習させ、各学習器が以前の学習器を修正しようとします。つまり、弱い学習器は前の学習器によって修正され、強い学習器へと変換されます。補正はバイアスを減らすことで行われます。

3\. **スタッキング**

このアンサンブル学習法は、多くの機械学習アルゴリズムを*メタ学習*によって組み合わせるものです。ベースレベルのアルゴリズムは完全な学習データセットを用いて学習され、メタモデルはすべてのベースレベルのモデルの結果を特徴として学習されます。あまり良くないモデルの予測をより良いモデルの入力として利用することで、予測精度を向上させることができます。

今回は、これらのアンサンブル学習の手法を、JavaとGridDBを用いて実装します。

## データセットについて

この記事で使用するデータは、様々な気象条件と、個人が遊ぶことができるかどうかを示しています。データセットには、outlook (見通し), temperature (気温), humidity (湿度), windy (風), play (遊び) の5つの属性があります。最初の4つの属性が独立変数で、最後の属性が従属変数です。データは `weather.csv` という名前の CSV ファイルに格納されています。

## GridDBにデータを格納する

GridDBは、CSVファイルと比較して様々な利点があります。例えば、クエリーはより高速なパフォーマンスを提供します。したがって、CSVファイルからデータを移行し、GridDBに格納することは良いことだと思います。

まず、Javaのコードでこれを実現するためのライブラリをインポートしてみましょう。

```java
import java.io.File;
import java.util.Scanner;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;

import com.toshiba.mwcloud.gs.Query;
import com.toshiba.mwcloud.gs.RowKey;
import com.toshiba.mwcloud.gs.RowSet;
import com.toshiba.mwcloud.gs.GridStore;
import com.toshiba.mwcloud.gs.Collection;
import com.toshiba.mwcloud.gs.GSException;
import com.toshiba.mwcloud.gs.GridStoreFactory;
```

GridDBはコンテナにデータを格納します。データの格納に使用するGridDBコンテナを表す静的なJavaクラスを作成しましょう。名前は `Weather` とします。

```java
public static class Weather {
     @RowKey String outlook;
     String temperature; 
     String humidity;
     String windy;
     String play;
}
```

上記の各変数は、GridDB コンテナ内のカラムを表します。SQL のテーブルと同様です。

次に、JavaからGridDBに接続します。これを成功させるためには、認証などの情報を提供する必要があります。以下のコードがその例です。

```java
Properties props = new Properties();
props.setProperty("notificationAddress", "239.0.0.1");
props.setProperty("notificationPort", "31999");
props.setProperty("clusterName", "defaultCluster");
props.setProperty("user", "admin");
props.setProperty("password", "admin");
GridStore store = GridStoreFactory.getInstance().getGridStore(props);
```

クラスタ名、GriDBに接続するユーザーのユーザー名とパスワードが指定されているのが分かります。接続が確立されたので、作業するコンテナを選択しましょう。

```java
Collection&lt;String, Weather> coll = store.putCollection("col01", Weather.class);
```

ここでは、コンテナを参照するために `coll` という名前を使用します。
それでは、CSVファイルからGridDBコンテナにデータを書き込んでみましょう。

```java
File file1 = new File("weather.csv");
Scanner sc = new Scanner(file1);
String data = sc.next();

while (sc.hasNext()){
    String scData = sc.next();
    String dataList[] = scData.split(",");
    String outlook = dataList[0];
    String temperature = dataList[1];
    String humidity = dataList[2];
    String windy = dataList[3];
    String play = dataList[4];
    
    Weather wc = new Weather();
    wc.outlook = outlook;
    wc.temperature = temperature;
    wc.humidity = humidity;
    wc.windy = windy;
    wc.play = play;
    
    coll.append(wc);
}
```

このコードでは、GridDB コンテナにデータを挿入します。

## データを取得する

このデータを使って、アンサンブル学習の手法で様々な機械学習モデルを構築したいのです。つまり、GridDBコンテナからデータを引き出す必要があります。これを実現するのが以下のコードです。

```java
Query<weather> query = coll.query("select *");
RowSet<weather> rs = query.fetch(false);
RowSet res = query.fetch();
```

ここでは、`select *` 文を使用して、`Weather` というGridDBコンテナに格納されているすべてのデータを選択しています。

## 機械学習モデルの適合

これで、データとアンサンブル学習法を用いて機械学習モデルを当てはめることができるようになりました。使用する手法は、AdaBoost、Bagging、Stacking、Votingなどです。

まず、これらのメソッドを実装するために、Weka API からライブラリをインポートしましょう。

```java
import weka.classifiers.bayes.NaiveBayes;
import weka.classifiers.meta.Bagging;
import weka.core.Instances;
import java.io.BufferedReader;
import java.io.FileReader;
import weka.classifiers.functions.Logistic;
import weka.classifiers.meta.AdaBoostM1;
import weka.classifiers.meta.Stacking;
import weka.classifiers.trees.DecisionStump;
import weka.classifiers.trees.J48;
import weka.classifiers.meta.Vote;
import weka.classifiers.trees.RandomForest;
import weka.classifiers.trees.RandomTree;
import weka.core.converters.ConverterUtils.DataSource;
import weka.classifiers.functions.LinearRegression;
import weka.classifiers.Classifier;
```

次に、データセット用のバッファードリーダーを作成します。

```java
BufferedReader bufferedReader
    = new BufferedReader(
        new FileReader(res));

// Create dataset instances
Instances datasetInstances
    = new Instances(bufferedReader);
```

Weka API の `AdaBoost()` 関数をインスタンス化して、AdaBoost アルゴリズムを使ってデータ上に機械学習モデルを実装するのに役立てましょう。

```java
datasetInstances.setClassIndex(datasetInstances.numAttributes()-1);

/**
 * AdaBoost
 */
AdaBoostM1 learner1 = new AdaBoostM1();
learner1.setClassifier(new DecisionStump());
learner1.setNumIterations(100);
learner1.buildClassifier(datasetInstances);
```

次に、Baggingアルゴリズムを用いて、データセットに機械学習モデルを実装します。

```java
/**
 * bagging
 */
Bagging learner2 = new Bagging();
learner2.setClassifier(new RandomTree());
learner2.setNumIterations(25);
learner2.buildClassifier(datasetInstances);
```

Weka API の `Stacking()` メソッドを使って、Stacking アルゴリズムを用いた機械学習モデルを実装してみましょう。

```java
/**
 * stacking
 */
Stacking learner3 = new Stacking();
learner3.setMetaClassifier(new Logistic());
Classifier[] classifiers = {new J48(), new NaiveBayes(), new RandomForest()};
learner3.setClassifiers(classifiers);
learner3.buildClassifier(datasetInstances);
```

上記のコードでは、決定木、ナイーブベイズ、ランダムフォレストのアルゴリズムを使って作られた3つのモデルを積み重ねています。

そして最後に、Voting アルゴリズムがどのように機能するかを説明します。上記のベースとなる学習器や分類器、つまり決定木、ナイーブベイズ、ランダムフォレストの学習器の出力を集約したいと思います。以下のように Weka API の `Vote()` 関数を使用します。

```java
/**
 * voting
 */
Vote vote = new Vote();
vote.setClassifiers(classifiers);
vote.buildClassifier(datasetInstances);
```

これらのモデルを評価するには、Weka API が提供する `Evaluation()` 関数を使用します。しかし、その前に以下のように関数をインポートしてください。

```java
import weka.classifiers.Evaluation;
```

その後、関数をインスタンス化し、評価指標を取得することができます。

```java
Evaluation eval = new Evaluation(datasetInstances);
eval.evaluateModel(classifiers, datasetInstances);
```

そして、評価指標のサマリーを出力することができます。

```java
System.out.println(eval.toSummaryString());
```

## 予測する

我々は、ある人が屋外で遊ぶかどうかを知るために予測することができます。そのためには、データセットの最後のインスタンスを利用します。以下のように、Weka の `ClassifyInstance()` 関数を使用します。

```java
Instance pred = datasetInstances.lastInstance();
double answer = classifier.classifyInstance(pred);
System.out.println(answer);
```

## モデルのコンパイルと実行

モデルをコンパイルして実行するには、Weka API が必要です。以下のURLからダウンロードしてください。

<http://www.java2s.com/Code/Jar/w/weka.htm>

次に、`gsadm` ユーザでログインします。作成した `.java` ファイルを GridDB の `bin` フォルダに移動しましょう。移動先は以下の通りです。

/griddb_4.6.0-1_amd64/usr/griddb-4.6.0/bin

Linux端末で以下のコマンドを実行し、gridstore.jarファイルのパスを設定します。

```bash
export CLASSPATH=$CLASSPATH:/home/osboxes/Downloads/griddb_4.6.0-1_amd64/usr/griddb-4.6.0/bin/gridstore.jar
```

次に、以下のコマンドを使用して `.java` ファイルをコンパイルします。

```bash
javac -cp weka-3-7-0/weka.jar EnsembleLearningProject.java
```

以下のコマンドを実行して、生成された.classファイルを実行します。

```bash
java -cp .:weka-3-7-0/weka.jar EnsembleLearningProject
```

プロジェクトコードは正常に実行されるはずです。予測の出力は1.0になるはずで、これは屋外で遊ぶことができることを意味します。