2016年7月11日月曜日

Fully Convolutional Networks 〜 Chainerによる実装 再考1 〜

in English

はじめに


 先のページで、簡易化したFully Convolutional Networks(FCN)を Chainer を使って実装した。残念ながら、その精度は文献のものより低かった。今回以下の改良を行ったところ、格段に精度は向上した。
  1. FCNのソースはCaffeである。CaffeにはCropLayerなる層が実装されており、FCNはこれを利用している。Chainerにはこれに相当するものがないが、chainer.functions.deconvolution_2dのoutsizeという引数を利用すれば同じような効果が得られることが分った。この関数の利用により、任意サイズの画像を受け付けることができるようになった。従って、入力画像に対しては何も手を加えていない。前回は$224\times 224$にリサイズしたのであった。
  2. ラベルの情報を持つ画像(ラベル画像)はRGB画像でなく、インデックス画像である。前回はこれを理解しておらず、わざわざRGBから整数値に変換する関数を介在させていた。今回はインデックス画像としてラベル画像を読み込むようにした。
  3. FCNの正式な構築手順は、fcn32s $\rightarrow$ fcn16s $\rightarrow$ fcn8sである。FCNの精度は、ほぼfcn32sの精度で決まっており、fcn16s/fcn8sからの寄与は1%程度である。前回はfcn8sを実装したが、今回はfcn32sの実装を示す。
  4. 前回はミニバッチ処理を行ったので関数chainer.links.BatchNormalizationを導入したが、今回は1枚ずつ処理するのでこの層を削除した。
  5. 前回はchainer.links.Deconvolution2Dを利用して拡大時のパラメータも学習していた。今回はchainer.functions.deconvolution_2dを利用し、単純なbilinear補間による拡大を採用した。

学習曲線


 最初に学習曲線を示す。

Accuracy
Loss
どちらも理想的な曲線となった。またテスト画像の正解率は92%弱となり前回よりも格段に向上した。このあとfcn16s/fcn8sを行えば93%程度にはなりそうである。以下詳細を記す。

計算機環境


 前回と同じく、Amazon EC2 にある g2.2xlarge を利用した。GPU を搭載したインスタンスである。 ただし、今回はミニバッチ処理ができないので1枚ずつ学習する。上の結果を得るのに3.5日ほどかかっている。課金量も相当である。

データセット


 前回と同じく、データセットは VOC2012 である。領域分割の教師データの数は2913枚、これを4:1に分割し、前者を訓練データ、後者をテストデータとした。

number of train number of test
2330 583

前回は、訓練データ数は10で、テストデータ数は5で割り切れるように端数を切り捨てたが、今回はミニバッチ処理を行わないので、そのような操作はしていない。上述したように画像のサイズもそのままである。 カテゴリ数は前回と同じく20+1(背景)である。

label category
0 background
1 aeroplane
2 bicycle
3 bird
4 boat
5 bottle
6 bus
7 car
8 cat
9 chair
10 cow
11 diningtable
12 dog
13 horse
14 motorbike
15 person
16 potted plant
17 sheep
18 sofa
19 train
20 tv/monitor

ネットワークの構造


 ネットワークの構造は以下の通りである。
これは前回と同じである。文献では、pool5 の後ろにfc6とfc7があるが、これらを入れると拡大時の処理が煩雑となる(というかよく分らない)ので、これら2層は残念ながら今回も削除した。上記の表では224$\times$224の正方形画像を想定してその1辺の長さだけをinputに記してあるが、縦横の長さは任意で構わない。表の各項目の意味は以下の通りである。
  1. name: 層の名前
  2. input: 入力featureマップの1辺のサイズ
  3. in_channels: 入力featureマップ数
  4. out_channels: 出力featureマップ数
  5. ksize: カーネルのサイズ
  6. stride: ストライドのサイズ
  7. pad: paddingのサイズ
  8. output: 出力featureマップの1辺のサイズ
今回はfcn32sを実装するのでpool5 の出力に接続する層(score-pool5)だけが必要である。これを通ったあとの出力データ名をp5とする。このデータのfeatureマップの数はクラスの数に等しくなる。
この表も、正方形画像を想定してその1辺の長さをinputに記してあるが縦横任意で構わない。出力p5はbilinear補間で元画像と同じサイズに拡大され、ラベル画像と比較される。


ネットワークの実装


 上記の構造をそのままChainerで記述する。

-- myfcn_32s_with_any_size.py -- 物体の境界線には-1を配置し、softmax_cross_entropyの計算時に境界の寄与を無視するようにした(Chainerの仕様ではラベルが-1の画素は評価されない)。また、関数F.accuracyの引数 ignore_labelを使えば境界線上の画素を除いてaccuracyを計算することができる(前回はわざわざ実装していた)。99行目から100行目にかけて記載した関数F.deconvolution_2dでp5を32倍している。さらに、引数outsizeにtのサイズを渡すことで結果がtと同じになるようにリサイズを行う。引数padには辻褄合わせのpadding量を渡している。

訓練


 訓練時のスクリプトは以下の通りである。

-- train_32s_with_any_size.py -- 関数 copy_model とVGGNet.pyは前回と同じである。 train_32s_with_any_size.pyでしていることは、
  1. オブジェクトmini_batch_loader_with_any_sizeを作る。
  2. VGGNet.pyで定義されたネットワークのオブジェクトを作る。
  3. MyFcn32sWithAnySize.pyで定義されたネットワークのオブジェクトを作る。
  4. VGGNetのパラメータをこれにコピーする。
  5. 最適化アルゴリズムとして MomentumSGD を選択する。
  6. あとは、通常の訓練過程である。
mini_batch_loader_with_any_size.pyの内容は以下の通りである。

-- mini_batch_loader_with_any_size.py --
91行目から96行目までの関数load_voc_labelでは、pngのインデックス画像をインデックスのまま読み込み、値が255の画素を-1に置き換えている。データを一括読み込みすると、ビデオメモリーが足りなくなるので、要求されるごとに読み込むようにしてある。

結果画像


 以下の左の列に、テスト画像に対する予測結果をテスト画像にオーバレイした画像を示す。下に記した数値はaccuracyである。ここでaccuracyとは、1枚の画像の中で何%の画素が正解したかを計算したものである(境界線上の画素は除く)。右側の列は、Ground Truthである。

このあと、fcn16s/fcn8sを行えば、物体の輪郭はもう少し正確になるだろう。

ダウンロード


 ここの、タグが2016-07-09のものです。今となっては不要なファイルがたくさん残っています。

0 件のコメント:

コメントを投稿