今回はこの前の記事で書いた「signateの天気コンペ」の優勝者(天気の専門家ガチ勢)が実装したモデルを、既存のデータでも予測できる構成で個人的に作ってみた。
convLSTMを使わずにUnetにしたのは「予測」タスクではなく、「パターン認識」タスクにしたかったから。
「特定の気象条件なら、次の24時間はこういう画像になる」というパターン認識の形で次の24枚の天気画像を生成(予測)する。
目次
1.ネットワーク構成
2.ネットワークに使った技術
3.予測した画像
4.他の上位入賞者のテクの備忘録
1. ネットワーク構成
優勝者の使ったモデル
・ハイスペックマシンで計算可能な環境を持っていた
・efficient-Netという最高精度の画像分類ネットワークを使ったencoderを2つ使ったUnet
・hypercolumnの使用
・Feature Pyramid Attention(FPA)の使用
という点が特徴で下のネットワーク構成
自分が作ったモデル
自分が目をつぶったポイント
・gooogle colabで計算できる計算量(金かけたくない)
上のポイントでケチったため、自分なりにかなり近い形の、下図のネットワークを作成した(encoderのinputは予測する一時間前の画像(直前の画像))
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で単位を合わせてあげなければ、画像がチャけた白黒テレビのようにブレブレになる。
sigmoidとhypercolumnの組み合わせで、ぶれを解消。
3. PSP moduleの使用
PSP moduleはセグメントで有名なPSPNetで使われているモジュール。
PSPNetはEncoderにResNet101を使い、EncoderとDecoderの間にPyramid Pooling Module(PSP module)を追加している。
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で抽出した汎用特徴量の細かい部分まで補ってくれる。
セグメンテーションタスクだと細かい部分のピクセルまで再現できるので、セグメンテーションタスクでも使ってる例は多い。
FPAを使うときは以下の点に注意
・計算量が多くなる
今回は計算量の関係で使わなかったが、試しに使ってみると精度はかなり良くなった。
FPAのセグメンテーション使用例
6. SCSE(Concurrent Spatial and Channel Squeeze & Excitation)
SCSEはすでに出回ってる既存モデル(resnet50とかその他多数)に計算量をそこまで増やさず & 簡単に組み込むことができる。
特にセグメンテーション系などのタスクで性能を向上させることができる。
実際、DecoderのConvolution層の代わりに、SCSEを使ったら、計算量が減り、精度が上がった。
7. hypercolumn
「hypercolumn」はConvolution層の出力を組み合わせて精度を上げる手法。
ベターな組み合わせは「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)
予測した画像
前回のと比べるとかなり良くなった
【GroundTruth】
【自分が予測した画像】
total mae 0.04931812573362152 total psnr 22.823581818309222 total ssim 0.7469266660346182
4. 他の上位入賞者のテクの備忘録
・画像サイズを変更して、同じネットワークでアンサンブル学習する。
・efficient-Netのb4とかb5とかでうまくネットワーク変えながら、アンサンブルしてた。
・metaデータからうまく天気と関連した特徴量を作成(マイナスの値を0に置換して、風の向きをうまくプラスのベクトルのみで表現してた)
・論文から実装が多く、特にネットワークに変更は加えてない(PredRNN++とか)
・セグメントタスクみたいにチャンネルを24にして予測する手法はみんなしてた。
・データ量は少なくして(1 batchとか)、爆速でPDCA回す。
・精度指標は多めにして多角的視点から精度評価してた
参考サイト