アプリとサービスのすすめ

アプリやIT系のサービスを中心に書いていきます。たまに副業やビジネス関係の情報なども気ままにつづります

Unetを使った海氷領域セグメンテーションコンペの記録【機械学習】

SARという特殊な衛星画像から海氷領域を見つけるsignateのコンペで、その技術・やったこと等のログ。


コンペデータ構成

画像
train画像:hh 80枚、hv80枚、アノテーション画像80枚
Test画像 hh 40枚、hv 40枚、アノテーションなし



ラベル構成
Not sea ice領域0
Sea ice 領域1, 4, 7, 9
11
12



海氷領域の例
f:id:trafalbad:20191201153246j:plain


陸領域の例
f:id:trafalbad:20191201153302j:plain



湖は画像内でほとんんどなく誤検出の対象が陸だった。誤検出をいかに抑えながらセグメントできるかが焦点。

誤検出はHOG+SVMopencvの検出系メソッドを使う、アノテの誤検出領域を染める等、「誤検出領域を強調してセグメンテーションしないようにする」とか後になって、いろいろ手法を思いついた。

コンペ中はそういう工夫よりネットワークをいじることに時間を費やしてしまったのがミス。







使ったNN、パラメータ、前処理等

・Residual構造のUnet

→Wide Residual構造のSEblock, Squeeze-and-ExcitationをSEblockに追加

→resblockでベストプラクティスと言われてる形だと上手くいかなかったので普通のResblock(左のやつ)を使用

f:id:trafalbad:20191201153211j:plain

x = Activation('relu')(init)
x = Conv2D(nb_filter * k, (3, 3), padding='same', use_bias=False)(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Dropout(0.4)(x)
x = Conv2D(nb_filter * k, (3, 3), padding='same', use_bias=False)(x)
x = BatchNormalization()(x)

x = layers.add([init, x])


Wide Residual Network:幅を広くすることでモデルの表現力を上げる、計算を並行して行うことで計算時間の短縮、Dropoutを入れることで過学習を抑制

Squeeze-and-Excitation:パラメータ数の増加を抑える







アノテーション


grayscaleで0,1でピクセル間の確率を出す手法

f:id:trafalbad:20191201153347j:plain

# 0, 11, 12 は黒 (0)、それ以外は白(255)
dst = np.where((img == 0) | (img >= 11), 0, 255)





・Loss


binary cross entropy(bce) dice loss




・optimizer


RMSprop(lr=1e-3), Adam(lr=1e-3)





・Augmentation


flip系、回転系など位置変換系のみ使用。

grayscaleなのでピクセル間の関係性を壊すコントラストや色彩変換系は使わなかった。

Imagegeneratorでも位置変換の引数のみを指定





TTA(Test Time Augmentation)


augmentationでflip系を使ってたので使えなかった。augmentationの時にflip系を使わないなら必須




過学習対策



・Random Erase(マスクを傷つけたのか不明)

・validationデータ増やす

・L2正規化

・augmentation

・Dropout層をencoderのconv層前とResblock内に追加



・epoch、batch sizeパラメータ



イテレーション数 3000

・batch size:20

イテレーション数は「データ数/バッチサイズ」より3000くらいにした方が安定してlossが減少。



画像前処理

ヒストグラム平均化を使った


効果あったのがStretching、CLAHE (Stretchingの方がCLAHEより精度がいい)。


f:id:trafalbad:20191201153117p:plain



stretchingはいい具合に画像のピクセルをおおそ15単位ごとに分散して平均化してくれるからhhとhvで特性が顕著になりやすいため精度が良くなった


CLAHEピクセルが広範囲で、hh,hvが似たようなかんじのピクセル構成になり、hh, hvの特性を活かせないため精度に影響が少なかった。

あとリサイズするときはピクセル構成を維持するためにcv2.resize()に "interpolation=cv2.INTER_NEAREST" を指定した。

# stretching
p2, p98 = np.percentile(hh_image, (2, 98))
img_rescale = exposure.rescale_intensity(image, in_range=(p2, p98))


# CLAHE (Contrast Limited Adaptive Histogram Equalization)
param = 20.0
clahe = cv2.createCLAHE(clipLimit=param, tileGridSize=(8,8))
cl1 = clahe.apply(image)



# リサイズ
cv2.resize(image, (H, W), interpolation=cv2.INTER_NEAREST)




反省・考察

上手く行かなかったこと


・海氷領域以外の誤検知抑制のために「IOUに閾値を設ける」「誤検出領域の強調をする」とかの工夫をしなかった


・テスト画像を学習させてから、訓練画像を再学習させるfinetuneの活用が下手だった


過学習にてこずってネットワークを改造に時間をかけてしまったこと。普通にdropout層増やせばある程度は解決した。


・maxpooling層をresnet34+unetで除外したら精度が上がった事例があったので除外したけど、loss の収束スピードが鈍くなるし精度はあんまり変わらなく、少し良くなった程度。dropout層の追加で補えた





反省点


・下準備がおろそかだったこと


・自分で考えてしまい、先人の真似するという根本的なことを序盤からしなかった(先人の知恵を借りず独力でやってしまった)


アルゴリズムを多く選びすぎて選択のパラドックスに陥った。アルゴリズムは1、2に絞ってあとは工夫で補うべきだった






順位27位。誤検出をうまく抑えながらhh, hvの特性をうまく利用しながらセグメンテーションしなければならなったけど終盤はあまり時間がなくうまく回せなかった。

f:id:trafalbad:20191201153435j:plain





次に生かすこと



・保守的なモデルと挑戦的なモデル2つのみを使う、あとは工夫で補う


・スタートは大会優勝者の王道的な手法を真似をするのことから始める


・とにかく「下準備を十分する」、「質重視で精度検証」などをすること。データの追従もせず投稿量ばかり増える愚策は避ける


・ノートやエクセルでデータ記録、追従できるようにすること


・一回投稿するまでの流れをコード化して一回投稿することをはじめにしておき、PDCAサイクルを回せるようにしておく


・小データ数で精度が出てからやる等、学習の効率化をはかる

データ追従に役立つツール&サイト

・draw.io
draw.io を使ってみた
draw.io

パワポ、エクセル

シーケンス図(ULM)

フローチャート