LoginSignup
1
1

More than 5 years have passed since last update.

CNNの表現能力の高さを改めて思い知る

Last updated at Posted at 2017-03-10

深層学習の表現力の高さにはいつも驚かされます。
画像認識系のタスクであれば割と何でも識別できるイメージです。

今回はMNISTのデータセットを用いて、ラベルをランダムに振り分けなおしてから学習させた場合に、訓練誤差が減少するのかを確かめてみました。
すなわち丸暗記ができるかどうかの調査です。

テストプログラム

以下のプログラムを用いて実験をしました。
ラベルをランダムに振り分けたあとに、データの数を3000に減らします。

tensorflowをバックエンドとしたkerasを用いています。
データの与え方は(データ数、チャンネル数、画像の高さ、画像の幅)です。

mnist_conv.py
# -*- coding: utf-8 -*-
from __future__ import print_function

from keras.datasets import mnist
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras.layers.convolutional import Convolution2D, MaxPooling2D
from keras.optimizers import Adam
from keras.utils import np_utils
from keras.callbacks import EarlyStopping

import sys
import numpy as np

import matplotlib.pyplot as plt

batch_size = 32
nb_classes = 10
nb_epoch = 1000

def main():
    # load MNIST data
    (X_train, y_train), (X_test, y_test) = mnist.load_data()

    img_channels = 1
    _, img_cols, img_rows = X_train.shape

    X_train = X_train.reshape(60000, 1, 28, 28).astype('float32')[:3000]
    X_test = X_test.reshape(10000, 1, 28, 28).astype('float32')
    X_train /= 255.0
    X_test /= 255.0
    X_train -= 0.5
    X_test -= 0.5

    # 学習データのラベルをランダムに振り分ける
    y_train = y_train[np.random.permutation(y_train.shape[0])][:3000]

    # convert class vectors to 1-of-K format
    y_train = np_utils.to_categorical(y_train, nb_classes)
    y_test = np_utils.to_categorical(y_test, nb_classes)

    print('train samples: ', X_train.shape)
    print('test samples: ', X_test.shape)

    # building the model
    print('building the model ...')

    # build model
    model = Sequential()

    model.add(Convolution2D(32, 3, 3, border_mode='same',
                            input_shape=(img_channels, img_rows, img_cols)))
    model.add(Activation('relu'))

    model.add(Convolution2D(32, 3, 3))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Convolution2D(64, 3, 3, border_mode='same'))
    model.add(Activation('relu'))

    model.add(Convolution2D(64, 3, 3))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))

    model.add(Flatten())
    model.add(Dense(512))
    model.add(Activation('relu'))
    model.add(Dense(nb_classes))
    model.add(Activation('softmax'))

    opt = Adam()
    model.compile(loss='categorical_crossentropy',
                  optimizer=opt,
                  metrics=['accuracy'])

    # training
    hist = model.fit(X_train, y_train,
                     batch_size=batch_size,
                     verbose=1,
                     nb_epoch=nb_epoch,
                     validation_split=0.1)

    # evaluate
    score = model.evaluate(X_test, y_test, verbose=1)
    print('Test loss:', score[0])
    print('Test accuracy:', score[1])

    # plot loss
    loss = hist.history['loss']
    val_loss = hist.history['val_loss']

    nb_epoch = len(loss)
    plt.plot(range(nb_epoch), loss, marker='.', label='loss')
    plt.plot(range(nb_epoch), val_loss, marker='.', label='val_loss')
    plt.legend(loc='best', fontsize=10)
    plt.grid()
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.show()


if __name__ == '__main__':
    main()

結果

丸暗記大成功です。

loss_valLoss.png

青い線が訓練誤差で、緑の線がバリデーション誤差(訓練時に使っていない画像による推定誤差)になります。
訓練誤差が0に収束し、バリデーション誤差が増加する、火を見るより明らかな美しい過学習の様相を呈しております。

訓練データに対する正答率は99.7%で、テストデータに対する正答率は8.67%でした。
ランダムに答えた場合の正答率の平均は10%になりますから、ランダムよりも雑魚い推定器の完成です。実用性皆無です。

ちなみに、訓練データの数を増やすと丸暗記ができなくなっていきます。データの数は重要ですね。

1
1
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
1
1