windows環境を持っていなかったため、 ちゃんと検証ができていません。
javaのswingで実装さているので それほど影響はないと思いますが、適宜サポートします。
http://www.cs.waikato.ac.nz/ml/weka/downloading.html からDeveloper version(3.7.13)をダウンロード
※stable(3.6系)とはUIが異なっています。 混乱をさけるため、3.7を使ってください。
今回使うデータの説明です。
weka-3-7.13/data/iris.arffというデータを使います。
これはあやめの
があって、品種をそれ以外の情報から予測するのに使えるものです。
wekaではarffという形式のファイルを使います
これはCSVを拡張したようなもので、 「@」でデータに関する情報を定義できます
irisの場合
@RELATION iris
@ATTRIBUTE sepallength REAL
@ATTRIBUTE sepalwidth REAL
@ATTRIBUTE petallength REAL
@ATTRIBUTE petalwidth REAL
@ATTRIBUTE class {Iris-setosa,Iris-versicolor,Iris-virginica}
@DATA
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
...
...
@RELATIONはデータセット名
@ATTRIBUTEは各カラムの名前とその変数の型を定義しています
上4つは実数値、classはIris-setosa,Iris-versicolor,Iris-virginicaのうちどれかをとると定義されています
その他にdateやstring型も使えます
@DATA以降に実際のデータが記載されています
公式サイトのDeveloper versionを使用
http://www.cs.waikato.ac.nz/ml/weka/downloading.html
ダウンロードすると、実行ファイルとディレクトリがある。
実行ファイルをそのまま実行するか、ディレクトリ内にある jarを実行する
jarからの実行は
java -jar weka.jar
最初の概要で説明した通り観測されたデータ({sepal/petal},{length/width}) から、どの品種かを予測する機械学習を行った。
そう、これだけですでに「はじめての機械学習」ができたのです!
今回使ったのはロジスティック回帰という手法です。
これは0-1の範囲(確率値)で2つ(yes/no, o/xなど)でどちらに所属するかの 確率を出しています。
先ほどの説明でいうと\(f({\bf x})\)の\({\bf x}\)に {sepal/petal},{length/width}を入れて、\(x_1 w_1 + x_2 w_2 + ...\)を計算したのち、それをシグモイド関数というのに通して0-1の範囲に収まるようにしています
シグモイド関数は\(1/(1 + exp(z))\)の形式であわらされる関数です。
これを
の3種類分作って、正しく予測できるようにしています
いろいろ表示されますが、今回見る部分は
“=== Summary ===”より下の中の幾つかを説明します。
と、その前に機械学習の結果を評価する指標を幾つか紹介します
正解率
データ中の正解に対して、機械学習の予測結果が当たっていた割合
混同行列とそこの指標
正解率はわかりやすい指標ですが、それだけでは不十分な場合もある
たとえば、極端にデータに偏りがある場合は常にどちらかと予測しておけば、正解率は高くなるなど
なので、もっといろいろみたい
そこで使うのが混同行列
/ | oと予測 | xと予測 |
---|---|---|
正解はo | True Positive(TP) | False Negative(FN) |
正解はx | False Positive(FP) | True Negative(TN) |
と表の形式で表して
という値を計算する
この2つはトレードオフの関係にあるため、ちょうどいいあんばいとするために
をみることがよくある
実際にはxが多く含まれててもoは確実にoとしたい(Recall重視)、
oの多くがxと判定されたとしても確実にo名もののみoにしたい(Precision重視)
など実際に解きたい問題によってどの値を重視するか考える必要がある
そしてデータの見方へ…
が表示されている
と見るとなんとなく見方がわかった気がしますよね?
さて、やってみてなんとなく気づかれた方もいるかと思いますが、 機械学習をやるときにすごく大事な話があります。
それは、機械学習でやりたいことは
です。
なので、目的としては未知のデータを予測できる部分になります。
なぜ、ここを注意しておく必要があるのか、例を出して説明すると
試験勉強をするときに、過去問などをといて学習しますが、
大事なのは本番の試験結果ですよね?
その際、解けば解くほど結果はよくなるかもしれませんが、
過去問を丸暗記したところでそれが本番で役に立つとは限りません。
ただし、本番試験の問題は開始時までわからないのと同様、
未知のデータも入ってくるまでわかりません。
そこで既存のデータを分割しておき、 学習に使うものと、テストで使うものに分けておきます。
学習した後のモデルを利用してテスト用のデータをどれだけ 予測できるかで性能評価をするわけですね
実は、正解率や混同行列はこのテストデータに対しての結果でした。
また、この話で過去問が解ければ本番でもかなりいい点数を取れるのでは? と思われる方もいたかもしれません。
実は、機械学習には過学習という問題があり、 学習データに現れるパターンのみを学習してしまい、それ以外はうまくいかなくなるという状況がよく発生します。
なので、必ずテストデータは別に用意しておき、性能評価はそちらで行うわけです。
しかし、ここにも問題があって、 データを分けるとその分学習できる量が減ってしまいます。
そこで、データが少ない場合は * 全データを何個かに分割する * 分割した1つ目をテストデータ、それ以外を学習データに使用 * 分割した2つ目をテストデータ、それ以外を学習データに使用 * 3, 4, 5…と分割数分それを繰り返す * 全部のパターンでの性能評価結果の平均値を評価値として見る
ということをやります。
これはクロスバリデーションと呼ばれる手法です。
実は今やったものもこのクロスバリデーションを利用しており、
Test options > Cross-validationにチェックが入っており、 その横のフォームで分割数を設定していますね。
ここまでの内容でTest optionsの他の項目が何を意味するかなんとなくわかるかと思います
さて、GUIを使った機械学習の最後にパラメータの説明をしておきます。
Classifiersに選択した“Logistic”の他に
-R 1.0E-8 -M -1
という謎のオプチョンのようなものが付いているかと思います。
実はずっと黙っていましたが、機械学習には
ハイパーパラメータという強そうなパラメータ設定があります。
これは
などを設定するものとなります。
実際には利用する手法によって設定が必要なパラメータ数や その意味は異なります。
この値をどう設定するかによって結果の良し悪しに影響がでるのですが、 どんな値がデータによって変わってきます。
そこでグリッドサーチと呼ばれるやり方で最適な値を決めます。
このグリッドサーチというのは一定の範囲内の値で学習->テストを網羅的に 行って、一番結果が良くなるものを発見するというやり方です。
ガチでパラメータ探索をし始めるとかなり時間をくってしまうので、 今回は割愛します。
さて、実際にプログラムに組み込んで予測できるシステムを作ってみましょう!
Result listに表示されている学習させた結果を
右クリック > Save model
で保存しておきます。
今回はmavenを使う前提とします。 宗教上の理由などで他のビルドツールを使いたい場合は 対応する設定に置き換えて貰えば大丈夫ですが、自己責任でお願いします。
また、maven自体の使い方はご存知の前提とさせていただきますので、 もしわからない場合はこっそり聞いていただければと思います(堂々と聞いていただいても大丈夫です)
あ、でも私はemacs派なので、IDEのサポートはできません
まずはpom.xmlにwekaへの依存を追加します。
<dependency>
<groupId>nz.ac.waikato.cms.weka</groupId>
<artifactId>weka-dev</artifactId>
<version>3.7.12</version>
</dependency>
また、今回は簡単にmainメソッドを作って動かしてみますので、 exec-pluginも設定しておくと便利です
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>Iris</mainClass>
</configuration>
</plugin>
次にmainを実装する
public class Iris {
public static void main(String[] args) {
try {
// データセットの読み込み
DataSource source = new DataSource("iris.arff");
Instances instances = source.getDataSet();
// クラスの次元数
instances.setClassIndex(4);
// モデルを読み込む
// 先ほどsaveしたものはjavaのシリアライズされたオブジェクト
FileInputStream fis = new FileInputStream("iris.model");
ObjectInputStream ois = new ObjectInputStream(fis);
Classifier classifier = (Classifier) ois.readObject();
// 属性を設定
Attribute sepallength = new Attribute("sepallength", 0);
Attribute sepalwidth = new Attribute("sepalwidth", 1);
Attribute petallength = new Attribute("petallength", 2);
Attribute petalwidth = new Attribute("petalwidth", 3);
// 属性数を指定してinstanceを作る
Instance instance = new DenseInstance(4);
// iris.arffから適当な行の値を取ってきて入れてみたりしてください
// 実際に使うものではユーザの入力とかに対応すると思います
instance.setValue(sepallength, 6.3);
instance.setValue(sepalwidth, 3.3);
instance.setValue(petallength, 6.0);
instance.setValue(petalwidth, 2.5);
instance.setDataset(instances);
// 事例を分類させて結果を得る
// arffのclassで指定した値の左から順に1, 2, 3と数値が対応していて
// そのうちどれかが予測結果として出る
double result = classifier.classifyInstance(instance);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
さて、機械学習ができるようになったので色々試して遊んでみましょう!