もものきとPythonでディープラーニング・手書き数字を分類してみよう2

もものきとPythonでディープラーニング・手書き数字を分類してみよう2

Keras/Tensorflowを使った手書き文字MNISTデータセットの画像分類(CNN)




畳み込みを使ったCNNで画像分類をやってみよう

ナノネ、前回の続きでKeras(+TensorFlow)を使ってDeep Learningを試してみよう。

今回もデータは、手書き数字MNISTを使うの?

うん、またMNISTを使うよ。前回は全結合層だけでモデルを組んだけど、今回は畳み込み層を入れたCNN(Convolutional Neural Network)という手法で画像を分類してみよう。

畳み込みをすると、何かいいことあるの?

畳み込みを使うと、データの空間的な特徴を効率的に捉えることができるよ。畳み込みを使ったCNNは、画像認識でよく利用される手法だよ。

今回もGoogle Colaboratoryの環境を使ってコードを書くよ。(もしローカルなどで動かす場合は、利用環境に応じて必要なライブラリを準備してね)

In [0]:
# Colaboratory環境 @2019/05 ****
# Ubuntu 18.04.2 LTS
# Python 3.6.7
# :Keras 2.2.4
# :tensorflow 1.13.1
# :numpy 1.16.3
# :matplotlib 3.0.3
In [2]:
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
Using TensorFlow backend.

インポートは前回と少しだけ違うね。keras.layersのFlatten、Conv2D、Maxpooling2Dとか増えてる。

In [3]:
# データセット読み込み(学習用、検証用)
(X_train, y_train), (X_test, y_test) = mnist.load_data()
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
In [4]:
# データ数と次元(確認)
print('X_train {}'.format(X_train.shape)) # 学習用画像データ
print('y_train {}'.format(y_train.shape)) # 学習用正解ラベル
print('X_test {}'.format(X_test.shape)) # 検証用画像データ
print('y_test {}'.format(y_test.shape)) # 検証用正解ラベル
X_train (60000, 28, 28)
y_train (60000,)
X_test (10000, 28, 28)
y_test (10000,)

データセットの読み込みは前回と一緒。学習用が60,000件、検証用が10,000件。

In [5]:
# 学習画像データを一部だけ可視化(画像確認)
fig, axes = plt.subplots(10, figsize=(10, 15))
for i in range(10):
  imgs = X_train[y_train == i][10:20] # 各数字10個
  axes[i].imshow(imgs.transpose(1, 0, 2).reshape(28, 28 * len(imgs)), cmap='gray')
  axes[i].axis('off')
  axes[i].set_title("Number({})".format(i))

数字画像のイメージチェックまで完了。

データを変換するところが、前回と少し違うから注意ね。

In [0]:
# 画像次元(画像サイズ)
img_rows, img_cols = 28, 28

Kerasのバックエンドによってチャンネルの指定順が違うようなので、サンプルコード(mnist_cnn.py)の場合分けをそのまま引用。TensorflowだとChanels_Lastだったので、(画像行数、画像列数、画像ch数)の順でよさげ。

In [0]:
# Kerasのバックエンド(Tensorflow、Theanoなど)で入力チャンネルの順番が違うため場合分けする
if K.image_data_format() == 'channels_first':
    X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
    X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else: # 'channels_last'
    X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
    X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)
In [8]:
# 確認 -> Tensorflowの場合、channel_last
K.image_data_format()
Out[8]:
'channels_last'

画像データを0~1の実数に正規化、正解ラベルをOne-Hot化するのも前回と一緒だよ。

In [0]:
# データ正規化

X_train = X_train.astype('float32') # unit8 -> float32
X_test= X_test.astype('float32')

X_train /= 255 # 画像データ(0~255)を0~1.0に正規化
X_test /= 255
In [0]:
# 正解ラベルOne-hot化
num_classes = 10 # MNISTラベルの種類(数字0~9)
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
In [11]:
# 正解ラベルOne-hot化(データ確認)
fig = plt.figure(figsize=(10,6))

for i in range(3):
    ax = fig.add_subplot(1, 3, i+1, xticks=[], yticks=[])
    ax.imshow(X_train[i].reshape(28, 28), cmap='gray')
    ax.set_title(str(y_train[i]))

データ準備はOK。このあとはモデル構築だね。

前回同様、Sequentialモデルで構築しよう。

In [12]:
# モデル構築

# シーケンシャルモデル
model = Sequential()

# 畳み込み層(フィルター)
model.add(Conv2D(32, kernel_size=(3, 3),
                activation='relu',
                input_shape=input_shape))

# 畳み込み層(フィルター)
model.add(Conv2D(64, (3,3), activation='relu'))

# プーリング層(縮小)
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Flatten()) # 一次元に戻す

