SVM(Supporort Vector Machine)は、
「データセットを二つにちょうど分類できるような線」
を見つける学習機です。2クラスに分類するため2クラス分類専門の学習機です。もちろん工夫すれば他クラス分類が可能かもしれませんが、基本的には2クラスの分類器です。なおSVMの理論は本稿では難しすぎるうえ、かなり長くなるので取り上げません。基本的な概念と実際の使い方について説明します。
2クラス分類器であるSVMは、例えば画像が顔であるかどうかについて、今顔画像(POSITIVE)と、顔でない画像(NEGATIVE)の2つに分けるような直線を求めます。
データが二次元(X,Y)でれば線を、3次元(X,Y,Z)であれば分割できるような平面を引くこと、これがSVMの目的です。次元に併せて線、平面など言い換えは面倒なので、「超平面」と呼ばれています。超平面はある次元において分割できる線や面のようなものです。(多次元ではどういう形をしているのか我々3次元の生き物からは想像ができませんね。)
上図はイメージです。実際には綺麗に切れることはありませんので、ちょっとは混じってしまいます。超平面はなるべく離れるように線をひこうと頑張ります。(ソフトマージン(点線までの距離)といいますが、コレを最大化する)。
なお、SVMで超平面でなく非線形な超平面も扱う方法があります。これを非線形SVMといいます。 非線形SVMは高次元空間に写像して無理やり超平面を引く方法です。
ただ、非線形SVMはあまり有用とは思いません。非線形だとオーバーフィッティング(いわゆる過学習)などの問題も出てきやすくなります。また、経験的にですが線形で結果が出ないものは非線形でも出ません。実際にはほとんどの空間においてですが、線形で十分なのです。
余談ですがサポートベクターとは、混じってしまったベクトルや、ソフトマージンを最大化するために利用するベクトルを指します。(線を引くために参考にするベクトルを指す)
SVMを用いた画像の分類例
SVMを用いて先程の画像を顔画像か、顔画像でないかの2つのグループに分ける方法はどうやるのでしょうか。
まず、画像を1つの配列にします。例えば64x64の画像があるとすると、それを4096次元(64×64=4096)の配列を作ります。
SVMはこの4096次元の空間において、線形にぶった切れる線(超平面)を探してくれます。SVMの学習結果では、4096個の重みの結果が返されます。
SVMからの出力はa_0, a_1, a_2, a_3.. a_nといった係数が出てきます。nは学習した次元数によります。今回の場合は4096個です。これは超平面の方程式を指しているのです。この係数を利用して、計算結果がマイナスであると不正解、プラスであれば正解となります。
SVMを実際に使ってみる
早速SVMを使ってみましょう。線形SVMで説明をします。
liblinearをインストールしてください。これはLibSVMの非線形バージョンです。
http://www.csie.ntu.edu.tw/~cjlin/liblinear/
データセットとして、次のa9aをダウンロードしておきます。
http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/binary/a9a
ダウンロード解凍後、以下のコマンドを叩きます。
make ./train -v 5 a9a
-v はクロスバリデーションといって、用意した画像を全部学習に使わず、上記例で言えば1/5だけを残して、4/5を学習に使いそれでチェックするということを行うオプションです。n-fold cross vlidationと呼ばれています。たとえば3にすれば、2/3を学習に用いて、1/3をチェックに使うのです。なお、結果としては以下のような内容が帰ってきます。
Cross Validation Accuracy = 84.8131%
クロスバリデーションの精度の結果で、これにより分類精度をチェックできます。
本番で使うときは全部学習に用いたほうがいいのでオプションの指定はしなくて構いません。クロスバリデーションは学習が上手く行ったかなど、確認するときに使います。
本学習では従って引数なしで、
./train a9a
とします。a9a.modelができているのを確認してください。その中のファイルを見ると、重みが並んでいます。(w以降の数字が重みです。)学習に使用した特徴量と、この重みをかけ合わせ、正負により判別を行うことが出来るのです。