2016.10. 8.
2017.10. 1. OpenCV3.3.0とVisual C++ 2017で確認済み、このままのプログラムで使用可
石立 喬

OpenCV3.1.0とVisual C++ 2015による画像処理と認識(15)

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

 これは、「OpenCVとVisual C++による画像処理と認識(15)」をOpenCV3.1.0に書き換えたもので、最も優れていたRBFカーネルを用いて4種類の訓練データで分類を行った。さらに、OpenCV3.1.0で追加されたCHI2カーネルとINTERカーネルについても試してみた。
 前記資料で紹介した基礎的な内容は省略してある。

サポートベクターマシン(Support Vector Machine、SVMと略す)
 サポートベクターマシンは、基本的には、データの集合を、二つのクラスに分類するバイナリ型処理で、分類には超平面(hyperplane)が必要になる。超平面とは、多次元の空間を二つに分割する仕切りのようなもので、空想的な存在である。しかし、実用的には、文字認識などのように、「どの文字であるか」のマルチクラス型が必要で、バイナリ型を組み合わせて対応する。
 超平面を含む仮想空間に変換するために、カーネルと呼ばれる変換関数を用いる。RBF(Radial Basis Function)は、その一つである。

OpenCV3.1.0になって変わったところ
◎モジュール構成
 従来は、Machine Learningモジュール→Support Vector Machinesサブモジュールの下に、CvSVMクラスがあり、そのメンバーメソッドであるcreate(生成)、train(訓練)、predict(予測、分類)を使用してSVMを作動させていた。また、同じサブモジュール内のCvSVMParamsクラスで各種パラメータの設定を行い、trainメソッドに渡していた。
 OpenCV3.1.0では、サブモジュールが無くなり、すでに解説したニューラルネットワークのANN_MLPなどと同様に、直接、SVMクラスが配置された。ANN_MPLと同様に、SVMクラスのメソッドはcreateと、各種パラメータを設定するset******などだけになり、trainメソッド、predictメソッドは、StatModel基底クラスのものを使用する。
◎カーネルの種類が増えた
 従来のLINEAR、POLY、RBF、SIGMOIDに、OpenCV3.1.0では、CUSTOM、CHI2、INTERが追加された。
   CUSTOM ----- カスタムカーネルを作成した場合に使用
   CHI2 -------- K(xi, xj) = exp(-γx2(xi, xj))、 ただし x2(xi, xj) = (xi - xj)2 / (xi + xj)
   INTER ------- K(xi, xj) = min(xi, xj)の総和、ヒストグラム交差法を使用
◎trainメソッド
 すでに説明してあるml::StatModel基底クラスのメソッドであるが、SVM::C_SVCとSVM::NU_SVCのcategoricalタイプに対しては、training_dataをCV_32F型の配列に、labelsをCV_32S型(OpenCV2.4ではCV_32F型であった)の配列にする必要がある。
◎predictメソッド
 これも、すでに説明したが、分類する対象のsamplesはCV_32F型の配列であることが要求され、分類結果の戻り値はfloat型である。これは、OpenCV2.4以来変わっていない。しかし、OpenCV3.1.0で訓練の際のlabelsがint型になったのに、分類結果がfloat型のままであるのは注意を要する。

