Help us understand the problem. What is going on with this article?

【Keras入門(5)】単純なRNNモデル定義

More than 1 year has passed since last update.

入門者に向けてKerasを使ったRNN(Recurrentニューラルネットワーク)の初歩を解説します。RNNは時系列データの予測やNLP(自然言語処理)などに強く、使いどころが多い便利な手法です。
Google Colaboratoryを使っているのでローカルでの環境準備すらしていません。Google Colaboratoryについては「Google Colaboratory概要と使用手順(TensorFlowもGPUも使える)」の記事を参照ください。

以下のシリーズにしています。

- 【Keras入門(1)】単純なディープラーニングモデル定義
- 【Keras入門(2)】訓練モデル保存(KerasモデルとSavedModel)
- 【Keras入門(3)】TensorBoardで見える化
- 【Keras入門(4)】Kerasの評価関数(Metrics)
- 【Keras入門(5)】単純なRNNモデル定義 <- 本記事
- 【Keras入門(6)】単純なRNNモデル定義(最終出力のみ使用)
- 【Keras入門(7)】単純なSeq2Seqモデル定義

使ったPythonパッケージ

Google Colaboratoryでインストール済の以下のパッケージとバージョンを使っています。KerasはTensorFlowに統合されているものを使っているので、ピュアなKerasは使っていません。Pythonは3.6です。

  • tensorflow: 1.14.0
  • Numpy: 1.16.4
  • matplotlib: 3.0.3

処理概要

時系列データ予測として、正弦関数(sine)の値を予測します。
正弦関数は、こんなウネウネした値が変わっていくやつです。
image.png

以下の2種類の配列を作って、Kerasを使ったディープラーニングで予測モデルを作ります。

種類 Shape データ
説明変数(配列x_train) 10 X 40 xが-4.9から4.9までの0.2間隔の正弦関数の値
目的変数(配列y_train) 10 X 40 説明変数の1ずれた値

言葉だとわかりにくいですが、表とグラフにするとこんな感じ。目的変数の次の値を予測しています(つまり時系列データ予測)。そして、2つ目のデータセットでは、1つ目のデータセットから1つずつずらしています。!
50.Keras_RNN_list01.JPG

そして、訓練時のRNNへのデータの渡し方は下記のような形です。
51.Keras_RNN_Overview01.JPG

でもって、予測時は下記のように予測結果を次の予測データ元へ渡します。モデルが予測した値を再度使うあたりが再帰(Recurrent)たる所以ですね。
image.png

処理プログラム

プログラム全体はGitHubを参照ください。

1. ライブラリインポート

今回はmatplotlibとnumpyとtensorflowに統合されているkerasを使います。ピュアなkerasでも問題なく、インポート元を変えるだけです。

import numpy as np
import matplotlib.pyplot as plt

# TensorFlowに統合されたKerasを使用
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, SimpleRNN

2. 前処理

2.1. 正弦関数配列作成

-4.9から4.9までの50要素の等差数列(0.2間隔)の配列(x_sin)を作成し、対応する正弦関数の値の配列(y_sin)も
作成します。

# -4.9から4.9までの50要素の等差数列(0.2間隔)
x_sin = np.linspace(-4.9, 4.9)
y_sin = np.sin(x_sin)
plt.plot(x_sin, y_sin)
plt.show()

matplotlibでグラフ出力するとこんなです。
image.png

2.2. 説明変数(x_train)と目的変数(y_train)作成

RNNに流すための形に整形します。

NUM_RNN = 10  # 1時系列のデータ数
NUM_DATA = len(x_sin) - NUM_RNN # 今回は40(=50-10)
x = []
y = []

for i in range(NUM_DATA):
    x.append(y_sin[i:i+NUM_RNN])      # 説明変数
    y.append(y_sin[i+1:i+NUM_RNN+1])  # 正解データなので1ずらした値

