LoginSignup
0
2

More than 3 years have passed since last update.

MLP、CNNを使って物体認識を行う〜精度を向上させる(その2) / Kaggle CIFAR-10

Posted at

前回の記事では、CIFAR-10を題材にMLPのModelの構築と、パラメーターチューニングを行い、0.53049〜というスコア(Accuracy)を得ることができました。

今回はCNNを使いさらなる精度向上を目指してみたいと思います。

Model作成その③ CNN

データを準備するコードはこちら。
前回からreshapeを少し変えています。

# データのインポート
import numpy as np
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.utils import to_categorical

(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X_train, X_test = X_train.reshape(-1, 32, 32, 3), X_test.reshape(-1, 32, 32, 3)
X_train, X_test = X_train.astype('float32'), X_test.astype('float32')
X_train, X_test = X_train/255.0, X_test/255.0
y_train, y_test = to_categorical(y_train), to_categorical(y_test)

Modelのコードです。
チューニングはこの後行いますので、シンプルに畳み込み層とプーリング層を1層ずつだけ実装しました。

# ニューラルネットワーク構築(CNN)
import time
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Conv2D, MaxPooling2D, Flatten
from tensorflow.keras import optimizers

# Sequentialオブジェクト
model = Sequential()

# 畳み込み層
model.add(Conv2D(filters=64, kernel_size=(5,5), padding='same', input_shape=(32, 32, 3), activation='relu'))

# プーリング層
model.add(MaxPooling2D(pool_size=(2,2)))

# Flatten層
model.add(Flatten())

# 出力層
model.add(Dense(10, activation='softmax'))

# コンパイル
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

# 学習を行う(GPU:オン)
start = time.time()
history = model.fit(
    X_train,
    y_train,
    epochs=20,
    batch_size=128,
    validation_data=(X_test, y_test)
)
end = time.time()

# 結果
score = model.evaluate(X_test, y_test, verbose=1)

print('accuracy:', score[1])
print('loss    :', score[0])
print('所要時間 :',end-start,'sec')

accuracy: 0.6527000069618225
loss    : 1.1159632205963135
所要時間 : 43.79876708984375 sec

チューニング後のMLPよりも10%以上精度を上げられていますね!
CNNの強力さをシンプルに実感します。

Model作成その④ CNNパラメーターチューニング

前回同様hyperoptを使ってパラメーターチューニングを行います。
チューニング対象は畳み込み層のfilter、kernel_size、プーリング層/ドロップアウト層の配置の有無と、以降追加する層の数、そしてそのユニット数ならびに活性化関数……うん、ほぼ全部ですね。

データ作成/Model作成の関数です。

from hyperopt import hp
from hyperopt import Trials, tpe
from hyperas import optim
from hyperas.distributions import choice, uniform

def create_data():
    import numpy as np
    import time
    from tensorflow.keras.datasets import cifar10
    from tensorflow.keras.models import Sequential
    from tensorflow.keras.layers import Dense, Dropout, Conv2D, MaxPooling2D, Flatten
    from tensorflow.keras import optimizers
    from tensorflow.keras.utils import to_categorical

    (X_train, y_train), (X_test, y_test) = cifar10.load_data()
    X_train, X_test = X_train.reshape(-1, 32, 32, 3), X_test.reshape(-1, 32, 32, 3)
    X_train, X_test = X_train.astype('float32'), X_test.astype('float32')
    X_train, X_test = X_train/255.0, X_test/255.0
    y_train, y_test = to_categorical(y_train), to_categorical(y_test)

    return X_train, X_test, y_train, y_test

def create_model(X_train, X_test):

    # Sequentialオブジェクト
    model = Sequential()

    # 畳み込み層(第一層)
    model.add(
        Conv2D(
            filters={{choice([64, 128])}},
            kernel_size={{choice([(3,3), (5,5), (7,7)])}},
            padding='same',
            input_shape=(32, 32, 3),
            activation={{choice(['tanh', 'relu'])}}
        )
    )
    # プーリング層
    if {{choice([0, 1])}} == 0:
        pass
    else:
        model.add(MaxPooling2D(pool_size=(2,2)))
    # ドロップアウト
    if {{choice([0, 1])}} == 0:
        pass
    else:
        model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))

    # 畳み込み層(第二層)
    model.add(
        Conv2D(
            filters={{choice([32, 64])}},
            kernel_size={{choice([(3,3), (5,5), (7,7)])}},
            padding='same',
            input_shape=(32, 32, 3),
            activation={{choice(['tanh', 'relu'])}}
        )
    )
    # プーリング層
    if {{choice([0, 1])}} == 0:
        pass
    else:
        model.add(MaxPooling2D(pool_size=(2,2)))
    # ドロップアウト
    if {{choice([0, 1])}} == 0:
        pass
    else:
        model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))

    # 畳み込み層(第三層)
    model.add(
        Conv2D(
            filters={{choice([32, 64])}},
            kernel_size={{choice([(3,3), (5,5), (7,7)])}},
            padding='same',
            input_shape=(32, 32, 3),
            activation={{choice(['tanh', 'relu'])}}
        )
    )
    # プーリング層
    if {{choice([0, 1])}} == 0:
        pass
    else:
        model.add(MaxPooling2D(pool_size=(2,2)))
    # ドロップアウト
    if {{choice([0, 1])}} == 0:
        pass
    else:
        model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))

    # Flatten層
    model.add(Flatten())

    # 以降
    if {{choice([0, 1, 2])}} == 0:
        pass
    elif {{choice([0, 1, 2])}} == 1:
        model.add(Dense({{choice([400, 500, 600])}},activation={{choice(['tanh', 'relu'])}}))
        model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
    elif {{choice([0, 1, 2])}} == 2:
        model.add(Dense({{choice([400, 500, 600])}},activation={{choice(['tanh', 'relu'])}}))
        model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))
        model.add(Dense({{choice([100, 200, 300])}},activation={{choice(['tanh', 'relu'])}}))
        model.add(Dropout({{quniform(0.2, 0.6, 0.1)}}))

    # 出力層
    model.add(Dense(10, activation='softmax'))

    model.compile(loss='categorical_crossentropy',optimizer={{choice(['adam', 'rmsprop'])}},metrics=['accuracy'])

    # 学習を行う(GPU:オン)
    history = model.fit(
        X_train,
        y_train,
        epochs=30,
        batch_size={{choice([128, 256])}},
        validation_data=(X_test, y_test),
        verbose=0
    )
    val_acc = np.amax(history.history['val_accuracy'])
    return  {'loss': -val_acc, 'status':STATUS_OK, 'model':model}

