気温と気圧配置図から季節を予測する

#はじめに
以前、地図上にデータをプロットしたり、cifar10で分類をさせてみたりしましたが、今回はそれを組み合わせてやろうという思いつきで始めてみました。要するに、既存のデータセットではなく自作データでやってみようというわけです。

###目次
・使用したもの
・画像データの作成
・訓練データ、テストデータの作成
・識別機に放り込む

###使用したもの
```python 
import cartopy.crs as ccrs 
import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib import cm 
from PIL import Image 
from tensorflow.keras.utils import to_categorical
```

いろいろimportしているときなんかワクワクするの私だけでしょうか。

###画像データの作成
まず、日本周辺の気温と気圧配置の図をcartopyを使って作成しました。その点は、使っているデータセットによるのでコードは割愛します。
![pic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2499154/0ba6e1db-5c46-7dcf-8b58-bba714d20145.png)
このような画像データを３年分、365*3枚の画像を作成しました。

###訓練データ、テストデータの作成
保存したままのデータだと(4000, 4000, 4)のデータで、一度に乗せることができなかったので解像度を荒くしていきます。画像データがpic_0.pngとして保存されているとすると

```python
from PIL import Image 
img = Image.open(f'pic_0.png')
img = img.resize((300, 300))
```
(300, 300, 4)まで落とすことができました。これを全データで行いました。ここから、訓練データとテストデータを作っていきます。画像１年分は１月から365日分12月までです。また、冬を0、春を1、夏を2、秋を3としてラベルを振り当てます。

```python 
import numpy as np 
from tensorflow.keras.utils import to_categorical

x_all = np.zeros((365*3, 300, 300, 4))
y_all = np.zeros((365*3, 1))

for y in range(3):
  for i in range(365*3):
    k = y * 365
    img = Image.open(f'pic_{i+k}.png')
    img = img.resize((300, 300))
    x_all[i+k, :, :, :] = img
    if i < 59+k:
       y_all[k+d, :] = 0
    elif 59+k <= d < 151+k:
        y_all[k+d, :] = 1
    elif 151+k <= d < 243+k:
        y_all[k+d, :] = 2
    elif 243+k <= d < 334+k:
        y_all[k+d, :] = 3
    else:
        y_all[k+d, :] = 0

x_test = x_all[:250, :, :, :].astype('float32') / 255
x_train = x_all[250:, :, :, :].astype('float32') / 255
y_test = to_categorical(y_all[:250, :], 4)
y_train = to_categorical(y_all[250:, :], 4)

np.savez(f'season_data.npz', x_train=x_train, x_test=x_test, y_train=y_train, y_test=y_test)
```
訓練するたびにこれを実行するのはめんどくさいので保存。これで準備ができました。

###識別機に放り込む

識別機自体は以前作成したのでこちらから

[cifar10のkerasでの分類にチャレンジ]
https://qiita.com/white_coffee/items/bda12686ff490934caed

コード以下の通りです。

```python 
from tensorflow.keras.models import Model 
from tensorflow.keras.optimizers import Adam 
from tensorflow.keras.layers import Input, Dense, BatchNormalization, Dropout, Conv2D, LeakyReLU, Flatten

class discri():
    def __init__(self, input_dim, fil, ker, stri, use_batch, use_dropout, lr):
        self.input_dim = input_dim 
        self.fil = fil 
        self.ker = ker 
        self.stri = stri 
        self.name = 'discri'
        self.n_layers = len(fil)
        self.use_batch = use_batch
        self.use_dropout = use_dropout
        self.lr = lr 
        
        self.build()
    
    def build(self):
        input_layer = Input(shape=self.input_dim)
        x = input_layer
        for i in range(self.n_layers):
            conv = Conv2D(filters=self.fil[i], kernel_size=self.ker[i], strides=self.stri[i], 
            padding='same', name='conv_'+str(i))
            x = conv(x)
            if self.use_batch:
                x = BatchNormalization(momentum=self.use_batch)(x)
            x = LeakyReLU()(x)
            if self.use_dropout:
                x = Dropout(rate=0.5)(x)
        x = Flatten()(x)
        x = Dense(units=4, activation='sigmoid')(x)
        output_layer = x
        self.model = Model(input_layer, output_layer)
    
    def compile(self, lr):
        self.model.compile(optimizer=Adam(lr=lr), loss='categorical_crossentropy', metrics=['accuracy'])
    
    def train(self, data1, data2, batch_size, epochs):
        self.model.fit(data1, data2, batch_size=batch_size, epochs=epochs, shuffle='True')

```
各値を以下のように設定。

```python
indim = (300, 300, 4)
filter = [300, 600, 600, 300]
kernel = [3, 3, 3, 3]
stri = [3, 3, 3, 2]
usebatch = None
usedrop = True 
Lr = 0.0005 
Epochs = 10 
Batchsize = 32
```
最初の入力の次元、フィルターの数を変更。動物の顔等と違いそこまで細かく見なくても大丈夫かなと思いストライドは3と2でやってみることに、Batchsizeは前回のまま変え忘れました。

```python 
import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.image as mpimg 
out = np.load(f'season_data.npz')
x_train = out['x_train']
x_test = out['x_test']
y_train = out['y_train']
y_test = out['y_test']

DISCRI = discri(input_dim=indim, fil=filter, ker=kernel, stri=stri, \
    use_batch=usebatch, use_dropout=usedrop, lr=Lr)
DISCRI.model.summary()
DISCRI.compile(lr=Lr)

DISCRI.train(data1=x_train, data2=y_train, batch_size=Batchsize, epochs=Epochs)
```
データを呼び出しいざ実行。
![Screenshot from 2022-01-23 15-44-26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2499154/bb4e0edb-27fd-8ec7-c048-457b40a14278.png)

確かめてみましょう。

```python 
y = DISCRI.model.predict(x_test)
season_list = {0:'winter', 1:'spring', 2:'summer', 3:'autumn'}
y.shape
fig = plt.figure(1, figsize=(20, 10), dpi=300, facecolor='white')
ax = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
for i in range(10):
    n = 24 * i
    img = x_test[n, :, :, :]
    name = season_list[np.argmax(y[n, :])]
    name2 = season_list[np.argmax(y_test[n, :])]
    ax[i] = fig.add_subplot(2, 5, i+1)
    ax[i].axis('off')
    ax[i].set_title('pred={0:}, actual={1:}'.format(name, name2))
    ax[i].imshow(img)
 
plt.show()
```
![season_pred.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2499154/5dcbd48f-6aa8-5d1d-6f34-331a96ef4f7a.png)

いい感じ予測できてる気がしますが、やはり季節の変わり目、そして秋と春の違いは難しいみたいですね。やっぱkeras 楽しい。

###参考文献

[生成Deep Learning]
・David foster 著　
・松田晃一、小沼千絵　訳
・オライリージャパン
・2020年発行