2015. 3.24.
石立 喬

OpenCVとVisual C++による画像処理と認識(15)

----- サポートベクターマシンを使ってみる -----

 精度が高い実用的な画像認識や識別を行うには、やはり、サポートベクターマシが有用になる。入力として、HOGなどの特徴量を用いるのが一般的である。本格的には、多数の真サンプルと偽サンプルで訓練する必要があるので、サンプルの収集と訓練に多大の労力と時間が必要になる。
 ここでは、OpenCVが提供するサポートベクターマシンのクラスやメソッドを理解するために、簡単な実証を行った。

サポートベクターマシン(Support Vector Machine、SVMと略す)
 サポートベクターマシンは、基本的には、データの集合を、二つのクラスに分類する処理で、個々のデータが多次元のベクトルから成る場合(一つのデータは多くのデータの集まりである)、分類には超平面(hyperplane)が必要になる。超平面とは、多次元の空間を二つに分割する仕切りのようなもので、空想的な存在である。この超平面を算出するのに寄与したデータをサポートベクトルと呼び(得られた超平面から離れた訓練データと区別して・・・)、これがサポートベクターマシンと呼ばれる理由である。
 上述のように、SVMの原理は、本来、「顔であるか無いか」などの2クラスに分けるバイナリ型である。しかし、実用的には、文字認識などのように、「どの文字であるか」のマルチクラス型が必要で、バイナリ型を組み合わせて対応している。OpenCVの場合、二つの型を区別する必要はなく、訓練の際のラベルに、+1と-1や、0と1のみの二値でなく、0〜25などの多数のラベルを与えると。マルチクラス型になる。
 入力データとして、読み取った画像やパターンのピクセル値をそのまま用いるのではなく、実際には、すでに述べた特徴量を抽出して入力データとすることにより、精度を向上させる。ただし、ここでは、SVMを理解し、実証するために、生のままのデータを使用した。

非線形SVMとカーネル関数
 基本的なSVMは、入力データをそのまま用いる線形SVMであるが、これでは、二つのクラスへの分類が不可能な場合が多く生じる。そこで、カーネル関数(kernel function)を用いて高次元の別の座標空間に写像し、線形空間では複数の場所に分散していた領域でも、一体化することを可能にした。これを非線形SVMと呼び、代表的な非線形カーネルに、多項式カーネル(POLY)、ガウス放射基底関数カーネル(RBF)、シグモイド関数カーネル(SIGMOID)がある。カーネルを用いない線形SVMの場合は、線形カーネル(LINEAR)として明示的に設定する。速度的には線形SVMが優れているが、非線形SVMで、カーネルにRBFを用いるのが最も良いとされる。

特にRBF(radial basis function)カーネルについて
 RBFは、直訳すれば「放射状の(または半径の)基底関数」となり、ある中心から周辺に向かった距離とともに変化する関数である。「放射基底関数」または、「動径基底関数」と呼ばれる。しかし、これには特定の関数の意味がなく、SVMで使われるRBFは、正確にはガウシアンRBFであり、下記で表される。xおよびx'は写像前の多次元ベクトルである。

   

 RBF関数で調節可能なパラメータはgammaで、デフォルト値は1.0である。上式で分かるように、訓練用データの値が大きく、分散sigmaが大きいと、gammaを小さくする必要がある。ここでは、最大値が200程度になる座標値を、そのまま訓練用データとして用いたので、gamma = 0.0005のような小さな値を選ぶ必要があった。
ハードマージンSVMとソフトマージンSVM
 分類誤りを全く許容しないと、超平面が得られない場合が多い。そこで、一定量以下の誤りを容認するのがソフトマージンSVMである。これに対して、誤りを許容しない基本形のSVMをハードマージンSVMと呼ぶ。ソフトマージン化のためにペナルティ係数Cを導入し、誤判定量にCをかけて最適化を行う。Cが無限大の場合は、誤判定量が0となるハードマージンSVMになる。Cのデフォルト値は1.0である。OpenCVの資料によると、パラメータ名がCvalueとなっているが、これは使用できず、単にCとする必要がある。他に、Mat型のパラメータclass_weightsがあるが、これは、分類するラベルごとにCを変えるための重みである。

