【LSTM】Sin波から減衰振動系のフィッティングから株価。。。♬

Keras　Exampleにあるように。。。記事されまくりなので詳細は控えるが、とにかく特定の関数を導入しなくともそれなりにフィッティングできるのはやはりすごい。

ということでLSTMについて記載したいと思う。
【参考】※【追記】Exampleのリンク先が間違っていました。変更しました。
・[NVIDIA/keras/examples/lstm_stateful.py](https://github.com/NVIDIA/keras/blob/master/examples/stateful_lstm.py) 

###やったこと
（１）代表的なコードの解説
（２）Sin波のフィッティング
（３）ノイズがある減衰振動系のフィッティング
（４）応用として株価フィッティング
###（１）代表的なコードの解説
まず代表的なコードはおまけに記載したとおり
ここでは主な流れを解説する
まず、被フィッティング関数を定義する。
※一般論はたとえば株価など適当な時系列データを定義すればよい（csvファイルやjsonファイルなどを読み込む）。

```PY
def gen_cosine_amp(amp=100, period=2000, x0=0, xn=50000, step=1, k=0.0001):
    cos = np.zeros(((xn - x0) * step, 1, 1))
    for i in range(len(cos)):
        idx = x0 + i * step
        cos[i, 0, 0] = amp * (np.cos(2 * np.pi * idx / period)
    return cos
print('Generating Data...')
cos = gen_cosine_amp()
```
ちなみに、減衰係数kは減衰振動系のとき利用するので、ここでは使わない。
次に、上記被フィッティング関数を（lahead+1）平均計算し、expected_outputに代入する。

```PY
expected_output = np.zeros((len(cos), 1))
for i in range(len(cos) - lahead):
    expected_output[i, 0] = np.mean(cos[i + 1:i + lahead + 1])

print('Output shape:', expected_output.shape)
```
Modelを作成します。
一応、ここではLSTM二回回してますが、最初の一回でもフィッティングはできます。
※二回の方が精度がいいとウワンは思っている
　計算時間は２個。。２２ｓ／epochに対して１個。。１９ｓ／epoch位である。
　また、return_sequences=Flaseと変更が必要です。
※HiddenLayerを50としているが、これも一定以上の大きさがあればフィッティングできる。
　Keras　Exampleは20としている。
※Optimizerはadamを使った。~~Keras Exampleも今は"adam"だが、かつては'rmsprop'だったと思う。~~もちろん'rmsprop'でもフィッティングできる。これもウワンの趣味でadamがいいと思っている程度です。
※loss関数はフィッティングなので、みなさん"mse"のようです。

```PY
print('Creating Model...')
model = Sequential()
model.add(LSTM(50,
               input_shape=(tsteps, 1),
               batch_size=batch_size,
               return_sequences=True,
               stateful=True))
model.add(LSTM(50,
               return_sequences=False,
               stateful=True))
model.add(Dense(1))
opt=Adam(lr=0.0001, beta_1=0.5, beta_2=0.999, epsilon=1e-08, decay=0.)
model.compile(loss='mse', optimizer=opt)   #'rmsprop')
model.summary()
#model.load_weights('params_model_lstm_epoch_049.hdf5')
```
Trainingはepochs回繰り返せるようになっていますが、１回ごとに結果を見てWeightを保存しています。
※フィッティングが回数かかるときは１０回に１回に変更しています。

```py
print('Training')
for i in range(0,epochs):
    #plt.figure(num=None, figsize=(15, 15), dpi=60)
    print('Epoch', i, '/', epochs)
    # Note that the last state for sample i in a batch will
    # be used as initial state for sample i in the next batch.
    # Thus we are simultaneously training on batch_size series with
    # lower resolution than the original series contained in cos.
    # Each of these series are offset by one step and can be
    # extracted with cos[i::batch_size].

    model.fit(cos, expected_output,
              batch_size=batch_size,
              epochs=1,
              verbose=1,
              shuffle=False)
　　# let's reset the states of the LSTM layer:
    model.reset_states()
    # save weights every epoch
    model.save_weights(
          'params_model_lstm_epoch_{0:03d}.hdf5'.format(i), True)
```
最後は予測を計算して、画像出力します。
今は、１０回に１回の出力にしています。

```py
    #print('Predicting')
    #predicted_output = model.predict(cos, batch_size=batch_size)
    if i%10==0:
        print('Predicting')
        predicted_output = model.predict(cos, batch_size=batch_size)
        model.save_weights(
          'params_model_lstm_epoch_{0:03d}.hdf5'.format(i), True)
        plt.figure(num=None, figsize=(15, 15), dpi=60)
        print('Plotting Results')
        plt.subplot(2, 1, 1)
        plt.plot(expected_output)
        plt.ylim(-120, 120)

        plt.title('Expected')
        plt.subplot(2, 1, 2)
        plt.plot(predicted_output)
        plt.ylim(-120, 120)
        plt.title('Predicted')
        plt.pause(3)
        plt.savefig('plot_epoch_{0:03d}_lstm.png'.format(i), dpi=60)
        plt.close()
    else:
        continue
```        
###（２）Sin波のフィッティング
Cos波だけど、。。。フィッティング結果は以下のようになりました。
![cosLSTM.gif](https://qiita-image-store.s3.amazonaws.com/0/233744/ac1325c5-c03c-32d3-0763-23bb3122a190.gif)
###（３）減衰振動系のフィッティング
変更は以下の関数だけである。
乱数（-0.1,0.1)を追加するとともに、指数関数で減衰させている。

```py
    cos = np.zeros(((xn - x0) * step, 1, 1))
    for i in range(len(cos)):
        idx = x0 + i * step
        cos[i, 0, 0] = amp * (np.cos(2 * np.pi * idx / period)+　0.1*np.random.uniform(-1.0, +1.0))
        cos[i, 0, 0] = cos[i, 0, 0]* np.exp(-k * idx)
    return cos
```
結果は以下の通り、フィッティングできている。
![cosexprnd.gif](https://qiita-image-store.s3.amazonaws.com/0/233744/45a0863d-5c84-0df5-c300-6abe809332d7.gif)
###（４）応用として株価フィッティング
この応用として、ファイルデータ読込で株価フィッティングを以下のように実施しているが、詳細は次回にしたいと思う
![Stock100.gif](https://qiita-image-store.s3.amazonaws.com/0/233744/b8f9dea4-72f8-521a-600f-8c6bc5d506d7.gif)

###まとめ
・LSTMをSin波と減衰振動系に適用した
・ノイズがのっていても綺麗にフィッティングできる

・パラメータは最適化できているとは言えない
・多次元時系列データには応用できていない
・株価データなどファイル読込などの対応は次回予定とする
###おまけ

```py
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense, LSTM
from keras.optimizers import Adam

# since we are using stateful rnn tsteps can be set to 1
tsteps = 1
batch_size = 25
epochs = 1000
# number of elements ahead that are used to make the prediction
lahead = 1

def gen_cosine_amp(amp=100, period=2000, x0=0, xn=50000, step=1, k=0.0001):
    """Generates an absolute cosine time series with the amplitude
    exponentially decreasing

    Arguments:
        amp: amplitude of the cosine function
        period: period of the cosine function
        x0: initial x of the time series
        xn: final x of the time series
        step: step of the time series discretization
        k: exponential rate
    """
    cos = np.zeros(((xn - x0) * step, 1, 1))
    for i in range(len(cos)):
        idx = x0 + i * step
        cos[i, 0, 0] = amp * (np.cos(2 * np.pi * idx / period)+0.1*np.random.uniform(-1.0, +1.0))
        #cos[i, 0, 0] = amp * (np.cos(2 * np.pi * idx / period))
        cos[i, 0, 0] = cos[i, 0, 0]* np.exp(-k * idx)
    return cos

print('Generating Data...')
cos = gen_cosine_amp()
print('Input shape:', cos.shape)

expected_output = np.zeros((len(cos), 1))
for i in range(len(cos) - lahead):
    expected_output[i, 0] = np.mean(cos[i + 1:i + lahead + 1])

print('Output shape:', expected_output.shape)

print('Creating Model...')
model = Sequential()
model.add(LSTM(50,
               input_shape=(tsteps, 1),
               batch_size=batch_size,
               return_sequences=True,
               stateful=True))
model.add(LSTM(50,
               return_sequences=False,
               stateful=True))
model.add(Dense(1))
opt=Adam(lr=0.0001, beta_1=0.5, beta_2=0.999, epsilon=1e-08, decay=0.)
model.compile(loss='mse', optimizer=opt)   #'rmsprop')
model.summary()
#model.load_weights('params_model_lstm_epoch_049.hdf5')

print('Training')
for i in range(0,epochs):
    #plt.figure(num=None, figsize=(15, 15), dpi=60)
    print('Epoch', i, '/', epochs)

    # Note that the last state for sample i in a batch will
    # be used as initial state for sample i in the next batch.
    # Thus we are simultaneously training on batch_size series with
    # lower resolution than the original series contained in cos.
    # Each of these series are offset by one step and can be
    # extracted with cos[i::batch_size].

    model.fit(cos, expected_output,
              batch_size=batch_size,
              epochs=1,
              verbose=1,
              shuffle=False)
    model.reset_states()
    # save weights every epoch
    #model.save_weights(
    #      'params_model_lstm_epoch_{0:03d}.hdf5'.format(i), True)
    
    if i%10==0:
        print('Predicting')
        predicted_output = model.predict(cos, batch_size=batch_size)
        model.save_weights(
          'params_model_lstm_epoch_{0:03d}.hdf5'.format(i), True)
        plt.figure(num=None, figsize=(15, 15), dpi=60)
        print('Plotting Results')
        plt.subplot(2, 1, 1)
        plt.plot(expected_output)
        plt.ylim(-120, 120)

        plt.title('Expected')
        plt.subplot(2, 1, 2)
        plt.plot(predicted_output)
        plt.ylim(-120, 120)
        plt.title('Predicted')
        plt.pause(3)
        plt.savefig('plot_epoch_{0:03d}_lstm.png'.format(i), dpi=60)
        plt.close()
    else:
        continue
```