DeepLearning
Chainer
VGG16
FCN

Fully Convolutional NetworksにVGGの学習済みパラメータを使ってみたときのメモ

More than 1 year has passed since last update.

GPUを買ったので,DNNを試してみる.

CNN+fine-tuneによる画像分類は以前やったので,今回はFCNを試してみる.

  1. 論文を理解する
  2. 自分で実装する

くらいをやってFCNについてある程度把握しておく.

ソースは https://github.com/eisoku9618/kuroiwa_demos/tree/master/python/semantic_segmentation/fcn

参考サイト

解説サイト

基本的にはこれらのプログラムをガッチャンコしたもの

まず論文を読む

https://arxiv.org/pdf/1411.4038.pdf

Fully Convolutional Networks for Semantic Segmentation

UC Berkeleyの人がCVPR2015に出した論文.

Abstract

AlexNet / VGG net / GoogLeNetを使ってfine-tuneができるらしい.

Introduction

Sementic Segmentationはsemanticとlocationのどっちを取るか的な課題を持つ.大域的な情報は何があるかを教えてくれて,局所的な情報はそれがどこにあるかを教えてくれる.

Related work

レイヤーにまたがって特徴量をフュージョンすることで,非線形な局所・大局表現を行う.

Segmentation Architecture

From classifier to dense FCN

  • VGG 16-layer netを採用
  • GoogLeNetは最後のloss層だけ使う.average pooling層は使わない
  • それぞれのnetの最後のFully Connected層をConvolution層にすげ替える

FCN-AlexNet vs FCN-VGG16 vs FCN-GoogLeNetを比較して,FCN-VGG16がbsetらしい.

Combining what and where

FCN-32sよりもFCN-8sの方が詳細に表現できている.32とか8とかはstrideの値らしいので.

Experimental framework

Optimization: SGD with momentum
minibatch size: 20 images
learning rate: $10^{-4}$ for FCN-VGG16

分かったこと

FCN32s -> FCN16s -> FCN8sの順番に学習させていくらしい.
FCN32sだけ試してみた.

また,世の中に出ている日本語解説記事はfc層を省きがちなことに注意.ここでは省いていない.

VGG16はchainerに入っている

https://github.com/eisoku9618/kuroiwa_demos/pull/110/files#diff-05a35d5dd788d339bede949d5f16091dR31 でダウンロードできて便利

from chainer.links import VGG16Layers
vgg16_model = VGG16Layers()

でok

fcnのconv1_2のpadを100にするとメモリをめっちゃ食う

original実装の https://github.com/shelhamer/fcn.berkeleyvision.org/blob/master/voc-fcn32s/net.py#L28 で100になっていて,その理由は https://github.com/shelhamer/fcn.berkeleyvision.org#frequently-asked-questions に書いてあるが,これだと画像サイズが大きくなってしまって,メモリを10GBとか食う.
これだと手元のGPUは4GBなので,メモリが足りずに実行できない.

padを100にしているのは任意のサイズの画像を受け付けられるように簡単にしたまでで,入力前にVGGの入力である224にresizeするようにして,解決.

batch_size / iteration / epoch

1000個の学習データがあったとして,batch_sizeを500個にすると,1epochで2iterationが回る.
1000個の学習データがあったとして,batch_sizeを100個にすると,1epochで10iterationが回る.

iterationの度に重み行列が更新されるので,batch_sizeが小さくなると重み行列が更新されがちになる.これをiterationをたくさん回せる,と言うこともある.
batch_sizeが大きすぎると,iterationが回しづらくなる.
batch_sizeが小さすぎると,重みの更新があっちゃこっちゃ行きがち.

というバランスを取る必要があるみたい.
今回はmemoryが4GBなので,batch_sizeは否応なく1になった.

chainerが抽象化されていた

おしゃれになっていた.
ただ,学習させたmodelを使って,適当な画像を入力して,その結果を見る,というやり方が分からず,めんどくなって諦めた.
ここはちゃんと調べたいところ.

まず,extensions.snapshot()でtrainerは保存されているが,これだけではmodelを呼び出せないのかどうかを知りたい.分からなかったので,仕方なく,extensions.snapshot_objectでmodelを直接saveしてみた.

b.png

if __name__ == "__main__":
    model = FCN32s(num_output=21)
    model = L.Classifier(model)

    chainer.serializers.load_npz("./result2/model_iter_102480", model)
    import fcn.utils
    import scipy.misc
    import numpy as np
    img = PIL.Image.open("/tmp/2007_000129.jpg")
    img = np.array(img, dtype=np.uint8)
    img = scipy.misc.imresize(img, (224, 224))
    datum = img.astype(np.float32)
    datum = datum[:,:,::-1]
    datum -= VOC2012ClassSeg.mean_bgr
    datum = datum.transpose((2, 0, 1))

    lbl_pred = chainer.functions.argmax(model.predictor(datum.reshape(1, 3, 224, 224)), axis=1).data[0]

    print fcn.datasets.VOC2012ClassSeg.class_names
    viz = fcn.utils.visualize_segmentation(
        lbl_pred=lbl_pred, img=img, n_class=21,
        label_names=VOC2012ClassSeg.label_names)
    import skimage
    skimage.io.imsave("/tmp/aaaaa.png", viz)

こんな感じで無理やり可視化すると上のようになる.pip install fcnが必要.
70回くらいepochを回した結果で,動いている気はする.

怪しい部分

  1. 学習データを読み込む部分

  2. fcnの最後のdeconvolutionのところ

式としては,わかりやすさのために,$IN=deconvの入力$,$OUT=deconvの出力$として,deconvはconvの逆であることから,

$$
IN = \frac{OUT - ksize + pad * 2}{stride} + 1
$$

となる.
例えば,resizeしないとして,入力画像が224だとしたら,$OUT=224*224$で,$IN=1*1$となるので,右辺の分数は0になればよくなる.
ksizeが64で,strideが32だとすると,padは80になって,これでいいのかなぁという気持ちになる.

ので,今回はresizeすることにして,$IN=1*1$,$OUT=224*224$で,strideは不定なのでとりあえず32にして,padは小さいほうが良い気もするので0にして,その結果,ksizeが224になった.

とても怪しい.