簡単な2クラス分類で、各種カーネルを比較する
 訓練用データとして、XY座標を表す二次元ベクトルを用意し、これに0か1かの分類ラベルを与えてSVMを訓練する。その後、すべてのXY座標を入力データとして順次SVMに与え、分類結果を調べる。この際、使用するカーネルによる違いを比較する。
 プログラムは、下記から成っている。
 1)個々の訓練用データに対して設定する分類ラベルパターンを作成する(図1)。
    斜め方向の分類、水平方向の分類、三角形での分類、飛び地のある分類の4種類
 2)上記の種類から一つを選択して、SVMに訓練用分類ラベルとして与えるMat型labelsを設定する(図1)。
 3)訓練用データとなるXY座標を25個作成し、SVM入力用のMat型training_dataを設定する(図2)。
 4)SVMパラメータを設定し、trainメソッドで訓練する(図2)。
    カーネルの種類、パラメータの設定は、ここで行う。
 5)SVMに、あらゆる組み合わせのXY座標を入力し、predictメソッドで得た結果を画像out_imageに書き込む(図3)。
 6)訓練用データを画像out_image上に書き込み、out_imageを表示する(図3)。

使用したOpenCVクラス、構造体の説明
 以下は、頭のCvを付けなくても使用できる(typedef設定あり)。
◎CvSVMクラス
 SVMの基本クラスで、次の二つのメソッドを使用する。コンストラクタを使用しなくでも、trainメソッドで、詳細の設定ができる。
 trainメソッド ------ 訓練を実行する。

     train(訓練データ、訓練用分類ラベル、Mat(), Mat(), パラメータ);

 訓練用データは、CV_32FC1のMat型で、一つのrowに一つのデータベクトルを入れておく。rowの数がデータの数である。訓練用分類ラベルは、CV_32FC1のMat型で、labelsやresponsesと呼ばれる。訓練用のデータのそれぞれが、どのクラス(ラベル)に属すべきかを教えるもので、一つのrowのみに、直列的に順次入れておく。
 パラメータには、後述するCvSVMParams構造体のオブジェクトを入れる。Mat()は、デフォルトのままとする。
 predictメソッド ---- 入力データを与えて、分類結果responseを返す。

     response = predict(入力データ);

 戻り値は、Mat(1,分類ラベル値, CV_32F)の形式で得られる。
◎CvTermCriteria構造体
 繰り返し処理を終了させる条件を設定する構造体で、パラメータには次のものがある。
 type ---------- 最大繰り返し回数のみは、CV_TERMCRIT_ITERとする。精度のみは、CV_TERMCRIT_EPSとする。
             両方を使う場合には、「|」(論理和記号)または「+」を使って併記する。
 max_iter ------- 最大繰り返し回数を設定する。
 epsilon -------- 繰り返しごとにこれ以上変化がなければ終了する値を設定する。
 
    TermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 100, FLT_EPSILON);

のように、コンストラクタを使って設定しても良い。
◎CvSVMParams構造体
 SVMの各種パラメータを設定する構造体で、下記のパラメータを設定できる。
 svm_type ------ SVMのタイプで、CvSVM::C_SVC他がある。
 kernel_type ---- CvSVM::LINEAR、CvSVM::POLY、CvSVM::RBF、CvSVM::SIGMOIDがある。
 degree ------- POLY用の係数
 gamma ------- POLY、RBF、SIGMOID用の係数svm_typeに応じて個別に設定し、共用できない。
 coef0 -------- POLY、SIGMOIDで使う。これも共用できない。
 C ----------- C_SVC他のタイプで使う。これも共用できない(説明書にはCvalueとなっているが、使えない)。
 nu、p -------- 省略
 class_weights -- C_SVCで使用し、分類クラスに応じてCを変えたいときの係数ベクトルである。
 term_crit ------ CvTermCriteria構造体を指定する。
 これらは、個別に設定しても良いし、コンストラクタで一度に設定しても良い。


図1 訓練用ラベルを用意する



図2 訓練用データを準備し、SVMを訓練する



図3 分類結果と、元になった訓練用データを表示する


