0
0

ゼロからの画像認識②(CNNの理解と実践)

Posted at

前回の直後に書き、下書きのまま放置されていた記事です。

前回は、TensorFlow版「Hello World」の解説と自筆文字の判定を行いました。

今回は、畳み込みニューラルネットワーク(CNN: convolutional neural network)の理解と実装をし、自筆文字の判定精度を上げます。

参考:定番のConvolutional Neural Networkをゼロから理解する - DeepAge

理論

1. 視覚の生理学

この項は、着想の話をします。難しかったら次の項へ進んでも構いません。そもそもニューラルネットワークは神経のモデル化を試みたものでした。CNNも視覚情報処理をヒントとしています。脳の一次視覚野には、単純型細胞・複雑型細胞・超複雑型細胞という細胞が見つかっています。

  • 網膜:境界を抽出する。オン領域とオフ領域の組み合わせで判断している。
  • 単純型細胞:特定位置・特定傾きの線分を認識する。オン領域とオフ領域の組み合わせで判断している。
  • 複雑型細胞:位置がズレても特定傾きの線分を認識する。複数の単純型細胞から入力をもらうと実現可能。(実物は未解明)
  • 超複雑型細胞:さらに複雑な形に対応。複数の複雑型細胞から入力をもらうと実現可能。(実物は未解明)

参考1:第一次視覚野:線の傾きを検出する細胞 - 脳の世界
参考2:花沢 明俊, 視覚心理 (第2回), 映像情報メディア学会誌, 2004, 58巻, 2号, p.199-204. - J-STAGE
参考3:Deep learningで画像認識②〜視覚野と畳み込みニューラルネットワーク〜 - IMACEL Academy

2. 畳み込み と プーリング

網膜と単純型細胞の機能は畳み込みによって、複雑型細胞の機能はプーリングによって実現できます。

畳み込み Convolution

畳み込みとは、ある範囲に対し特徴抽出を行う操作です。カーネル(フィルターともいう)との内積を一通り、スライドしながら計算します。このカーネルが視覚の受容野に相当します。

畳み込み.png

このままだと端の特徴を捉えられないので、端にデータを増やしてあげます。これをパディングと言います。MNISTでは端が0なので、ゼロパディングだとちょうどよさそうですね。

ゼロパディング.png

パディングを行ったので、出力の形は小さくなりません。

プーリング Pooling

プーリングとは、範囲内での代表値を計算する作業。

最大値プーリング.png

ちょっとぐらい位置がズレても対応できるようになります。本番は、このあとに活性化関数で次の層への出力を決めます。

実践

ライブラリの読み込み
import tensorflow as tf
データの読み込みと整形
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# (60000枚,縦28,横28)->(60000枚,縦28,横28,1色)
x_train = x_train.reshape((60000,28,28,1))

# (10000枚,縦28,横28)->(10000枚,縦28,横28,1色)
x_test = x_test.reshape((10000,28,28,1))

x_train, x_test = x_train/255.0, x_test/255.0

MNISTのデータは(枚,縦,横)という形のテンソル1ですが、次に出てくるConv2D層に入れる前に(枚,縦,横,色)という形に変えなければなりません。reshape(枚,縦,横,色)をする必要があります2

モデル作成
model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3),padding='same', activation='relu',
                            input_shape=(28, 28, 1)),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),

    tf.keras.layers.Flatten(),
    
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dropout(0.25),
    tf.keras.layers.Dense(10, activation='softmax')
])

モデルの層をそれぞれ作ってます。

Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=(28, 28, 1))
(縦28,横28,1色)を、ゼロパディングした上で畳み込みします。3×3(×1色)のカーネルを32種類使って、活性化関数はReLU。(縦28,横28,32種類)のテンソルになりました。MNISTは端が0の画像なので、ゼロパディングが妥当です。

MaxPooling2D((2, 2))
(縦28,横28,32種)を2×2で最大値プーリングします。(縦14,横14,32種)になりました。

Conv2D(64, (3, 3), activation='relu')
(縦14,横14,32種)を、パディングせず畳み込みします。3×3(×32)のカーネルを64種類使って、活性化関数はReLU。(縦12,横12,64種)になりました。

MaxPooling2D((2, 2))
(縦12,横12,64種)を2×2で最大値プーリングします。(縦6,横6,64種)になりました。

Conv2D(64, (3, 3), activation='relu')
(縦6,横6,64種) → (縦4,横4,64種)

Flatten()
(縦4,横4,64枚) → (1024,)

Dense(64, activation='relu')
(1024,) → (64,)

Dropout(0.25)
25%、つまり16個を0にする。

Dense(10, activation='softmax')
「0」〜「9」の10個の神経細胞に接続。

  1. スカラーを横に並べたものをベクトル、ベクトルを縦に並べたものを行列、行列を奥に並べたものを3次元テンソルと呼びます。例えば、行列[[a,b],[c,d]]を2つ並べた3次元テンソルは[ [[a,b],[c,d]], [[a,b],[c,d]] ]です。MNISTのデータは「画像の行列を並べたもの」であり、このような形式になっています。

  2. (枚,縦,横,色)のデータセットは、(縦,横,色)の画像を並べた配列です。本文中のreshape操作では、[ [a,b], [c,d] ][ [[a],[b]], [[c],[d]] ]のように各ピクセルの値a[a]へ変えました。これは、RGB画像を使いたいときに、各ピクセルが[aR,aG,aB]のようなデータを扱うための仕様です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0