のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

ChainerでCIFAR-10の分類を行ってみる

Sponsored Links

皆さんこんにちは
お元気ですか。私は元気です。

前回はChainerの紹介をしました。nonbiri-tereka.hatenablog.com


本日はこのChainerを使って、CIFAR-10の分類を行ってみようと思います。
アーキテクチャとして利用するのはConvolutional Neural Networkを利用します。

What is CIFAR-10

CIFAR-10 and CIFAR-100 datasetsにあるデータセットです。32x32pixelのカラー画像を10のクラスに分類する問題が含まれています。
画像の大きさはTraining画像が50000枚、Test画像が10000枚です。
研究とかのベンチマークでお世話になっている人も多いと思いますが・・・

以下の図は本家に掲載されているサンプル画像です。

f:id:tereka:20150621194735p:plain:w320:h250

データセットの読み込み

CIFAR-10ではPythonでの読み込みが苦労しません。
なぜならば、pickleで読み込むだけです。
CIFAR-10の公式サイトでPythonデータの読み込み方が記載されているので、利用させて頂きます。
但し、データ数10000ごとに固められているので、工夫が必要です。

def unpickle(file):
    fo = open(file, 'rb')
    dict = cPickle.load(fo)
    fo.close()
    return dict

x_train = None
y_train = []

for i in xrange(1,6):
	data_dictionary = unpickle("cifar-10-batches-py/data_batch_%d" % i)
	if x_train == None:
		x_train = data_dictionary['data']
	else:
		x_train = np.vstack((x_train,data_dictionary['data']))
	y_train = y_train + data_dictionary['labels']

test_data_dictionary = unpickle("cifar-10-batches-py/test_batch")
x_test = test_data_dictionary['data']
x_test = x_test.reshape(len(x_test),3,32,32)
y_train = np.array(y_train)
x_train = x_train.reshape((len(x_train),3, 32, 32))
y_test = np.array(test_data_dictionary['labels'])

ChainerのCNNにおけるデータは(channel, height, width)なので、
仮にOpenCVを利用した場合はtranspose関数で入れ替えを行わなければなりません。
なぜならば、OpenCVの構造は(height,width,channel)の順番だからです。

モデルの構築

早速Convolutional Neural Networkを構築してみます。

model = FunctionSet(conv1=F.Convolution2D(3,32,3),
                    bn1   = F.BatchNormalization( 32),
                    conv2=F.Convolution2D(32,64,3,pad=1),
                    bn2   = F.BatchNormalization( 64),
                    conv3=F.Convolution2D(64,64,3,pad=1),
                    fl4=F.Linear(1024, 256),
                    fl5=F.Linear(256, 10))

def forward(x_data, y_data, train=True):
	 x, t = Variable(x_data, volatile=not train), Variable(y_data, volatile=not train)
	 h = F.max_pooling_2d(F.relu(model.bn1(model.conv1(x))), 2)
	 h = F.max_pooling_2d(F.relu(model.bn2(model.conv2(h))), 2)
	 h = F.max_pooling_2d(F.relu(model.conv3(h)), 2)
	 h = F.dropout(F.relu(model.fl4(h)),train=train)
	 y = model.fl5(h)

	 return F.softmax_cross_entropy(y, t), F.accuracy(y, t)

Convolutional Neural Networkの畳み込み演算はConvolutional2Dで行えます。
Full-Connect-Layerとの接続は出力された特徴マップをそのまま引数として
与えると、自動的に計算してくれるようですね。

因みに、学習部分は前回と変化ありませんので解説はなしですね。
以下、ソースコード、見たい人はどうぞ。

ソースコード全文

cudaを利用したい場合は、gpu_flagをTrueにしてください。

import cPickle
import numpy as np
import argparse
from chainer import cuda, Variable, FunctionSet, optimizers
import chainer.functions  as F
import pdb
def unpickle(file):
    fo = open(file, 'rb')
    dict = cPickle.load(fo)
    fo.close()
    return dict

x_train = None
y_train = []

for i in xrange(1,6):
	data_dictionary = unpickle("cifar-10-batches-py/data_batch_%d" % i)
	if x_train == None:
		x_train = data_dictionary['data']
	else:
		x_train = np.vstack((x_train,data_dictionary['data']))
	y_train = y_train + data_dictionary['labels']

test_data_dictionary = unpickle("cifar-10-batches-py/test_batch")
x_test = test_data_dictionary['data']
x_test = x_test.reshape(len(x_test),3,32,32)
y_train = np.array(y_train)
x_train = x_train.reshape((len(x_train),3, 32, 32))
y_test = np.array(test_data_dictionary['labels'])

model = FunctionSet(conv1=F.Convolution2D(3,32,3),
                    bn1   = F.BatchNormalization( 32),
                    conv2=F.Convolution2D(32,64,3,pad=1),
                    bn2   = F.BatchNormalization( 64),
                    conv3=F.Convolution2D(64,64,3,pad=1),
                    fl4=F.Linear(1024, 256),
                    fl5=F.Linear(256, 10))
N = len(x_train)
N_test = len(x_test)
n_epoch = 25
batchsize = 100
gpu_flag = False
optimizer = optimizers.Adam()
optimizer.setup(model.collect_parameters())

def forward(x_data, y_data, train=True):
	 x, t = Variable(x_data, volatile=not train), Variable(y_data, volatile=not train)
	 h = F.max_pooling_2d(F.relu(model.bn1(model.conv1(x))), 2)
	 h = F.max_pooling_2d(F.relu(model.bn2(model.conv2(h))), 2)
	 h = F.max_pooling_2d(F.relu(model.conv3(h)), 2)
	 h = F.dropout(F.relu(model.fl4(h)),train=train)
	 y = model.fl5(h)

	 return F.softmax_cross_entropy(y, t), F.accuracy(y, t)

for epoch in xrange(1, n_epoch+1):
    print 'epoch', epoch

    # training
    perm = np.random.permutation(N)
    sum_accuracy = 0
    sum_loss = 0
    for i in xrange(0, N, batchsize):
        x_batch = x_train[perm[i:i+batchsize]]
        y_batch = y_train[perm[i:i+batchsize]]
        if gpu_flag is True:
            x_batch = cuda.to_gpu(x_batch)
            y_batch = cuda.to_gpu(y_batch)

        optimizer.zero_grads()
        loss, acc = forward(x_batch, y_batch)
        loss.backward()
        optimizer.update()

        sum_loss     += float(cuda.to_cpu(loss.data)) * batchsize
        sum_accuracy += float(cuda.to_cpu(acc.data)) * batchsize
    print 'train mean loss={}, accuracy={}'.format(
        sum_loss / N, sum_accuracy / N)

    # evaluation
    sum_accuracy = 0
    sum_loss     = 0
    for i in xrange(0, N_test, batchsize):
        x_batch = x_test[i:i+batchsize]
        y_batch = y_test[i:i+batchsize]
        if gpu_flag is True:
            x_batch = cuda.to_gpu(x_batch)
            y_batch = cuda.to_gpu(y_batch)

        loss, acc = forward(x_batch, y_batch, train=False)

        sum_loss     += float(cuda.to_cpu(loss.data)) * batchsize
        sum_accuracy += float(cuda.to_cpu(acc.data)) * batchsize

    print 'test  mean loss={}, accuracy={}'.format(
        sum_loss / N_test, sum_accuracy / N_test)