【CV 11】CIFAR-10の画像分類におけるCNNの再開発


文書ディレクトリ

  • 1. モデル評価テスト
  • 1.1データセット紹介
  • 1.2データセット
  • をロードする
  • 1.3スケーリングデータ
  • 1.4プロット曲線
  • 1.5評価モデル
  • 1.6定義モデル
  • 1.6.1 1 VGG Block
  • 1.6.2 2 VGG Block
  • 1.6.3 3 VGG Block
  • 2. 改良モデルTrick
  • 2.1正規化
  • 2.1.1 Dropout
  • 2.1.2 L 2正規化(重み減衰正規化)
  • 2.2データ拡張
  • 2.3 Dropout Rate
  • を変更
  • 2.4 Dropout+データ強化
  • 2.5 Dropout+データ強化+BN
  • Summary

  • 1.モデル評価テスト


    1.1データセットの紹介


    CIFAR-10データセットは10種類の物体を含み、60000枚の32×32ピクセルのカラー写真.これらのカテゴリラベルとその整数値は次のとおりです.
  • 0:airplane
  • 1:automobile
  • 2:bird
  • 3:cat
  • 4:deer
  • 5:dog
  • 6:frog
  • 7:horse
  • 8:ship
  • 9:truck

  • これらはいずれも非常に小さな画像であり,典型的な写真よりもずっと小さく,このデータセットはコンピュータ視覚研究に用いることを目的としている.

    1.2データセットのロード

    import sys
    import matplotlib.pyplot as plt
    from keras.datasets import cifar10
    from keras.utils import to_categorical
    from keras.models import Sequential
    from keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout
    from keras.optimizers import SGD
    from keras.regularizers import l2
    
    def load_dataset():
        # load dataset
        (trainX, trainY), (testX, testY) = cifar10.load_data()
        
        # one hot encode
        trainY = to_categorical(trainY)
        testY = to_categorical(testY)
        
        return trainX, trainY, testX, testY
    
    

    1.3データのスケーリング


    データセットの各画像のピクセル値は、0~255の符号なし整数です.トレーニングモデルの良い出発点は、画素値を標準化することであり、例えば、それらを[0,1]の範囲に再スケールすることである.これは、まず、データ型を符号なし整数から浮動小数点数に変換し、次に画素値を最大値で除算することに関する.
    def prep_pixels(train, test):
        # convert from integers to floats
        train_norm = train.astype('float32')
        test_norm = test.astype('float32')
        
        # normalize to range 0-1
        train_norm = train_norm / 255.0
        test_norm = test_norm / 255.0
        
        return train_norm, test_norm
    

    1.4曲線の描画

    plt.rcParams['figure.dpi'] = 200
    def summarize_diagnostics(history):
        # plot loss
        plt.subplot(211)
        plt.title('Cross Entropy Loss')
        plt.plot(history.history['loss'], color='blue', label='train')
        plt.plot(history.history['val_loss'], color='orange', label='test')
        # plot accuracy
        plt.subplot(212)
        plt.title('Classification Accuracy')
        plt.plot(history.history['accuracy'], color='blue', label='train')
        plt.plot(history.history['val_accuracy'], color='orange', label='test')
        # save plot to file
        filename = sys.argv[0].split('/')[-1]
        plt.savefig(filename + '_plot.png')
        plt.close()
        plt.tight_layout()
        plt.grid(linestyle='-', alpha=0.5)
    

    1.5評価モデル

    def run_test_harness():
        # load dataset
        trainX, trainY, testX, testY = load_dataset()
        # prepare pixel data
        trainX, testX = prep_pixels(trainX, testX)
        # define model
        model = define_model()
        # fit model
        history = model.fit(trainX, trainY, epochs=100, batch_size=64, validation_data=(testX, testY), verbose=2)
        # evaluate model
        _, acc = model.evaluate(testX, testY, verbose=0)
        print('> %.3f' % (acc * 100.0))
        # learning curves
        summarize_diagnostics(history)
    

    1.6モデルの定義


    1.6.1 1 VGG Block

    def define_model():
        model = Sequential()
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(32, 32, 3)))
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Flatten())
        model.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))
        model.add(Dense(10, activation='softmax'))
        
        # compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
        
        return model
    

    トレーニング:
    run_test_harness()
    

    分類精度:
    67.070
    

    1.6.2 2 VGG Block

    # define cnn model
    def define_model():
        model = Sequential()
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(32, 32, 3)))
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Flatten())
        model.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))
        model.add(Dense(10, activation='softmax'))
        
        # compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
        
        return model
    

    トレーニング
    run_test_harness()
    

    印刷精度:
    71.080
    

    1.6.3 3 VGG Block

    # define cnn model
    def define_model():
        model = Sequential()
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(32, 32, 3)))
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Flatten())
        model.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))
        model.add(Dense(10, activation='softmax'))
        
        # compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
        
        return model
    

    トレーニング:
    run_test_harness()
    

    印刷精度:
    73.500
    

    2.モデルTrickの改良


    2.1正規化


    2.1.1 Dropout

    def define_model():
        model = Sequential()
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(32, 32, 3)))
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.2)) #  0.2
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.2))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.2))
        model.add(Flatten())
        model.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))
        model.add(Dropout(0.2))
        model.add(Dense(10, activation='softmax'))
        
        # compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
        
        return model
    

    トレーニング:
    run_test_harness()
    

    印刷精度:
     83.450
    

    2.1.2 L 2正規化(ウェイト減衰正規化)

    def define_model():
        model = Sequential()
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', kernel_regularizer=l2(0.001), input_shape=(32, 32, 3)))
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', kernel_regularizer=l2(0.001)))
        model.add(MaxPooling2D((2, 2)))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', kernel_regularizer=l2(0.001)))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', kernel_regularizer=l2(0.001)))
        model.add(MaxPooling2D((2, 2)))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', kernel_regularizer=l2(0.001)))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', kernel_regularizer=l2(0.001)))
        model.add(MaxPooling2D((2, 2)))
        model.add(Flatten())
        model.add(Dense(128, activation='relu', kernel_initializer='he_uniform', kernel_regularizer=l2(0.001)))
        model.add(Dense(10, activation='softmax'))
        
        # compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
        
        return model
    

    トレーニング:
    run_test_harness()
    

    印刷精度:
    72.550
    

    2.2データの強化


    データ強化には、トレーニングデータセットでサンプルを少量ランダムに変更したコピーが含まれます.これは、トレーニングデータセットを拡張するとともに、モデルが同じ一般的な特徴を学習するためにより一般的な方法を採用するため、正規化効果を有する.
    有用なランダム拡張タイプには、水平反転、画像の微小な移動、および画像の小さなスケールまたはカットが含まれる可能性があります.次の例では、ベースライン画像、特に水平反転、および画像の高さおよび幅に10%のオフセットが生じる簡単な画像増強によって、ベースライン画像に及ぼす影響を説明する.これはKerasにおいてImageDataGeneratorクラスを用いて実現することができる.例:
    # create data generator
    datagen = ImageDataGenerator(width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)
    # prepare iterator
    it_train = datagen.flow(trainX, trainY, batch_size=64)
    

    反復器をmodel.fit_generator()関数に渡し、単一epochでバッチ数を定義する方法は、トレーニング中に使用することができる.
    # fit model
    steps = int(trainX.shape[0] / 64)
    history = model.fit_generator(it_train, steps_per_epoch=steps, epochs=100, validation_data=(testX, testY), verbose=0)
    

    元のテスト関数を書き換えます.
    def run_test_harness():
        # load dataset
        trainX, trainY, testX, testY = load_dataset()
        # prepare pixel data
        trainX, testX = prep_pixels(trainX, testX)
        # define model
        model = define_model()
        # create data generator
        datagen = ImageDataGenerator(width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)
        # prepare iterator
        it_train = datagen.flow(trainX, trainY, batch_size=64)
        # fit model
        steps = int(trainX.shape[0] / 64)
        history = model.fit_generator(it_train, steps_per_epoch=steps, epochs=100, validation_data=(testX, testY), verbose=0)
        # evaluate model
        _, acc = model.evaluate(testX, testY, verbose=0)
        print('> %.3f' % (acc * 100.0))
        # learning curves
        summarize_diagnostics(history)
    

    トレーニング:
    run_test_harness()
    

    印刷精度:
    84.470
    

    2.3 Dropout Rateの変更


    1つの興味深い変化はDropout Rateを20%から25%または30%に増加させることである.もう1つの興味深い変化は、モデルの最初のブロックの20%、2番目のブロックの30%から、モデルの分類器部分の完全な接続層でDropout Rateを50%に増加させるモードを使用することである.このようなモデル深さの増加に伴って増加する欠落は一般的なモードである.これは、入力したレイヤに近づくのではなく、モデル内の深いレイヤを正規化するため有効です.
    # define cnn model
    def define_model():
        model = Sequential()
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(32, 32, 3)))
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.2))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.3))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.4))
        model.add(Flatten())
        model.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))
        model.add(Dropout(0.5))
        model.add(Dense(10, activation='softmax'))
        # compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
        return model
    

    トレーニング:
    run_test_harness()
    

    印刷精度:
    84.690
    

    2.4 Dropout+データ強化


    トレーニング:
    run_test_harness()
    

    印刷精度:
    85.880
    

    2.5 Dropout+データ強化+BN

    # define cnn model
    def define_model():
        model = Sequential()
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(32, 32, 3)))
        model.add(BatchNormalization())
        model.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(BatchNormalization())
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.2))
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(BatchNormalization())
        model.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(BatchNormalization())
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.3))
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(BatchNormalization())
        model.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))
        model.add(BatchNormalization())
        model.add(MaxPooling2D((2, 2)))
        model.add(Dropout(0.4))
        model.add(Flatten())
        model.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))
        model.add(BatchNormalization())
        model.add(Dropout(0.5))
        model.add(Dense(10, activation='softmax'))
        # compile model
        opt = SGD(lr=0.001, momentum=0.9)
        model.compile(optimizer=opt, loss='categorical_crossentropy', metrics=['accuracy'])
        return model
    

    トレーニング:
    run_test_harness()
    

    印刷精度:
    88.620
    

    Summary

  • VGG 1: 67.070%
  • VGG 2: 71.080%
  • VGG 3: 73.500% (Baseline)
  • Baseline + Dropout: 83.450%
  • Baseline + Weight Decay: 72.550%
  • Baseline + Data Augmentation: 84.470%
  • Baseline + Increasing Dropout: 84.690%
  • Baseline + Dropout + Data Augmentation: 85.880%
  • Baseline + Increasing Dropout + Data Augmentation + Batch Normalization: 88.620%

  • これまで、学習率などの超パラメータが調整されていなかったものもあり、最も重要な超パラメータである可能性があります.適応学習速度技術Adamを使用するなど、学習速度を適応的に変更することによってさらに改善することが望ましいかもしれない.これらのタイプの変更は、収束後のモデルに役立つ可能性があります.
    参照先:https://machinelearningmastery.com/how-to-develop-a-cnn-from-scratch-for-cifar-10-photo-classification/