2クラス分類での各種カーネル比較の結果
 図4に、容易に分離できる場合の結果を示す。いずれのカーネルでも、gamma = 0.0005、C = 1.0の設定で十分に分離できている。図5は、三角形状と飛び地のある、やや複雑な場合で、LINEARは全く無能力、POLYは誤差が多いものの、使える可能性があり(後に詳述するパラメータCとgammaを調整すると、やや改善が見られた)、RBFは優秀であった。


図4 単純な分類は、どのカーネルも良好である



図5 難しい分類は、RBFカーネルしかできない


簡単なマルチクラス分類で、LINEARカーネルとRBFカーネルを比較する
 2クラス分類と同じ条件(gamma = 0.0005、C = 1.0)でマルチクラス分類を試みた。カーネルは、基本的なLINEARと、最も推奨されるRFBのみに絞った。
 入力を0〜3のラベルに分類するパターンを図6で設定し、4種類の結果を図示するために、図7のプログラムを用いた。
 得られた結果を図8と図9に示す。一本の直線で分割できる場合には、LINEARカーネルでも十分な成果が得られているが、同心円状のような場合では、LINEARでは意味不明の分割直線ができてしまう。RBFの場合には、同心円状にはなっているものの、0ラベルがなく、2ラベルが広すぎるなど、正しくない。これは、後述のように、パラメータの詳細設定により改善できる。


図6 マルチクラス分類のためのラベルパターン



図7 マルチクラスのために出力種類を増やした



図8 比較的単純な分類はどちらのカーネルでもできる



図9 同心円状の難しい分離は、RBFが可能性あり


RBFカーネルで、パラメータCとパラメータgammaを変えてみる
 すでに説明した通り、SVMのタイプにC_SVCを使い、カーネルにRBFを使った場合(いずれもデフォルトの推奨条件)、パラメータとして調節できるのは、誤分類の許容度を表すCパラメータと、写像時にガウス分布の広がりを表すパラメータgammaのみである。したがって、最も難しい同心円上の訓練データパターンについて、この二つを変化させて、分類能力を調べた。
 図10〜12は、gammaを変えながら、Cを次第に大きくして誤分類を厳しく制限したもので、これらの結果で見る限り、C = 2.0、gamma = 0.0005当たりが良いかと思われる。


図10 gamma = 0.0002の場合、同一ラベルの範囲が広すぎる



図11 gamma = 0.0005の場合、これが最も良い



図12 gamma = 0.001の場合、同一ラベルの範囲が狭過ぎるものもある


数字パターンでの訓練とテスト
 前項の「・・・による画像処理と認識(14)」で、ニューラルネットワークに用いたのと同じ数字パターンを用いて、SVMを訓練し、結果を確認した。カーネルLINEARでも、RBFでも、デフォルト設定で十分の性能が得られた。プログラムの一部と結果を、図13と図14に示す。


図13 SVMを文字パターンで訓練し、テストする



図14 文字認識で得られた結果


結 論
 サポートベクターマシン(SVM)には、線形と非線形がある。線形SVMは高速であるが、分類不可能なケースが発生する。一方、非線形SVMは、カーネルとしてガウシアンRBFを用いると、非常に優れた分類能力を示す。したがって、実用的には、ほとんどが非線形SVMであり、訓練に時間がかかる欠点があるが、一度訓練しておけば良いので、あまり問題にならない。
 ここでは、線形SVMと非線形SVMについて、簡単な例で比較したが、上述の説明を裏付ける結果になった。RBFの唯一の設定パラメータであるgammaの選択は難しい。分類するデータの値により、また、訓練に使用するデータの多寡により最適化する必要がある。誤分類を許容するパラメータCは、デフォルト値の1.0よりも大きくして厳しくする方が良かった。
 前項のニューラルネットワークで使用した数字パターンを用いて認識の確認をしたところ、訓練パターンと同様のテストパターンと言う好条件ではあったが、デフォルト設定で、十分な結果が得られた。

参考文献
 C.Cortes and V.Vapnik, "Support-Vector Networks"(1995)
 http://csee.wvu.edu/~xinl/library/papers/comp/ML/svm.pdf




「Visual C++の勉強部屋」(目次)へ