人工知能に関する断創録

このブログでは人工知能のさまざまな分野について調査したことをまとめています(更新停止: 2019年12月31日)

ニューラルネットによるパターン認識

3層パーセプトロンと呼ばれるニューラルネットを用いたパターン認識のサンプルです。ニューラルネットがどういうものかという解説はまた後でしますが、ここではサンプルの遊び方だけ書いておきます。まあゲームではないんですが・・・

pattern_recognition.jar

Pythonによる実装
多層パーセプトロンでMNISTの手書き数字認識(2014/2/5)

操作法

起動すると下のようなウィンドウが表示されます。

f:id:aidiary:20100518153831g:plain

左側がパターン入力画面です。マウスでクリックするとセルが赤く塗りつぶされます。右側は情報パネルです。プログラムからの出力情報が表示されます。右下がコントロールパネルです。いろいろなボタンがあります。

まず0〜9までの数字の認識を試してみます。

f:id:aidiary:20100518153906g:plain

パターン入力画面に上図の0のパターンを入力して追加ボタンを押してください。追加ボタンを押すと今入力したパターンが訓練データとして保存されます。入力を間違えた場合は消去ボタンを押せば「全部」消えます。残念ながら一部だけ消す機能は実装してません・・・数字パターンを訓練データとして追加していってください。9個の訓練データを入力したら学習ボタンを押してください。ニューラルネットが今入力した 9つの訓練データを学習してパターンを分類できるようになります。

   学習が完了しました
   パターンを入力し認識できるか試してください

と表示されたら学習は完了です。認識ボタンが押せるようになってます。では認識させてみましょう。まずパターン入力画面に3を書いて認識ボタンを押してみてください。「このパターンは何?」とニューラルネットに聞いてるわけです。認識ボタンを押すと

   このパターンは3です

と表示されるはずです。つまり、3番目に入力したパターンだと認識できたことになります

注意:入力した文字が3だと答えてるわけではありません。今回はたまたま3という数字を3番目のパターンとして入力したから3が出力されてるわけです。

4や9なども認識できるか試してみてください。ちゃんと認識できます。ニューラルネットが本当にすごいのは入力にノイズが入っていてもちゃんと認識できる点です。たとえば、完全な3 や4や7や9の代わりに少しノイズが入った

f:id:aidiary:20100518153956g:plain

を入力して認識ボタンを押してもきちんと認識できます。ニューラルネットってすごいでしょ?今回は数字パターンを例にしましたが、数字以外にも認識させることができます。下のようなアルファベットで試してみるのも面白いかもしれません。一度学習ボタンを押すとパターンの追加はできないのでプログラムを終了してやり直してください。あと各パターンを何番目に入力したかはちゃんと覚えておいてください。認識結果はパターン番号を返すだけなので。

f:id:aidiary:20100518154019g:plain

ただ何でも認識できるってわけではありません。パターンによっては間違えることもあるので注意してください。ただこのパターンの読み間違えは人間の脳の不完全さを表しているようで興味深いところがあります。

3層パーセプトロン

ニューラルネットとは人間の脳の仕組みをまねた学習アルゴリズムの一種です。人間の脳はニューロンという細胞が多数つながったネットワーク構造になってます。このネットワークをすごく単純化・定式化したのがニューラルネットです。人間の脳にははるかに及ばないけどけっこう面白いことができます。

f:id:aidiary:20100518154020g:plain

上の形をしたニューラルネットは3層パーセプトロンと呼ばれています(他の形のニューラルネットもあります)。ノードの集合が層構造をとっているのが特徴です。左から入力層、隠れ層、出力層と呼ばれます。ノードとノードの接続部分に重みがついています。入力層に入ったパターンは重みをもとにぐちゃぐちゃ計算されながら隠れ層を通って出力層から出てくるようなイメージです。ニューラルネットを使うと入力パターンと出力パターンの対応関係を学習させることができます。さっきのパターン認識を例に説明します。入力層のノード数は5×5の25個です。各マスが塗りつぶされていれば1、塗りつぶされてなかったら0が入力されます。隠れ層は適当だけど50個、出力層は10個取っています。たとえば、3というパターンが入力されたときは、出力層3番目のノード出力だけが1、他は0になるように、9というパターンが入力されたときは、出力層9番目のノード出力だけが1、他は0になるようにニューラルネットを訓練します(下図参照)。

f:id:aidiary:20100518154140g:plain

ニューラルネットの訓練

