はじめに
機械学習初学者のfumioです。機械学習プログラミングの面白さにのめりこんで、日々学んでいます。
koshian2さんが書かれた「モザイク除去から学ぶ 最先端のディープラーニング」を学んでいます。自身が学んだ内容の理解を深めるため、畳み込みニューラルネットワーク(CNN)を画像判別に適用した例をまとめていきたいと思います。
https://qiita.com/koshian2/items/aefbe4b26a7a235b5a5e
要点は下記になります。
- 畳み込みニューラルネットワークの構造を理解する。
- 層の畳み込み+1層のDenseの計10層の畳み込みニューラルネットワークモデルを作る。
- そのモデルによりCIFAR-10データセットの判別を行う。
##畳み込みニューラルネットワーク構造
畳み込みニューラルネットワーク(CNN)は、畳み込み層とプーリング層という2種類の層を含む順伝搬型ネットワークで、画像認識に応用されています。
※順伝搬型ニューラルネットワークとはネットワークにループする結合を持たず、入力層→中間層→出力層というように単一方向へのみ信号が伝播するものを指します。一方で、再帰型ニューラルネットワークはその逆で可逆的なものです。
##データセットの読み込み
import matplotlib.pyplot as plt
cifar_classes = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck"]
(X_train, y_train),(X_test,y_test) = tf.keras.datasets.cifar10.load_data()
print(X_train.shape,y_train.shape)
print(X_test.shape,y_test.shape)
Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 13s 0us/step
(50000, 32, 32, 3) (50000, 1)
(10000, 32, 32, 3) (10000, 1)
Kerasのデータセットから直接読み込みます。訓練データの次元を確認すると、5万枚の32×32×3のデータであることが分かります。カラー画像なので3次元となっていますね。
fig = plt.figure(figsize=(14,14))
for i in range(100):
ax = plt.subplot(10,10,i+1)
ax.imshow(X_train[i])
ax.axis('off')
ax.set_title(cifar_classes[y_train[i,0]])
画像はこのようになっております。既に最初からぼやけていますが、なんとなくそれぞれの名前と写真の意味が分かります。ただ、判別が難しい種類のものも含まれていることが分かります(deer と horseとか、automobileとtruckとか)。
##プーリングとは
CNNにおけるプーリングとは、情報を圧縮してダウンサンプリングすることを指します。通常、畳み込み層の後にプーリング層として適用されます。主な効果は下記になります。
- 微小な位置変化に対応できる
- ある程度過学習を抑制できる
- 計算コストを下げられる
1.の位置変化に対応している多少特徴の位置がずれていてもプーリング層での出力を一定になるようにできます。つまり、手書き数字を例にとると少し位置がずれたものでも同じ数字だと認識させることができます。
##10層のニューラルネットワークモデルを作る
今回のCIFAR-10判別を行うために、9層の畳み込み+1層の全結合層の計10層のモデルを作ります。
- 畳み込み(Conv) 64ch -> バッチ正規化(BN) -> 活性化(ReLU)) repeat x3 -> プーリング(Pooling)
- (Conv 128ch -> BN -> ReLU) repeat x3 -> Pooling
- (Conv 256ch -> BN -> ReLU) repeat x3
- Global Average Pooling -> Dense 10 -> Softmax
このような順番でモデルを作ります。活性化関数としてはReLUを用いています。
inputs = layers.Input((32,32,3))
x = inputs
for ch in [64, 128, 256]:
for i in range(3):
x = layers.Conv2D(ch, 3, padding="same")(x)
x = layers.BatchNormalization()(x)
x = layers.ReLU()(x)
if ch != 256:
x = layers.AveragePooling2D()(x)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(10, activation="softmax")(x)
model = tf.keras.models.Model(inputs, x)
model.summary()
conv2d_12 (Conv2D) (None, 8, 8, 256) 590080
batch_normalization_12 (Batc (None, 8, 8, 256) 1024
re_lu_12 (ReLU) (None, 32, 32, 256) 0
average_pooling2d_3 (Average (None, 8, 8, 256) 0
global_average_pooling2d_1 ( (None, 256) 0
dense_1 (Dense) (None, 10) 2570
出力される最後の部分だけ抽出しました。
次元としては、下記のように変わります。
(None,32,32,3)→(None,32,32,64)→(None, 16, 16, 128)
→(None, 8, 8, 256)→(None, 256)→(None, 10)
プーリング層を通ると次元が半分になっていることが分かります。
##データセットの下準備
X_train = X_train.astype(np.float32) / 255.0
X_test = X_test.astype(np.float32) / 255.0
y_train = y_train.astype(np.float32)
y_test = y_test.astype(np.float32)
次に、元々のデータはunit8型で、[0,255]スケールであることから、データの型をfloat32及びスケールを[0,1]に変換します。
##モデル学習
model.fit(X_train,y_train, validation_data=(X_test, y_test),epochs=10)
PCスペックによってはかなり時間がかかるものと思われます(私のPCスペックだとepochごとに10分程度かかりました)。従って、Google Colabの力を借りて進めることをお勧めします。
下記画像が予測した結果と正解です。赤で書かれているところが誤っているところです。
10回しかepochを取らなかったので、大よそ誤答率38%でした。
プログラム全文はこちらに置いています。
https://github.com/Fumio-eisan/cifar10_20200308