Keras に入門する 画像分類 Part.1 サンプルを動かしてみる

前回サンプルで扱うデータを眺めてみたので、今回は実際にサンプルを動かして画像の分類をしてみたい

サンプルはこれ

keras/cifar10_cnn.py at master · keras-team/keras · GitHub

colab

https://colab.research.google.com/drive/1oCHanNltHykuY2R89lUFYY90cLyIKBrv

基本的にサンプルをそのまま動かしていくだけ コメントもcolabの方に書いていく

一層目は

# 一層目を追加
model.add(Conv2D(32, (3, 3), padding='same',
                 input_shape=x_train.shape[1:]))
model.add(Activation('relu'))

Conv2Dのドキュメントを見ると

keras.layers.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid', data_format=None, dilation_rate=(1, 1), activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)

32(filters)は出力される画像の枚数、(3, 3) (kernel_size) はフィルタのサイズとのこと

また

x_train.shape[1:]

→ (32, 32, 3)

でinput_shape には画像1枚のデータの形を渡している

あと、現在の出力のshapeはmodel.output_shape で見れるみたい

model.output_shape
→ (None, 32, 32, 32)

2層目もほぼ同じ

# 二層目を追加
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))

このあとMaxPooling2Dで画像のサイズを小さくしている

# pooling で画像を縮小
model.add(MaxPooling2D(pool_size=(2, 2)))

model.output_shape
→ (None, 16, 16, 32)

Dropoutは指定した割合のノードを非活性化させて過学習を抑えるやつ

# Dropout 
model.add(Dropout(0.25))

3層目(と数えるのか? poolingが3層目?)以降も1~2層目と同じ感じ

model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

つぎにFlattenを挟んでいる これは (50000, 32, 32, 3) とかを (50000, 32323) みたいな形に変形するやつ

model.add(Flatten())

フラットにした出力を512個のノードを持つ層に全連結する

model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))

最後に、10種類の分類問題なので10個のノードを持つ層に全連結、活性化関数はsoftmax

model.add(Dense(num_classes))
model.add(Activation('softmax'))

ここまででレイヤの設定は終了

次にオプティマイザの設定

# パラメータの更新に使うオプティマイザを指定
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)

SGDとかAdagradとかは知ってたけど RMSPropって初めて知った

損失関数とかを設定してコンパイル

model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])

画像データは0~255の整数値だけど 0~1.0の実数にしたいので変換

# データの準備
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

ImageDataGenerator が何かよくわかっていないけど、学習データにノイズをのせたりちょっと変形させたりして学習データを増やすやつ?? あとで調べる

データの拡張をしないなら

model.fit(x_train, y_train,
              batch_size=batch_size,
              epochs=epochs,
              validation_data=(x_test, y_test),
              shuffle=True)

で学習できます

今回は拡張しないでやりました

Google Colaboratory の GPU ランタイムで、およそ1時間ほどで学習が完了しました (結構早い段階でloss, accは変わらなくなってたみたいです)

Train on 50000 samples, validate on 10000 samples
Epoch 1/100
50000/50000 [==============================] - 34s 678us/step - loss: 1.7920 - acc: 0.3452 - val_loss: 1.5589 - val_acc: 0.4344
Epoch 2/100
50000/50000 [==============================] - 33s 669us/step - loss: 1.4828 - acc: 0.4602 - val_loss: 1.3297 - val_acc: 0.5235
Epoch 3/100

...

Epoch 99/100
50000/50000 [==============================] - 33s 667us/step - loss: 0.6290 - acc: 0.7919 - val_loss: 0.7397 - val_acc: 0.7645
Epoch 100/100
50000/50000 [==============================] - 33s 667us/step - loss: 0.6290 - acc: 0.7928 - val_loss: 0.6674 - val_acc: 0.7836
<keras.callbacks.History at 0x7f5460e23a20>

最終的に80%程度の精度で判別できるようになったみたいです

本当に判別できるようになっているのか、確認してみます

model.predict(x_train[0:1])
→ array([[5.1839565e-05, 9.5230262e-06, 4.2473815e-02, 1.3438360e-01,
        1.5156801e-02, 4.1686233e-02, 7.6447803e-01, 1.6840984e-03,
        4.3788212e-05, 3.2197495e-05]], dtype=float32)

y_train[0]
→ array([0., 0., 0., 0., 0., 0., 1., 0., 0., 0.], dtype=float32)

学習データに使ったデータで確認するべきではないとは思いますが、とりあえず最初の画像は 7番目のラベルが 0.764で一番大きな値になっていて、y_train[0]と一致しているようです

model.predict(x_train[1:2])
→ array([[3.8416937e-07, 4.2660031e-05, 4.9953406e-09, 4.7760366e-08,
        1.3378058e-10, 6.4784986e-09, 4.5471082e-11, 1.5476447e-08,
        1.4176493e-06, 9.9995542e-01]], dtype=float32)

y_train[1]
→ array([0., 0., 0., 0., 0., 0., 0., 0., 0., 1.], dtype=float32)

2つ目の画像も10番目のラベルが99.99でy_train[1]と一致していますね

ということでサンプルを実際に動かしてみることができました

次回はこれを応用して独自の画像分類器を作って見たいと思っています。