深層学習(ディープラーニング)手法を用いて画像認識してみたいです。
このような要望にお応えします。
今回は画像認識タスクの一つである画像分類を深層学習手法を用いて行います。
また、PythonライブラリKerasを利用します。
Kerasは、ニューラルネットワークモデルの構築をサポートしてくれます。このライブラリを使用して、画像分類のモデル構築と評価を行います。
画像分類に使用するデータは、画像認識のサンプルデータとして頻繁に使用されるMNIST手書き文字画像データセットを使用します。
画像分類のモデルは、NN(Neural Network)、CNN(Convolutional Neural Network)を使用します。
実行環境は、Pythonの開発環境のひとつAnacondaをベースとしてjupyter notebookを利用しました。
Anacondaの環境構築については、下記の記事にまとめています。
今回使用するライブラリのバージョンは以下になります。
- python 3.7
- tensorflow 1.7.0
- Keras 2.2.2
画像認識をはじめていきます。手順は、大きく以下のようになります。
- 画像データの準備
- モデルの定義
- モデルの学習/評価
- 画像分類結果出力
画像データの準備
各手書き数字画像は、28*28ピクセル(=784ピクセル)で構成されています。
学習データには60000個の画像が用意され、検証用データには10000個の画像を使用することができます。 そして、0~9までの10クラスがあり、これらのクラスに正しく分類できるようにモデルの学習を行います。
画像データの各ピクセル値は0~255のグレースケールです。そのため、入力値が0~1の範囲となるように正規化を実施します。各値の最大値が255ですので、255で除算することにより正規化します。
今回の問題は、画像が0~9の整数のうち、どれに相当するのかを学習するマルチクラス分類の問題になりますので、学習時に必要となる0~9のクラスを示すベクトルを用意することになります。
それでは、データの準備をしてみます。まず、学習用データと検証用データに分けて、画像データをx_train, y_train, x_test, y_testに読み出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense from keras.layers import Dropout from keras.layers import Flatten from keras.layers.convolutional import Conv2D from keras.layers.convolutional import MaxPooling2D from keras.utils import np_utils import numpy as np import matplotlib.pyplot as plt (x_train, y_train), (x_test, y_test) = mnist.load_data() # 画像データ28*28を学習用、検証用に分けて格納 num_pixels = x_train.shape[1] * x_train.shape[2] x_train = x_train.reshape((x_train.shape[0], 28, 28, 1)).astype('float32') x_test = x_test.reshape((x_test.shape[0], 28, 28, 1)).astype('float32') # ニューラルネットワークの入力に合わせてデータ正規化 x_train = x_train / 255 x_test = x_test / 255 # 出力クラスに対応するようにデータ整形 y_train = np_utils.to_categorical(y_train) test_label = y_test y_test = np_utils.to_categorical(y_test) num_classes = y_test.shape[1] |
MNISTの手書き数字画像以外の自前の画像データを使用したい場合は、Image, cv2等のライブラリを用いて、各画像を読み込んでベクトル化するといいと思います。
これでデータの準備は完了です。
モデルの定義
次は学習モデルを定義していきます。
NN, CNNのモデルを定義します。
まずは、NNを定義します。
入力層を784(画像ベクトル表現の次元数)、中間層ユニット数64、活性化関数としてrelu、出力層のユニット数を10としてsoftmax関数を使用します。
1 2 3 4 5 6 7 8 9 |
def nn_model(): model = Sequential() model.add(Dense(num_pixels, input_dim=num_pixels, kernel_initializer='normal', activation='relu')) model.add(Dense(64, kernel_initializer='normal', activation='relu')) model.add(Dense(num_classes, kernel_initializer='normal', activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model |
次にCNNを定義します。
CNNでは、畳み込み層と多層ニューラルネットワークを組み合わせたモデルを構築します。
ざっくりとCNNのモデル構築について整理します。
畳み込み層を用いて、分類に役立つ特徴的な箇所を抽出し、分類に役立たない箇所を除去するような特徴量を獲得したうえで、これを多層ニューラルネットワークの入力として使用します。
畳み込み層は、Conv2D, MaxPooling2Dを用いて構築します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# CNNモデル定義 def cnn_model(): model = Sequential() model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(Flatten()) model.add(Dense(64, activation='relu')) model.add(Dense(10, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) model.summary() return model |
Conv2D(32, (3, 3), activation=’relu’, input_shape=(28, 28, 1))では、入力画像のサイズを指定し、フィルタ数32、フィルタサイズ3*3を指定しています。この処理により、サイズ26(=28-2)*26(=28-2)の特徴マップが32個作成されます。
MaxPooling2D((2, 2))では、ダウンサンプリングを行い、サイズ13*13の特徴マップが32個作成されます。
Conv2D(64, (3, 3), activation=’relu’)では、サイズ11(=13-2)*11(=13-2)の特徴マップが64個作成されます。 MaxPooling2D((2, 2))では、ダウンサンプリングを行い、サイズ5*5の特徴マップが64個作成されます。
Conv2D(64, (3, 3), activation=’relu’)では、サイズ3(=5-2)*3(=5-2)の特徴マップが64個作成されます。
このように、ネットワークが深くなるに従い、データのサイズが縮小する傾向にあります。
畳み込み層の構造は、summary()を用いることで確認することができます。
このような畳み込み層から得られた特徴ベクトルを多層ニューラルネットワークに入力します。ニューラルネットワークの入力はベクトルですので、画像をベクトルデータになるように変換します。
そのために、Conv2D(64, (3, 3), activation=’relu’)で獲得した特徴量は(3, 3, 64)の3次元ですので、
これをFlatten()を用いて入力できるようにベクトルに変換します。
次にshape(3, 3, 64)を一つ以上のDenseレイヤーを用意して分類を実行します。
MNISTは10クラスですので、Denseレイヤーの出力を10にし、softmax関数を指定します。
これでCNNのモデル定義は完了です。
モデルの学習/評価
モデルを学習し、検証用データでモデルを評価してみます。
NNの学習/モデル評価
1 2 3 4 5 6 7 |
model = nn_model() # 学習開始 model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, batch_size=200, verbose=2) # モデル評価 scores = model.evaluate(x_test, y_test, verbose=0) print("NN classification Error: %.2f%%"%(100-scores[1]*100)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Train on 60000 samples, validate on 10000 samples Epoch 1/10 - 7s - loss: 0.3386 - acc: 0.9065 - val_loss: 0.1476 - val_acc: 0.9569 Epoch 2/10 - 6s - loss: 0.1177 - acc: 0.9656 - val_loss: 0.0973 - val_acc: 0.9711 Epoch 3/10 - 7s - loss: 0.0744 - acc: 0.9778 - val_loss: 0.0829 - val_acc: 0.9737 Epoch 4/10 - 7s - loss: 0.0527 - acc: 0.9839 - val_loss: 0.0783 - val_acc: 0.9751 Epoch 5/10 - 7s - loss: 0.0381 - acc: 0.9883 - val_loss: 0.0660 - val_acc: 0.9793 Epoch 6/10 - 6s - loss: 0.0279 - acc: 0.9918 - val_loss: 0.0616 - val_acc: 0.9804 Epoch 7/10 - 6s - loss: 0.0202 - acc: 0.9944 - val_loss: 0.0658 - val_acc: 0.9801 Epoch 8/10 - 6s - loss: 0.0154 - acc: 0.9955 - val_loss: 0.0712 - val_acc: 0.9794 Epoch 9/10 - 6s - loss: 0.0135 - acc: 0.9959 - val_loss: 0.0667 - val_acc: 0.9808 Epoch 10/10 - 6s - loss: 0.0102 - acc: 0.9970 - val_loss: 0.0666 - val_acc: 0.9817 NN classification Error: 1.83% |
CNNの学習/モデル評価
1 2 3 4 5 |
model = cnn_model() model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, batch_size=200, verbose=2) scores = model.evaluate(x_test, y_test, verbose=0) print("CNN classification Error: %.2f%%"%(100-scores[1]*100)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d_19 (Conv2D) (None, 26, 26, 32) 320 _________________________________________________________________ max_pooling2d_13 (MaxPooling (None, 13, 13, 32) 0 _________________________________________________________________ conv2d_20 (Conv2D) (None, 11, 11, 64) 18496 _________________________________________________________________ max_pooling2d_14 (MaxPooling (None, 5, 5, 64) 0 _________________________________________________________________ conv2d_21 (Conv2D) (None, 3, 3, 64) 36928 _________________________________________________________________ flatten_7 (Flatten) (None, 576) 0 _________________________________________________________________ dense_30 (Dense) (None, 64) 36928 _________________________________________________________________ dense_31 (Dense) (None, 10) 650 ================================================================= Total params: 93,322 Trainable params: 93,322 Non-trainable params: 0 _________________________________________________________________ Train on 60000 samples, validate on 10000 samples Epoch 1/10 - 49s - loss: 0.2854 - acc: 0.9176 - val_loss: 0.0686 - val_acc: 0.9802 Epoch 2/10 - 48s - loss: 0.0679 - acc: 0.9792 - val_loss: 0.0437 - val_acc: 0.9864 Epoch 3/10 - 49s - loss: 0.0468 - acc: 0.9853 - val_loss: 0.0392 - val_acc: 0.9869 Epoch 4/10 - 53s - loss: 0.0371 - acc: 0.9883 - val_loss: 0.0298 - val_acc: 0.9909 Epoch 5/10 - 49s - loss: 0.0294 - acc: 0.9909 - val_loss: 0.0323 - val_acc: 0.9893 Epoch 6/10 - 48s - loss: 0.0239 - acc: 0.9924 - val_loss: 0.0298 - val_acc: 0.9903 Epoch 7/10 - 47s - loss: 0.0201 - acc: 0.9935 - val_loss: 0.0240 - val_acc: 0.9924 Epoch 8/10 - 47s - loss: 0.0183 - acc: 0.9941 - val_loss: 0.0262 - val_acc: 0.9913 Epoch 9/10 - 47s - loss: 0.0144 - acc: 0.9953 - val_loss: 0.0271 - val_acc: 0.9914 Epoch 10/10 - 51s - loss: 0.0140 - acc: 0.9953 - val_loss: 0.0294 - val_acc: 0.9903 CNN classification Error: 0.97% |
画像分類結果
画像分類の結果は、以下のようになりました。
NN, CNNでの分類結果を載せます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
predict_result = model.predict(x_test) plt.figure(figsize=(20, 20)) for index in range(30): plt.subplot(5, 6, index + 1) plt.imshow(x_test[index].reshape(28, 28, 1), cmap=plt.get_cmap('gray')) # 画像データの予測ラベルと正解ラベルを付与 predict_label = predict_result[index] teach_label = y_test[index] # 正解の場合は青、不正解の場合は赤色を使用する if predict_label == teach_label: color = 'blue' else: color = 'red' plt.xlabel("y^->{}, y->{}".format(predict_label, teach_label), color=color) |
画像下に出力したラベルの色が青の場合、正解データと予測値が一致し、赤色の場合は、一致しないように出力しています。
[NNモデルによる画像分類結果]
[CNNモデルによる画像分類結果]
30枚の画像を取り出して表示してみました。4行1列目の画像の分類を誤っているみたいです。正解は3ですが、8と予測したみたいです。8にも見えるような気がします。。。
ソースコード全体を載せます。
NNモデルのソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense from keras.layers import Dropout from keras.layers import Flatten from keras.layers.convolutional import Conv2D from keras.layers.convolutional import MaxPooling2D from keras.utils import np_utils import numpy as np import matplotlib.pyplot as plt (x_train, y_train), (x_test, y_test) = mnist.load_data() # 画像データ28*28を784次元のベクトルデータに変換する num_pixels = x_train.shape[1] * x_train.shape[2] x_train = x_train.reshape((x_train.shape[0], num_pixels)).astype('float32') x_test = x_test.reshape((x_test.shape[0], num_pixels)).astype('float32') # ニューラルネットワークの入力に合わせてデータ正規化 x_train = x_train / 255 x_test = x_test / 255 # 出力クラスに対応するようにデータ整形 y_train = np_utils.to_categorical(y_train) test_label = y_test y_test = np_utils.to_categorical(y_test) num_classes = y_test.shape[1] def nn_model(): model = Sequential() model.add(Dense(num_pixels, input_dim=num_pixels, kernel_initializer='normal', activation='relu')) model.add(Dense(64, kernel_initializer='normal', activation='relu')) model.add(Dense(num_classes, kernel_initializer='normal', activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model model = nn_model() # 学習開始 model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, batch_size=200, verbose=2) # モデル評価 scores = model.evaluate(x_test, y_test, verbose=0) print("NN classification Error: %.2f%%"%(100-scores[1]*100)) # 学習したモデルによる予測 predict_result = model.predict(x_test) # 予測結果表示 plt.figure(figsize=(20, 20)) for index in range(30): plt.subplot(5, 6, index+1) plt.imshow(x_test[index].reshape(28, 28), cmap=plt.get_cmap('gray')) # 正解の場合は青、不正解の場合は赤 predict_label = predict_result[index].argmax() # print(type(predict_label)) teach_label = test_label[index] if predict_label == teach_label: color = 'blue' else: color = 'red' plt.xlabel("y^->{}, y->{}".format(predict_label, teach_label), color=color, fontsize=20) |
CNNモデルのソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense from keras.layers import Dropout from keras.layers import Flatten from keras.layers.convolutional import Conv2D from keras.layers.convolutional import MaxPooling2D from keras.utils import np_utils import numpy as np import matplotlib.pyplot as plt (x_train, y_train), (x_test, y_test) = mnist.load_data() # 画像データ28*28を学習用、検証用に分けて格納 num_pixels = x_train.shape[1] * x_train.shape[2] x_train = x_train.reshape((x_train.shape[0], 28, 28, 1)).astype('float32') x_test = x_test.reshape((x_test.shape[0], 28, 28, 1)).astype('float32') # ニューラルネットワークの入力に合わせてデータ正規化 x_train = x_train / 255 x_test = x_test / 255 # 出力クラスに対応するようにデータ整形 y_train = np_utils.to_categorical(y_train) test_label = y_test y_test = np_utils.to_categorical(y_test) num_classes = y_test.shape[1] # CNNモデル定義 def cnn_model(): model = Sequential() model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1))) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(Flatten()) model.add(Dense(64, activation='relu')) model.add(Dense(10, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) model.summary() return model # 学習したモデルによる予測 predict_result = model.predict(x_test) # 予測結果表示 plt.figure(figsize=(20, 20)) for index in range(30): plt.subplot(5, 6, index+1) plt.imshow(x_test[index].reshape(28, 28), cmap=plt.get_cmap('gray')) # 正解の場合は青、不正解の場合は赤 predict_label = predict_result[index].argmax() # print(type(predict_label)) teach_label = test_label[index] if predict_label == teach_label: color = 'blue' else: color = 'red' plt.xlabel("y^->{}, y->{}".format(predict_label, teach_label), color=color, fontsize=20) |
いかがでしょうか。
このように、Kerasを使用すれば比較的簡単にディープラーニングモデルを構築できます。また、モデルの学習/評価も簡単にできます。
今回は、MNISTの手書き画像分類を行いましたが、自前画像を用意して試すこともできますので活用してみてはいかがでしょうか。
コメント
[…] […]