SARという特殊な衛星画像から海氷領域を見つけるsignateのコンペで、その技術・やったこと等のログ。
コンペデータ構成
画像
・train画像:hh 80枚、hv80枚、アノテーション画像80枚
・Test画像 hh 40枚、hv 40枚、アノテーションなし
ラベル構成
・Not sea ice領域 : 0
・Sea ice 領域 :1, 4, 7, 9
・湖 :11
・陸 :12
海氷領域の例
陸領域の例
湖は画像内でほとんんどなく誤検出の対象が陸だった。誤検出をいかに抑えながらセグメントできるかが焦点。
誤検出はHOG+SVMやopencvの検出系メソッドを使う、アノテの誤検出領域を染める等、「誤検出領域を強調してセグメンテーションしないようにする」とか後になって、いろいろ手法を思いついた。
コンペ中はそういう工夫よりネットワークをいじることに時間を費やしてしまったのがミス。
使ったNN、パラメータ、前処理等
・Residual構造のUnet→Wide Residual構造のSEblock, Squeeze-and-ExcitationをSEblockに追加
→resblockでベストプラクティスと言われてる形だと上手くいかなかったので普通のResblock(左のやつ)を使用
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でピクセル間の確率を出す手法
# 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より精度がいい)。
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)
反省・考察
上手く行かなかったこと
・テスト画像を学習させてから、訓練画像を再学習させるfinetuneの活用が下手だった
・過学習にてこずってネットワークを改造に時間をかけてしまったこと。普通にdropout層増やせばある程度は解決した。
・maxpooling層をresnet34+unetで除外したら精度が上がった事例があったので除外したけど、loss の収束スピードが鈍くなるし精度はあんまり変わらなく、少し良くなった程度。dropout層の追加で補えた
反省点
順位27位。誤検出をうまく抑えながらhh, hvの特性をうまく利用しながらセグメンテーションしなければならなったけど終盤はあまり時間がなくうまく回せなかった。
次に生かすこと
・スタートは大会優勝者の王道的な手法を真似をするのことから始める
・とにかく「下準備を十分する」、「質重視で精度検証」などをすること。データの追従もせず投稿量ばかり増える愚策は避ける
・ノートやエクセルでデータ記録、追従できるようにすること
・一回投稿するまでの流れをコード化して一回投稿することをはじめにしておき、PDCAサイクルを回せるようにしておく
・小データ数で精度が出てからやる等、学習の効率化をはかる
データ追従に役立つツール&サイト
・draw.io・draw.io を使ってみた
・draw.io
・パワポ、エクセル