2008年06月12日
SVMでパターン認識
SVM(サポートベクターマシン)でちょっと遊んでみる。
以前はk-NN法でパターン認識したが今回はSVMだ。
SVMは・・・えーと・・・どんなのかというと・・・、うーんと・・・・難しくて理解不能でしたorz
理解しようと思っても、各要素分野がすでに難しすぎるのでさっぱり。
詳しくはサポートベクターマシン入門とか。
教師有り学習のパターン認識で、TinySVMを使えばサクッと使うことができると理解しておけばとりあえず良いだろう(ホントか?)
学習内容は、例によって5x5マスに書いた数字を認識するというものだ。
以下のように点の有る無しを0,1で表す。
1 1:0 2:1 3:1 4:0 5:0 6:0 7:0 8:1 9:0 10:0 11:0 12:0 13:1 14:0 15:0 16:0 17:0 18:1 19:0 20:0 21:0 22:1 23:1 24:1 25:0
2 1:0 2:1 3:1 4:1 5:0 6:0 7:0 8:0 9:0 10:1 11:0 12:0 13:0 14:1 15:0 16:0 17:0 18:1 19:0 20:0 21:0 22:1 23:1 24:1 25:1
3 1:0 2:1 3:1 4:1 5:0 6:0 7:0 8:0 9:0 10:1 11:0 12:0 13:1 14:1 15:0 16:0 17:0 18:0 19:0 20:1 21:0 22:1 23:1 24:1 25:0
4 1:1 2:0 3:0 4:1 5:0 6:1 7:0 8:0 9:1 10:0 11:1 12:1 13:1 14:1 15:0 16:0 17:0 18:0 19:1 20:0 21:0 22:0 23:0 24:1 25:0
5 1:1 2:1 3:1 4:1 5:1 6:1 7:0 8:0 9:0 10:0 11:1 12:1 13:1 14:1 15:1 16:0 17:0 18:0 19:0 20:1 21:1 22:1 23:1 24:1 25:1
6 1:1 2:1 3:1 4:1 5:1 6:1 7:0 8:0 9:0 10:0 11:1 12:1 13:1 14:1 15:1 16:1 17:0 18:0 19:0 20:1 21:1 22:1 23:1 24:1 25:1
7 1:1 2:1 3:1 4:1 5:1 6:0 7:0 8:0 9:1 10:0 11:0 12:0 13:1 14:0 15:0 16:0 17:0 18:1 19:0 20:0 21:0 22:0 23:1 24:0 25:0
8 1:1 2:1 3:1 4:1 5:1 6:1 7:0 8:0 9:0 10:1 11:1 12:1 13:1 14:1 15:1 16:1 17:0 18:0 19:0 20:1 21:1 22:1 23:1 24:1 25:1
9 1:1 2:1 3:1 4:1 5:1 6:1 7:0 8:0 9:0 10:1 11:1 12:1 13:1 14:1 15:1 16:0 17:0 18:0 19:0 20:1 21:1 22:1 23:1 24:1 25:1
10 1:1 2:1 3:1 4:1 5:1 6:1 7:0 8:0 9:0 10:1 11:1 12:0 13:0 14:0 15:1 16:1 17:0 18:0 19:0 20:1 21:1 22:1 23:1 24:1 25:1
10行目は「数字の0」なんだけどClassIDは10にする。これはClassIDを0にすると未定義という意味になるからだ。
そしてこれをnumber.learn.datに保存し、以下のように学習。
$ svm_learn -l 1 number.learn.dat number.model.dat
学習したモデルをテストする。以下のようにして実行。学習データでテストするので全問正解してくれないと困る。
$ svm_classify -V number.learn.dat number.model.dat
1 1.09901
2 2.10007
3 3.10051
4 4.10014
5 5.65482
6 6.23774
7 6.90056
8 7.89958
9 7.31665
10 9.90014
Accuracy: 100.00000% (10/10)
Precision: 100.00000% (10/10)
Recall: 100.00000% (10/10)
System/Answer p/p p/n n/p n/n: 10 0 0 0
あれ?9が7.31665で間違ってるように思えるがスコアはどれも100%だ。これはこれで良いのか??謎。
他はおおむね良好。
このモデルをC++のプログラムから使う。
それには以下のようにする。
TinySVM::Model m;
if (! m.read("number.model.dat")) throw;
cout << m.classify("1:0 2:1 3:1 4:0 5:0 6:0 7:0 8:1 9:0 10:0 11:0 12:0 13:1 14:0 15:0 16:0 17:0 18:1 19:0 20:0 21:0 22:0 23:0 24:0 25:0") << endl;
下のようなちょっと形を変えた1を食わせてみる。
.xx..
..x..
..x..
..x..
..x..
結果は・・・2.64268。あれ?
戻り値はReturn distance from the decision hyperplane. とあるけど超平面からの距離=識別結果のクラスID??なんだかよくわからないな。
仕事でよりよいパターン認識をする必要があってSVMを使おうかとちょっといじってみたけど、この理解度ではまだ手を出さない方が良さそうだな。昔の偉い人は正体不明のものを使って失敗したときに「よくわからないものを無理して使うからよ」と言って怒ったことだし。
SVMは基本的には二値分類器なので、「AなのかBなのか」の判定に使うものです。
この例のように10クラスに分類するためには少し工夫が必要です。(例:pairwise 法、one versus rest 法)
上で用いている "-l 1" というオプションは SVR用 、つまり SVMを用いた回帰分析(regression)のためのもののようですね。5x5 マス上の数字を回帰曲線できれいに分離するのはむずかしいと思うので、解いている問題がちょっと違ってしまっているのではないかと。
(カーネルを -t 1 とか -t 3 とかでやったらひょっとするとうまくいくかも?)
-t 1と-t 3試してみたけどダメでした。
ベクトル空間が25次元しか無いのがSVMには厳しいのかな。
もっと多次元であればマージン最大化の要素が生きてくるとか。
5、6、8、9がそれぞれ点が1個違うぐらいなので厳しいと言えば厳しいですね。