先月からディープラーニングを教えてくれる講座を受講しています。
今週からCNNに入ったのですが、先生が「CNNは特徴抽出器としてかなり優秀で、学習していないランダムなCNNでも高い精度が出せる」と教えてくれました。
「え、そうなの!?」とびっくりしましたので、MNISTを使って簡単に実験してみました。
まずはデータセットをロードして整形します。
import numpy as np from keras.models import Sequential from keras.layers import Conv2D, MaxPool2D, Dense, Flatten from keras.losses import categorical_crossentropy from keras.optimizers import Adam from keras.metrics import categorical_accuracy from keras.utils import to_categorical from keras.datasets import mnist # データセットのロード (X_train, y_train), (X_test, y_test) = mnist.load_data() train_samples, input_w, input_h = X_train.shape test_samples = X_test.shape[0] categories = len(np.unique(y_test)) # チャネル数 (1) を加える X_train = X_train.reshape(train_samples, input_w, input_h, 1) X_test = X_test.reshape(test_samples, input_w, input_h, 1) # 0〜1に正規化 X_train = X_train.astype('float32') / 255 X_test = X_test.astype('float32') / 255 # 10次元のone-hotベクトル化 y_train = to_categorical(y_train, categories) y_test = to_categorical(y_test, categories) # 学習パラメータ batch_size = 32 epochs = 10
比較対象となる全結合層でのみで構成したNNです。入力層 (784次元) -> 出力層 (10次元のソフトマックス) のみのシンプル過ぎなネットワークです。
- パラメータ数は7850 (784×10 + 10) で、CNNを挟んでいない分、1層でもかなり多数になります
- 精度は92.6%
model_nn = Sequential() model_nn.add(Flatten(input_shape=(input_w, input_h, 1))) model_nn.add(Dense(10, activation='softmax')) model_nn.compile( loss=categorical_crossentropy, optimizer=Adam(), metrics=[categorical_accuracy] ) print(model_nn.summary()) # _________________________________________________________________ # Layer (type) Output Shape Param # # ================================================================= # flatten_18 (Flatten) (None, 784) 0 # _________________________________________________________________ # dense_49 (Dense) (None, 10) 7850 # ================================================================= # Total params: 7,850 # Trainable params: 7,850 # Non-trainable params: 0 # _________________________________________________________________ model_nn.fit( X_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(X_test, y_test), verbose=1 ) # Train on 60000 samples, validate on 10000 samples # Epoch 1/10 # 60000/60000 [==============================] - 6s 101us/step - loss: 0.4653 - categorical_accuracy: 0.8794 - val_loss: 0.3068 - val_categorical_accuracy: 0.9128 # ... # Epoch 10/10 # 60000/60000 [==============================] - 6s 93us/step - loss: 0.2507 - categorical_accuracy: 0.9306 - val_loss: 0.2644 - val_categorical_accuracy: 0.9262
次に、特徴抽出器としてランダムなCNNを全結合層の前段に加えたネットワークを構成します。
- CNNは畳み込み+プーリングの2セットの計4層構成となっており、7x7x8で出力されます
cnn_layers.trainable=False
とすることで、学習させないようにしてます
- 28x28x1から7x7x8に削減されたことで、パラメータ数も約半分となってます
- 精度は94.2%
# ランダムなCNN cnn_layers = Sequential([ Conv2D(4, (2, 2), padding='same', activation='relu', input_shape=(input_w, input_h, 1)), MaxPool2D((2, 2)), Conv2D(8, (2, 2), padding='same', activation='relu'), MaxPool2D((2, 2)) ]) # 学習をさせない cnn_layers.trainable = False # 後ろに全結合層をつなげる model_cnn = Sequential() model_cnn.add(cnn_layers) model_cnn.add(Flatten()) model_cnn.add(Dense(10, activation='softmax')) model_cnn.compile( loss=categorical_crossentropy, optimizer=Adam(), metrics=[categorical_accuracy] ) print(model_cnn.summary()) # _________________________________________________________________ # Layer (type) Output Shape Param # # ================================================================= # sequential_34 (Sequential) (None, 7, 7, 8) 156 # _________________________________________________________________ # flatten_23 (Flatten) (None, 392) 0 # _________________________________________________________________ # dense_54 (Dense) (None, 10) 3930 # ================================================================= # Total params: 4,086 # Trainable params: 3,930 # Non-trainable params: 156 # _________________________________________________________________ model_cnn.fit( X_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(X_test, y_test), verbose=1 ) # Train on 60000 samples, validate on 10000 samples # Epoch 1/10 # 60000/60000 [==============================] - 7s 116us/step - loss: 0.9706 - categorical_accuracy: 0.8011 - val_loss: 0.5502 - val_categorical_accuracy: 0.8767 # ... # Epoch 10/10 # 60000/60000 [==============================] - 6s 105us/step - loss: 0.2198 - categorical_accuracy: 0.9390 - val_loss: 0.2128 - val_categorical_accuracy: 0.9421
ということで、全てのピクセルを全結合層でつなぐよりも、ランダムなCNNで特徴抽出したほうが、パラメータ数が減り (7850 -> 3930) 、精度も向上する (92.6 -> 94.2) という結果が得られました。
ちなみに、Conv2Dを除いてMaxPool2Dのみ (つまり縮約のみ) にしたバージョンでも試してみましたが、精度は83.1%にとどまりました。このことからも、畳み込み計算が特徴抽出器として優秀であることがわかります。