ある入力を与えたとき望ましい出力が出るようにニューラルネットを訓練する必要があります。ある入力パターンを与えたときに出てほしい出力パターンを教師信号と呼びます。たとえば、3を入力したときの望ましい出力パターンは3番目だけ1で他は0というパターンです。先ほど入力パターンは重みによってぐちゃぐちゃ計算され出力パターンが出てくると書きました。望ましい出力が出るようにニューラルネットを訓練することは重みを調整することに他なりません。ニューラルネットを訓練するために重みを調整するアルゴリズムが誤差逆伝播法(バックプロパゲーション)です。

訓練データセット

ニューラルネットに与えるデータは訓練データと教師信号です。文字認識のプログラム(PatternRecognition.java)ではユーザが自由に与えられるようになっていますが数字認識の場合は下のようなデータを与えます(NNTest2.javaがテストプログラムです)。

// 訓練データの作成
double[][] trainingSet = new double[][] {
  {1,1,1,1,1,1,0,0,0,1,1,0,0,0,1,1,0,0,0,1,1,1,1,1,1},  // 0
  {0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0},  // 1
  {1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1},  // 2
  {1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1},  // 3
  {1,0,0,0,0,1,0,0,0,0,1,0,1,0,0,1,1,1,1,1,0,0,1,0,0},  // 4
  {1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1},  // 5
  {1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1},  // 6
  {1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1},  // 7
  {1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1},  // 8
  {1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,1},  // 9
};
      
// 教師信号(それぞれのパターンにあたる部分だけ1)
// 単位行列になることに注意!
double[][] teacherSet = new double[][] {
  {1,0,0,0,0,0,0,0,0,0},  // 0
  {0,1,0,0,0,0,0,0,0,0},  // 1
  {0,0,1,0,0,0,0,0,0,0},  // 2
  {0,0,0,1,0,0,0,0,0,0},  // 3
  {0,0,0,0,1,0,0,0,0,0},  // 4
  {0,0,0,0,0,1,0,0,0,0},  // 5
  {0,0,0,0,0,0,1,0,0,0},  // 6
  {0,0,0,0,0,0,0,1,0,0},  // 7
  {0,0,0,0,0,0,0,0,1,0},  // 8
  {0,0,0,0,0,0,0,0,0,1},  // 9
};

trainingSetは5×5に描かれた各数字パターンを長さ25の1次元配列に展開したものです。教師信号は先ほど説明したように数字の対応する場所のみ1、他は0となるようなパターンです。訓練データは各数字に1つずつ合計10個用意しています。この訓練データをニューラルネットに食わせて訓練します。訓練が終わると入力パターンと出力パターンの対応が学習され、ある入力パターンを入れたときに正しい出力パターン(教師信号で与えた出力パターン)が出せるようになります。

誤差逆伝播法(バックプロパゲーション)

ある入力パターンを入れたときに正しい出力パターンが出るようにニューラルネットの重みを調節する方法を誤差逆伝播法(バックプロパゲーション: Back Propagation)と呼びます。ここでは誤差逆伝播法の流れを説明します。どのようにコードを書くかはコード解説を見てください。説明で使う各記号の意味は下図のようになってます。

f:id:aidiary:20100518154235g:plain

また誤差逆伝播法の流れは次のようになります。

(1) 重みを初期化する
(2) 訓練データを入力層にセットする
(3) 隠れ層の値を求める

H_j = f(\Sigma_i W_{ij} I_i + \theta_j)
f(x) = \frac{1}{1 + e^{-x}}

(4) 出力層の値を求める

O_k = f(\Sigma_j V_{jk} H_j + \gamma_k)

(5) 教師信号と出力層の値から出力層の誤差を求める

\delta_k = (T_k - O_k) O_k (1 - O_k)

(6) 出力層の誤差と隠れ層の値から隠れ層の誤差を求める

\sigma_j = \Sigma_k \delta_k V_{jk} H_j (1 - H_j)

(7) 隠れ層と出力層間の重み、出力層の閾値(バイアス)を更新する

V_{jk} = V_{jk} + \alpha \delta_k H_j
\gamma_k = \gamma_k + \beta \delta_k

(8) 入力層と隠れ層間の重み、隠れ層の閾値(バイアス)を更新する

W_{ji} = W_{ji} + \alpha \sigma_j I_i
\theta_j = \theta_j + \beta \sigma_j

(9) 次の訓練データを入力層にセットして3に戻る
(10) 平均2乗誤差が十分小さくなったら学習を終了する