みらいテックラボ

音声・画像認識や機械学習など, 週末プログラマである管理人が興味のある技術の紹介や実際にトライしてみた様子などメモしていく.

Fashion-MNISTやってみた(2)

8月末頃に, TwitterだったかFacebookだったかで, Fashion-MNIST[1][2]なるデータセットの存在を知った.

以前に, ファッション画像における洋服の「色」分類にチャレンジ[3]したこともあり, 少し試してみた.


関連記事:
Fashion-MNISTやってみた(1)
・Fashion-MNISTやってみた(2)


今回は, 予備実験的に前処理を中心に少し試してみた.

2. 予備実験(前処理編)
2.1 ベースシステム
Keras[4]のexamples/mnist_cnn.pyは, 畳み込み層が2層のCNNモデルであるが, ベースモデルとして畳み込み層が4層のCNNモデルを考えてみた.

モデル構造:

_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 64)        640
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 28, 28, 64)        36928
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 64)        0
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 14, 14, 128)       73856
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 14, 14, 128)       147584
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 7, 7, 128)         0
_________________________________________________________________
flatten_1 (Flatten)          (None, 6272)              0
_________________________________________________________________
dense_1 (Dense)              (None, 256)               1605888
_________________________________________________________________
dense_2 (Dense)              (None, 256)               65792
_________________________________________________________________
dense_3 (Dense)              (None, 10)                2570
=================================================================

コード:

from __future__ import print_function

import os
import gzip
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pylab as plt

from utils import mnist_reader

from keras.utils import to_categorical
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Input, Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.callbacks import EarlyStopping

from keras import optimizers

TRAIN_DIR = './data/fashion/train'
VAL_DIR = './data/fashion/val'
TEST_DIR = './data/fashion/test'
MODEL_DIR = './model'
MODEL_FILE = 'cnn_v2_base.hdf5'
HIST_FILE = 'cnn_v2_base.png'

TRAIN_SAMPLES = 60000
VAL_SAMPLES = 2000
TEST_SAMPLES = 10000

IMG_ROWS = 56
IMG_COLS = 56
IMG_CHANNELS = 1

classes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

BATCH_SIZE = 100
EPOCHS = 100

# 学習データ準備
def train_generator():
    train_data_generator = ImageDataGenerator(
        rescale = 1.0 / 255)

    train_data = train_data_generator.flow_from_directory(
        TRAIN_DIR,
        target_size = (IMG_ROWS, IMG_COLS),
        color_mode = 'grayscale',
        classes = classes,
        class_mode = 'categorical',
        batch_size = BATCH_SIZE,
        seed = 1,
        shuffle = True)

    return train_data

# 評価データ準備
def val_generator():
    val_data_generator = ImageDataGenerator(
        rescale = 1.0 / 255)

    val_data = val_data_generator.flow_from_directory(
        VAL_DIR,
        target_size = (IMG_ROWS, IMG_COLS),
        color_mode = 'grayscale',
        classes = classes,
        class_mode = 'categorical',
        shuffle = False)
    return val_data