RBFカーネルで4種類の訓練パターンを分類するプログラム
 「OpenCVとVisual C++による画像処理と認識(15)」で、SVMのカーネルタイプではRBFが良く、そのパラメータでは、C = 2.0、gamma = 0.0005が良いとの結論が出ているので、この条件を使用して、OpenCV3.1.0に書き換えた。
 訓練用データとして、XY座標を表す二次元データを25個用意し、これに0〜3の分類ラベルを与えてSVMを訓練する。その後、すべてのXY座標を入力データとして順次SVMに与え、分類結果を調べる。
 プログラムは、下記から成っている。
 1)25個の訓練用データに対応する分類ラベルパターンを4種類作成する(図1)。
 2)上記の4種類のパターンから一つを選択して、SVMに与える訓練用分類ラベルlabels配列(CV_32Sタイプ)を設定する(図1)。
 3)訓練用データとなるXY座標を25個作成し、SVM入力用のtraining_data配列(CV_32Fタイプ)を設定する(図2)。
 4)SVMを生成し、その各種パラメータを設定して、trainメソッドで訓練する(図2)。
    SVMのタイプ、カーネルの種類、パラメータの設定は、ここで行う。
    TermCriteriaは、デフォルト値を使用する。これは、さきの資料「・・・による画像処理と認識(15)」より厳しくなった。
 5)SVMに、あらゆる組み合わせのXY座標を入力し、predictメソッドで得た分類結果を色で画像mage_outに書き込む(図3)。
 6)訓練用データの座標を画像image_out上に書き込み、image_outを表示する(図3)。


図1 訓練用ラベルを4種類用意し、切り替えて使う



図2 訓練用データを準備し、SVMを訓練する(設定値は一例)



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


RBFカーネルによる結果
 RBFカーネルを使用し、C = 2.0、gamma = 0.0005で得た結果を図4に示す。いずれも、正しく分類できている。OpenCV2.4による結果と変わりはない。


図4 RFBカーネルによる結果


CHI2カーネルによる結果
 OpenCV3.1.0で追加になったCHI2カーネルを試してみた。RBFカーネルの場合よりもgammaを大きくする必要があり、すべての訓練パターンに同じgammaを使うことはできなかったが、結果は良好であった。CHI2カーネルのために変更したプログラムを図5に示す。結果を示す図6の左上のパターンでは、gammma = 0.1が良く、右上のパターンでは、gamma = 0.11を使用した。左下のパターンは、再びgamma = 0.1が良く、右下のパターンでは、広範囲に訓練不可能域があり、gamma = 0.13でようやく解が得られた。一般的に、gammaを減らすと訓練不可能になり、必要以上に大きくするとoverfitting(訓練に使用したデータは正しく分類するが、その他への汎化ができない)状態になる。


図5 CHI2カーネルのためのプログラム、gammma = 0.1は一例



図6 CHI2カーネルによる結果、RBFより優れているかも


INTERカーネルによる結果
 これも、OpenCV3.1.0で追加になったカーネルである。gammaは使わないので、その設定は不必要である。
   svm->setKernel(SVM::INTER);
のみで良い。
 得られた結果を図7に示すが、これらの訓練パターンしか訓練できず、分類できなかった。左のパターンは正しく分類されておらず、Cをこれ以上増やすと、訓練不可能になる。中央のパターンはC = 2.0(精度よく訓練することを要求)でも、訓練可能で、正しく分類されている。右は、これ以上のCでは訓練不可能で、若干の誤分類が見られる。同心円状のパターンは、全く訓練不可能であった。これらの訓練不可能は、TermCriteriaを厳しく設定(デフォルト値は厳しい)したのも一因と考えられる。


図7 INTERカーネルによる結果、誤分類が多い


結 論
 「OpenCVとVisual C++による画像処理と認識(15)」で、SVMのカーネルの中では、RBFが最も優れ、推奨されていることを紹介した。ここでは、OpenCV3.1.0の形式にプログラムを書き換え、RBFを確認したところ、従来と同様の結果が得られた。
 OpenCV3.1.0で新しく追加になったカーネルを試みた。CHI2カーネルについては、分類するパターンに応じて最適のgammaを選ぶ必要があった(RBFの場合は、gamma = 0.0005で、すべてのパターンで十分な結果が得られた)が、非常に良好な結果が得られた。INTERカーネルでは、Cを小さくして、訓練時の誤差の許容範囲を緩くして、ようやく訓練が可能になったが、結果には誤分類が多かった。同心円状のパターンは、全く訓練できなかった。
 カーネルに対する評価はむつかしい。今回使用した訓練サンプルに限った評価であることを、念のために付け加える。




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