CIFAR-10にチャレンジしてみた【Keras】

なかなかハイペースに進めています。この記事はシリーズ物なのですが、わずか一日でCIFAR-10にまでやってきました。

CIFER-10とは何か

10種類のカラー画像をまとめたデータセットのことです。MNISTと同じ10クラスですが、こちらはカラー画像であるという点が大きな違いです。もちろん数字ではなく飛行機、自動車などのオブジェクトの括りになっています。基本的には応用なのですが、これを分類するネットワークを作って、今日は終わりにしておきましょう。

import os
import keras
from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten, Dropout
from keras.layers.core import Dense
from keras.datasets import cifar10
from keras.optimizers import RMSprop
from keras.callbacks import TensorBoard, ModelCheckpoint

def network(input_shape, num_classes):
    model = Sequential()

    # extract image features by convolution and max pooling layers
    model.add(Conv2D(
        32, kernel_size=3, padding="same",
        input_shape=input_shape, activation="relu"
        ))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.25))
    model.add(Conv2D(64, kernel_size=3, padding="same", activation="relu"))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    # classify the class by fully-connected layers
    model.add(Flatten())
    model.add(Dense(512, activation="relu"))
    model.add(Dropout(0.5))
    model.add(Dense(num_classes))
    model.add(Activation("softmax"))
    return model

class CIFAR10Dataset():

    def __init__(self):
        self.image_shape = (32, 32, 3)
        self.num_classes = 10

    def get_batch(self):
        (x_train, y_train), (x_test, y_test) = cifar10.load_data()

        x_train, x_test = [self.preprocess(d) for d in [x_train, x_test]]
        y_train, y_test = [self.preprocess(d, label_data=True) for d in
                           [y_train, y_test]]

        return x_train, y_train, x_test, y_test

    def preprocess(self, data, label_data=False):
        if label_data:
            # convert class vectors to binary class matrices
            data = keras.utils.to_categorical(data, self.num_classes)
        else:
            data = data.astype("float32")
            data /= 255  # convert the value to 0~1 scale
            shape = (data.shape[0],) + self.image_shape  # add dataset length
            data = data.reshape(shape)

        return data

class Trainer():

    def __init__(self, model, loss, optimizer):
        self._target = model
        self._target.compile(
            loss=loss, optimizer=optimizer, metrics=["accuracy"]
            )
        self.verbose = 1
        logdir = "logdir_cifar10_net"
        self.log_dir = os.path.join(os.path.dirname(__file__), logdir)
        self.model_file_name = "model_file.hdf5"

    def train(self, x_train, y_train, batch_size, epochs, validation_split):
        if os.path.exists(self.log_dir):
            import shutil
            shutil.rmtree(self.log_dir)  # remove previous execution
        os.mkdir(self.log_dir)

        model_path = os.path.join(self.log_dir, self.model_file_name)
        self._target.fit(
            x_train, y_train,
            batch_size=batch_size, epochs=epochs,
            validation_split=validation_split,
            callbacks=[
                TensorBoard(log_dir=self.log_dir),
                ModelCheckpoint(model_path, save_best_only=True)
            ],
            verbose=self.verbose
        )

dataset = CIFAR10Dataset()

# make model
model = network(dataset.image_shape, dataset.num_classes)

# train the model
x_train, y_train, x_test, y_test = dataset.get_batch()
trainer = Trainer(model, loss="categorical_crossentropy", optimizer=RMSprop())
trainer.train(
    x_train, y_train, batch_size=128, epochs=12, validation_split=0.2
    )

# show result
score = model.evaluate(x_test, y_test, verbose=0)
print("Test loss:", score[0])
print("Test accuracy:", score[1])

意外にも、本に掲載された内容そのままで実行することができました。画像ファイルも事前に準備するのかと思いましたが、プログラム自体が勝手にダウンロードしてくれました。便利な時代になったものですね。

動かしてみたよ

Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 191s 1us/step
Train on 40000 samples, validate on 10000 samples
Epoch 1/12
40000/40000 [==============================] - 83s 2ms/step - loss: 1.7585 - acc: 0.3689 - val_loss: 1.4300 - val_acc: 0.4941
Epoch 2/12
40000/40000 [==============================] - 83s 2ms/step - loss: 1.3339 - acc: 0.5281 - val_loss: 1.2361 - val_acc: 0.5749
Epoch 3/12
40000/40000 [==============================] - 90s 2ms/step - loss: 1.1653 - acc: 0.5911 - val_loss: 1.1378 - val_acc: 0.6001
Epoch 4/12
40000/40000 [==============================] - 82s 2ms/step - loss: 1.0476 - acc: 0.6321 - val_loss: 1.0420 - val_acc: 0.6337
Epoch 5/12
40000/40000 [==============================] - 80s 2ms/step - loss: 0.9616 - acc: 0.6615 - val_loss: 0.9585 - val_acc: 0.6602
Epoch 6/12
40000/40000 [==============================] - 80s 2ms/step - loss: 0.8937 - acc: 0.6882 - val_loss: 0.9359 - val_acc: 0.6781
Epoch 7/12
40000/40000 [==============================] - 76s 2ms/step - loss: 0.8425 - acc: 0.7058 - val_loss: 0.8910 - val_acc: 0.7006
Epoch 8/12
40000/40000 [==============================] - 74s 2ms/step - loss: 0.7818 - acc: 0.7270 - val_loss: 0.8884 - val_acc: 0.7000
Epoch 9/12
40000/40000 [==============================] - 74s 2ms/step - loss: 0.7397 - acc: 0.7434 - val_loss: 0.8670 - val_acc: 0.7085
Epoch 10/12
40000/40000 [==============================] - 74s 2ms/step - loss: 0.6927 - acc: 0.7605 - val_loss: 0.8795 - val_acc: 0.7043
Epoch 11/12
40000/40000 [==============================] - 75s 2ms/step - loss: 0.6524 - acc: 0.7751 - val_loss: 0.8307 - val_acc: 0.7244
Epoch 12/12
40000/40000 [==============================] - 78s 2ms/step - loss: 0.6230 - acc: 0.7878 - val_loss: 0.8078 - val_acc: 0.7294
Test loss: 0.8138949976921082
Test accuracy: 0.7255

まとめ

結果としては72.55%の正答率ということになりました。MINSTに比べると格段に性能が悪いことが判ります。書籍の実行例も同じようになっていたので、これはこういうものだと理解しましょう。4つに3つは見分けられるという点を評価すべきなのでしょうね。

Conv2Dの階層を増やす事例も書籍には掲載されていましたが、精度が75%くらいになるのがせいぜいのようです。他にも、データ水増しの事例も載っていましたが、76%くらいにしかならないようです。学習時間に比べて割りに合わないように見えるのは気のせいでしょうか。

ただ、論文上では最新の精度は96%を超えるところまで到達しているそうです。どうやって、そんな精度を実現することができたのか、興味深いです。

システム開発

Posted by @erestage