Machine Learning Handson

ごめんなさい

windows環境を持っていなかったため、 ちゃんと検証ができていません。

javaのswingで実装さているので それほど影響はないと思いますが、適宜サポートします。

wekaのダウンロード

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というデータを使います。

これはあやめの

  • がくの幅と長さ(Sepal width/length)
  • 花びらの幅と長さ(Petal width/length)
  • 3種類の品種(setosa, versicolor, virginica)

があって、品種をそれ以外の情報から予測するのに使えるものです。

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以降に実際のデータが記載されています

wekaのダウンロード

公式サイトのDeveloper versionを使用
http://www.cs.waikato.ac.nz/ml/weka/downloading.html

ダウンロードすると、実行ファイルとディレクトリがある。 ファイル

  • Windowsの場合はインストール先にある?
  • Macはdmgをマウントして適当なディレクトリに移す
  • Linuxはzipを展開

実行ファイルをそのまま実行するか、ディレクトリ内にある jarを実行する

jarからの実行は

java -jar weka.jar
  • 起動する
  • Explorerを選択
  • Open file…
  • weka-3-7-13/data/iris.arffを選択
    irisにはあやめのデータが入っている
    これは品種(setosa, versicolor, virginica)の3種類と がく(sepal)と花びら(petal)の幅と長さを150件分記録したもの
    データ分析系ではわりと最初に遊ぶデータセット
  • 読み込んだら上のタブにある“Classifiy”を選択
  • Classifier > Choose
  • weka > classifiers > functions > Logistic
  • 左側中央よりやや下にある“Start”を押す
  • Classifier outputに結果が出力される

何をしたのかと出力の見方

やったこと

最初の概要で説明した通り観測されたデータ({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))\)の形式であわらされる関数です。

これを

  • データに含まれる正解がsetosaの場合は1, それ以外は0が正解として、 関数の結果が正解に近づくように\({\bf w}\)を値を調整したもの
  • データに含まれる正解がversicolorの場合は1, それ以外は0が正解として、 関数の結果が正解に近づくように\({\bf w}\)を値を調整したもの
  • データに含まれる正解がvirginicaの場合は1, それ以外は0が正解として、 関数の結果が正解に近づくように\({\bf w}\)を値を調整したもの

の3種類分作って、正しく予測できるようにしています

表示されたものは?

いろいろ表示されますが、今回見る部分は
“=== Summary ===”より下の中の幾つかを説明します。

と、その前に機械学習の結果を評価する指標を幾つか紹介します

正解率
データ中の正解に対して、機械学習の予測結果が当たっていた割合

混同行列とそこの指標
正解率はわかりやすい指標ですが、それだけでは不十分な場合もある
たとえば、極端にデータに偏りがある場合は常にどちらかと予測しておけば、正解率は高くなるなど

なので、もっといろいろみたい
そこで使うのが混同行列

/ oと予測 xと予測
正解はo True Positive(TP) False Negative(FN)
正解はx False Positive(FP) True Negative(TN)

と表の形式で表して

  • Precision
    • \(TP / (TP + FP)\)
    • oと予測したもののうち本当にoであった割合
  • Recall
    • \(TP / (TP + FN)\)
    • 本当はoであるものをoと予測できた割合

という値を計算する
この2つはトレードオフの関係にあるため、ちょうどいいあんばいとするために

  • F-measuer
    • \(\frac{2 * Precision * Recall}{Precition + Recall}\)
    • PrecisionとRecallの調和平均

をみることがよくある

実際にはxが多く含まれててもoは確実にoとしたい(Recall重視)、
oの多くがxと判定されたとしても確実にo名もののみoにしたい(Precision重視)
など実際に解きたい問題によってどの値を重視するか考える必要がある

そしてデータの見方へ…

  • Correctly Classified Instances
    • 正解率
  • === Detailed Accuracy By Class ===
    • Precision/Recall/F-measure
  • === Confusion Matrix ===
    • 混同行列

が表示されている
と見るとなんとなく見方がわかった気がしますよね?

予測、Cross Validationについて

さて、やってみてなんとなく気づかれた方もいるかと思いますが、 機械学習をやるときにすごく大事な話があります。

それは、機械学習でやりたいことは

  1. 既存のデータからパターンを見つける
  2. それを新しく入ってきたデータに適用して結果を予測する

です。
なので、目的としては未知のデータを予測できる部分になります。

なぜ、ここを注意しておく必要があるのか、例を出して説明すると  

試験勉強をするときに、過去問などをといて学習しますが、
大事なのは本番の試験結果ですよね?

その際、解けば解くほど結果はよくなるかもしれませんが、
過去問を丸暗記したところでそれが本番で役に立つとは限りません。

ただし、本番試験の問題は開始時までわからないのと同様、
未知のデータも入ってくるまでわかりません。

そこで既存のデータを分割しておき、 学習に使うものと、テストで使うものに分けておきます。

学習した後のモデルを利用してテスト用のデータをどれだけ 予測できるかで性能評価をするわけですね

実は、正解率や混同行列はこのテストデータに対しての結果でした。

また、この話で過去問が解ければ本番でもかなりいい点数を取れるのでは? と思われる方もいたかもしれません。

実は、機械学習には過学習という問題があり、 学習データに現れるパターンのみを学習してしまい、それ以外はうまくいかなくなるという状況がよく発生します。

なので、必ずテストデータは別に用意しておき、性能評価はそちらで行うわけです。

しかし、ここにも問題があって、 データを分けるとその分学習できる量が減ってしまいます。

そこで、データが少ない場合は * 全データを何個かに分割する * 分割した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();
    }
  }
}

遊んでみる

さて、機械学習ができるようになったので色々試して遊んでみましょう!

  • 手法を変えて
    • ロジスティック回帰と同様に分類に使えるアルゴリズム
    • weka > classifiers > bayes > NaiveBayes (ナイーブベイズ)
    • weka > classifiers > functions > SMO (SVM)
    • などそれぞれ特徴がるので試しみると面白いかも
  • パラメータを変えて
    • パラメータを変えた時に結果にどう影響するのかを見てみる
  • 別なデータで
    • 実際に機械学習でやりたいことのデータがあればつかってみるとか
    • weka-3-7-13/dataには他にいろんなデータがあったりします
    • UCI Machine Learning Repository