アメダスのデータで遊んでみる～その３

[前回の記事](https://qiita.com/tubarin/items/cd70989f3e1f387a90a8)の続きです。

アメダスのデータから、とりあえず自前のニューラルネットで回帰分析っぽいことが出来ました。
今回は同じことをkerasを用いてみようと思います。
この記事は、kerasの勉強した結果内容のメモという感じになります。

最初にやったのはkerasのインストール。
バージョンがかみ合わず、エラーでまくったりと若干はまりましたが、何とか動いてくれました。
最終的には以下のバージョンで動いています。

python:3.6.19
tensorflow:1.14.0
keras:2.2.0


そして、色々使い方を調べて、kerasを用いたコードを作ってみました。
利用したのは、Sequentialというオブジェクトです。
[こちら](https://keras.io/ja/getting-started/sequential-model-guide/)の公式サイトで、詳細の仕様が説明されていますが、便利そうです。

そして、使い方は以下の記事を参考にさせていただきました。
[Kerasによる、ものすごくシンプルな深層学習の例](https://qiita.com/hiroeorz@github/items/33b85529be0829f34973)

この記事によると、以下の簡単なコードで全部ニューラルネットワークが組めるそうです。
(x(1層) -> 32層 -> y(1層)の場合)

```python
from keras.models import Sequential
from keras.layers import Activation, Dense

# 学習のためのモデルを作る
model = Sequential()
# 全結合層(1層->32層)
model.add(Dense(input_dim=1, output_dim=32, bias=True))
# 活性化関数(Sigmoid関数)
model.add(Activation("sigmoid"))

# 全結合層(32層->1層)
model.add(Dense(output_dim=1))
# モデルをコンパイル
model.compile(loss="mean_squared_error", optimizer="sgd", metrics=["accuracy"])
# 学習を実行
model.fit(x, y, nb_epoch=1000, batch_size=32)
```

これだけか！？素晴らしいです。
ニューラルネットのモデルをちょっと定義させてから、コンパイルすると、多分各種パラメタの初期化やら学習プログラムやらが勝手に定義されていくんだと思われます。
そして、fitメソッドに学習用データ(インプットと正解)を入れることで学習するようでした。

というわけで、アメダスのデータ分析用のものを突っ込んでみます。
一応全部のコードを載せます、無駄に長いです･･･。

```python
import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers import Activation, Dense


# deta making???
csv_input = pd.read_csv(filepath_or_buffer="data_out.csv",
                        encoding="ms932",
                        sep=",")

# インプットの項目数（行数 * カラム数）を返却します。
print(csv_input.size)

# 指定したカラムだけ抽出したDataFrameオブジェクトを返却します。
x = np.array(csv_input[["hour"]])
y = np.array(csv_input[["wind"]])

# num of records
N = len(x)

# 正規化
x_max = np.max(x,axis=0)
x_min = np.min(x,axis=0)
y_max = np.max(y,axis=0)
y_min = np.min(y,axis=0)
x = (x - np.min(x,axis=0))/(np.max(x,axis=0) - np.min(x,axis=0))
y = (y - np.min(y,axis=0))/(np.max(y,axis=0) - np.min(y,axis=0))

# 学習のためのモデルを作る
model = Sequential()
# 全結合層(1層->XXX層)
model.add(Dense(input_dim=1, output_dim=32, bias=True))
# 活性化関数(Sigmoid関数)
model.add(Activation("sigmoid"))

# 全結合層(XXX層->1層)
model.add(Dense(output_dim=1))
# モデルをコンパイル
model.compile(loss="mean_squared_error", optimizer="sgd", metrics=["accuracy"])
# 学習を実行
model.fit(x, y, nb_epoch=1000, batch_size=32)

# 真値のプロット
plt.plot(x,y,marker='x',label="true")
# 推論でKerasの結果を計算,表示
y_predict = model.predict(x)
# Keras計算結果のプロット
plt.plot(x,y_predict,marker='x',label="predict")
# 凡例表示
plt.legend()

```

コンソール上の結果を見ると、途中経過がいい感じに入ってます。

```
(中略)
23/23 [==============================] - 0s 0us/step - loss: 0.0563 - acc: 0.0435
Epoch 994/1000
23/23 [==============================] - 0s 435us/step - loss: 0.0563 - acc: 0.0435
Epoch 995/1000
23/23 [==============================] - 0s 0us/step - loss: 0.0563 - acc: 0.0435
Epoch 996/1000
23/23 [==============================] - 0s 0us/step - loss: 0.0563 - acc: 0.0435
Epoch 997/1000
23/23 [==============================] - 0s 0us/step - loss: 0.0563 - acc: 0.0435
Epoch 998/1000
23/23 [==============================] - 0s 0us/step - loss: 0.0563 - acc: 0.0435
Epoch 999/1000
23/23 [==============================] - 0s 0us/step - loss: 0.0563 - acc: 0.0435
Epoch 1000/1000
23/23 [==============================] - 0s 435us/step - loss: 0.0563 - acc: 0.0435
```

なんだか上手く動いてそうな雰囲気ですが、結果のグラフを見てみると???

![Figure 2020-04-30 203427.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607345/06ae6fe1-8726-da92-23c7-769f2b0100ca.png)

うーむ、以前やったときのような、単なる近似曲線に落ち着いてしまいました。
中間層の数をいじってみたりと色々やってみましたが、大きな変化はなし。
前回の結果を見ると、ニューラルネットワーク内の係数の初期値さえ上手く設定できれば、もうちょっと値を捉えることが出来そうでした。
そこで、ニューラルネットワークの初期値設定用のClassを設計してみました。
係数計算部分は前回のを使っています(コメントなどを増やしています)
中間層の2つのノード???で1つのステップ関数を作っているような感じです。

```python
# init infomation for keras layers or models
class InitInfo:
    
    # constractor
    #  x:input y:output
    def __init__(self,x,y):
        self.x = x
        self.y = y
        
    # calc coefficient of keras models(1st layer)
    # input  s:changing point in [0,1]
    #        sign:[1]raise,[0]down
    # return b:coefficient of bias
    #        w:coefficient of x
    # notice - it can make like step function using this return values(s,sign)
    def calc_b_w(self,s,sign):
    
        N = 1000 # 仮置き
        # s = -b/w
        if sign > 0:
            b = -N
        else:
            b = N
        if s != 0:
            w = -b/s
        else:
            w = 1
        return b,w
    
    # calc coefficient of keras models(1st and 2nd layer)
    def calc_w_h(self):
    
        K = len(self.x)
        # coefficient of 1st layer(x,w)
        w_array = np.zeros([K*2,2])
        # coefficient of 2nd layer
        h_array = np.zeros([K*2,1])
        
        w_idx = 0
        for k in range(K):
            # x[k] , y[k]
            # make one step function
            # startX : calc raise point in [0,1]
            if k > 0:
                startX = self.x[k] +  (self.x[k-1] - self.x[k])/2
            else:
                startX = 0
    
            # endX : calc down point in [0,1]
            if k < K-1:
                endX = self.x[k] + (self.x[k+1] - self.x[k])/2
            else:
                endX = 1
    
            # calc b,w
            if k > 0:
                b,w = self.calc_b_w(startX,1)
            else:
                # init???
                b = 100
                w = 1
    
            # stepfunction 1stHalf
            #            __________
            # 0 ________|
            #        
            w_array[w_idx,0] = w
            w_array[w_idx,1] = b
            h_array[w_idx,0] = self.y[k]
            w_idx += 1
            
            # stepfunction 2ndHalf
            #        
            # 0 __________
            #             |________
            b,w = self.calc_b_w(endX,1)
            w_array[w_idx,0] = w
            w_array[w_idx,1] = b
            h_array[w_idx,0] = self.y[k]*-1
            
            # shape of 1st + 2nd is under wave
            #            _
            # 0 ________| |________
            #
            
            w_idx += 1
        
        # record param
        self.w = w_array
        self.h = h_array
        self.w_init = w_array[:,0]
        self.b_init = w_array[:,1]
        self.paramN = len(h_array)
        return
    
    # for bias coefficients　setting
    def initB(self, shape, name=None):
        #L = np.prod(shape)
        #value = np.random.randn(L).reshape(shape)*5
        value = self.b_init
        value = value.reshape(shape)
        return K.variable(value, name=name)

    # for w coefficients (x) setting
    def initW(self, shape, name=None):
        #L = np.prod(shape)
        #value = np.random.random(shape)
        #value = np.random.randn(L).reshape(shape)*5
        value = self.w_init
        value = value.reshape(shape)
        return K.variable(value, name=name)
    
    # for h coefficients setting
    def initH(self, shape, name=None):
        #L = np.prod(shape)
        #value = np.random.randn(L).reshape(shape)*1
        value = self.h
        value = value.reshape(shape)
        return K.variable(value, name=name)

```

initB(self, shape, name=None):
initW(self, shape, name=None):
initH(self, shape, name=None):
の3つのメソッドは、Denseオブジェクトの係数設定用の関数に使うためのものです。
一部コメントアウトしているのは、完全に乱数を突っ込んだときのものです(研究、デバッグ用途)。

中間層のノードの数も、このInitInfoオブジェクトのメンバーから取ることで、学習部分などは以下のようなコードに変化すれば動きそうです。

```python
# create InitInfo object
objInitInfo = InitInfo(x,y)
# calc init value of w and h(and bias)
objInitInfo.calc_w_h()

# 学習のためのモデルを作る
model = Sequential()
# 全結合層(1層->XXX層)
model.add(Dense(input_dim=1, output_dim=objInitInfo.paramN,
                bias=True,
                kernel_initializer=objInitInfo.initW,
                bias_initializer=objInitInfo.initB))
# 活性化関数(Sigmoid関数)
model.add(Activation("sigmoid"))

# 全結合層(XXX層->1層)
model.add(Dense(output_dim=1, kernel_initializer=objInitInfo.initH))
# モデルをコンパイル
model.compile(loss="mean_squared_error", optimizer="sgd", metrics=["accuracy"])
# 学習を実行
model.fit(x, y, nb_epoch=1000, batch_size=32)
```

初期値設定オプション名などは、[Keras公式サイト](https://keras.io/ja/initializations/)を参考にしました。
これを利用したコード全体を以下に貼り付けてみます。

```python:sample_KerasNewral.py
import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers import Activation, Dense
from keras import backend as K


# init infomation for keras layers or models
class InitInfo:
    
    # constractor
    #  x:input y:output
    def __init__(self,x,y):
        self.x = x
        self.y = y
        
    # calc coefficient of keras models(1st layer)
    # input  s:changing point in [0,1]
    #        sign:[1]raise,[0]down
    # return b:coefficient of bias
    #        w:coefficient of x
    # notice - it can make like step function using this return values(s,sign)
    def calc_b_w(self,s,sign):
    
        N = 1000 # 仮置き
        # s = -b/w
        if sign > 0:
            b = -N
        else:
            b = N
        if s != 0:
            w = -b/s
        else:
            w = 1
        return b,w
    
    # calc coefficient of keras models(1st and 2nd layer)
    def calc_w_h(self):
    
        K = len(self.x)
        # coefficient of 1st layer(x,w)
        w_array = np.zeros([K*2,2])
        # coefficient of 2nd layer
        h_array = np.zeros([K*2,1])
        
        w_idx = 0
        for k in range(K):
            # x[k] , y[k]
            # make one step function
            # startX : calc raise point in [0,1]
            if k > 0:
                startX = self.x[k] +  (self.x[k-1] - self.x[k])/2
            else:
                startX = 0
    
            # endX : calc down point in [0,1]
            if k < K-1:
                endX = self.x[k] + (self.x[k+1] - self.x[k])/2
            else:
                endX = 1
    
            # calc b,w
            if k > 0:
                b,w = self.calc_b_w(startX,1)
            else:
                # init???
                b = 100
                w = 1
    
            # stepfunction 1stHalf
            #            __________
            # 0 ________|
            #        
            w_array[w_idx,0] = w
            w_array[w_idx,1] = b
            h_array[w_idx,0] = self.y[k]
            w_idx += 1
            
            # stepfunction 2ndHalf
            #        
            # 0 __________
            #             |________
            b,w = self.calc_b_w(endX,1)
            w_array[w_idx,0] = w
            w_array[w_idx,1] = b
            h_array[w_idx,0] = self.y[k]*-1
            
            # shape of 1st + 2nd is under wave
            #            _
            # 0 ________| |________
            #
            
            w_idx += 1
        
        # record param
        self.w = w_array
        self.h = h_array
        self.w_init = w_array[:,0]
        self.b_init = w_array[:,1]
        self.paramN = len(h_array)
        return
    
    # for bias coefficients　setting
    def initB(self, shape, name=None):
        #L = np.prod(shape)
        #value = np.random.randn(L).reshape(shape)*5
        value = self.b_init
        value = value.reshape(shape)
        return K.variable(value, name=name)

    # for w coefficients (x) setting
    def initW(self, shape, name=None):
        #L = np.prod(shape)
        #value = np.random.random(shape)
        #value = np.random.randn(L).reshape(shape)*5
        value = self.w_init
        value = value.reshape(shape)
        return K.variable(value, name=name)
    
    # for h coefficients setting
    def initH(self, shape, name=None):
        #L = np.prod(shape)
        #value = np.random.randn(L).reshape(shape)*1
        value = self.h
        value = value.reshape(shape)
        return K.variable(value, name=name)

 
# deta making???
csv_input = pd.read_csv(filepath_or_buffer="data_out.csv",
                        encoding="ms932",
                        sep=",")

# インプットの項目数（行数 * カラム数）を返却します。
print(csv_input.size)

# 指定したカラムだけ抽出したDataFrameオブジェクトを返却します。
x = np.array(csv_input[["hour"]])
y = np.array(csv_input[["wind"]])

# num of records
N = len(x)

# 正規化
x_max = np.max(x,axis=0)
x_min = np.min(x,axis=0)
y_max = np.max(y,axis=0)
y_min = np.min(y,axis=0)
x = (x - np.min(x,axis=0))/(np.max(x,axis=0) - np.min(x,axis=0))
y = (y - np.min(y,axis=0))/(np.max(y,axis=0) - np.min(y,axis=0))

# create InitInfo object
objInitInfo = InitInfo(x,y)
# calc init value of w and h(and bias)
objInitInfo.calc_w_h()

# 学習のためのモデルを作る
model = Sequential()
# 全結合層(1層->XXX層)
model.add(Dense(input_dim=1, output_dim=objInitInfo.paramN,
                bias=True,
                kernel_initializer=objInitInfo.initW,
                bias_initializer=objInitInfo.initB))
# 活性化関数(Sigmoid関数)
model.add(Activation("sigmoid"))

# 全結合層(XXX層->1層)
model.add(Dense(output_dim=1, kernel_initializer=objInitInfo.initH))
# モデルをコンパイル
model.compile(loss="mean_squared_error", optimizer="sgd", metrics=["accuracy"])
# 学習を実行
model.fit(x, y, nb_epoch=1000, batch_size=32)

# 真値のプロット
plt.plot(x,y,marker='x',label="true")
# 推論でKerasの結果を計算,表示
y_predict = model.predict(x)
# Keras計算結果のプロット
plt.plot(x,y_predict,marker='x',label="predict")
# 凡例表示
plt.legend()

```

結果のグラフを見てみると???

![Figure 2020-04-30 204804.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607345/5a99968b-fb7f-b11f-830f-a923df5bf8fe.png)

ほぼほぼ一致してくれました！
前回同様、過学習状態だとは思いますが、敢えてこれをやりたかったので、うまくいったようなもんです。

hの初期値を完全に乱数(正規分布)にしたときには???

![Figure 2020-04-30 205037.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607345/62af426f-3cc0-5908-2bfb-0ab91f51cc36.png)

N=1000だと収束が甘かったので、ちょっと増やすと上のようになりました。
結構いい感じです。
これに対して、w,bを完全に乱数に設定すると???

![Figure 2020-04-30 205237.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607345/5d2f465d-a6e4-0d3c-1ccb-da2f8276fbd6.png)

結構ざっくりとした曲線っぽくなりそうです。
初期値が乱数なので、実行するたびに若干変わりそうです。



こんな感じで、kerasを利用しても、初期値をかなり自由に選択でき、それによって挙動が変わるようでした。
興味深い内容ですね。

今回はkerasの使い方の勉強結果のメモということで書いてきました。
まだ色々コールバックなど楽しそうな機能もあるので追々みていくとして、次は分類系のことを見ていこうと思います。