def set_model():
    input_shape = (IMG_ROWS, IMG_COLS, IMG_CHANNELS)
    model = Sequential()
    model.add(Conv2D(64, kernel_size=(3, 3),
                     padding='same',
                     activation='relu',
                     input_shape=input_shape))
    model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    model.add(Conv2D(128, (3, 3), padding='same', activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Flatten())
    model.add(Dense(256, activation='relu'))
    model.add(Dense(256, activation='relu'))
    model.add(Dense(len(classes), activation='softmax'))

    model.summary()
    model.compile(loss='categorical_crossentropy',
                  optimizer=optimizers.Adam(lr=0.0005, beta_1=0.9, beta_2=0.999),
                  metrics=['accuracy'])
    return model

def draw_history(hist):
    val_acc = hist.history['val_acc']
    val_loss = hist.history['val_loss']

    fig = plt.figure()
    ax_acc = fig.add_subplot(111)
    ax_acc.plot(val_acc, label='acc', linestyle='solid', color='blue')

    ax_loss = ax_acc.twinx()
    ax_loss.plot(val_loss, label='loss', linestyle='dashed', color='red')

    ax_acc.set_xlabel('epochs')
    plt.savefig(HIST_FILE)

def training(train_data, val_data, model):
    early_stopping = EarlyStopping(monitor='val_loss', patience=10, verbose=1)

    hist = model.fit_generator(
        train_data,
        steps_per_epoch = int(TRAIN_SAMPLES/BATCH_SIZE),
        epochs = EPOCHS,
        validation_data = val_data,
        validation_steps = VAL_SAMPLES,
        callbacks = [early_stopping])

    model.save_weights(os.path.join(MODEL_DIR, MODEL_FILE))

    draw_history(hist)
    return model

def evaluate(model, path='./data/fashion'):
    labels_path = os.path.join(path, 't10k-labels-idx1-ubyte.gz')
    with gzip.open(labels_path, 'rb') as lbpath:
        y_test = np.frombuffer(lbpath.read(), dtype=np.uint8, offset=8)
        y_test = to_categorical(y_test, len(classes))
    x_test = np.empty((TEST_SAMPLES, IMG_ROWS, IMG_COLS, IMG_CHANNELS))
    for i in range(len(y_test)):
        fname = 't10k_{0:05d}.bmp'.format(i)
        img = image.load_img(os.path.join(TEST_DIR, fname),
                             grayscale=True, target_size=(IMG_ROWS, IMG_COLS))
        x = image.img_to_array(img) / 255.0
        x_test[i,:,:,:] = x
    score = model.evaluate(x_test, y_test, verbose=0)
    print('Test loss:', score[0])
    print('Test accuracy:', score[1])

if __name__ == '__main__':
    model = set_model()
    train_data = train_generator()
    val_data = val_generator()
    model = training(train_data, val_data, model)
    evaluate(model)


2.2 Data Augmentation
keras.preprocessing.image.ImageDataGeneratorを活用して, 学習時に以下の4つの処理により画像を変形し, ロバストなモデルを作成してみた.

・width_shift_range: 横幅に対する割合0.1 --- ランダムに水平シフトする範囲
・height_shift_range: 縦幅に対する割合0.1 --- ランダムに垂直シフトする範囲
・zoom_range: 0.1 --- ランダムにズームする範囲. [lower, upper] = [1-zoom_range, 1+zoom_range]
・horizontal_flip: True --- 水平方向に入力をランダムに反転

変更箇所:

# 学習データ準備
def train_generator():
    train_data_generator = ImageDataGenerator(
        rescale = 1.0 / 255,
        width_shift_range = 0.2,
        height_shift_range = 0.2,
        zoom_range = 0.1,
        horizontal_flip = True)


2.3 画像サイズ拡大
画像サイズを拡大して学習すると, 精度が向上する場合があるといった話を聞いたことがあったので, これも試してみた.

変更箇所:

IMG_ROWS = 56
IMG_COLS = 56
IMG_CHANNELS = 1


2.4 結果
以下の条件で, 比較実験を行ってみた.

学習/評価条件
データセット  :Fashion-MNIST
学習データ   :60,000サンプル
ミニバッチサイズ:100サンプル
テストデータ  :10,000サンプル

実験結果

前処理エポック数精度
ベースモデル150.9266
Data Augmentation660.9427
Data Augmentation+画像サイズ拡大450.9338
注) エポック数は, テストデータ2,000サンプルの評価によりEarly Stoppingした回数.

精度0.94程度までは, 簡単なモデル構造と前処理で達成できることを確認した.

ここから, 現時点(2017/9/20)のTopスコア0.967に近付けるにはかなりの工夫が必要そう.
引き続き, 時間のある時に試してみようと思う.

----
参照URL:
[1] GitHub - zalandoresearch/fashion-mnist: A MNIST-like fashion product database. Benchmark
[2] http://tensorflow.classcat.com/category/fashion-mnist/
[3] 第1回FR FRONTIER:ファッション画像における洋服の「色」分類にチャレンジ!!(1)
[4] GitHub - keras-team/keras: Deep Learning for humans





PythonユーザのためのJupyter[実践]入門

PythonユーザのためのJupyter[実践]入門




Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