RNN (リカレント ニューラル ネットワーク) チュートリアル: TensorFlow の例
⚡ スマートサマリー
リカレントニューラルネットワークは、通常のニューロンに記憶状態を追加し、各出力を次の入力としてフィードバックすることで、モデルが順序から学習できるようにします。このチュートリアルでは、TensorFlowでリカレントニューラルネットワークを構築し、時系列データを予測します。
なぜリカレント ニューラル ネットワーク (RNN) が必要なのでしょうか?
リカレント ニューラル ネットワーク (RNN) を使用すると、メモリ ユニットをモデル化してデータを永続化し、短期的な依存関係をモデル化できます。 データの相関関係やパターンを特定するための時系列予測にも使用されます。 また、人間の脳と同様の動作を提供することで、連続データの予測結果を生成するのにも役立ちます。
の構造 人工ニューラルネットワーク これは比較的単純なもので、主に行列乗算に関するものです。最初のステップでは、入力に初期状態でランダムな重みとバイアスを乗算し、活性化関数で変換した後、出力値を使用して予測を行います。このステップにより、ネットワークが現実からどれだけ離れているかを把握できます。
適用されるメトリックは損失です。損失関数が高いほど、モデルは愚かになります。ネットワークの知識を向上させるには、ネットの重みを調整して最適化する必要があります。確率的勾配降下法は、重みの値を正しい方向に変更するために採用される方法です。調整が完了すると、ネットワークは別のデータ バッチを使用して新しい知識をテストできます。
幸いなことに、誤差は以前より小さくなりましたが、まだ十分小さくはありません。最適化ステップは、誤差が最小化されるまで、つまり、これ以上情報を追加できなくなるまで繰り返し実行されます。tracテッド。
このタイプのモデルの問題は、メモリがまったくないことです。 それは入力と出力が独立していることを意味します。 言い換えれば、モデルは以前のものを気にしません。 ネットワークには履歴データや過去の単語に関する情報が必要であるため、時系列や文章を予測する必要がある場合、いくつかの疑問が生じます。
この問題を克服するために、新しいタイプのアーキテクチャが開発されました。リカレントニューラルネットワーク(以下RNN)
リカレント ニューラル ネットワーク (RNN) とは何ですか?
リカレントニューラルネットワーク(RNN)は、人工ニューラルネットワークの一種で、異なるノード間の接続が有向グラフを形成し、時間的な動的な挙動を実現します。フィードフォワードネットワークから得られる時系列データのモデリングに役立ち、人間の脳と同様の働きで予測結果を提供します。
リカレント ニューラル ネットワークは、記憶状態がニューロンに追加される点を除けば、従来のニューラル ネットワークと非常によく似ています。 メモリを含める計算は簡単です。
データのバッチによって入力されるニューロンが1つだけのシンプルなモデルを想像してみてください。従来のニューラルネットワークでは、モデルは入力に重みと活性化関数を乗算して出力を生成します。RNNでは、この出力は自身に何回も送り返されます。 タイムステップ 出力が次の行列乗算の入力となるまでの時間。
たとえば、下の図では、ネットワークが 1 つのニューロンで構成されていることがわかります。 ネットワークは入力と重みの間の行列の乗算を計算し、アクティベーション関数による非線形性を追加します。 t-XNUMXの出力となります。 この出力は、XNUMX 番目の行列乗算の入力になります。
リカレントニューラルネットワーク(RNN)
以下では、ステップと出力の形状を理解するために TensorFlow で単純な RNN をコーディングします。
ネットワークは次のもので構成されます。
- XNUMX つの入力
- XNUMXつのニューロン
- 2タイムステップ
ネットワークは下の図のように進みます。
このネットワークは、各アクティブ スクエアで同じ操作を実行するため、「再帰的」と呼ばれます。ネットワークは、アクティブ化関数を使用する前に、入力と前の出力の重みを計算します。
バージョンノート: このチュートリアルのコードは、TensorFlow 1.x グラフ API を対象としています。TensorFlow 2.x では、同じスクリプトが引き続き実行されます。 import tensorflow.compat.v1 as tf さらに tf.disable_v2_behavior()プレースホルダー、セッション、rnn_cell モジュールは現在、 tf.compat.v12.x の慣用的な同等表現は次のとおりです。 tf.keras.layers.SimpleRNN.
import numpy as np import tensorflow as tf n_inputs = 4 n_neurons = 6 n_timesteps = 2 The data is a sequence of a number from 0 to 9 and divided into three batches of data. ## Data X_batch = np.array([ [[0, 1, 2, 5], [9, 8, 7, 4]], # Batch 1 [[3, 4, 5, 2], [0, 0, 0, 0]], # Batch 2 [[6, 7, 8, 5], [6, 5, 4, 2]], # Batch 3 ])
データ、反復ステージ、出力用のプレースホルダーを使用してネットワークを構築できます。
データのプレースホルダーを定義する
X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs])
ここに:
- なし: 不明であり、バッチのサイズが必要になります
- n_timesteps: ネットワークが出力をニューロンに送り返す回数
- n_inputs: バッチごとの入力数
リカレントネットワークを定義する
上の図で述べたように、ネットワークは 6 つのニューロンで構成されています。 ネットワークは XNUMX つのドット積を計算します。
- 最初の重みセット (つまり、6: ニューロンの数に等しい) を持つ入力データ
- 2番目の重みセット(つまり、6:出力数に対応)を使用した前回の出力
最初のフィードフォワード中、使用可能な値がないため、前の出力の値はゼロに等しいことに注意してください。
RNN を構築するオブジェクトは tf.contrib.rnn.BasicRNNCell で、引数 num_units で入力の数を定義します。
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons)
ネットワークが定義されたので、出力と状態を計算できます。
outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
このオブジェクトは、内部ループを使用して行列を適切な回数乗算します。
リカレントニューロンは、前のタイムステップのすべての入力の関数であることに注意してください。このようにして、ネットワークは独自のメモリを構築します。前のタイムステップの情報は、将来のタイムステップに伝播することができます。これがリカレントニューラルネットワークの魔法です。
## Define the shape of the tensor X = tf.placeholder(tf.float32, [None, n_timesteps, n_inputs]) ## Define the network basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=n_neurons) outputs, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32) init = tf.global_variables_initializer() init = tf.global_variables_initializer() with tf.Session() as sess: init.run() outputs_val = outputs.eval(feed_dict={X: X_batch}) print(states.eval(feed_dict={X: X_batch})) [[ 0.38941205 -0.9980438 0.99750966 0.7892596 0.9978241 0.9999997 ] [ 0.61096436 0.7255889 0.82977575 -0.88226104 0.29261455 -0.15597084] [ 0.62091285 -0.87023467 0.99729395 -0.58261937 0.9811445 0.99969864]]
説明のために、前の状態の値を表示します。上記の出力は、最後の状態からの出力を示しています。すべての出力を表示すると、各状態が各バッチの前の出力であることがわかります。つまり、前の出力にはシーケンス全体の情報が含まれています。
print(outputs_val) print(outputs_val.shape) [[[-0.75934666 -0.99537754 0.9735819 -0.9722234 -0.14234993 -0.9984044 ] [ 0.99975264 -0.9983206 0.9999993 -1. -0.9997506 -1. ]] [[ 0.97486496 -0.98773265 0.9969686 -0.99950117 -0.7092863 -0.99998885] [ 0.9326837 0.2673438 0.2808514 -0.7535883 -0.43337247 0.5700631 ]] [[ 0.99628735 -0.9998728 0.99999213 -0.99999976 -0.9884324 -1. ] [ 0.99962527 -0.9467421 0.9997403 -0.99999714 -0.99929446 -0.9999795 ]]] (3, 2, 6)
以下の注釈付きビューでは、同じ印刷されたテンソルをバッチごとに分解して表示しています。
出力は (3, 2, 6) の形になります。
- 3: バッチ数
- 2: タイムステップの数
- 6: ニューロンの数
リカレント ニューラル ネットワークの最適化は、従来のニューラル ネットワークと同じです。 このリカレント ニューラル ネットワーク チュートリアルの次の部分で、コードの最適化の方法を詳しく説明します。
RNNの応用
RNN には、特に将来の予測に関して、さまざまな用途があります。 金融業界では、RNN は株価や株式市場の方向性 (つまり、プラスかマイナスか) を予測するのに役立ちます。
RNN は、車両の軌道を予測することで自動車事故を回避できるため、自動運転車に役立ちます。
RNN は、テキスト分析、画像キャプション、感情分析、機械翻訳で広く使用されています。 たとえば、映画のレビューを使用して、映画を観た後に観客が感じた感情を理解することができます。 このタスクの自動化は、映画会社がレビューをレビュー、ラベル付け、統合、分析する十分な時間がない場合に非常に役立ちます。 機械はより高いレベルの精度で仕事を行うことができます。
RNN の制限
理論的には、RNN は情報を最新の時刻まで伝送することになっています。 ただし、タイム ステップが長すぎる場合、このすべての情報を伝播することは非常に困難です。 ネットワークに深い層が多すぎると、トレーニングできなくなります。 この問題は次のように呼ばれます。 勾配問題の消失。 覚えていると思いますが、ニューラル ネットワークは勾配降下法アルゴリズムを使用して重みを更新します。 ネットワークが下位層に進むにつれて、勾配は小さくなります。
結論として、勾配が一定のままということは、改善の余地がないことを意味します。モデルは勾配の変化から学習し、この変化がネットワークの出力に影響を与えます。しかし、勾配の差が小さすぎる場合(つまり、重みがわずかに変化するだけの場合)、ネットワークは何も学習できず、出力も変化しません。したがって、勾配消失問題に直面したネットワークは、良好な解に収束することができません。
LSTMの改善
RNNが抱える勾配消失という潜在的な問題を克服するため、ホッホライター、シュミットフーバー、ベンジオの3人の研究者は、長短期記憶(LSTM)と呼ばれるアーキテクチャを用いてRNNを改良した。簡単に言うと、LSTMはネットワークに、より最近の時点に関連する過去の情報を提供する。このシステムは、より優れたアーキテクチャを用いて、情報を後続の時点まで選択し、保持する。
LSTMアーキテクチャはTensorFlowのtf.contrib.rnn.LSTMCellで利用可能です。LSTMはこのチュートリアルの範囲外です。公式ドキュメントを参照してください。 ドキュメント 詳細については、
TensorFlow 2.x では、そのセルには次のように到達します。 tf.compat.v1.nn.rnn_cell.LSTMCell新しいコードは通常、 tf.keras.layers.LSTM またはライターゲート tf.keras.layers.GRUこれは、より少ないパラメータで同等の精度を達成する。
時系列の RNN
このTensorFlow RNNチュートリアルでは、時系列データを用いたRNNの使い方を学びます。時系列データは過去のデータに依存しており、過去の値にはネットワークが学習できる関連情報が含まれています。時系列予測の基本的な考え方は、株価、気温、GDPなど、系列の将来の値を推定することです。
Keras RNN と時系列のデータの準備は少し難しい場合があります。まず、目的は系列の次の値を予測することです。つまり、過去の情報を使用して t + 1 の値を推定します。ラベルは入力シーケンスと同じで、1 期間先にシフトされます。次に、入力数は 10 に設定され、つまり、XNUMX 回につき XNUMX つの観測が行われます。最後に、時間ステップは数値のシーケンスと同じです。たとえば、時間ステップを XNUMX に設定すると、入力シーケンスは XNUMX 回連続して返されます。
以下のグラフを見てください。左側は時系列データ、右側は架空の入力シーケンスを表しています。 2001 年 2016 月から XNUMX 年 XNUMX 月までの毎日のランダムな値を含むデータセットを返す関数を作成します。
# To plot pretty figures %matplotlib inline import matplotlib import matplotlib.pyplot as plt import pandas as pd def create_ts(start = '2001', n = 201, freq = 'M'): rng = pd.date_range(start=start, periods=n, freq=freq) ts = pd.Series(np.random.uniform(-18, 18, size=len(rng)), rng).cumsum() return ts ts= create_ts(start = '2001', n = 192, freq = 'M') ts.tail(5)
出力
2016-08-31 -93.459631 2016-09-30 -95.264791 2016-10-31 -95.551935 2016-11-30 -105.879611 2016-12-31 -123.729319 Freq: M, dtype: float64
ts = create_ts(start = '2001', n = 222) # Left plt.figure(figsize=(11,4)) plt.subplot(121) plt.plot(ts.index, ts) plt.plot(ts.index[90:100], ts[90:100], "b-", linewidth=3, label="A training instance") plt.title("A time series (generated)", fontsize=14) # Right plt.subplot(122) plt.title("A training instance", fontsize=14) plt.plot(ts.index[90:100], ts[90:100], "b-", markersize=8, label="instance") plt.plot(ts.index[91:101], ts[91:101], "bo", markersize=10, label="target", markerfacecolor='red') plt.legend(loc="upper left") plt.xlabel("Time") plt.show()
グラフの右側にはすべての時系列データが表示されています。2001年から始まり、2019年に終了します。ネットワークにすべてのデータを入力しても意味がないため、時間ステップと同じ長さのデータバッチを作成する必要があります。このバッチがX変数になります。Y変数はXと同じですが、1期間ずらされています(つまり、t+1を予測したいということです)。
どちらのベクトルも同じ長さです。 上のグラフの右側でそれがわかります。 線は X 入力の XNUMX 個の値を表し、赤い点はラベル Y の XNUMX 個の値を表します。ラベルは X より XNUMX 周期前に開始され、XNUMX 周期後に終了することに注意してください。
TensorFlow で時系列を予測するための RNN を構築する
この RNN トレーニングでは、上記の系列を予測するための最初の RNN を構築します。 モデルに対していくつかのハイパーパラメータ (モデルのパラメータ、つまりニューロンの数など) を指定する必要があります。
- 入力数:1
- 時間ステップ(時系列のウィンドウ): 10
- ニューロンの数: 120
- 出力数:1
このネットワークは10日間のデータ系列から学習し、120個のリカレントニューロンで構成されます。モデルには1つの入力、つまり1日分のデータを与えます。モデルの改善状況を確認するために、値を自由に変更してみてください。
モデルを構築する前に、データセットをトレーニングセットとテストセットに分割する必要があります。データセット全体は222個のデータポイントで構成されています。最初の201個のデータポイントをモデルのトレーニングに使用し、最後の21個のデータポイントをモデルのテストに使用します。
トレーニングセットとテストセットを定義したら、バッチを含むオブジェクトを作成する必要があります。これらのバッチには、X値とY値が含まれます。X値は1期間遅れていることに注意してください。そのため、最初の200個の観測値を使用し、タイムステップは10になります。X_batchesオブジェクトには、サイズ10*1のバッチが20個含まれている必要があります。y_batchesはX_batchesオブジェクトと同じ形状ですが、1期間先になっています。
ステップ1) トレインを作成してテストする
まず最初に、系列を に変換します。 NumPy 配列; 次に、以下の TensorFlow RNN の例に示すように、ウィンドウ (つまり、ネットワークが学習する時間の数)、入力数、出力数、およびトレーニング セットのサイズを定義します。
series = np.array(ts) n_windows = 20 n_input = 1 n_output = 1 size_train = 201
その後、配列を XNUMX つのデータセットに分割するだけです。
## Split data train = series[:size_train] test = series[size_train:] print(train.shape, test.shape) (201,) (21,)
ステップ2) X_batches と y_batches を返す関数を作成します。
これを簡単にするために、X_batches 用と y_batches 用の XNUMX つの異なる配列を返す関数を作成できます。
バッチを構築するための RNN TensorFlow 関数を作成しましょう。
X バッチは 1 期間遅れていることに注意してください (値は t-XNUMX になります)。関数の出力は XNUMX つの次元を持つ必要があります。最初の次元はバッチの数、XNUMX 番目の次元はウィンドウのサイズ、最後の次元は入力の数です。
難しいのは、データ ポイントを正しく選択することです。 X データ ポイントの場合は t = 1 から t =200 までの観測値を選択し、Y データ ポイントの場合は t = 2 から 201 までの観測値を返します。正しいデータ ポイントを取得したら、簡単に再形成できます。シリーズ。
バッチを使用してオブジェクトを構築するには、データセットを同じ長さの 20 個のバッチ (つまり 1) に分割する必要があります。 reshape メソッドを使用して -20 を渡すと、シリーズがバッチ サイズと同様になるようにできます。 値 1 はバッチごとの観測数、XNUMX は入力数です。
ラベルに関しては同じ手順を実行する必要があります。
なお、予測したい期間に応じてデータをシフトする必要があります。例えば、1日先を予測したい場合は、時系列データを1つシフトします。2日先を予測したい場合は、データを2つシフトします。
x_data = train[:size_train-1]: Select all the training instance minus one day X_batches = x_data.reshape(-1, windows, input): create the right shape for the batch e.g (10, 20, 1) def create_batches(df, windows, input, output): ## Create X x_data = train[:size_train-1] # Select the data X_batches = x_data.reshape(-1, windows, input) # Reshape the data ## Create y y_data = train[n_output:size_train] y_batches = y_data.reshape(-1, windows, output) return X_batches, y_batches
関数が定義されたので、以下の RNN の例に示すように、それを呼び出してバッチを作成できます。
X_batches, y_batches = create_batches(df = train,
windows = n_windows,
input = n_input,
output = n_output)
形状を印刷して、寸法が正しいことを確認できます。
print(X_batches.shape, y_batches.shape)
(10, 20, 1) (10, 20, 1)
20 つのデータ バッチと XNUMX 個の観測値のみを含むテスト セットを作成する必要があります。
何日もかけて予測するということは、1 番目の予測値がテスト データセットの最初の日 (t+XNUMX) の真の値に基づくことになることに注意してください。 実際にその真価が分かります。
t+2 (つまり 1 日先) を予測する場合は、予測値 t+3 を使用する必要があります。 t+1 (2 日先) を予測する場合は、予測値 t+XNUMX と t+XNUMX を使用する必要があります。 当然のことながら、t+n 日先を正確に予測するのは困難です。
X_test, y_test = create_batches(df = test, windows = 20,input = 1, output = 1)
print(X_test.shape, y_test.shape)
(10, 20, 1) (10, 20, 1)
さて、バッチ サイズの準備ができました。RNN アーキテクチャを構築できます。120 個の再帰ニューロンがあることを覚えておいてください。
ステップ3) モデルを構築する
モデルを作成するには、次の XNUMX つの部分を定義する必要があります。
- テンソルを持つ変数
- RNN
- 損失と最適化
ステップ3.1) 変数
適切な形式で X 変数と y 変数を指定する必要があります。 このステップは簡単です。 テンソルは、オブジェクト X_batches および y_batches と同じ次元を持ちます。
例えば、テンソル X はプレースホルダーです (チュートリアルを確認してください) TensorFlowの概要 変数宣言についてもう一度思い出してください) には XNUMX つの側面があります。
- 注: バッチのサイズ
- n_windows: ウィンドウの長さ。つまり、モデルが過去を振り返る回数。
- n_input: 入力数
結果は次のとおりです。
tf.placeholder(tf.float32, [None, n_windows, n_input])
## 1. Construct the tensors X = tf.placeholder(tf.float32, [None, n_windows, n_input]) y = tf.placeholder(tf.float32, [None, n_windows, n_output])
ステップ3.2) RNN を作成する
この RNN TensorFlow の例の 2 番目の部分では、ネットワークのアーキテクチャを定義する必要があります。前と同様に、TensorFlow 推定器のオブジェクト BasicRNNCell と dynamic_rnn を使用します。
## 2. create the model
basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu)
rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32)
次の部分は少し複雑ですが、より高速な計算が可能になります。 実行出力を高密度レイヤーに変換してから、入力と同じ次元になるように再変換する必要があります。
stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron]) stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output) outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output])
ステップ3.3) 損失と最適化を生み出す
モデルの最適化は、実行しているタスクによって異なります。前回のチュートリアルでは、 CNN前回のチュートリアルでは画像の分類が目的でしたが、今回のRNNチュートリアルでは目的が少し異なります。クラスと比較して連続変数の予測を行うことが求められます。
この違いは最適化問題を変えるため、重要です。 連続変数の最適化問題は、平均二乗誤差を最小限に抑えることです。 TF でこれらのメトリクスを構築するには、以下を使用できます。
tf.reduce_sum(tf.square(出力 – y))
RNN コードの残りの部分は以前と同じです。 Adam オプティマイザーを使用して損失 (つまり、MSE) を削減します。
tf.train.AdamOptimizer(学習率=学習率)
オプティマイザー.最小化(損失)
以上で、すべてをまとめれば、モデルをトレーニングする準備が整います。
tf.reset_default_graph() r_neuron = 120 ## 1. Construct the tensors X = tf.placeholder(tf.float32, [None, n_windows, n_input]) y = tf.placeholder(tf.float32, [None, n_windows, n_output]) ## 2. create the model basic_cell = tf.contrib.rnn.BasicRNNCell(num_units=r_neuron, activation=tf.nn.relu) rnn_output, states = tf.nn.dynamic_rnn(basic_cell, X, dtype=tf.float32) stacked_rnn_output = tf.reshape(rnn_output, [-1, r_neuron]) stacked_outputs = tf.layers.dense(stacked_rnn_output, n_output) outputs = tf.reshape(stacked_outputs, [-1, n_windows, n_output]) ## 3. Loss + optimization learning_rate = 0.001 loss = tf.reduce_sum(tf.square(outputs - y)) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate) training_op = optimizer.minimize(loss) init = tf.global_variables_initializer()
1500 エポックを使用してモデルをトレーニングし、150 回の反復ごとに損失を出力します。 モデルがトレーニングされたら、以下のリカレント ニューラル ネットワークの例に示すように、テスト セットでモデルを評価し、予測を含むオブジェクトを作成します。
iteration = 1500 with tf.Session() as sess: init.run() for iters in range(iteration): sess.run(training_op, feed_dict={X: X_batches, y: y_batches}) if iters % 150 == 0: mse = loss.eval(feed_dict={X: X_batches, y: y_batches}) print(iters, "\tMSE:", mse) y_pred = sess.run(outputs, feed_dict={X: X_test}) 0 MSE: 502893.34 150 MSE: 13839.129 300 MSE: 3964.835 450 MSE: 2619.885 600 MSE: 2418.772 750 MSE: 2110.5923 900 MSE: 1887.9644 1050 MSE: 1747.1377 1200 MSE: 1556.3398 1350 MSE: 1384.6113
この RNN 深層学習チュートリアルでは、最後に、系列の実際の値を予測値とともにプロットできます。 モデルが修正された場合は、予測値が実際の値の上に重ねられる必要があります。
ご覧のとおり、このモデルには改善の余地があります。ウィンドウサイズ、バッチサイズ、リカレントニューロンの数などのハイパーパラメータを変更するかどうかは、あなた次第です。
plt.title("Forecast vs Actual", fontsize=14) plt.plot(pd.Series(np.ravel(y_test)), "bo", markersize=8, label="Actual", color='green') plt.plot(pd.Series(np.ravel(y_pred)), "r.", markersize=8, label="Forecast", color='red') plt.legend(loc="lower left") plt.xlabel("Time") plt.show()
予測と実際





