catalinaの備忘録

ソフトウェアやハードウェアの備忘録。後で逆引きできるように。

chainercv0.6付属のSSDサンプルコードを読んでいく

背景

chainercvが0.6になって、rcnnの実装のひとつであるSSDの訓練をするためのサンプルコードが提供されるようになりました。

論文読んでも少々わからない部分があったので、実際にネットワークの訓練コードを動かしながら追っていきたいと思います。

まだ理解しきれていない部分もありますので誤解・間違いなどあるかもしれません。

環境はいつものです。chainerとchainercvのバージョンだけあがってます。

  • Windows 10 Pro
  • Python 3.5.2 : Anaconda 4.2.0 (64-bit)
  • chainer 2.1.0
  • chainercv 0.6.0

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です。 f:id:Catalina1344:20171014000310p:plain

論文と照らし合わせて確認

まだ論文全部を理解しきれてないので、概要だけざっと照らし合わせます。

入力画像からの特徴抽出

ネットワークの前半(画像での上のほう)は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ディレクトリのファイル名(拡張子を除く)を示していて、 画像ファイルと、その正解データの組を表現しています。

上記ファイル名を例にとると、

  • Annotations/2008_000008.xmlに画像内の情報(どこに何があるか)
  • JPEGImages/2008_000008.jpegが実際の画像

というわけですね。

おまけ

SSDでは検出したオブジェクトのバウンディングボックスを正解として使うため、詳細な輪郭情報とそれに付随するラベルは使われていません。

データとしては SegmentationClassディレクトリとSegmentationObjectディレクトリがあって領域とラベル情報が入っているようなのでそのうち詳しく見てみたいところです。

感想と今後の展望

R-CNNのSSDの中身までは理解できませんでしたが、使い方はおおよそ目星がつきました。

私自身はカードゲームのAR化,その後のARアプリの拡張を目標としているので、この訓練データの一部を自前の訓練データで置き換えてやればいい結果が出せるかもしれません。

とりあえず今回はこれくらいで。