探索を実行するコードは基本前回と同じですが、検証回数を増やしています。

# 探索実行
best_run, best_model = optim.minimize(
                model=create_model, 
                data=create_data, 
                algo=tpe.suggest, 
                max_evals=50, 
                eval_space=True, 
                notebook_name='__notebook_source__', 
                trials=Trials()
)

# 結果の確認
X_train, X_test, y_train, y_test = create_data()
score = best_model.evaluate(X_test, y_test)

print('accuracy:', score[1])
print('loss    :', score[0])
100%|██████████| 50/50 [2:27:28<00:00, 176.98s/trial, best loss: -0.805400013923645]   
accuracy: 0.8014000058174133
loss    : 0.6208286881446838

無事、これまでで一番高いスコアが得られました。

参考までにチューニング後のModelの構造も見てみることとします。

# Model構造の表示
best_model.summary()
Model: "sequential_42"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_126 (Conv2D)          (None, 32, 32, 64)        1792      
_________________________________________________________________
conv2d_127 (Conv2D)          (None, 32, 32, 64)        102464    
_________________________________________________________________
max_pooling2d_71 (MaxPooling (None, 16, 16, 64)        0         
_________________________________________________________________
conv2d_128 (Conv2D)          (None, 16, 16, 64)        200768    
_________________________________________________________________
max_pooling2d_72 (MaxPooling (None, 8, 8, 64)          0         
_________________________________________________________________
dropout_94 (Dropout)         (None, 8, 8, 64)          0         
_________________________________________________________________
flatten_42 (Flatten)         (None, 4096)              0         
_________________________________________________________________
dense_75 (Dense)             (None, 500)               2048500   
_________________________________________________________________
dropout_95 (Dropout)         (None, 500)               0         
_________________________________________________________________
dense_76 (Dense)             (None, 10)                5010      
=================================================================
Total params: 2,358,534
Trainable params: 2,358,534
Non-trainable params: 0
_________________________________________________________________

まとめ

各Modelでのスコアは以下の通りです。

Model スコア
MLP(チューニングなし) 0.4948999881744385
MLP(チューニングあり) 0.5304999947547913
CNN(チューニングなし) 0.6527000069618225
CNN(チューニングあり) 0.8014000058174133

良いですね、工夫をしていくにつれてしっかりスコアも伸びています。
記事としてもまとめやすいね!

ちなみにこのスコアでSubmitすると、Leaderboard上は50位+くらいの成績です。
全体でも230名強しかいないのでアレですが、上位20%までもう一押し、ってところでしょうか。

分かりきっていたことですが、この程度の規模感のデータでもパラメーターチューニングを頑張ろうとするとそれなりに時間を喰いますね。
CNN(チューニングあり)では2時間半以上掛かっているので、その間に貯まってる家事でも済ませちゃいましょう。

ここから更にスコアを伸ばすとすれば、まだパラメーターのチューニングや層の配置に工夫の余地はあるかと思います。
割と適当に決めてしまったので、時間があるのであればグリッドサーチ等も試して見るべきでしょうか。

あとはやはりアンサンブルか、学習率の制御も一つのアプローチかと思います。
ただこの辺りは自分自身も勉強不足なところも多いので、他のコンペに参加しつつ知見を深めていければと考えています。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2