【その1】DeepLearningを使って気象画像から天気予報をする


#変更履歴
**2016/8/31 AUC計算が「晴」だけしか見ていなかったので、修正**

#概要
DeepLearningでCNN(畳み込みニューラルネットワーク)といえば、画像処理がメインになるのに、画像処理に関する分析を全くやっていなかったので、今回は **気象画像から天気予報ができるか** を検証してみたいと思います。

#準備
## 気象画像の入手
気象画像は[高知大学のサイト](http://weather.is.kochi-u.ac.jp/wiki/archive)からぽちぽちダウンロードしました。2015年1月から2016年7月のデータを入手します。使ったのは[このような](http://weather.is.kochi-u.ac.jp/sat/gms.fareast/2016/08/27/fe.16082717.jpg)画像です。
なお、1時間ごとにデータがあるので、17時のデータを入手しました。

![fe.16082717.jpg](https://qiita-image-store.s3.amazonaws.com/0/54539/440c5540-8fba-7ed6-80d2-e4f6af0105eb.jpeg "fe.16082717.jpg")

出典: 高知大学・東京大学・気象庁提供

## 過去の天気の入手
過去の天気は気象庁の[ページ](http://www.data.jma.go.jp/risk/obsdl/index.php)から入手します。こちらも2015年1月から2016年7月までの日ごとの天気データです。場所は東京で、日中の天気を使用しました。

#問題設定
問題としては「前日の17時の日本付近の気象画像から、次の日の晴・雨を予測する」です。
なお、気象庁の天気には「晴後雨」とか「曇」とかありますが、雨が含まれれば「雨」、それ以外は「晴」として扱い、二値分類問題としました。

#前処理
明日の天気には、東京付近の雲の動きだけではなく偏西風等の影響もあるので、もっと広い範囲の気象画像が必要ですが、日本から遠く離れた場所の天気は不要でしょう。
なので、画像をトリミングして日本付近のデータにしてあげます。

```python
import numpy as np
from PIL import Image
import datetime as dt

w = 640
h = 480
"""日本付近だけを切り取る設定"""
sw = 320
sh = 65
ew = 540
eh = 320
"""イメージ圧縮"""
is_comp = False

def get_mat(dates=[]):
    """

    :param dates:
    :return:
    """
    l = len(dates)
    if not is_comp:
        wr = ew - sw
        hr = eh - sh
    else:
        wr = 50
        hr = 50

    mat = np.zeros((l,3,wr,hr),dtype=np.float32)
    file_base = base_file_dir + "fe.%s" + base_hour + ".jpg"

    j = 0
    err_dates = []
    for ddd in dates:
        dd = dt.datetime.strptime(ddd,"%Y/%m/%d")
        dd_str = dd.strftime("%y%m%d")
        try:
            im = Image.open(file_base % (dd_str))
            im = im.crop((sw,sh,ew,eh))
            im = im.resize((wr, hr))
            mat0 = np.array(im)
            for i in range(0,3):
                mat[j,i,:,:] = mat0[:,:,i].T
            j += 1
        except:
            err_dates.append(ddd)
            print dd_str + " --> Error!!"
    return mat[0:j],err_dates

```

日付のリストを受け取って、画像をnumpyの行列にして返し、ついでにエラーが出た日付も返してあげます。
イメージオブジェクトimには画像が入りますが、numpyで行列に直した場合、(幅、高、チャネル)の順になるので、転置している点に注意が必要です。
ここでは、画像(480x640)から日本列島が入る範囲でトリミングを行っています。
以下のような感じ。

![test02.jpg](https://qiita-image-store.s3.amazonaws.com/0/54539/de63e8f0-ccf3-3860-bf0a-91b3dea69176.jpeg "test02.jpg")


行列にしたのちのデータは各セルに0-255までのデータが入るので、255で割って0から1のデータに変換します。

その後のCNNの学習時間の関係で、画像を圧縮することも考えましたが、今回はやめました。


#CNNモデル
畳み込みニューラルネットワークのモデルは従前使用していたものを使用します。
[このへん](http://qiita.com/wbh/items/e07fd287393cab7211e5)とか[このへん](http://qiita.com/wbh/items/b2b178073afb9d1face2)とか[このへん](http://qiita.com/wbh/items/da881fac695f17042b19)を参照ください。

パラメータ設定としては、以下のようにしています。

```python
params = {"clm_dim":clm_dim,"in_channels":3,"out_channels":3,"row_dim":row_dim,"filt_clm":3,"filt_row":3,"pool_clm":3,"pool_row":3,"batchsize":200,"hidden_dim":500,"n_classes":2}
```

clm_dimとrow_dimは各画像の幅と高さに対応します。また、チャネル数はRGBなので3としています。また、max poolingを使用します。
なお、フィルタサイズとかプーリングサイズを大きくすると計算時間がかかるようになります。(はまった)

この設定でも手元のmacbook proでは非常に時間がかかります。なので、今回はepoch=50としました。

#学習データとテストデータ
非常に計算時間がかかるので、学習データを2015/1/1から2015/12/31の1年間のデータ、テストデータを2016/1/1から2016/7/31までとしています。
これでも時間かかるけど。画像処理恐ろしい・・・

#モデルの学習とテスト精度
学習の進み方とテスト精度は以下のような形になりました。
![train_test.png](https://qiita-image-store.s3.amazonaws.com/0/54539/a0af54aa-e460-cee8-caef-73a721fd4b8e.png "train_test.png")

また学習精度としては伸びそうだけど、テスト精度が追いついていないなぁ。

#テスト結果

AUCで ~~0.78~~ **0.70** で各種指標が以下。

||Precision|Recall|F-Score|
|--:|--:|--:|--:|
|雨|0.54|0.67|0.6|
|晴|0.83|0.74|0.78|
|平均|0.74|0.72|0.72|

まぁざくっとやった割には結構いい感じではないでしょうか。
ただ、雨と予測したうち、実際に雨だったのは54%と低めです。

雨確率で降順に並べて、実績の雨の比率と晴の比率をプロットしてみましょう。

* 雨累積 = (確率高い順での雨日数)/(合計雨日数)
* 晴累積 = (確率低い順での晴日数)/(合計晴日数)

![test_ruiseki.png](https://qiita-image-store.s3.amazonaws.com/0/54539/e4e7a7bd-2abb-788c-a903-eda3967600e0.png "test_ruiseki.png")

ちゃんとランダムではなく予測しているっぽい。
テストデータ中の実際の雨の比率は30%、晴の比率は60%なので、そうでしょう。

#曇の扱い

精度が上がらない要因の一つに「曇」の扱いがあると思われます。
「晴れのち曇」の場合とか「曇一時雨」とかがあり、実際データ中の曇の比率は70%を超えます。(曇すぎ)
今回前者を晴、後者を雨として扱っています。そのため、微妙な状況というのはどちらかわからないのだと思われます。このへんは課題です。
ただ、今回の結果では、明らかな雨については結構な精度で当たっているようです。

三値分類にすると曇が支配的になってしまうので、難しい問題になります。

気象予報士さんはすごいですね。

#今後
アルゴリズム的にはこれでいこうかなぁとは思っていますが、データについては、例えば前日の画像との差分を取るとか、ベースになる地図のピクセルを抜くとか考えられます。
ちょぼちょぼとやっていこうかなぁと思います。