else:
    x_train = np.array(x).reshape(NUM_DATA, NUM_RNN, 1) # 入力を(サンプル数、時系列の数、入力層のニューロン数)にする
    y_train = np.array(y).reshape(NUM_DATA, NUM_RNN, 1) # 説明変数(x_train)と同様のshape

3. モデル定義

記事「【Keras入門(1)】単純なディープラーニングモデル定義」のときと違い、simpleRNNを使っています。実際にはLSTMやGRUなどを使うことが多いかと思いますが、今回はsimpleRNNで十分な精度が出ます。また、LSTMやGRUを使う場合も呼び出し方はほとんど変わりません。

NUM_DIM = 8  # 中間層の次元数

model = Sequential()

# return_sequenceがTrueなので全RNN層が出力を返す(Falseだと最後のRNN層のみが出力を返す)
model.add(SimpleRNN(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))
model.add(Dense(1, activation="linear"))  #全結合層
model.compile(loss="mean_squared_error", optimizer="sgd")
model.summary()

summary関数で以下のようなモデルサマリを出してくれます。

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
simple_rnn (SimpleRNN)       (None, 10, 8)             80        
_________________________________________________________________
dense (Dense)                (None, 10, 1)             9         
=================================================================
Total params: 89
Trainable params: 89
Non-trainable params: 0
_________________________________________________________________

4. 訓練実行

fit関数を使って訓練実行です。20epoch程度でそこそこいい精度が出ます。

history = model.fit(x_train, y_train, epochs=20, batch_size=8)

# Lossをグラフ表示
loss = history.history['loss']
plt.plot(np.arange(len(loss)), loss) # np.arangeはlossの連番数列を生成(今回はepoch数の0から19)
plt.show()

image.png

5. テスト

最後にテストです。

5.1. テストデータ作成

訓練データの最初の10件をテストデータとします。

# x[0]は最初の入力(時系列10個の数)。reshape(-1)で一次元のベクトルにする。
x_test = x_train[0].reshape(-1)  

4.2. 訓練済モデルを使ったテストデータの答え合わせ

predict関数を使ってテストデータから予測値を出力し、さらに次の予測の元データとします。

# データ数(40回)ループ
for i in range(NUM_DATA):
    y_pred = model.predict(x_test[-NUM_RNN:].reshape(1, NUM_RNN, 1))  # 直近データ(最後から10要素)を使って予測
    x_test = np.append(x_test, y_pred[0][NUM_RNN-1][0])  # 出力結果をx_testに追加(n_rnn-1が10番目を意味している)

# 最初の10要素は完全に同じ
plt.plot(x_sin, y_sin, label="Training data")
plt.plot(x_sin, x_test, label="Predicted")
plt.legend()
plt.show()

予測値と訓練データを比較でグラフ表示してみます。-4.9から-2.9までの値は完全に同じです。それ以降は、微妙にずれてはいますが、それっぽい値が出ています。
image.png

簡単な変更でモデルを複雑に変更

simpleRNNではなく、実務上はLSTMを使うことが多いはずです。
その場合、モデルの層をこんな感じにするだけです。
GPU向けのCuDNNLSTMもあります(確かモデル間の互換性がなかったような気がします(未確認))。

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, LSTM

model.add(LSTM(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))

GRUだとこう。GPU向けのCuDNNGRUもあります(確かモデル間の互換性がなかったような気がします(未確認))。

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, GRU

model.add(GRU(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))

双方向にしたい場合はBidirectionalを使います。

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, simpleRNN, Bidirectional

model.add(Bidirectional(SimpleRNN(NUM_DIM, return_sequences=True), input_shape=(NUM_RNN, 1)))

複数層に重ねることもできます。

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, simpleRNN

model.add(SimpleRNN(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))
model.add(SimpleRNN(NUM_DIM, input_shape=(NUM_RNN, 1), return_sequences=True))
FukuharaYohei
気の向いたままにいろいろと書きます。 仕事はSAP関連で、HANA、Fiori、SAPUI5、BusinessObjectsなどいろいろやっています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away