ChainerによるCIFAR-10の一般物体認識 (1)
Chainerによる畳み込みニューラルネットワークの実装(2015/10/7)のつづき。今回はMNISTの数字画像認識ではなく、CIFAR-10(2015/10/14)という画像データを使った一般物体認識をやってみた。画像を10個のクラスに分類するタスク。実装にはChainerを使った。
MNISTは1チャンネルの白黒画像だったけれどCIFAR-10は3チャンネル(RGB)のカラー画像なので少しだけ複雑になる。CIFAR-10(2015/10/14)でも書いたけれどCIFAR-10の提供データは、各画像サンプルがchannel(3チャンネル)、row(32ピクセル)、column(32ピクセル)のフラット形式3*32*32=3072
次元ベクトルの形で格納されている。Chainerでは画像を (nsample, channel, height, width)
の形式にする必要があるためreshape()
して次元を変換している。
# 画像を (nsample, channel, height, width) の4次元テンソルに変換 X_train = X_train.reshape((len(X_train), 3, 32, 32)) X_test = X_test.reshape((len(X_test), 3, 32, 32))
今回は初めてなので畳み込み層とプーリング層が1つずつの簡単な構成で実験した。
INPUT -> (CONV -> RELU) -> POOL -> FC
CONV
は畳み込み層、RELU
はReLU活性化関数、POOL
はプーリング層(max-pooling)、FC
は全結合層(隠れ層1つ)である。Chainerでは下のようなコードで簡潔に書ける。model
の方には学習によって変化するパラメータがある層だけまとめられている。プーリング層はパラメータがないのでmodel
には含まれない。
model = chainer.FunctionSet(conv1=F.Convolution2D(3, 32, 3, pad=0), l1=F.Linear(7200, 512), l2=F.Linear(512, 10)) def forward(x_data, y_data, train=True): x, t = chainer.Variable(x_data), chainer.Variable(y_data) h = F.max_pooling_2d(F.relu(model.conv1(x)), 2) h = F.dropout(F.relu(model.l1(h)), train=train) y = model.l2(h) if train: return F.softmax_cross_entropy(y, t) else: return F.accuracy(y, t)
畳込み層のconv1=F.Convolution2D(3, 32, 3, pad=0)
は、入力が3チャンネル(RGB)、出力の特徴マップが32チャンネル、フィルタ(カーネル)サイズが3x3、パディングサイズが0であることを意味する。
フル結合層は2層からなりl1=F.Linear(7200, 512)
は、7200ユニットから512ユニットへ変換する層、l2=F.Linear(512, 10)
は512ユニットから10ユニットへ変換する層を意味する。CIFAR-10は10クラスの画像分類なので出力ユニット数は10になる。
畳み込み層ではパディングサイズが0だと出力の特徴マップの画像サイズが入力画像より少し小さくなる。入力画像サイズがでフィルタがだと出力画像サイズはになる。ここでは小数点以下切り下げて整数化する演算。
今回の例だと入力が3x32x32
でconv1
によって32x30x30
になる。さらにmax_pooling_2d
のウィンドウサイズが2なので画像サイズは半分になって32x15x15
になる。これをフラット化してからフル結合層に入力するためフル結合層のユニット数は32x15x15=7200
となる。これがl1
の入力ユニット数が7200となっている理由。
このユニット数が間違っているとChainerが実行時に正しい値を教えてくれるので、自分で計算するのが面倒なら適当に入れておいてもよいかもしれない(笑)たとえば、適当に1000にしてみると
Actual: 7200 != 1000
というエラーが出る。これは「フル結合層のユニット数は本当は7200なのに1000になっていて違うよ!」という意味。
この構成だとテスト精度で最大67.7%だった。次は畳み込みニューラルネットワークの構成をいろいろ変えたとき精度がどのように変化するか調べてみよう。