背景
chainercvが0.6になって、rcnnの実装のひとつであるSSDの訓練をするためのサンプルコードが提供されるようになりました。
論文読んでも少々わからない部分があったので、実際にネットワークの訓練コードを動かしながら追っていきたいと思います。
まだ理解しきれていない部分もありますので誤解・間違いなどあるかもしれません。
環境はいつものです。chainerとchainercvのバージョンだけあがってます。
SSDの何を知りたいのか
SSDの論文はこちら
https://arxiv.org/abs/1512.02325
今回知りたかったのは「一枚の画像からN個のオブジェクトを検出することを考えたとき、その個数Nとそれに付随する領域はネットワークの出力ではどのような表現になっているのだろう?」という内容です。
まだ私の中での答えはでていませんが、少しずつやっていったほうがモチベーション維持に繋がるので、ここで一旦ブログに残そうといった次第です。
ちなみにopencv本家でもYOLOという物体検出アルゴリズムがマージされたようなので、あちらも安定板がリリースされ次第試してみたいですね。
ssd学習データの入力を確認する
まずSSDの訓練で使われているデータがどんなものか見てみることにします。
/examples/ssd/train.pyにあるTransformクラスのcall関数に次のコードを入れてみます。
このクラスは不変性を高めるためにデータを加工するのが目的のようです。 訓練データをネットワークに入力する前に変換を行うことで、それらの変換に対する不変性を得られるという話ですね。
というわけで変換をする直前に入力データと教師データの組を出してしまえば実際の訓練データが見れるわけです。
コード末尾exitしてるのは、この関数は訓練データの入力ごとに呼び出されるためです。 まあ一個だけでもサンプル見れればいいかなという乱暴な実験です。
これでSSDを使った識別とと同じような出力結果を得ることができます。
import cv2 from chainercv.datasets import voc_detection_label_names oimg = img.transpose(1,2,0) # channel,width,heightの順に並んでるので、width,height,channelにする for b,l in zip(bbox,label): # 正解ラベル名を得る object_name = voc_detection_label_names[l] # 正解の矩形領域を表示 red = (255,0,0) cv2.rectangle(oimg, (b[1], b[0]), (b[3], b[2]), red, 2 ) # 正解のラベルを表示 cv2.putText(oimg, object_name, (b[1], b[0]), cv2.FONT_HERSHEY_SIMPLEX, 1, red) # 保存して終了 cv2.imwrite('transformed.jpg', oimg) exit()
コードの説明
画像に変換をかけているコードを見れば、だいたいどんな感じのことやってるのかわかるので、そのあたりは省略します。
読むときのポイントとして、正解ラベルと領域は1つではないことに気を付ければよいかと思います。
あと上記コードで画像として出力するために私が慣れているopencv関数を使っています。x,yの順序, カラーチャネルの順序に要注意です。
ちなみにこのコードではカラーチャネルの順序を意識していないので、色味がおかしくなります。(デフォルトでpillowはRGB, opencvはBGR。)
最後に、ラベル名を表すvoc_detection_label_namesという変数は chainercv/datasets/voc/voc_utils.py に定義されています。
VOCデータセットでの正解ラベルってことですね。
ネットワークグラフを見てみる
trainerがあるので、グラフをダンプさせれば見れます。
trainer.extend(extensions.dump_graph('main/loss'))
ダンプしたファイルはdot形式なので、例によってgraphvizのdotコマンドで画像化します。
dot -Tpng cg.dot -o network-graph.png # もしくは dot -Tsvg cg.dot -o network-graph.svg
pngだとネットワークが大きすぎて超巨大な解像度の画像になってしまうので、ベクター形式(svg)のファイルのほうが幸せになれそうです。
こんなのできました。 svg形式張れなかったのでpngです。
論文と照らし合わせて確認
まだ論文全部を理解しきれてないので、概要だけざっと照らし合わせます。
入力画像からの特徴抽出
ネットワークの前半(画像での上のほう)はVGG16と呼ばれるネットワーク構造で、特徴抽出を目的としています。 これはリンク先の文書中ではbase networkと呼ばれているものです。必ずしもVGG16でなければならないというものではないようです。
ネットワークの後半部分
スケールを変化させつつ、それぞれの解像度で特徴抽出と分類をしているように見えますが、よくわかりません。 分類結果をconcatしているようにも見えるのは何なのだろう。。。 私にはまだ早すぎたのかもしれません。一度に全部理解するのは大変なので、少しずつマイペースで調べていきましょう。
とりあえず利用することを目的にする
この分野は進歩が速いので、せっかく中身を把握してもまた新しいネットワークが提案されるということは充分にあり得ます。
というわけで、それらを利用する観点からも分析をしてみます。 ネットワークを利用する簡単な方法は、訓練データを自分がやりたいように改変してあげればよいかと思います。
自分の環境では訓練データは次の場所でした。
C:\Users\ <ユーザー名>.chainer\dataset\pfnet\chainercv\voc\VOCdevkit\VOC2012\
また、SSDの訓練サンプルで使用されている学習データのパスは次のファイルを指しています。
ImageSets\Main\train.txt
訓練データの概要
SSDのサンプルコードを例に動作概要を示します。 まず上記train.txtファイルから適当なレコードを取り出します。
たとえば一行目は "2008_000008" です。
これはAnnocationsとJPEGImagesディレクトリのファイル名(拡張子を除く)を示していて、 画像ファイルと、その正解データの組を表現しています。
上記ファイル名を例にとると、
というわけですね。
おまけ
SSDでは検出したオブジェクトのバウンディングボックスを正解として使うため、詳細な輪郭情報とそれに付随するラベルは使われていません。
データとしては SegmentationClassディレクトリとSegmentationObjectディレクトリがあって領域とラベル情報が入っているようなのでそのうち詳しく見てみたいところです。
感想と今後の展望
R-CNNのSSDの中身までは理解できませんでしたが、使い方はおおよそ目星がつきました。
私自身はカードゲームのAR化,その後のARアプリの拡張を目標としているので、この訓練データの一部を自前の訓練データで置き換えてやればいい結果が出せるかもしれません。
とりあえず今回はこれくらいで。