畳み込みニューラルネット(CNN)による画像識別(MNIST)

はじめに

今回は、畳み込みニューラルネットワークを使った画像識別を行ってみたいと思います. データは前回と同じくMNISTの手書き数字のデータを用います.またかい、と思われるかもしれないですがこれは誰しもが通る道なのです.たぶん.


今回もnogawanogawa様の以下の記事を参考にしています.
ありがとうございます(勝手に写経してすみません)

tsunotsuno.hatenablog.com

  • 今日の目標:畳み込みニューラルネットによる画像識別
  • 使用するデータ:MNIST(0~9の数値データ)
  • 環境:Google Colaboratory

私が知っている畳み込みニューラルネットワークの事前知識

私が畳み込みニューラルネットワークについて知っている知識と言えば、 - 画像識別に使いやすいらしい(当然のごとく使われている) - フィルタを使うらしい ‐ 位置ずれに強いらしい
くらいなものです.全部かなりぼんやりとしています.

ですのでまずは畳み込みニューラルネットワークについて少し整理していきます.畳み込みニューラルネットワークは英語でConvolutional Neural Networkと言いますので、以下CNNと略していきます.

こんなことを整理した内容の記事は飽和状態であり、新しいことは何も出てきません.

CNNの基本構造

有名なCNNの構造を以下に示します.これはImageNETの画像認識コンペで登場した16層からなるネットワーク構造で、学習済モデルがVGG16として公開されています.(おそらく論文はこちら

f:id:shirakonotempura:20181221011245p:plain

分類モデルなので、基本的には入力データを入力層に入力して、最後はsoftmax関数で確率を出力する構造になっています. しかし、単純なニューラルネットと異なり、全結合(fully connected)の前に、畳み込み・プーリングといった操作を行っています.

今回、実装する構造は以下になります.
畳み込み層の数・全結合層の数は異なりますが、基本はVGG16 の構造と同じと言えます.

畳み込み層(Convolutional Layer)

で、畳み込み層では何をしているかというと、畳み込み層では、以下のようにフィルタを使って入力データの特徴抽出を行っています.(図ではバイアス項を無視しています)

f:id:shirakonotempura:20181221011550p:plain
畳み込み(Convolution)イメージ

フィルタをかけられて出来たデータは特徴マップと呼ばれます.特徴マップはフィルタの数だけ出力されます.CNNでは、このフィルタが学習によって変化していくことになります.

フィルタといっても結局、全結合ではないレイヤー間の重みのことであり、学習はその重みを学習していることと変わりありません.という理解です.合っているといいのですが.

私は、この部分が良く分かってなかったため、長くの間、フィルタの話とニューラルネットの話がどうしてもつながらなかったのですがようやく少し理解できました.

CNNでやっていることの全体像を掴むにはこちらが分かりやすいです.

畳み込みニューラルネットワークの仕組み | POSTD

ただし、畳み込んだ際にフィルターのピクセル数で割ること(例えば、3×3フィルタなら、積和で得られた値を9で除すること)が必要なのかいまいち理解できず.画素の値を255で割るのと同じように値が大きくなりすぎるのを防いでいるということでしょうか.

プーリング層(Pooling Layer)

次にプーリング層(Pooling layer)についてみていきます.プーリング層の目的は、特徴を残しつつ、データを縮小することになります.

以下に、MAXプーリングとAverageプーリングのイメージを示します.見てのとおり、操作としては非常に簡単な計算です.プーリング層では、重みやバイアスのようなパラメータはありません.

f:id:shirakonotempura:20181221012445p:plain
プーリング(Pooling)イメージ"

また、データを縮小することに加え、位置ずれに対して頑健にするという役割があります.
どういうことかというと、フィルタをかける範囲でデータをならしているため、位置に対する感度が低下し、回転や少しの移動による違いには鈍感になるようです.

パティング、ストライドについて

フィルタの操作において、パディング、ストライドという操作があります.


パディング
- 入力データの周囲に固定の値(多くは0)を埋める処理のことを言います.フィルタ処理によって、通常出力サイズは入力サイズより小さくなってしまうのですが、パディングをすることによって、出力サイズと入力サイズをそろえることができます.

ストライド
- フィルタを適用する間隔になります.先の畳み込みのイメージ図では、ストライド1としていますが、ストライド2とすると出力データのサイズは2×2となります.


入力サイズ I、出力サイズ O、フィルタサイズ F、パディングサイズ Pストライド間隔 Sには以下の関係があります.ネットワークの構成を自分で設計する場合には、覚えておくとよいかと思います.

 O = (I + 2P - F)/S +1


もっとパディング、ストライドについて知りたい場合、以下の記事が参考になります.
パディング??ストライド??CNNに使われるテクニックと用語 - "0"からAI~ふらっとどうぞ~


実装(写経)結果

他人のコードを写経したものをどかどかと記事に書くのもあまり良くないかもと不安になったので、 基本的にコードはColaboratory のnote上での公開だけとします.githubを使った公開も覚えたいのですが、必要に迫られたら覚えるようにします.

Colaboratory上のnoteはコチラ

最後にちろっと以下のコードを追加して、学習済パラメータファイルおよびグラフのダウンロードを行っています.

# グラフデータの保存
plt.savefig('history_log.png')

plt.show()

import google.colab
google.colab.files.download("params.pkl")
google.colab.files.download("history_log.png")

savefigをplt.show()の後に書いてしまうと、うまく保存できません.

私の知能で理解するには大分複雑になってきましたので、今回は写経より構成の理解に時間がかかりました.
やはり下記文献があった方がいいのだと思います.

bookmeter.com

一応識別精度の結果は以下のようになりました。テストデータに対する最終精度99.1%!
すご.何もしてないのに・・.

f:id:shirakonotempura:20181221025243p:plain
学習精度のヒストリー

とにかく今回はCNNを実際に動かしてみて、精度よく識別できることが確認できました.
ただこれまではIRISなりMNISTなり用意されたデータを使っているので、近いうちに自分で用意した画像の識別をやってみたいと思います.
あとは、Kerasも使ってみたいし、学習経過の保存なんかももう少し勉強してみたい.
GANでブログのアイコンを作るのはまだまだ先になりそうです.