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

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

Efficient-Unetで天気画像の予測(パターン認識)リベンジ【機械学習】

今回はこの前の記事で書いた「signateの天気コンペ」の優勝者(天気の専門家ガチ勢)が実装したモデルを、既存のデータでも予測できる構成で個人的に作ってみた。


convLSTMを使わずにUnetにしたのは「予測」タスクではなく、「パターン認識」タスクにしたかったから。


特定の気象条件なら、次の24時間はこういう画像になる」というパターン認識の形で次の24枚の天気画像を生成(予測)する。


目次
1.ネットワーク構成
2.ネットワークに使った技術
3.予測した画像
4.他の上位入賞者のテクの備忘録




1. ネットワーク構成


優勝者の使ったモデル



・自分で天気の特徴量を350こ作った

・ハイスペックマシンで計算可能な環境を持っていた

efficient-Netという最高精度の画像分類ネットワークを使ったencoderを2つ使ったUnet

hypercolumnの使用

Feature Pyramid Attention(FPA)の使用


という点が特徴で下のネットワーク構成
f:id:trafalbad:20200223012634j:plain




自分が作ったモデル




自分が目をつぶったポイント

・特別な特徴量を作成しない(面倒くさい、ガチ勢ちゃうから)

gooogle colabで計算できる計算量(金かけたくない)



上のポイントでケチったため、自分なりにかなり近い形の、下図のネットワークを作成した(encoderのinputは予測する一時間前の画像(直前の画像))

f:id:trafalbad:20200223012544j:plain

encoder input予測直前の画像(前日の23時の画像)
met input前日の3時間ごとの風の特徴量8こ
sat input前日の3時間ごとの天気画像8枚







2. ネットワークに使った技術

1.最後の出力層のoutput channelを24にして、セグメントタスクと同じ形式にした



out_channels = 24
o = Conv2D(out_channels, (1, 1), padding='same', kernel_initializer=conv_kernel_initializer)(o)




2. sigmoidを入れて、生成画像のブレをなくす


met-inputとsat-inputをdecoderに注入してるので、sigmoidで単位を合わせてあげなければ、画像がチャけた白黒テレビのようにブレブレになる。


f:id:trafalbad:20200223013111j:plain


sigmoidとhypercolumnの組み合わせで、ぶれを解消





3. PSP moduleの使用


PSP moduleはセグメントで有名なPSPNetで使われているモジュール。


PSPNetはEncoderにResNet101を使い、EncoderとDecoderの間にPyramid Pooling Module(PSP module)を追加している。

f:id:trafalbad:20200223012742p:plain


PSP moduleはhypercolmunと、注入したデータ(met-input, sat-Input)に適用したら、全てのケースで精度があがった。





4. 天気の特徴量(met-input, sat-input)をdecoderに挿入


met-input(風の特徴量), sat-input(天気画像)を全DecoderのConvtranspose層に挿入

# LambdaでreshapeしないとNonetypeエラーが出る
def psp_layer(inputs, skip_h, skip_w):
    inputs = PyramidPoolingModule()(inputs)
    output = Lambda(lambda x:tf.image.resize_images(x, size=(skip_h, skip_w), method=tf.image.ResizeMethod.BILINEAR))(inputs)
    return output



# encoderで挿入するより、直接Decoderに挿入の方がいい(やりやすい、早い、便利)。
def Conv2DTranspose_block(filters, kernel_size=(3, 3), transpose_kernel_size=(2, 2), upsample_rate=(2, 2),
                          initializer='glorot_uniform', skip=None, met_input=None, sat_input=None):
    def layer(input_tensor):
        x = Conv2DTranspose(filters, transpose_kernel_size, strides=upsample_rate, padding='same')(input_tensor)
        
        if skip is not None and met_input is not None:
            h, w = skip.get_shape().as_list()[1:3]
            met_output = psp_layer(met_input, h, w)
            sat_output = psp_layer(sat_input, h, w)
            x = Concatenate()([x, skip, met_output, sat_output])
        elif skip is not None and met_input is None:
            x = Concatenate()([x, skip])

        x = channel_spatial_squeeze_excite(x)
        return x

    return layer






