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

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

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




はじめてのディープラーニング。まずは簡単なコードで動くかな?

ナノネ、今回はPythonでディープラーニング(Deep Learning)を試してみよう。

Tensorflowとか使うやつだよね?手持ちの古いPCで動くの?

GoogleのColaboratoryというクラウドサービスを使うから大丈夫だよ。Googleアカウントとブラウザーだけあれば、特に設定不要で高スペックなPython環境を利用できるから便利だよ。Jupyterノートブックでコーティングができて、ライブラリも追加可能、さらにGPUも使えるよ。しかもキホン無料、ありがたいよね。

それなら大丈夫かも。古いPCで四苦八苦してたけど、これからはディープラーニングも実践練習できそうだね。

さっそくだけど、ディープラーニングのコードを動かしてみよう。今回使うデータは定番の手書き数字MNISTを使うよ。サンプルコードも沢山あるしね。

まずは、必要なライブラリをインポートするよ。Colaboratoryだと主要なライブラリはデフォルトでインストール済みだから、importするだけでOK。(もしローカルなどで動かす場合は、利用環境に応じて必要なライブラリをインストールしてね)

In [1]:
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout
from keras.optimizers import RMSprop
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
Using TensorFlow backend.

kerasっていうのを使うの?

Kerasはディープラーニング用ライブラリで、Tensorflow(CNTK、Theanoなども利用可能)を容易に実行できるようにする便利なライブラリだよ。今回使う環境でも、KerasのバックエンドでTensorflowが動く設定になっているよ。

続いてMNISTデータセットの読み込み。

In [0]:
# データセット読み込み(学習用、検証用)
(X_train, y_train), (X_test, y_test) = mnist.load_data()
In [3]:
# データ数と次元(確認)
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 [4]:
# 学習画像データを一部だけ可視化(画像確認)
fig, axes = plt.subplots(10, figsize=(10, 15))
for i in range(10):
  imgs = X_train[y_train == i][:10]
  axes[i].imshow(imgs.transpose(1, 0, 2).reshape(28, 28 * 10), cmap='gray')
  axes[i].axis('off')
  axes[i].set_title("Number({})".format(i))

ヘンテコな形の数字も混じっているけど。気にしないことにする。

次はデータを少々変換。今回つくるモデルで扱いやすい形に変換しておくよ。

画像データは2次元を1次元にして、0から1.0に正規化。正解ラベルはOne-hot化しているよ。

In [0]:
# データ変換
X_train = X_train.reshape(60000, 784) # 画像データを一次元に変換 28x28->784
X_test = X_test.reshape(10000, 784)

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 [7]:
# 正解ラベルOne-hot化(データ確認)
fig = plt.figure(figsize=(10,6))

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

正解ラベル(0から9)は、カテゴリ化されて該当のところだけ1が入っているね。

次はモデルの構築。調べてみるとSequentialとFunctioanlAPIを使ったモデル構築方法があるみたい。今回は、より簡単そうなSequentialモデルを使ってみよう。

まず、Sequential()でモデルのインスタンスを作って。あとはモデルにadd()で、任意の層を追加していけばOK(くわしい説明はまだできないけど...)。

層の追加で利用しているDenseというのは、全結合のニューラルネットワークだよ。第一引数にノード数、Activationで活性化関数を指定するよ。今回、ノード数は試しで適当な値、活性化関数はReLUを指定しているよ。

Dropoutっていうのは?なんか捨てちゃうの?

Dropoutは過学習防止に役立つ処理で、学習中、ランダムに入力ユニットの値を0にする割合が指定できるよ。

それから、最初の層だけは、input_shapeで入力データの形式をタプルで指定してね。今回は784(画像28x28)をタプルで指定すればOKだよ。

In [8]:
# モデル構築

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

# Layer1(隠れ層)
# ノード数: 512
# 活性化関数: ReLU
# 入力: 784次元
# ドロップアプト比: 0.2
model.add(Dense(512, activation='relu', input_shape=(784,)))
model.add(Dropout(rate=0.2))

# Layer2(隠れ層)
# ノード数: 256
# 活性化関数: ReLU
# ドロップアプト比: 0.2
model.add(Dense(256, activation='relu'))
model.add(Dropout(rate=0.2))

