2016年2月6日土曜日

Chainer によるシーン認識

in English

はじめに


 先のページで Caffe を使ったシーン認識(15分類問題)を試みた。今回は、Caffe の学習済みモデルを Chainer を使って Fine-Tuning することにより同じシーン認識を行ってみる。

計算機環境


 これまでと同じく、Amazon EC2 にある g2.2xlarge を利用した。GPU を搭載したインスタンスである。

データセット


 先と同じ LSP15 を利用する。このデータセットは15個のディレクトリから構成されている。
  1. MITcoast
  2. MITforest
  3. MIThighway
  4. MITinsidecity
  5. MITmountain
  6. MITopencountry
  7. MITstreet
  8. MITtallbuilding
  9. bedroom
  10. CALsuburb
  11. industrial
  12. kitchen
  13. livingroom
  14. PARoffice
  15. store
ディレクトリ名はシーンのカテゴリを表している。各ディレクトリは200〜300枚程度の画像を含む。

データセットの増量


 左右反転画像を追加する。そのあと、データセットを 6:1 に分割し、前者を訓練画像、後者をテスト画像とした。最終的な画像の枚数は以下の通りである。

label name number of train number of test
0 MITcoast 610 100
1 MIThighway 440 70
2 MITmountain 630 100
3 MITstreet 490 80
4 MITforest 550 90
5 MITinsidecity 520 80
6 MITopencountry 690 110
7 MITtallbuilding 600 100
8 bedroom 360 60
9 CALsuburb 400 60
10 industrial 520 80
11 kitchen 360 60
12 livingroom 490 80
13 PARoffice 360 60
14 store 540 90
7560 1220

画像の中心部分から最大の正方形を取り出し、このサイズを $256 \times 256$ に変更した。画像へのパスとラベルを記したテキストファイルを以下のように作成した。

--- test.txt ---
--- train_valid.txt ---

Caffe モデルの読み込みと保存


 今回は Caffe が提供するモデル bvlc_reference_caffenet.caffemodel を利用した。以下のコードで読み込む。 CaffeFunction による読み込みは時間がかかる処理である。cPickle を使って保存し直しておく。

ネットワークの実装


 今回使用する Caffe モデルと等価なネットワークを Chainer を使って再現する。bvlc_reference_caffenet.caffemodel と一緒に配布されるprototxt を見ながら実装すれば良い。Chainer のソースコードに含まれるサンプル(chainer-1.6.0/examples/imagenet/alex.py)をベースにした。

--- reference_caffenet.py ---
このネットワークの一番上にある fc8 層の名前を scene_fc8 に、この層の出力ユニット数を 1000 から 15 に変更する。変更後のクラスを以下に示す。

--- modified_reference_caffenet.py ---

Fine-Tuning


 pickle で保存した Caffe モデルは、L.Convolution2D と L.Linear のオブジェクトを含んでおり、それらは $W$ と $b$ の学習済みパラメータを持っている。上で定義したクラス ModifiedReferenceCaffeNet は scene_fc8 以外の層は Caffe モデル と同じあるから、{conv1, conv2, conv3, conv4, conv5, fc6, fc7} が持つ $W$ と $b$ をそのままModifiedReferenceCaffeNet 側にコピーする。そのあと、今回のデータセットで学習を行えば Fine-Tuning を実現できる。

訓練


 訓練時のコードを以下に示す。

--- train.py ---
  1. 15行目:先に保存した Caffe モデルへのパス
  2. 16行目:Fine-Tuning後のモデルを保存するファイルへのパス
  3. 17行目:平均画像へのパス
  4. 21行目:学習率
  5. 22行目:ミニバッチサイズ
  6. 23行目:エポック数
  7. 24行目:学習率のエポックごとの減衰率
  8. 29行目:訓練データへのパス
  9. 30行目:テストデータへのパス
  10. 33〜45行目:def test: テストデータを使った loss と accuracy の計算。全データの平均値を取る。
  11. 52〜63行目:データの読み込み。クラス DataLoader については後述。
  12. 75行目:Caffe モデルを読み込む。
  13. 78行目:今回使うネットワークのオブジェクトを作る。
  14. 81行目:学習済みパラメータ $W$ と $b$ をコピーする。最終層 scene_fc8 の $W$ と $b$ はデフォルト値に初期化されている。関数 copy_model については後述。
  15. 85行目:今回使うネットワークのオブジェクトを GPU 側へ転送。
  16. 86行目:最適化アルゴリズムとして確率的勾配降下(SGD)法を選択する。
  17. 87行目:最適化アルゴリズムに最適化すべき対象を渡す。
  18. 93〜115行目:Fine-Tuning のためのループ
  19. 123行目:Fine-Tuning 後のモデルを保存する。
クラス DataLoader を定義したファイルは以下の通り。

--- data_loader.py ---

--- image_cropper.py ---
関数 copy_model についてはこちらのページに掲載されているものを使用した。
--- copy_model.py ---


訓練結果


 以下に訓練データとテストデータの正解率と損失を示す。

訓練データ
テスト画像

テスト画像に対する正解率は96.23%である。

識別器の作成


 識別器のコードは以下の通り。本来であればこれを使って訓練すべきである。訓練をやり直すには時間とお金がかかるので今回は再度 copy_model を使うことにした。

--- modified_reference_caffenet_with_predictor.py ---
識別する際のコードは以下のようになる。

--- predict.py ---
  1. 12行目:テストデータへのパス
  2. 13行目:平均画像へのパス
  3. 14行目:Fine-Tuning 後のモデルへのパス
  4. 18行目:モデルの読み込み
  5. 21行目:識別器の作成
  6. 24行目:パラメータのコピー
  7. 27〜32行目:テストデータの読み込み
  8. 35〜40行目:テストデータの数を減らして Variable オブジェクトに格納
  9. 43,44行目:識別する。
実行結果は以下の通り。 一番最初の画像だけ間違っている。

特徴抽出器の作成


 fc7層から特徴ベクトルを抽出する。コードは以下の通り。

--- modified_reference_caffenet_with_feature_extractor.py ---
このクラスを使って、特徴ベクトルを抽出し、liblinear の入力ファイルを作成する。コードは以下の通り。

--- extract.py ---
  1. 59行目:Fine-Tuning したモデルを読み込む。
  2. 62行目:抽出器を作る。
  3. 65行目:Fine-Tuning したモデルから抽出器へパラメータをコピーする。
  4. 70,71行目:liblinear への入力ファイルを作る。

SVMの実行


 以下のコードで訓練。 以下のコードで予測。 既に示したように Softmax 層を使った時は 96.23% であったから 1% ほど良くなっている。