5. FPA(Feature Pyramid Attention)


FPAはCNNで抽出した汎用特徴量の細かい部分まで補ってくれる。

セグメンテーションタスクだと細かい部分のピクセルまで再現できるので、セグメンテーションタスクでも使ってる例は多い。

f:id:trafalbad:20200223093005j:plain




FPAを使うときは以下の点に注意

・FPAを使うときは 画像サイズは shape=(256, 256)かそれ以上のshapeの必要がある

計算量が多くなる

今回は計算量の関係で使わなかったが、試しに使ってみると精度はかなり良くなった。




FPAのセグメンテーション使用例
f:id:trafalbad:20200223012840p:plain






6. SCSE(Concurrent Spatial and Channel Squeeze & Excitation)



SCSEはすでに出回ってる既存モデル(resnet50とかその他多数)に計算量をそこまで増やさず & 簡単に組み込むことができる。

f:id:trafalbad:20200223012859p:plain

特にセグメンテーション系などのタスクで性能を向上させることができる。


実際、DecoderのConvolution層の代わりに、SCSEを使ったら、計算量が減り、精度が上がった。






7. hypercolumn


hypercolumn」はConvolution層の出力を組み合わせて精度を上げる手法。

f:id:trafalbad:20200223012923p:plain


ベターな組み合わせは「Convolutional hypercolumns in Python」に詳しく載ってる。


今回のベストプラクティスは「decoder1とdecoder5」だった。

なるべく対照的な位置のlayerを組み合わせると良いらしい。

def interpolation(inputs, interpolation_shape):
    patch_size = np.multiply(inputs.get_shape().as_list()[1:3], interpolation_shape)
    inputs = PyramidPoolingModule()(inputs)
    output = Lambda(lambda x: tf.image.resize_images(x, size=patch_size, method=tf.image.ResizeMethod.BILINEAR))(inputs)
    return output

# decoder のhypercolumnの部分
if hypercolumn:
        o = Lambda(lambda x: tf.concat([x, d5], axis=3))(o)



8.精度指標


精度指標はなるべく多く使い(MSE, PSNR, SSIM)、多角的にモデルの性能を判断した。

from sklearn.metrics import mean_absolute_error
from skimage.measure import compare_ssim, compare_psnr
def measurement(func, **kwargs):
    val = func(kwargs["img1"], kwargs["img2"])
    return val

maes= 0
psnrs = 0
ssims = 0
for idx, (pr, im) in enumerate(zip(y_pred, test)):
  maes += mean_absolute_error(pr, im)
  psnrs += measurement(compare_psnr, img1=im, img2=pr)
  ssims += measurement(compare_ssim, img1=im, img2=pr)

print('total mae', maes/24)
print('total psnr', psnrs/24)
print('total ssim', ssims/24)


参考:MSE/PSNR vs SSIM の比較画像紹介






予測した画像

前回のと比べるとかなり良くなった

【GroundTruth】
f:id:trafalbad:20200223013020g:plain




【自分が予測した画像】
f:id:trafalbad:20200223013037g:plain

total mae 0.04931812573362152
total psnr 22.823581818309222
total ssim 0.7469266660346182



4. 他の上位入賞者のテクの備忘録


・好奇心でやってる人がほぼ全員、義務感でやってる奴はほぼ下位

画像サイズを変更して、同じネットワークでアンサンブル学習する。

efficient-Netのb4とかb5とかでうまくネットワーク変えながら、アンサンブルしてた。

metaデータからうまく天気と関連した特徴量を作成(マイナスの値を0に置換して、風の向きをうまくプラスのベクトルのみで表現してた)

論文から実装が多く、特にネットワークに変更は加えてない(PredRNN++とか)

・セグメントタスクみたいにチャンネルを24にして予測する手法はみんなしてた。

データ量は少なくして(1 batchとか)、爆速でPDCA回す。

精度指標は多めにして多角的視点から精度評価してた




参考サイト


hypercolumn参考GitHub

FPAを使用したUnetの参考GitHub