stMind

about Tech, Computer vision and Machine learning

KerasでSemantic segmentation

画像ではなく、ピクセル単位でクラス分類するSegmentationのタスク。 fast.aiにあるtiramisuが実装もあって分かりやすいので試してみた。下記のコードスニペットは、fast.aiのオリジナル実装ではなく、keras2で書き直されたjupyter notebookのコードをベースに、自分で若干の手直しをしたものを使っている。

github.com

tiramisu

Down-sampling path

  • Down-sampling pathの構成と実装
    • DB(Dense Block)とTD(Transition Down)の繰り返しで特徴抽出
    • DBの最後の出力特徴マップをUp-sampling pathのためのskip connectionとして保存
def down_path(x, nb_layers, growth_rate, keep_prob, scale):
    skips = []
    for i, nb_layer in enumerate(nb_layers):
        x, added = dense_block(nb_layer, x, growth_rate, keep_prob, scale)
        skips.append(x)
        x = transition_down(x, keep_prob, scale)
    return skips, added
  • DBの構成と実装
    • BN(Batch Normalization)、ReLU、3x3 same convolution(とdropout)を組み合わせたLayerで特徴マップを生成
      • growth rateが特徴マップ数を表す
    • 特徴マップを入力に連結(concat)
    • 次のDBのために新しく生成した分の特徴マップを保存
def dense_block(nb_layers, x, growth_rate, keep_prob, scale):
    added = []
    for i in range(nb_layers):
        b = conv_relu_batch_norm(
            x, growth_rate, keep_prob=keep_prob, scale=scale)
        x = concat([x, b])
        added.append(b)
return x, added
  • TDは空間解像度の削減、要はpoolingと同等の処理
    • 論文では1x1 convolutionと2x2 poolingの組み合わせが提案されているが、fast.aiではstrideが2の1x1 convolutionを提案。
def transition_down(x, keep_prob, scale):
    return conv_relu_batch_norm(
        x,
        x.get_shape().as_list()[-1],
        ksize=1,
        scale=scale,
        keep_prob=keep_prob,
        stride=2)

Up-sampling path

  • Up-sampling pathの構成と実装
    • TU(Transition Up)、skip connectionとの連結(concat)、DBの繰り返しで入力画像サイズに復元
def up_path(added, skips, nb_layers, growth_rate, keep_prob, scale):
    for i, nb_layer in enumerate(nb_layers):
        x = transition_up(added, scale)
        x = concat([x, skips[i]])
        x, added = dense_block(nb_layer, x, growth_rate, keep_prob, scale)
    return x
  • TUは特徴マップをupsamplingするtransposed convolutionで構成
    • 入力と同じch数で、3x3 kernel size、strides 2で2倍の特徴マップを生成
def transition_up(added, scale):
    x = concat(added)
    _, row, col, ch = x.get_shape().as_list()
    return Conv2DTranspose(
        ch,
        3,
        kernel_initializer='he_uniform',
        padding='same',
        strides=(2, 2),
        kernel_regularizer=l2(scale))(x)

学習

  • 56層バージョン。FC-DenseNet56
  • OptimizerはRMSProp、学習率の初期値は1e-3、学習率減衰は0.00005
  • L2正則化の係数1e-4、Dropout rateは0.2、growth rateは12
  • データセットはcamvid、360x480から224x224をクロップして入力画像とする
  • batchsizeは10、epochsは30で実行

結果

  • 24epochでvalidation accuracyが85%
  • 結果画像を見ると、道路と白線は分類出来ているようだが、車と背景との分離はもう一歩。Ground-Truthにはない画像右上の柱が推定出来ている。
  • パラメータ数が少ないモデルという特徴があり、実際にGPUの無いMacでも学習を実行出来るのは良い点。

f:id:satojkovic:20180501234738p:plain