Kerasの作者、フランソワ・ショレ氏によるディープラーニングとKerasの解説本 『Deep Learning with Python』の読書メモ。
第二部「Deep Learning in practice」では、実践的な問題にディープラーニングを適用する方法を扱う。5章は畳み込みニューラルネット(convolutional neural network; CNN, convnet) を用いたコンピュータビジョンに関する話。
理論的な話は後で出てくるので、とりあえずCNNのサンプルプログラムを動かす。最初にも出てきたMNISTデータセットを使って、画像の分類を行う。
from keras import layers from keras import models model = models.Sequential() model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(64, (3, 3), activation='relu')) model.add(layers.MaxPooling2D((2, 2))) model.add(layers.Conv2D(64, (3, 3), activation='relu'))
Conv2D と MaxPooling の層が交互に置かれる形になる。
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
Conv2Dの最初の引数"32"はフィルタ(カーネル)の数を表し、出力の次元となる。次のタプル "(3, 3)" はフィルタ(カーネル)のサイズを表し、ここでは幅と高さが3x3のフィルタを用いることを意味している。
model.add(layers.MaxPooling2D((2, 2)))
MaxPooling層では出力をダウンスケールする。ここでは、前の畳み込み層の出力が幅と高さ方向にそれぞれ半分にされることを意味する。(畳み込み層とマックスプーリング層の処理の詳細は、後の節で解説があるので後述)
>>> model.summary() _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_1 (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 13, 13, 32) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 11, 11, 64) 18496 _________________________________________________________________ max_pooling2d_2 (MaxPooling2 (None, 5, 5, 64) 0 _________________________________________________________________ conv2d_3 (Conv2D) (None, 3, 3, 64) 36928 ================================================================= Total params: 55,744 Trainable params: 55,744 Non-trainable params: 0 _________________________________________________________________
model.summary() 関数でモデルの概要を出力することができる。Output Shapeの列に注目すると、conv2dの層で「畳み込み」が行なわれ、max_pooling層で出力が半分に落とされていることが分かる。
# 予測用のレイヤー model.add(layers.Flatten()) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(10, activation='softmax'))
予測用のレイヤーを追加する。畳み込み&マックスプーリング層の出力は3x3x64次元のテンソルなので、layers.Flatten() を使って576個の要素を持つ1次元のベクトルに変換し、全結合層を使い、その出力から予測を行う。
# 訓練データの変換 train_images = train_images.reshape((60000, 28, 28, 1)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28, 28, 1)) test_images = test_images.astype('float32') / 255 train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels)
訓練データをニューラルネットへの入力用に変換する。最初の全結合ネットワークを用いたサンプルでは、画像データを1次元ベクトルに変換した。
train_images = train_images.reshape((60000, 28, 28, 1))
今回の畳み込みニューラルネットでは、2次元のデータのまま入力する。 reshapeの引数のタプル "(60000, 28, 28, 1)" は、それぞれ、(データの数, 縦のピクセル数、横のピクセル数、チャネル) を表す。MNISTのデータは、0〜255の数値で表現されるグレースケールなので、今回のチャネル数は1となる。たとえば、RGBの3つの数で表現されるカラー画像の場合、チャネル数は3となるとか。
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy']) model.fit(train_images, train_labels, epochs=5, batch_size=64)
モデルをコンパイルして学習を行う。モデルがちょっと複雑になってきたためか、学習に多少時間を要する。GPUを使わない環境で実行したら、学習の1エポックに2〜3分程度掛かった。
>>> test_loss, test_acc = model.evaluate(test_images, test_labels) >>> print(test_acc) 10000/10000 [==============================] - 3s 339us/step 0.9915
テスト誤差を測定してみると、99.15%だった。もっとカリカリにチューニングすれば99.5%程度まで向上させられるらしい。