オッサンはDesktopが好き

自作PCや機械学習、自転車のことを脈絡無く書きます

U-Netで複数種類の傷を検出してみる

複数種類の傷を学習させるために,以前に作ったAutoEncoder*1を拡張します.
複数(=傷の種類)の出力画像を持てるようにネットワーク構造を変えればいいですね.
あれこれ調べてみると,今回やりたい事はU-Netと同じであることに気付きました.
Semantic Segmentationで有名なU-netですので,サンプルは豊富です.
ということで,U-Netベースの複数傷検出にトライしました.

入力画像

DAGM画像を↓↓の様に使いました.

使用画像
学習 Class1_def 1-120, Class1_def 121-150
テスト Class1_def 1-120, Class1_def 121-150

Class1とClass2に思い入れがあるわけではありません.
今回のトライがうまく行ったら,全種類の画像で学習させます.

ソース

こちら*2を参考にさせていただきました.
参照元ではtf.layers.conv2Dとtf.layers.batch_normalizationを使って正規化処理をされていたのですが,動きを良く理解していないライブラリを使うのが嫌いなので,実績のあるtf.nn.conv2Dを使いました.

ネットワークの構成も,入力画像の大きさに合わせて少し変えてあります.
また,特に理由は無いですが,padding有りで組んでいます.

f:id:changlikesdesktop:20190819175821p:plain:w500

少し迷ったのが,評価関数の立て方です.

def evaluate(output, y):
    s = tf.sign(output)
    z = tf.constant(0, shape=[TEST_DATA_SIZE*CATEGORY, IMG_SIZE*IMG_SIZE*CATEGORY], dtype=tf.float32)
    zero_cut = tf.maximum(s, z)
    correct_prediction = tf.multiply(zero_cut, y)
    accuracy = tf.reduce_sum(correct_prediction)
    return accuracy

mnist*3とは違って正解が領域を持つので,argmaxでの評価が難しいと思いました.
試行錯誤をして,出力値が0.0を変える領域をラベルと比較するように作ってみました.
ラベルは傷の大きさに応じて正規化しているので,60枚のテスト画像を使った今回のトライでは理想値60.0になります.

結果

こんな感じになりました.

f:id:changlikesdesktop:20190819181905p:plain:w500
f:id:changlikesdesktop:20190819181920p:plain:w500
f:id:changlikesdesktop:20190819181934p:plain:w500
f:id:changlikesdesktop:20190819181949p:plain:w500
f:id:changlikesdesktop:20190819182000p:plain:w500
f:id:changlikesdesktop:20190819182016p:plain:w500
左:元画像,左中:Class1用のラベル,中:Class2用のラベル,右中:Class1の出力画像,右:Class2の出力画像

概ね正しく傷検出できました.
ただ,Class2のネットワークは,Class1の傷にも少し反応してしまうようでした.
傷の特徴が多少似ているのかも知れないですね.
直感的には,傷の無いbackground部分のテクスチャーの違いが影響している様に感じました.

学習経過は↓です.

f:id:changlikesdesktop:20190819182846p:plain:w300
f:id:changlikesdesktop:20190819182858p:plain:w300

苦労した評価関数は,巧く動かずに暴れています(汗).
DAGMに付属しているラベルが大雑把なので,傷領域を綺麗に抽出すると,ラベルよりもずっと小さい領域になってしまうのです.
改良が必要ですね.

次回はClass6までを含めた学習にトライします.

今回書いたソースはここ*4です.