chainer(v1.14.0)で画像分類をはじめるための覚書
2016/09/04
環境:Ubuntu14.04(LTS), Chainer(v1.14.0), CUDA(7.5), python2.7
先人達の記事に従いchainerで画像分類を始める際、バージョンの違いによって変更が必要な点があったため記述する。
基本的な手順は、①
に従い、適宜②
hi-king.hatenablog.com
を参考にした。
①の著者が作成したコードをgit cloneする。
$ cd ~/work $ git clone https://github.com/shi3z/chainer_imagenet_tools.git $ cd chainer_imagenet_tools
次にCaltech 101の画像データをダウンロードする。
101種類のカテゴリにつき、およそ300 x 200pxの画像データが40〜800枚収められている。
$ wget http://www.vision.caltech.edu/Image_Datasets/Caltech101/101_ObjectCategories.tar.gz $ tar xzvf 101_ObjectCategories.tar.gz
①の著者によるスクリプトを実行。
$ python make_train_data.py 101_ObjectCategories
画像データを集めたimagesディレクトリと、train.txt, test.txt, label.txtの3ファイルが出力される。
前2ファイルはそれぞれ学習データとテストデータを決定しており、画像データのフルパスとクラスのラベル(数値)を1行に収めたものが画像の枚数分並べられている。
label.txtは元データのフォルダ名を元にクラスラベルを1行ごとに並べたもの。
次に画像データを255*255pxにリサイズする。
git cloneしたchainer_imagenet_toolsフォルダに収められているcrop.pyを使用する。
crop後のディレクトリを作成した上で、(9/4追記)
引数に元データのディレクトリとcrop後のディレクトリを指定する。
$ python crop.py images cropimages
train.txtとtest.txtに対して、画像のパスを修正した。(geditで開いて置換images→cropimages)
平均画像を作成する。
chainerのexampleに収められているcompute_mean.pyを使用する。
chainerはv1.14.0をインストールした際にgit clone したディレクトリがあったが、環境を合わせるためv1.5.0のソースコードをダウンロードした。
Release v1.5.0 · pfnet/chainer · GitHub
こちらの下部のリンクからzipをダウンロードして展開。
以下を実行する。
$ cd ~/work/chainer_imagenet_tools $ python chainer-1.5.0/examples/imagenet/compute_mean.py train.txt
カレントフォルダにmean.npyが出力される。
学習させる。
chainer_imagenet_toolsフォルダに収められているtrain_imagenet.pyを使用する。
ここでの変更点は以下の通り。
train_imagenet.py
283行目
o.write(c.build_computational_graph((loss,), False).dump())
をコメントアウトし、
o.write(c.build_computational_graph((loss,), True).dump())
を追加。(build_computational_graphの引数として、v1.14.0ではFalseが廃止されている。)
また、
310行目
pickle.dump(model, open('model', 'wb'), -1)
をコメントアウトし、
serializers.save_hdf5('modelhdf5', model)
を追加。
合わせて29行目あたりに
from chainer import serializers
を追加した。
さらに、91行目の
optimizer.setup(model.collect_parameters())
を、
optimizer.setup(model)
に置き換える。(v1.5以降はFunctionSetが廃止されているため。)
次に、同ディレクトリ内のnin.pyについて、以下のように構成を置き換えた。
import math import chainer import chainer.functions as F import numpy as np import chainer.links as L class NIN(chainer.Chain): """Network-in-Network example model.""" insize = 227 def __init__(self): w = math.sqrt(2) # MSRA scaling super(NIN, self).__init__( mlpconv1=L.MLPConvolution2D( 3, (96, 96, 96), 11, stride=4, wscale=w), mlpconv2=L.MLPConvolution2D( 96, (256, 256, 256), 5, pad=2, wscale=w), mlpconv3=L.MLPConvolution2D( 256, (384, 384, 384), 3, pad=1, wscale=w), mlpconv4=L.MLPConvolution2D( 384, (1024, 1024, 1000), 3, pad=1, wscale=w), ) self.train = True def forward(self, x_data,y_data,train=True): x = chainer.Variable(x_data, volatile=not train) t = chainer.Variable(y_data, volatile=not train) h = F.max_pooling_2d(F.relu(self.mlpconv1(x)), 3, stride=2) h = F.max_pooling_2d(F.relu(self.mlpconv2(h)), 3, stride=2) h = F.max_pooling_2d(F.relu(self.mlpconv3(h)), 3, stride=2) h = self.mlpconv4(F.dropout(h, train=self.train)) h = F.reshape(F.average_pooling_2d(h, 6), (x.data.shape[0], 1000)) return F.softmax_cross_entropy(h, t), F.accuracy(h, t) def predict(self, x_data): x = chainer.Variable(x_data, volatile=True) h = F.max_pooling_2d(F.relu(self.mlpconv1(x)), 3, stride=2) h = F.max_pooling_2d(F.relu(self.mlpconv2(h)), 3, stride=2) h = F.max_pooling_2d(F.relu(self.mlpconv3(h)), 3, stride=2) h = self.mlpconv4(F.dropout(h, train=False)) h = F.reshape(F.average_pooling_2d(h, 6), (x.data.shape[0], 1000)) return F.softmax(h)
元ファイルでは12層の畳み込み層と4層のプーリング層が定義されていたが、学習後にinspection.pyにより画像分類を実行した際に構成が合わないようであったため、やむなくchainerv1.5.0のexamplesに収録されたnin.pyの構成を、整合するように再現した。今後理解を深めた暁には原著者①の実装を試してみたい。
以上の変更を行い、学習ループを実行する。
$ python train_imagenet.py -g 0 -E 10 train.txt test.txt 2>&1 | tee log
筆者の環境(NVIDIA GeForce GTX 750 Ti 使用)では約13分を要した。
LOGによると1秒当たり約94枚を処理しているようだ。
最後に、学習が完了したモデルを用いて画像分類をする。
①の著者による画像を255*255にリサイズするスクリプトresize.pyで画像をリサイズする。
8行目の
os.mkdir("resized")
をコメントアウトし、代わりに手動でresizedディレクトリを作成した。
画像分類スクリプトinspection.pyに関しては、
73行目
serializers.HDF5Deserializer("model", model)
をコメントアウトし、
serializers.load_hdf5("modelhdf5", model)
を追加した。
画像をリサイズし、分類スクリプトに渡す。
今回はこちら
http://www.trago.co.uk/ekmps/shops/tragoshop/images/wk-650i-motorbike-35kw-restricted-version-for-a2-license-available-white-blue-35677-p.jpg
を使用させていただいた。
$ python resize.py bike.jpg $ python inspection.py resizedBike.jpg
次にその結果を示す。
#1 | Motorbikes | 85.5%
#2 | scorpion | 3.8%
#3 | joshua_tree | 3.6%
#4 | butterfly | 1.6%
#5 | watch | 1.4%
#6 | crayfish | 0.8%
#7 | cannon | 0.7%
#8 | lobster | 0.4%
#9 | cellphone | 0.3%
#10 | wild_cat | 0.3%
#11 | pigeon | 0.2%
#12 | bonsai | 0.1%
#13 | gramophone | 0.1%
#14 | octopus | 0.1%
#15 | wheelchair | 0.1%
#16 | pagoda | 0.1%
#17 | stegosaurus | 0.1%
#18 | hawksbill | 0.1%
#19 | BACKGROUND_Google | 0.1%
#20 | helicopter | 0.1%
Motorbikesが85%という結果が出た。
鮮明な写真ではあったが、これだけの精度が得られることは興味深い。
今後応用範囲を拡大していきたい。
謝辞
①および②の著者の方に、この場を借りてお礼申し上げます。
chainer開発者の方へも最大限の感謝を。