# 全結合層層
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))

# 出力層
model.add(Dense(num_classes, activation='softmax'))

model.summary()
WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/keras/backend/tensorflow_backend.py:3445: calling dropout (from tensorflow.python.ops.nn_ops) with keep_prob is deprecated and will be removed in a future version.
Instructions for updating:
Please use `rate` instead of `keep_prob`. Rate should be set to `rate = 1 - keep_prob`.
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 26, 26, 32)        320       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 24, 24, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 12, 12, 64)        0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 9216)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 128)               1179776   
_________________________________________________________________
dropout_2 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 10)                1290      
=================================================================
Total params: 1,199,882
Trainable params: 1,199,882
Non-trainable params: 0
_________________________________________________________________

ざっくりだけど、Conv2Dは畳み込み層で、3×3サイズのフィルター処理をおこなっているよ。

Maxpooling2Dはプーリング層で、2×2サイズの最大値を採用してデータの縮小処理をおこなっているよ。

畳み込みとプーリングが終わったら、Flatten()でデータを1次元に戻してから全結合層へ。後半の部分は前回とだいだい一緒だね。

づづいて、モデルのコンパイル。最適化アルゴリズム、損失関数、評価関数リストの3つ指定するよ。

In [0]:
# モデルのコンパイル(最適化アルゴリズム、損失関数、評価関数リストを指定)
model.compile(optimizer=keras.optimizers.adadelta(),
              loss=keras.losses.categorical_crossentropy,
              metrics=['accuracy'])

fit()で学習実行。これも一緒だね。

In [14]:
# 学習
batch_size = 128 # バッチサイズ
epochs = 10 # エポック

history = model.fit(X_train, y_train,
                    batch_size=batch_size,
                    epochs=epochs,
                    verbose=1,
                    validation_data=(X_test, y_test))
WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Train on 60000 samples, validate on 10000 samples
Epoch 1/10
60000/60000 [==============================] - 11s 184us/step - loss: 0.2653 - acc: 0.9187 - val_loss: 0.0595 - val_acc: 0.9816
Epoch 2/10
60000/60000 [==============================] - 5s 76us/step - loss: 0.0901 - acc: 0.9737 - val_loss: 0.0384 - val_acc: 0.9868
Epoch 3/10
60000/60000 [==============================] - 5s 78us/step - loss: 0.0672 - acc: 0.9801 - val_loss: 0.0384 - val_acc: 0.9879
Epoch 4/10
60000/60000 [==============================] - 5s 79us/step - loss: 0.0550 - acc: 0.9837 - val_loss: 0.0348 - val_acc: 0.9882
Epoch 5/10
60000/60000 [==============================] - 5s 81us/step - loss: 0.0479 - acc: 0.9859 - val_loss: 0.0300 - val_acc: 0.9895
Epoch 6/10
60000/60000 [==============================] - 5s 76us/step - loss: 0.0430 - acc: 0.9869 - val_loss: 0.0290 - val_acc: 0.9906
Epoch 7/10
60000/60000 [==============================] - 5s 78us/step - loss: 0.0375 - acc: 0.9888 - val_loss: 0.0288 - val_acc: 0.9909
Epoch 8/10
60000/60000 [==============================] - 5s 78us/step - loss: 0.0321 - acc: 0.9897 - val_loss: 0.0273 - val_acc: 0.9906
Epoch 9/10
60000/60000 [==============================] - 5s 79us/step - loss: 0.0326 - acc: 0.9897 - val_loss: 0.0298 - val_acc: 0.9897
Epoch 10/10
60000/60000 [==============================] - 5s 78us/step - loss: 0.0297 - acc: 0.9903 - val_loss: 0.0279 - val_acc: 0.9907

最後は検証データで、モデルを評価。

In [15]:
# モデル評価
score = model.evaluate(X_test, y_test, verbose=0)
print('Test loss:', np.round(score[0], 4))
print('Test accuracy:', score[1])
Test loss: 0.0279
Test accuracy: 0.9907

正答率は約99%。学習履歴もプロットしておく。

In [16]:
# モデル学習履歴をグラフで可視化
import matplotlib.pyplot as plt

#accuracy
plt.plot(range(1, len(history.history['acc'])+1), history.history['acc'])
plt.plot(range(1, len(history.history['val_acc'])+1), history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='best')
plt.show()

#loss
plt.plot(range(1, len(history.history['loss'])+1), history.history['loss'])
plt.plot(range(1, len(history.history['val_loss'])+1), history.history['val_loss'])
plt.title('model accuracy')
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='best')
plt.show()

今回の畳み込みを使ったディープラーニングの練習は以上で。

お疲れ様でした。またね!







スポンサーリンク

0 件のコメント :

コメントを投稿