2015. 3.11.
石立 喬

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

----- ニューラルネットワークで数字と文字を識別する -----

 ニューラルネットワークは、脳細胞であるニューロンの結合の働きを模したシステムで、特徴量のような具体的な判定基準を与えなくても、訓練(学習)のみで識別能力を持たせることができる。研究は古くからあるが、計算時間がかかることと、判定に論理性が薄いところから、他の技法に関心が移った感じもある。
 ここでは、OpenCVが提供する、比較的新しい方式のRPROPアルゴリズムと、従来の基本的なバックプロパゲーションとを比較しながら、文字の認識を試みた。

ニューラルネットワーク
 ニューラルネットワーク(Neural Network)は、ニューロン(Neuron、脳神経細胞)が結合してできたネットワーク(Network、回路網)のことで、OpenCVでは、特にANN(Artificial Neural Network、人工的脳神経回路網)として、コンピュータ上のプログラムであることを明確にしている。ここでは、以後、ニューラルネットワークやニューロンは、人工的を省略して使用する。
 ニューロンは、複数個の入力端子を持ち、信号が来ると、それぞれの端子に設定された重みを掛けて加算し、一定のしきい値を超えた分を活性化関数(activation function)に入れ、その結果を出力端子から出す。
 ニューラルネットワークは、これらのニューロンを入力層(判定すべき入力群を与える)、隠れ層(ネットワークの外部とは接続の無い、機能向上のためのもの)、出力層(判定結果として一部ののニューロンのみが信号を発する)に割り当てて構成する。OenCVでは、これをMLP(Multi-Layered Perceptron、多層パーセプトロン)と呼んでいる。
 ニューラルネットワークは、そのままでは判定能力がない。あらかじめ既知の訓練用データを入力層に与え、出力層に現れた結果を教師データ(分類用データ)と比較し、誤差をフィードバックしてニューロンの重みを修正し、これを正解を出すまで繰り返す。これを、バックプロパゲーション(Backpropagation、誤差逆伝播)アルゴリズムと言う。OpenCVには、この古典的な逐次型アルゴリズム(BACKPROPと称す)のための訓練メソッドと、高速化されたRPROP(Resilient Propagation、弾力的バックプロパゲーション)アルゴリズムの訓練メソッドの両者が用意されていて、後者がデフォルトである。

ニューラルネットワークで、プログラム上で作成した数字パターンを判定する
 訓練用データとして、数字パターン0〜9を教え込み、その後、訓練用データを順次ニューラルネットワークに与えて、正しく訓練されているかどうかを調べる簡単な数字判定プログラムで、下記から成っている。
 1)どの程度のニューロンを使用するのかを決めるレイヤー構成layerSizeを設定する(図1)。
    入力層 --- 5x7=35(ドット)、隠れ層 --- 10、出力層 --- 10(種類)とする。隠れ層の数は経験による。
 2)訓練して教え込むデータを用意する。ここでは、「0」〜「9」の数字を5x7で作成する(図1)。
 3)訓練用データを表示して確認する(図2、結果を図3に示す)。
 4)訓練用教師データを作成して、「入力した数字が0の場合は、出力0が1で、他は0」などを教える(図4)。
 5)createメソッドで、シグモイド関数を用いるニューロンを生成する(図4)。
 6)criteriaで、訓練回数と打ち切り条件を設定する(図4)。
 7)paramsで、上記criteriaの他、使用アルゴリズムの種類、収束条件などを設定する(図4、RPROPの場合の例)。
 8)ニューラルネットワークにテスト用データを順次与え、predictメソッドで判定をさせ、BPROPアルゴリズムを使った結果を表示する(図5、結果は図6)。
 9)同様に、RPROPアルゴリズムを実行する(結果は図7)。

使用したOpenCV関数の説明
◎CvANN_MLPクラス
 ニューラルネットワークの基本クラスで、次の三つのメソッドを使用する。
 createメソッド ---- ニューラルネットワークを生成する

     create(レイヤー構成、活性化関数、パラメータ1 = 0、パラメータ2 = 0);

レイヤー構成は、Mat(n,1,CV_32SC1)形式で与える。nはレイヤー(層)の数で、隠れ層が1層の場合は、3となる。活性化関数は、CvANN_MLP_SIGMOID_SYM(シグモイド関数)を使用する。パラメータ1,2はデフォルト値が0になっているが、パラメータ1 = 0.6〜1、パラメータ2 = 1が推奨値である。;
 trainメソッド ------ 訓練を実行する

     train(入力データ、教師データ、Mat(), Mat(), パラメータ);

入力データは、CV_32Fで、row(縦)方向にデータの種類、column(横)方向にデータの内容を入れておく。教師データは、row(縦)方向にデータの種類、column(横)方向に出力として期待する値を入れる。パラメータは、後述するCvANN_MLP_TrainParams構造体のオブジェクトを入れる。Mat()は、デフォルトのままとする。
 predictメソッド ---- 入力データを与えて、判定結果を出力させる

     predict(入力データ、判定結果);

