畳み込みニューラルネットワークで画像分類

畳み込みニューラルネットワークで写真の画像分類を行います。
畳み込みニューラルネットワークを使って効率よくデータの特徴を抽出することができます。

データセットはCIFAR-10を使用します。10個に分類された画像と正解ラベルを集めたデータセットで、訓練データ50000件、テストデータ10000件が含まれています。

まずGoogle Colaboratoryのランタイムのハードウェアアクセラレータを「TPU」に設定します。
ハードウェアアクセラレータの設定

次にtensorflowのバージョンを1.13.1にします。

1
2
!pip uninstall tensorflow -y
!pip install tensorflow==1.13.1

インストール後にランタイムをリスタートする必要があります。


畳み込みニューラルネットワークで必要なパッケージをインポートします。

1
2
3
4
5
6
7
8
9
# パッケージのインポート
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.layers import Activation, Dense, Dropout, Conv2D, Flatten, MaxPool2D
from tensorflow.keras.models import Sequential, load_model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.utils import to_categorical
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

データセットの準備を行います。

データセットの配列は下記のようになります。

配列 説明
train_images 訓練画像の配列
train_labels 訓練ラベルの配列
test_images テスト画像の配列
test_labels テストラベルの配列
1
2
# データセットの準備
(train_images, train_labels), (test_images, test_labels) = cifar10.load_data()

データセットのシェイプを確認します、

1
2
3
4
5
# データセットのシェイプの確認
print(train_images.shape)
print(train_labels.shape)
print(test_images.shape)
print(test_labels.shape)

実行結果1
訓練データと訓練ラベルは50000件、テスト画像とテストラベルは10000件です。
画像サイズは32×32×3です。RGB画像のため画像サイズ(32×32)×3となります。


先頭10件の訓練画像を確認します。

1
2
3
4
5
# データセットの画像の確認
for i in range(10):
plt.subplot(2, 5, i+1)
plt.imshow(train_images[i])
plt.show()

実行結果2
先頭10件の訓練ラベルの確認を行います。

1
2
# データセットのラベルの確認
print(train_labels[0:10])

実行結果3

ラベルの意味は下記の通りです。

ID 説明
0 airplane(飛行機)
1 automobile(自動車)
2 bird(鳥)
3 cat(猫)
4 deer(鹿)
5 dog(犬)
6 frog(カエル)
7 horse(馬)
8 ship(船)
9 truck(トラック)

データセットの前処理と確認を行います。 訓練画像とテスト画像の正規化を行います。画像のRGBは「0~255」なので255で割って「0.0~1.0」に変換します。
1
2
3
4
5
6
7
# データセットの画像の前処理
train_images = train_images.astype('float32')/255.0
test_images = test_images.astype('float32')/255.0

# データセットの画像の前処理後のシェイプの確認
print(train_images.shape)
print(test_images.shape)
![実行結果4](/img/chap3/3-4.png) 訓練ラベルとテストラベルをone-hot表現に変換します。
1
2
3
4
5
6
7
# データセットのラベルの前処理
train_labels = to_categorical(train_labels, 10)
test_labels = to_categorical(test_labels, 10)

# データセットのラベルの前処理後のシェイプの確認
print(train_labels.shape)
print(test_labels.shape)
![実行結果5](/img/chap3/3-5.png) 畳み込みニューラルネットワークのモデルを作成します。 モデルのネットワーク構造は下記の通りです。
  1. 特徴の抽出を行う。(畳み込みブロック)
  2. (1回目)畳み込み層→畳み込み層→プーリング層→Dropout
  3. (2回目)畳み込み層→畳み込み層→プーリング層→Dropout
  4. 1次元に変換する。(Flatten)
  5. 分類を行う。
  6. 全結合層
  7. Dropout
  8. 全結合層

利用するクラスは次の通りです。

クラス 説明
Conv2D 畳み込み層。引数はカーネル数、カーネルサイズ、活性化関数、パティング。
MaxPool2D プーリング層(Maxプーリング)。引数はプーリング適用領域。
Dense 全結合層。引数はユニット数と活性化関数。
Dropout ドロップアウト。引数は無効にする割合。
Flatten 層の入出力を1次元に変換。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#モデルの作成
model = Sequential()

# Conv→Conv→Pool→Dropout
model.add(Conv2D(32, (3, 3), activation='relu', padding='same', input_shape=(32, 32, 3)))
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

# Conv→Conv→Pool→Dropout
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

# Flatten→Dense→Dropout→Dense
model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

TPUモデルへの変換を行います。

1
2
3
4
5
6
7
8
9
# TPUモデルへの変換
import tensorflow as tf
import os
tpu_model = tf.contrib.tpu.keras_to_tpu_model(
model,
strategy=tf.contrib.tpu.TPUDistributionStrategy(
tf.contrib.cluster_resolver.TPUClusterResolver(tpu='grpc://' + os.environ['COLAB_TPU_ADDR'])
)
)

畳み込みニューラルネットワークモデルのコンパイルを行います。

1
2
# コンパイル
tpu_model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=0.001), metrics=['acc'])

訓練画像と訓練ラベルの配列をモデルに渡して学習を開始します。

1
2
3
# 学習
history = tpu_model.fit(train_images, train_labels, batch_size=128,
epochs=20, validation_split=0.1)

実行結果6(途中略)
畳み込みニューラルネットワークの学習結果をグラフ表示します。

1
2
3
4
5
6
7
# グラフの表示
plt.plot(history.history['acc'], label='acc')
plt.plot(history.history['val_acc'], label='val_acc')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(loc='best')
plt.show()

実行結果7
学習が進むと正解率が上がっていくことが分かります。


テスト画像とテストラベルの配列をモデルに渡して評価を行い正解率を取得します。

1
2
3
# 評価
test_loss, test_acc = tpu_model.evaluate(test_images, test_labels)
print('loss: {:.3f}\nacc: {:.3f}'.format(test_loss, test_acc ))

実行結果8
正解率は73.5%となりました。


最後に、先頭10件のテストデータの推論を行い予測結果を取得します。

1
2
3
4
5
6
7
8
9
10
11
12
# 推論する画像の表示
for i in range(10):
plt.subplot(2, 5, i+1)
plt.imshow(test_images[i])
plt.show()

# 推論したラベルの表示
test_predictions = tpu_model.predict(test_images[0:16]) # 学習データ数を16個に増やす
test_predictions = np.argmax(test_predictions, axis=1)[0:10] # 結果数を10個に減らす
labels = ['airplane', 'automobile', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck']
print([labels[n] for n in test_predictions])

実行結果9

画像と推測結果を比較すると正解率が90%であることが分かります。

(Google Colaboratoryで動作確認しています。)

参考

AlphaZero 深層学習・強化学習・探索 人工知能プログラミング実践入門 サポートページ