umi no mono tomo

海の物とも山の物ともつかないものを、1.計測して、 2.モデルを構築し, 3.現象をよく説明する法則を数学的手法を用いて記述することにより, 対象から有用な情報を取り出しもって情報技術の活用を促進することをねらう.

chainer(v1.14.0)で画像分類をはじめるための覚書

2016/09/04

環境:Ubuntu14.04(LTS), Chainer(v1.14.0), CUDA(7.5), python2.7


先人達の記事に従いchainerで画像分類を始める際、バージョンの違いによって変更が必要な点があったため記述する。

基本的な手順は、①

d.hatena.ne.jp

に従い、適宜②

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
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開発者の方へも最大限の感謝を。

python用cv2.soが見つからない場合の注意点(OpenCV)

2016/09/03

OS:Ubuntu14.04(LTS)

pyenv, anaconda2-4.1.0

 

 

ubuntu14.04でpython用にOpenCV3.0.0をインストールした。

chainerで使用するために画像のリサイズ処理を実行するため、cv2をimportする必要があった。インストールは基本的に

Ubuntu14.04にpython用にOpenCV3.0.0をインストール - Qiita    ー①

に従ったが、pyenvを使用した環境では注意する点があったためここに記載する。

①に従ってOpenCVのインストールを進めると、”python用設定”の項目にあるように

opencv-3.0.0/build/lib

にcv2.soが作成されるが、筆者の環境では作成されなかった。

 

 

調べたところ、

 ubuntu14にpython,numpy,opencv3を入れる - Qiita    ー②

opencvの項に、pythonのバージョンをsystemに戻さなければcv2.soが見つからないとの記載を発見し、

$ pyenv global system

する。そして再びbuildとinstallを繰り返すが、cv2.soは発見できなかった。

 

 

筆者の環境には

データサイエンティストを目指す人のpython環境構築 2016 - Qiita    ー③

に従い、pyenvを介してanacondaの仮想環境が構築されており、.bashrcに

export PATH="$PYENV_ROOT/versions/anaconda2-4.1.0/bin/:$PATH"

が記載されている。

③で示されているが、この記述があるとpyenv globalやpyenv localを受け付けず、常にanacondaが使用されることがわかった。

そこで上記の記述を削除し、build,installを繰り返したが、やはりcv2.soを発見できない。

 

 

ここで、筆者はUbuntu歴が浅く、現在のところpyenv,anacondaを使用していないため、今回はこれらをアンインストールすることとした。

まずはanacondaをアンインストールする。

公式

Anaconda install | Continuum Analytics: Documentation

に従い、anacondaをアンインストール(フォルダの削除)する。

次にpyenvをアンインストールする。

公式

GitHub - yyuu/pyenv: Simple Python version management

に従い、pyenvをアンインストール(フォルダの削除)する。

上記が終了後、ターミナルを再起動し、①のOpenCVのインストール手順を実行した。

尚、①のconfigureの項にあるビルド設定に関して、

cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D BUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D WITH_FFMPEG=OFF -D BUILD_opencv_python2=ON ..

の引数にある

BUILD_NEW_PYTHON_SUPPORT=ON

は”実行されなかった”という趣旨の警告が表示されるが、以降の手順に問題はない。(ソースを発見できなかったが、どこかのサイトでこの引数がすでに廃止されているという記述を目にした。)

 

 

インストールが完了すると、

~/opencv-3.0.0/build/lib

にcv2.soが作成されているので、

/usr/local/lib/python2.7/site-packages

および

/usr/local/lib/python2.7/dist-packages

の両方にコピーする。

 

 

コピーが完了したら、ターミナルから

pythot

import cv2

を実行して、エラーが出なければインストールが完了している。