判定結果は、Mat(1,出力数, CV_32F)の形式で得られる。
◎TermCriteriaクラス
 繰り返し処理を終了させる条件を設定するクラスで、パラメータには次のものがある。
 type ---------- maxCountのみを使う場合には、TermCriteria::COUNT、epsilonのみは、TermCriteria::EPSとする。
             両方を使う場合には、「|」(論理和記号)または「+」を使って併記する。
 maxCount ------ 訓練の最大繰り返し回数を設定する
 epsilon ------- 繰り返しごとの誤差の変化がこれ以下になれば繰り返しを打ち切る
◎ CvANN_MLP_TrainParams構造体
 以下に、パラメータの主なもの(実際に使用したもの)を示す。
 term_crit -------- criteriaを、そのまま持ってくる。
 train_method ----- CvANN_MLP_TrainParams::BACKPROP = 0 または CvANN_MLP_TrainParams::RPROP = 1(デフォルト)とする。
 bp_dw_scale ------ BACKPROPに使用し、重みの変更の度合いを指定する。0.05〜0.1が推奨値。
 bp_moment_scale -- BACKPROPに使用し、重みの大きな変化を抑制する。0.05〜0.1が推奨値。
 rp_dw0 ----------- RPROPに使用し、重み変更量の初期値。0.1を推奨。
 rp_dw_min -------- RPROPに使用し、変更量の最小値。10-6を推奨するが、FLT_EPSOLON(1.192092896F-07F)の例が多い。


図1 ニューラルネットワークの構成を決め、訓練用データを用意する



図2 訓練用データtraining_dataの1,0を0,255の濃度に変換して表示する



図3 訓練用データが表示された



図4 対角線が1で、他は0の教師テータを作成し、訓練条件を設定する



図5 結果配列resultsの列の内容を順次表示する



図6 BPROP(古典的なBackpropagation)アルゴリズムでは訓練がやや不十分である



図7 RPROPアルゴリズムではすばらしい結果が得られた


判定結果を出力するプログラム
 上で説明したプログラムは、すべての出力をアナログ的に表示した。しかし、実用的には、判定結果のみが重要であるので、minMaxLoc関数を使って、結果resultsの中から最大の値を持つ位置を求め表示した。なお、参考のために、その時のアナログ値も表示させた。プログラムを図8に、結果を図9に示す。minMaxLoc関数については、「・・・による画像処理と認識(12)」に述べてある。


図8 アナログ値ではなく、判定結果として表示するプログラム



図9 入力と判定結果は一致している


隠れ層の数、訓練繰り返し回数の確認
 これまで、隠れ層の数を10、繰り返し回数を100として、ニューラルネットワークを試みたが、これらが適切であったかどうかを確認した。
 結果は表1の通りで、隠れ層の影響はBACKPROPで顕著で、6ではかなり劣化し、12では改善されるものの、十分ではない。RPROPでは、隠れ層を減らしても、その影響は僅少で、一方、増やして変化がなかった。
 繰り返し回数についても、BACKPROPは回数の影響を強く受け、RPROPに比し劣っている。

表1 隠れ層のニューロンの数、繰り返し回数を変化させた場合の結果


ニューラルネットワークで、読み込んだ画像のアルファベットを判定する
 訓練用データとして、文字画像A〜Jを教え込み、その後、訓練用データと同じパターンを順次ニューラルネットワークに与えて、正しく訓練されているかを調べる。プログラムは下記から成っている。
 1)レイヤー構成layerSizeを設定する(図10)。
    入力層 --- 12x14=168(ドット)、隠れ層 --- 30、出力層 --- 10(種類)
 2)入力データとして、12x14から成る「A」〜「J」の文字を読み込む(図10)。
 3)訓練用データを作成する。10個の入力データを変換して、タテ10、ヨコ168の二次元配列にする(図10)。
 4)訓練用データを表示して確認する(図11、結果を図12に示す)。
 5)訓練用教師データを作成し、createメソッドでRPROP使用のニューラルネットワークを生成し、criteria、paramsで、各種設定を行う(これらは、すべて図4と同じなので省略)。
 6)ニューラルネットワークにデータを順次与え、predictメソッドで判定をさせ、結果を表示する(図13、結果は図14)。


図10 構成を設定し、訓練用データを用意する



図11 training_dataのピクセル濃度で、四角形を描画する



図12 中間調を含む画像を訓練用データとして用いる



図13 文字配列input[i] を使って入力の順番をアルファベットに変換する



図14 RPROPアルゴリズムでは、完ぺきに近い結果が得られた


結 論
 訓練用データをそのままテスト入力として使った限られた条件での結果ではあるが、BACKPROPに比べると、デフォルトのRPROPの方が、少ない繰り返しの訓練でも、十分の精度で判定ができた。1,0のドットで表した小規模の数字パターンでの結果と、中間調を含むやや大きいアルファベット画像での結果を比較すると、後者の方が高精度で判定できた。

参考文献
 M.Riedmiller and H.Braun, "A Direct Adaptive Method for Faster Backpropagation Learning"(1993)
 http://deeplearning.cs.cmu.edu/pdfs/Rprop.pdf




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