# Layer3(隠れ層)
# ノード数: 128
# 活性化関数: ReLU
# ドロップアプト比: 0.2
model.add(Dense(128, activation='relu'))
model.add(Dropout(rate=0.2))

# Layer4(出力層)
# ノード数: 10
# 活性化関数: SOFTMAX
model.add(Dense(10, 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 #   
=================================================================
dense_1 (Dense)              (None, 512)               401920    
_________________________________________________________________
dropout_1 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 256)               131328    
_________________________________________________________________
dropout_2 (Dropout)          (None, 256)               0         
_________________________________________________________________
dense_3 (Dense)              (None, 128)               32896     
_________________________________________________________________
dropout_3 (Dropout)          (None, 128)               0         
_________________________________________________________________
dense_4 (Dense)              (None, 10)                1290      
=================================================================
Total params: 567,434
Trainable params: 567,434
Non-trainable params: 0
_________________________________________________________________

ラストは出力層になるから、ノード数は分類したい数に合わせてね。今回は数字10通りだから10だよ。それと出力層の活性化関数には、多クラス分類でよく利用されるソフトマックス関数(0~1の実数値で総和が1となる値を出力)を指定しているよ。

づづいて、モデルのコンパイル。どんな風に学習を行うかを設定するんだ。必要な設定は最適化アルゴリズム、損失関数、評価関数リストの3つだよ。

今回はサンプルコードなどを参考にして、最適化アルゴリズムはRMSprop、損失関数はcategorical_crossentropy、評価関数にaccuracyを指定しているよ(別の手法など詳しくはドキュメントを調べてね)。

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

やっと学習だね。。。

学習はfit()で実行できるよ。最初の引数には学習用のデータ(X_train)と正解ラベル(y_train)を渡してね。

batch_sizeはデータをまとめて処理する件数で、学習中はバッチ単位処理が終わるとモデルが1回更新されるみたいだよ。epochsはデータセットを何回分学習するかを指定。verboseは学習中のログ出力有無を指定できるよ。あとvalidation_dataは、検証用のデータと正解ラベルを指定すればOK。

ラジャー、学習実行!

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

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/8
60000/60000 [==============================] - 4s 60us/step - loss: 0.2949 - acc: 0.9084 - val_loss: 0.1151 - val_acc: 0.9650
Epoch 2/8
60000/60000 [==============================] - 2s 36us/step - loss: 0.1233 - acc: 0.9631 - val_loss: 0.0815 - val_acc: 0.9768
Epoch 3/8
60000/60000 [==============================] - 2s 36us/step - loss: 0.0901 - acc: 0.9735 - val_loss: 0.0775 - val_acc: 0.9793
Epoch 4/8
60000/60000 [==============================] - 2s 36us/step - loss: 0.0739 - acc: 0.9785 - val_loss: 0.0885 - val_acc: 0.9754
Epoch 5/8
60000/60000 [==============================] - 2s 32us/step - loss: 0.0653 - acc: 0.9813 - val_loss: 0.0813 - val_acc: 0.9790
Epoch 6/8
60000/60000 [==============================] - 2s 32us/step - loss: 0.0559 - acc: 0.9836 - val_loss: 0.0787 - val_acc: 0.9795
Epoch 7/8
60000/60000 [==============================] - 2s 32us/step - loss: 0.0509 - acc: 0.9857 - val_loss: 0.0766 - val_acc: 0.9814
Epoch 8/8
60000/60000 [==============================] - 2s 32us/step - loss: 0.0467 - acc: 0.9871 - val_loss: 0.0798 - val_acc: 0.9831

無事学習できたみたいだけど、GPU使うと早いね。

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

無事学習できたみたい。やっぱりGPU使うと学習が早いね。

In [11]:
# モデル評価
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.0798
Test accuracy: 0.9831

正答率は98%前後。データの中にはヘンテコな数字画像もたくさんありそうだけど、分類結果は結構優秀かも。

参考までに、学習履歴もプロットしておくね。

In [12]:
# モデル学習履歴をグラフで可視化
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()

今回のディープラーニングの練習は以上で。次回は畳み込み(CNN)を実践する予定だよ。

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







スポンサーリンク

0 件のコメント :

コメントを投稿