[[327815]] この記事を読むと、次のことがわかります。 1. シーケンス予測問題のための単純な LSTM ネットワークを開発する方法。 2. LSTM ネットワークを使用して、バッチ処理と機能を通じて状態を慎重に管理する方法。 3. 状態予測のために LSTM ネットワーク内の状態を手動で管理する方法。 4. 18 のステップバイステップのチュートリアルと 9 つのプロジェクトを通じて、わずか数行のコードを使用して、さまざまな予測モデリングの問題に対応するディープラーニング モデルを開発する方法を学びます。 問題の説明: アルファベットの学習 このチュートリアルでは、さまざまな LSTM 再帰型ニューラル ネットワーク モデルを開発し、比較します。 これらの比較のコンテキストは、文字を学習するための単純なシーケンス予測問題になります。つまり、文字が与えられたら、その文字の次の文字を予測します。これは単純なシーケンス予測問題であり、一度理解すれば、時系列予測やシーケンス分類などの他のシーケンス予測問題に一般化できます。例間で再利用できる Python コードを使用して問題を準備しましょう。まず、このチュートリアルで使用する予定のすべてのクラスと関数をインポートしましょう。 - numpyをインポートする
- keras.modelsからSequentialをインポートする
- keras.layersからDenseをインポート
- keras.layersからLSTMをインポートする
- keras.utilsからnp_utilsをインポートする
次に、乱数ジェネレーターにシードを設定して、コードが実行されるたびに結果が同じになるようにします。 - # 再現性のためにランダムシードを修正
- numpy.ランダムシード(7)
ここで、文字であるデータセットを定義できます。読みやすさを考慮して、文字は大文字と定義します。ニューラル ネットワークは数値をモデル化するため、文字を整数にマッピングする必要があります。文字インデックスと文字の辞書 (マッピング) を作成することで、これを簡単に実行できます。後で使用するために、予測を文字に戻す逆クエリを作成することもできます。 - # 生のデータセットを定義する
- アルファベット= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- # 文字と整数(0~25)のマッピングとその逆のマッピングを作成します
- char_to_int = dict ((c, i) の場合、i、c は enumerate(アルファベット) になります)
- int_to_char = dict ((i, c)、i、c は enumerate(アルファベット) 内)
ここで、ニューラル ネットワークをトレーニングするための入力と出力のペアを作成する必要があります。これは、入力シーケンスの長さを定義し、入力文字シーケンスからシーケンスを読み取ることによって実行できます。この例では、入力長として 1 を使用します。元の入力データの先頭から、最初の文字「A」と次の文字を予測「B」として読み取ることができます。 1 文字ずつ移動し、「Z」の予測に到達するまで繰り返します。 - # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- シーケンス長= 1
- データX = []
- データY = []
- i が範囲(0, len(アルファベット) - seq_length, 1)内である場合:
- seq_in =アルファベット[i:i + seq_length]
- seq_out =アルファベット[i + seq_length]
- dataX.append([char_to_int[char] for char in seq_in])
- dataY.append(char_to_int[seq_out])
- 印刷(seq_in, '- > ', seq_out)
健全性チェックのために入力ペアも出力します。ここまでコードを実行すると、長さ 1 の入力シーケンスと単一の出力文字をまとめた次の出力が生成されます。 - A - > B
- B - > C
- C - > D
- D - > E
- E - > F
- F - > G
- G - > H
- H - >私
- 私 - > J
- J - > K
- K - > L
- L - >中
- ま - >な
- N - > O
- O - > P
- P - > Q
- Q - > R
- R - > S
- S - > T
- T - >ウ
- 上 - >上
- V - > W
- W - > X
- X - > Y
- Y - > Z
NumPy 配列を LSTM ネットワークが想定する形式 ([サンプル、時間ステップ、機能]) に再形成する必要があります。 - # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (len(dataX), seq_length, 1))
再形成後、入力整数を 0 ~ 1 の範囲に正規化できます。これは、LSTM ネットワークで使用されるシグモイド活性化関数の範囲です。 - # 正規化
- X X = X / float(len(アルファベット))
最後に、この問題は、26 文字のそれぞれが異なるクラスを表すシーケンス分類タスクとして考えることができます。このように、Keras の組み込み関数 to_categorical() を使用して、出力 (y) をワンホット エンコーディングに変換できます。 - # 出力変数をワンホットエンコードする
- y = np_utils.to_categorical (データY)
1文字対1文字のマッピングを学習するためのNaive LSTM まず、文字のコンテキストに基づいてアルファベットの次の文字を予測する方法を学習する単純な LSTM を設計することから始めましょう。私たちは、問題を、単一文字の入力と単一文字の出力のペアのランダムなコレクションとして定義します。後でわかるように、これは LSTM が学習するのが難しい問題です。予測のために、32 個のユニットとソフトマックス活性化関数を持つ出力層を持つ LSTM ネットワークを定義します。これは多クラス分類問題なので、対数損失関数(Keras では「categorical_crossentropy」と呼ばれます)を使用し、ADAM 最適化関数を使用してネットワークを最適化できます。モデルはバッチ サイズ 1 で 500 エポックに適合します。 - # モデルを作成して適合させる
- モデル=シーケンシャル()
- モデルを追加します(LSTM(32, input_shape =(X.shape[1], X.shape[2])))
- model.add(Dense(y.shape[1], activation = 'softmax' ))
- model.compile(損失= 'categorical_crossentropy' 、オプティマイザー= 'adam' 、メトリック= ['accuracy'])
- model.fit(X, y,エポック= 500 、バッチサイズ= 1 、詳細= 2 )
モデルを適合させた後、トレーニング データセット全体のパフォーマンスを評価して要約できます。 - # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y,詳細= 0 )
- print("モデル精度: %.2f%%" % (scores[1]*100))
次に、ネットワークを介してトレーニング データを再実行し、予測を生成し、入力と出力の両方のペアを元の文字形式に戻して、ネットワークが問題をどの程度学習したかを直感的に把握できます。 - # いくつかのモデル予測を示す
- dataXのパターンの場合:
- x = numpy.reshape (パターン、(1、len(パターン)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- 結果= int_to_char [インデックス]
- seq_in = [パターン内の値のint_to_char[値]]
- print(seq_in, "- > ", 結果)
完全なコード実装は次のとおりです。 - # 1文字から1文字へのマッピングを学習するNaive LSTM
- numpyをインポートする
- keras.modelsからSequentialをインポートする
- keras.layersからDenseをインポート
- keras.layersからLSTMをインポートする
- keras.utilsからnp_utilsをインポートする
- # 再現性のためにランダムシードを修正
- numpy.ランダムシード(7)
- # 生のデータセットを定義する
- アルファベット= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- # 文字と整数(0~25)のマッピングとその逆のマッピングを作成します
- char_to_int = dict ((c, i) の場合、i、c は enumerate(アルファベット) になります)
- int_to_char = dict ((i, c)、i、c は enumerate(アルファベット) 内)
- # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- シーケンス長= 1
- データX = []
- データY = []
- i が範囲(0, len(アルファベット) - seq_length, 1)内である場合:
- seq_in =アルファベット[i:i + seq_length]
- seq_out =アルファベット[i + seq_length]
- dataX.append([char_to_int[char] for char in seq_in])
- dataY.append(char_to_int[seq_out])
- 印刷(seq_in, '- > ', seq_out)
- # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (len(dataX), seq_length, 1))
- # 正規化
- X X = X / float(len(アルファベット))
- # 出力変数をワンホットエンコードする
- y = np_utils.to_categorical (データY)
- # モデルを作成して適合させる
- モデル=シーケンシャル()
- モデルを追加します(LSTM(32, input_shape =(X.shape[1], X.shape[2])))
- model.add(Dense(y.shape[1], activation = 'softmax' ))
- model.compile(損失= 'categorical_crossentropy' 、オプティマイザー= 'adam' 、メトリック= ['accuracy'])
- model.fit(X, y,エポック= 500 、バッチサイズ= 1 、詳細= 2 )
- # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y, verbose = 0 )
- print("モデル精度: %.2f%%" % (scores[1]*100))
- # いくつかのモデル予測を示す
- dataXのパターンの場合:
- x = numpy.reshape (パターン、(1、len(パターン)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- 結果= int_to_char [インデックス]
- seq_in = [パターン内の値のint_to_char[値]]
- print(seq_in, "- > ", 結果)
実行結果の出力は次のようになります。 - モデル精度: 84.00%
- ['A'] - > B
- ['B'] - > C
- ['C'] - > D
- ['D'] - > E
- ['E'] - > F
- ['F'] - > G
- ['G'] - > H
- ['H'] - >私
- ['私'] - > J
- ['J'] - > K
- ['K'] - > L
- ['L'] - > M
- ['M'] - > N
- ['N'] - > O
- ['O'] - > P
- ['P'] - > Q
- ['Q'] - > R
- ['R'] - > S
- ['S'] - > T
- ['T'] - >上
- ['U'] - > W
- ['V'] - > Y
- ['W'] - > Z
- ['X'] - > Z
- ['Y'] - > Z
ネットワークがこの問題を解決するのに本当に苦労していることがわかります。その理由は、貧弱な LSTM ユニットには使用できるコンテキストがないためです。各入出力パターンはランダムな順序でネットワークに表示され、各パターンの後にネットワークの状態がリセットされます (各バッチにはバッチごとに 1 つのパターンが含まれます)。これは LSTM ネットワーク アーキテクチャを乱用したもので、標準的な多層パーセプトロンのように扱っています。次に、ネットワークが学習するための秩序をさらに高めるために、問題を別の方法で定義してみましょう。 3文字の特徴ウィンドウから1文字へのマッピングのためのNaive LSTM 多層パーセプトロンのデータにさらにコンテキストを追加する一般的な方法は、ウィンドウ処理法を使用することです。ここで、シーケンスの前のステップがネットワークへの追加入力機能として提供されます。同じトリックを使用して、LSTM ネットワークにさらに多くのコンテキストを提供することもできます。ここでは、シーケンスの長さを 1 から 3 に増やします。例: - # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- シーケンス長= 3 ]
トレーニング データのサンプルは次のとおりです。 - ABC - > D
- BCD - > E
- CDE - > F
シーケンス内の各要素は、新しい入力機能としてネットワークに送られます。これには、データ準備ステップで入力シーケンスを再形成する方法を変更する必要があります。 - # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (len(dataX), 1, seq_length))
また、モデルから予測を行う際にサンプル パターンがどのように再形成されるかを示すためにも変更が必要です。 - x = numpy.reshape (パターン、(1、1、len(パターン)))
完全なコード実装は次のとおりです。 - # 3 文字のウィンドウから 1 文字のマッピングを学習する単純な LSTM
- numpyをインポートする
- keras.modelsからSequentialをインポートする
- keras.layersからDenseをインポート
- keras.layersからLSTMをインポートする
- keras.utilsからnp_utilsをインポートする
- # 再現性のためにランダムシードを修正
- numpy.ランダムシード(7)
- # 生のデータセットを定義する
- アルファベット= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- # 文字と整数(0~25)のマッピングとその逆のマッピングを作成します
- char_to_int = dict ((c, i) の場合、i、c は enumerate(アルファベット) になります)
- int_to_char = dict ((i, c)、i、c は enumerate(アルファベット) 内)
- # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- シーケンス長= 3
- データX = []
- データY = []
- i が範囲(0, len(アルファベット) - seq_length, 1)内である場合:
- seq_in =アルファベット[i:i + seq_length]
- seq_out =アルファベット[i + seq_length]
- dataX.append([char_to_int[char] for char in seq_in])
- dataY.append(char_to_int[seq_out])
- 印刷(seq_in, '- > ', seq_out)
- # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (len(dataX), 1, seq_length))
- # 正規化
- X X = X / float(len(アルファベット))
- # 出力変数をワンホットエンコードする
- y = np_utils.to_categorical (データY)
- # モデルを作成して適合させる
- モデル=シーケンシャル()
- モデルを追加します(LSTM(32, input_shape =(X.shape[1], X.shape[2])))
- model.add(Dense(y.shape[1], activation = 'softmax' ))
- model.compile(損失= 'categorical_crossentropy' 、オプティマイザー= 'adam' 、メトリック= ['accuracy'])
- model.fit(X, y,エポック= 500 、バッチサイズ= 1 、詳細= 2 )
- # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y,詳細= 0 )
- print("モデル精度: %.2f%%" % (scores[1]*100))
- # いくつかのモデル予測を示す
- dataXのパターンの場合:
- x = numpy.reshape (パターン、(1、1、len(パターン)))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- 結果= int_to_char [インデックス]
- seq_in = [パターン内の値のint_to_char[値]]
- print(seq_in, "- > ", 結果)
実行結果の出力は次のようになります。 - モデル精度: 86.96%
- ['A', 'B', 'C'] - > D
- ['B', 'C', 'D'] - > E
- ['C', 'D', 'E'] - > F
- ['D', 'E', 'F'] - > G
- ['E', 'F', 'G'] - > H
- ['F', 'G', 'H'] - > I
- ['G', 'H', 'I'] - > J
- ['H', 'I', 'J'] - > K
- ['I', 'J', 'K'] - > L
- ['J'、'K'、'L'] - > M
- ['K', 'L', 'M'] - > N
- ['L', 'M', 'N'] - > O
- ['M', 'N', 'O'] - > P
- ['N', 'O', 'P'] - > Q
- ['O', 'P', 'Q'] - > R
- ['P', 'Q', 'R'] - > S
- ['Q', 'R', 'S'] - > T
- ['R', 'S', 'T'] - > U
- ['S', 'T', 'U'] - > V
- ['T', 'U', 'V'] - > Y
- ['U', 'V', 'W'] - > Z
- ['V', 'W', 'X'] - > Z
- ['W', 'X', 'Y'] - > Z
パフォーマンスの向上は、大きくないかもしれないことがわかります。これは単純な問題ですが、ウィンドウ方式を使用しても、LSTM で学習することはできません。繰り返しますが、これは問題の不適切な表現であり、LSTM ネットワークの誤用です。実際には、文字のシーケンスは 1 つの機能の時間ステップであり、個々の機能の時間ステップではありません。ネットワークにより多くのコンテキストを提供しましたが、期待したほどの秩序は得られませんでした。 次のセクションでは、時間ステップの形式でネットワークにさらにコンテキストを提供します。 3文字の時間ステップウィンドウから1文字へのマッピングのためのNaive LSTM Keras では、LSTM の目的は、他のネットワーク タイプのようなウィンドウ関数ではなく、時間ステップの形式でコンテキストを提供することです。最初の例を取り上げて、シーケンスの長さを 1 から 3 に変更するだけです。 - シーケンス長= 3
入力と出力のサンプルを以下に示します。 - ABC - > D
- BCD - > E
- CDE - > F
- 防御 - > G
違いは、入力データの再形成により、シーケンスが複数の機能の単一の時間ステップとしてではなく、1 つの機能の時間ステップのシーケンスとして扱われることです。 - # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (len(dataX), seq_length, 1))
完全なコード実装は次のとおりです。 - # 3 文字の時間ステップを 1 文字のマッピングに学習する単純な LSTM
- numpyをインポートする
- keras.modelsからSequentialをインポートする
- keras.layersからDenseをインポート
- keras.layersからLSTMをインポートする
- keras.utilsからnp_utilsをインポートする
- # 再現性のためにランダムシードを修正
- numpy.ランダムシード(7)
- # 生のデータセットを定義する
- アルファベット= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- # 文字と整数(0~25)のマッピングとその逆のマッピングを作成します
- char_to_int = dict ((c, i) の場合、i、c は enumerate(アルファベット) になります)
- int_to_char = dict ((i, c)、i、c は enumerate(alphabet) 内)
- # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- シーケンス長= 3
- データX = []
- データY = []
- i が範囲(0, len(アルファベット) - seq_length, 1)内である場合:
- seq_in =アルファベット[i:i + seq_length]
- seq_out =アルファベット[i + seq_length]
- dataX.append([char_to_int[char] for char in seq_in])
- dataY.append(char_to_int[seq_out])
- 印刷(seq_in, '- > ', seq_out)
- # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (len(dataX), seq_length, 1))
- # 正規化
- X X = X / float(len(アルファベット))
- # 出力変数をワンホットエンコードする
- y = np_utils.to_categorical (データY)
- # モデルを作成して適合させる
- モデル=シーケンシャル()
- モデルを追加します(LSTM(32, input_shape =(X.shape[1], X.shape[2])))
- model.add(Dense(y.shape[1], activation = 'softmax' ))
- model.compile(損失= 'categorical_crossentropy' 、オプティマイザー= 'adam' 、メトリック= ['accuracy'])
- model.fit(X, y,エポック= 500 、バッチサイズ= 1 、詳細= 2 )
- # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y,詳細= 0 )
- print("モデル精度: %.2f%%" % (scores[1]*100))
- # いくつかのモデル予測を示す
- dataXのパターンの場合:
- x = numpy.reshape (パターン、(1、len(パターン)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- 結果= int_to_char [インデックス]
- seq_in = [パターン内の値のint_to_char[値]]
- print(seq_in, "- > ", 結果)
実行結果の出力は次のようになります。 - モデル精度: 100.00%
- ['A', 'B', 'C'] - > D
- ['B', 'C', 'D'] - > E
- ['C', 'D', 'E'] - > F
- ['D', 'E', 'F'] - > G
- ['E', 'F', 'G'] - > H
- ['F', 'G', 'H'] - > I
- ['G', 'H', 'I'] - > J
- ['H', 'I', 'J'] - > K
- ['I', 'J', 'K'] - > L
- ['J'、'K'、'L'] - > M
- ['K', 'L', 'M'] - > N
- ['L', 'M', 'N'] - > O
- ['M', 'N', 'O'] - > P
- ['N', 'O', 'P'] - > Q
- ['O', 'P', 'Q'] - > R
- ['P', 'Q', 'R'] - > S
- ['Q', 'R', 'S'] - > T
- ['R', 'S', 'T'] - > U
- ['S', 'T', 'U'] - > V
- ['T', 'U', 'V'] - > W
- ['U', 'V', 'W'] - > X
- ['V', 'W', 'X'] - > Y
- ['W', 'X', 'Y'] - > Z
モデルの評価と予測例からわかるように、モデルは問題を完全に学習できることがわかります。しかし、より簡単な問題をすでに学習しています。具体的には、アルファベットの 3 文字のシーケンスに基づいて次の文字を予測することを学習しました。アルファベットの 3 文字のランダムなシーケンスを表示し、次の文字を予測できます。実際に文字を列挙することはできません。十分に大きな多層パーセプトロン ネットワークであれば、ウィンドウ アプローチを使用して同じマッピングを学習できると期待しています。 LSTM ネットワークはステートフルです。アルファベットのシーケンス全体を学習できるはずですが、デフォルトでは、Keras 実装は各トレーニング バッチの後にネットワークの状態をリセットします。 バッチ内の LSTM 状態 LSTM の Keras 実装では、各バッチの後にネットワーク状態がリセットされます。これは、バッチ サイズがすべての入力パターンを収容できるほど大きく、すべての入力パターンが順番に並べられている場合、LSTM はバッチ内のシーケンスのコンテキストを使用してシーケンスをより適切に学習できることを示しています。これを簡単に実証するには、1 対 1 のマッピングを学習するための最初の例を変更し、バッチ サイズを 1 からトレーニング データセットのサイズに増やします。さらに、Keras は各トレーニング エポックの前にトレーニング データセットをシャッフルします。トレーニング データ パターンが連続的であることを保証するために、このシャッフルを無効にすることができます。 - model.fit(X, y,エポック= 5000 、バッチサイズ= len (dataX)、詳細= 2 、シャッフル= False )
ネットワークはバッチ内のシーケンスを使用して文字のマッピングを学習しますが、予測を行うときにこのコンテキストはネットワークで利用できません。ランダムとシーケンシャルの両方で予測を行うネットワークの能力を評価できます。 全体的なコード実装は次のとおりです。 - # 各バッチ内のすべてのデータを使用して 1 文字から 1 文字へのマッピングを学習する Naive LSTM
- numpyをインポートする
- keras.modelsからSequentialをインポートする
- keras.layersからDenseをインポート
- keras.layersからLSTMをインポートする
- keras.utilsからnp_utilsをインポートする
- keras.preprocessing.sequence から pad_sequences をインポートします
- # 再現性のためにランダムシードを修正
- numpy.ランダムシード(7)
- # 生のデータセットを定義する
- アルファベット= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- # 文字と整数(0~25)のマッピングとその逆のマッピングを作成します
- char_to_int = dict ((c, i) の場合、i、c は enumerate(アルファベット) になります)
- int_to_char = dict ((i, c)、i、c は enumerate(alphabet) 内)
- # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- シーケンス長= 1
- データX = []
- データY = []
- i が範囲(0, len(アルファベット) - seq_length, 1)内である場合:
- seq_in =アルファベット[i:i + seq_length]
- seq_out =アルファベット[i + seq_length]
- dataX.append([char_to_int[char] for char in seq_in])
- dataY.append(char_to_int[seq_out])
- 印刷(seq_in, '- > ', seq_out)
- # リストのリストを配列に変換し、必要に応じてシーケンスを埋め込む
- X = pad_sequences (dataX、 maxlen = seq_length 、 dtype = 'float32' )
- # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (X.shape[0], seq_length, 1))
- # 正規化
- X X = X / float(len(アルファベット))
- # 出力変数をワンホットエンコードする
- y = np_utils.to_categorical (データY)
- # モデルを作成して適合させる
- モデル=シーケンシャル()
- モデルを追加します(LSTM(16, input_shape =(X.shape[1], X.shape[2])))
- model.add(Dense(y.shape[1], activation = 'softmax' ))
- model.compile(損失= 'categorical_crossentropy' 、オプティマイザー= 'adam' 、メトリック= ['accuracy'])
- model.fit(X, y,エポック= 5000 、バッチサイズ= len (dataX)、詳細= 2 、シャッフル= False )
- # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y, verbose = 0 )
- print("モデル精度: %.2f%%" % (scores[1]*100))
- # いくつかのモデル予測を示す
- dataXのパターンの場合:
- x = numpy.reshape (パターン、(1、len(パターン)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- 結果= int_to_char [インデックス]
- seq_in = [パターン内の値のint_to_char[値]]
- print(seq_in, "- > ", 結果)
- # ランダムパターンの予測を実演する
- print("ランダムパターンをテストする:")
- iが範囲(0,20)内にある場合:
- パターンインデックス= numpy.random.randint (len(dataX))
- パターン=データX [パターンインデックス]
- x = numpy.reshape (パターン、(1、len(パターン)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- 結果= int_to_char [インデックス]
- seq_in = [パターン内の値のint_to_char[値]]
- print(seq_in, "- > ", 結果)
実行結果の出力は次のようになります。 - モデル精度: 100.00%
- ['A'] - > B
- ['B'] - > C
- ['C'] - > D
- ['D'] - > E
- ['E'] - > F
- ['F'] - > G
- ['G'] - > H
- ['H'] - >私
- ['私'] - > J
- ['J'] - > K
- ['K'] - > L
- ['L'] - > M
- ['M'] - > N
- ['N'] - > O
- ['O'] - > P
- ['P'] - > Q
- ['Q'] - > R
- ['R'] - > S
- ['S'] - > T
- ['T'] - >上
- ['U'] - > V
- ['V'] - > W
- ['W'] - > X
- ['X'] - > Y
- ['Y'] - > Z
- ランダムパターンをテストする:
- ['T'] - >上
- ['V'] - > W
- ['M'] - > N
- ['Q'] - > R
- ['D'] - > E
- ['V'] - > W
- ['T'] - >上
- ['U'] - > V
- ['J'] - > K
- ['F'] - > G
- ['N'] - > O
- ['B'] - > C
- ['M'] - > N
- ['F'] - > G
- ['F'] - > G
- ['P'] - > Q
- ['A'] - > B
- ['K'] - > L
- ['W'] - > X
- ['E'] - > F
予想どおり、ネットワークはシーケンス内のコンテキストを使用して文字を学習し、トレーニング データで 100% の精度を達成しました。重要なのは、ネットワークがランダムに選択された文字のアルファベットの次の文字を正確に予測できることです。とても印象的でした。 1文字から1文字へのマッピングのためのステートフルLSTM 元のデータを固定サイズのシーケンスに分割できること、そして LSTM がこの表現を学習できることを見てきましたが、学習できるのは 3 文字から 1 文字へのランダム マッピングだけです。また、バッチ サイズを変更して、ネットワークにさらに多くのシーケンスを供給できることもわかりましたが、これはトレーニング中のみです。理想的には、問題の枠組みの中で依存関係を明示的に定義するのではなく、ネットワークをシーケンス全体に公開して相互依存性を学習させたいと考えています。 Keras では、LSTM レイヤーをステートフルにして、エポックの終わり (つまり、トレーニング シーケンスの終わり) にネットワークの状態を手動でリセットすることで、これを実行できます。 これはまさに LSTM ネットワークが使用されることを意図した方法です。まず、LSTM レイヤーをステートフルとして定義する必要があります。これを実行する場合、入力シェイプの次元としてバッチ サイズを明示的に指定する必要があります。これは、ネットワークを評価したり予測を行ったりするときにも、同じバッチ サイズを指定してそれに従う必要があることも意味します。バッチ サイズ 1 を使用しているため、これは現時点では問題ではありません。予測はバッチで順番に行う必要があるため、バッチ サイズが 1 以外の場合は予測が困難になる可能性があります。 - バッチサイズ= 1
- model.add(LSTM(50, batch_input_shape =(batch_size, X.shape[1], X.shape[2]), stateful = True ))
ステートフル LSTM をトレーニングする際の重要な違いは、一度に 1 つのエポックを手動でトレーニングし、各エポックの後に状態をリセットすることです。これを for ループで実行できます。ここでも、入力をシャッフルするのではなく、入力トレーニング データが作成された順序を維持します。 - iが範囲(300)内にある場合:
- model.fit(X, y,エポック= 1 、バッチサイズbatch_size = batch_size、詳細= 2 、シャッフル= False )
- モデル.reset_states()
前述したように、トレーニング データセット全体でネットワークのパフォーマンスを評価するときにバッチ サイズを指定します。 - # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y, batch_size batch_size =batch_size, verbose = 0 )
- モデル.reset_states()
- print("モデル精度: %.2f%%" % (scores[1]*100))
最後に、ネットワークが実際にアルファベット全体を学習したことを示すことができます。最初の文字「A」をシードとして使用し、予測を要求し、予測を入力としてフィードバックし、「Z」までプロセス全体を繰り返すことができます。 - # いくつかのモデル予測を示す
- シード= [char_to_int[アルファベット[0]]]
- iが範囲(0, len(アルファベット)-1)内にある場合:
- x = numpy.reshape (シード、(1、len(シード)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- print(int_to_char[seed[0]], "- > ", int_to_char[index])
- シード= [インデックス]
- モデル.reset_states()
また、ネットワークが任意の文字から予測を行うことができるかどうかを確認することもできます。 - # ランダムな開始点を示す
- 文字= "K"
- シード= [char_to_int[文字]]
- print("新しい開始: ", 文字)
- iが範囲(0, 5)内にある場合:
- x = numpy.reshape (シード、(1、len(シード)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- print(int_to_char[seed[0]], "- > ", int_to_char[index])
- シード= [インデックス]
- モデル.reset_states()
完全なコード実装は次のとおりです。 - # 1文字から1文字へのマッピングを学習するステートフルLSTM
- numpyをインポートする
- keras.modelsからSequentialをインポートする
- keras.layersからDenseをインポート
- keras.layersからLSTMをインポートする
- keras.utilsからnp_utilsをインポートする
- # 再現性のためにランダムシードを修正
- numpy.ランダムシード(7)
- # 生のデータセットを定義する
- アルファベット= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- # 文字と整数(0~25)のマッピングとその逆のマッピングを作成します
- char_to_int = dict ((c, i) の場合、i、c は enumerate(アルファベット) になります)
- int_to_char = dict ((i, c)、i、c は enumerate(alphabet) 内)
- # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- シーケンス長= 1
- データX = []
- データY = []
- i が範囲(0, len(アルファベット) - seq_length, 1)内である場合:
- seq_in =アルファベット[i:i + seq_length]
- seq_out =アルファベット[i + seq_length]
- dataX.append([char_to_int[char] for char in seq_in])
- dataY.append(char_to_int[seq_out])
- 印刷(seq_in, '- > ', seq_out)
- # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (dataX, (len(dataX), seq_length, 1))
- # 正規化
- X X = X / float(len(アルファベット))
- # 出力変数をワンホットエンコードする
- y = np_utils.to_categorical (データY)
- # モデルを作成して適合させる
- バッチサイズ= 1
- モデル=シーケンシャル()
- model.add(LSTM(50, batch_input_shape =(batch_size, X.shape[1], X.shape[2]), stateful = True ))
- model.add(Dense(y.shape[1], activation = 'softmax' ))
- model.compile(損失= 'categorical_crossentropy' 、オプティマイザー= 'adam' 、メトリック= ['accuracy'])
- iが範囲(300)内にある場合:
- model.fit(X, y,エポック= 1 、バッチサイズbatch_size = batch_size、詳細= 2 、シャッフル= False )
- モデル.reset_states()
- # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y, batch_size batch_size =batch_size, verbose = 0 )
- モデル.reset_states()
- print("モデル精度: %.2f%%" % (scores[1]*100))
- # いくつかのモデル予測を示す
- シード= [char_to_int[アルファベット[0]]]
- iが範囲(0, len(アルファベット)-1)内にある場合:
- x = numpy.reshape (シード、(1、len(シード)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- print(int_to_char[seed[0]], "- > ", int_to_char[index])
- シード= [インデックス]
- モデル.reset_states()
- # ランダムな開始点を示す
- 文字= "K"
- シード= [char_to_int[文字]]
- print("新しい開始: ", 文字)
- iが範囲(0, 5)内にある場合:
- x = numpy.reshape (シード、(1、len(シード)、1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- print(int_to_char[seed[0]], "- > ", int_to_char[index])
- シード= [インデックス]
- モデル.reset_states()
実行結果は次のとおりです。 - モデル精度: 100.00%
- A - > B
- B - > C
- C - > D
- D - > E
- E - > F
- F - > G
- G - > H
- H - >私
- 私 - > J
- J - > K
- K - > L
- L - >中
- M - > N
- N - > O
- O - > P
- P - > Q
- Q - > R
- R - > S
- S - > T
- T - >ウ
- 上 - >上
- V - > W
- W - > X
- X - > Y
- Y - > Z
- 新たなスタート:K
- K - > B
- B - > C
- C - > D
- D - > E
- E - > F
ネットワークがアルファベット全体を完璧に記憶していることがわかります。サンプル自体のコンテキストを使用し、シーケンス内の次の文字を予測するために必要な依存関係を学習します。また、最初の文字をネットワーク シードとして使用すると、アルファベットの残りの部分を正しくシャッフルできることもわかります。また、文字の完全なシーケンスのみを学習しており、コールドスタートから学習したこともわかります。 「K」の次の文字を予測するように指示されると、「B」を予測し、アルファベット全体を遡ります。実際に「K」を予測するには、ネットワークの状態を「A」から「J」までの文字で表して反復的にウォームアップする必要があります。これは、次のトレーニング データを準備することで、「ステートレス」 LSTM を使用して同じ効果を達成できることを示しています。 - ---a - > b
- --ab - > c の
- -abc - > d
- abcd - > e
入力シーケンスは 25 に固定され (a から y への z を予測)、パターンの先頭にゼロ パディングが付けられます。最後に、可変長の入力シーケンスを使用して次の文字を予測する LSTM ネットワークをトレーニングするという問題が発生します。 可変長入力から1文字出力までのLSTM 前のセクションでは、Keras の「ステートフル」LSTM は実際には前の n シーケンスを再生するための単なるショートカットであり、アルファベットの一般的なモデルを学習するのにはあまり役立たないことがわかりました。 このセクションでは、アルファベットのランダムなサブシーケンスを学習する「ステートレス」 LSTM のバリエーションを検討し、任意の文字またはサブシーケンスを与えられ、アルファベットの次の文字を予測できるモデルの構築を目指します。まず、問題の枠組みを変えています。簡単にするために、最大入力シーケンス長を定義し、それを 5 などの小さな値に設定してトレーニングを高速化します。これは、トレーニングされる文字サブシーケンスの最大長を定義します。拡張すると、シーケンスの先頭にループバックできるようにすれば、これを完全なアルファベット (26) 以上の長さにすることができます。作成するランダムシーケンスの数も定義する必要があります (この場合は 1000)。多かれ少なかれあるかもしれません。実際に必要なパターンがもっと少なければいいのにと思います。 - # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- 入力数= 1000
- 最大長= 5
- データX = []
- データY = []
- i が範囲内(num_inputs)の場合:
- 開始= numpy.random.randint (len(アルファベット)-2)
- 終了= numpy.random.randint (開始、最小(開始+最大長さ、長さ(アルファベット)-1))
- シーケンス入力=アルファベット[開始:終了+1]
- シーケンス出力=アルファベット[終了 + 1]
- dataX.append([char_to_int[char] for char in sequence_in])
- dataY.append(char_to_int[sequence_out])
- 印刷(シーケンス入力、'- > '、シーケンス出力)
サンプル入力は次のとおりです。 - PQRST - > U
- W - > X
- O - > P
- OPQ - > R
- IJKLM - > N
- QRSTU - > V
- ABCD - > E
- X - > Y
- GHIJ - > K
入力シーケンスの長さは 1 から max_len の間で変化するため、ゼロ パディングが必要です。ここでは、組み込みの pad_sequences() 関数からの Keras の左側 (プレフィックス) パディングを使用します。 - X = pad_sequences (dataX, maxlen = max_len , dtype = 'float32' ) です。
ランダムに選択された入力パターンでトレーニングされたモデルを評価します。これは、ランダムに生成された新しい文字列と同じくらい簡単です。また、これは線形シーケンスになる可能性があり、シードとして「A」があり、出力は単一の文字入力として返されると考えられます。 完全なコード実装は次のとおりです。 - # 可変長入力シーケンスから 1 文字出力への LSTM
- numpyをインポートする
- keras.modelsからSequentialをインポートする
- keras.layersからDenseをインポート
- keras.layersからLSTMをインポートする
- keras.utilsからnp_utilsをインポートする
- keras.preprocessing.sequence から pad_sequences をインポートします
- theano.tensor.shared_randomstreams から RandomStreams をインポートします
- # 再現性のためにランダムシードを修正
- numpy.ランダムシード(7)
- # 生のデータセットを定義する
- アルファベット= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- # 文字と整数(0~25)のマッピングとその逆のマッピングを作成します
- char_to_int = dict ((c, i) の場合、i、c は enumerate(アルファベット) になります)
- int_to_char = dict ((i, c)、i、c は enumerate(alphabet) 内)
- # 整数としてエンコードされた入力と出力のペアのデータセットを準備する
- 入力数= 1000
- 最大長= 5
- データX = []
- データY = []
- i が範囲内(num_inputs)の場合:
- 開始= numpy.random.randint (len(アルファベット)-2)
- 終了= numpy.random.randint (開始、最小(開始+最大長さ、長さ(アルファベット)-1))
- シーケンス入力=アルファベット[開始:終了+1]
- シーケンス出力=アルファベット[終了 + 1]
- dataX.append([char_to_int[char] for char in sequence_in])
- dataY.append(char_to_int[sequence_out])
- 印刷(シーケンス入力、'- > '、シーケンス出力)
- # リストのリストを配列に変換し、必要に応じてシーケンスを埋め込む
- X = pad_sequences (dataX, maxlen = max_len , dtype = 'float32' ) です。
- # Xを[サンプル、時間ステップ、特徴]に再形成する
- X = numpy.reshape (X, (X.shape[0], max_len, 1))
- # 正規化
- X X = X / float(len(アルファベット))
- # 出力変数をワンホットエンコードする
- y = np_utils.to_categorical (データY)
- # モデルを作成して適合させる
- バッチサイズ= 1
- モデル=シーケンシャル()
- モデルを追加します(LSTM(32, input_shape =(X.shape[1], 1)))
- model.add(Dense(y.shape[1], activation = 'softmax' ))
- model.compile(損失= 'categorical_crossentropy' 、オプティマイザー= 'adam' 、メトリック= ['accuracy'])
- model.fit(X, y,エポック= 500 、バッチサイズbatch_size = batch_size、詳細= 2 )
- # モデルのパフォーマンスを要約する
- スコア=モデル.evaluate(X, y, verbose = 0 )
- print("モデル精度: %.2f%%" % (scores[1]*100))
- # いくつかのモデル予測を示す
- iが範囲(20)内にある場合:
- パターンインデックス= numpy.random.randint (len(dataX))
- パターン=データX [パターンインデックス]
- x = pad_sequences ([パターン], maxlen = max_len , dtype = 'float32' )
- x = numpy.reshape (x, (1, max_len, 1))
- x x = x / float(len(アルファベット))
- 予測=モデル.predict(x,詳細= 0 )
- インデックス= numpy.argmax (予測)
- 結果= int_to_char [インデックス]
- seq_in = [パターン内の値のint_to_char[値]]
- print(seq_in, "- > ", 結果)
結果の出力は次のようになります。 - モデル精度: 98.90%
- ['Q', 'R'] - > S
- ['W', 'X'] - > Y
- ['W', 'X'] - > Y
- ['C', 'D'] - > E
- ['E'] - > F
- ['S', 'T', 'U'] - > V
- ['G'、'H'、'I'、'J'、'K'] - > L
- ['O'、'P'、'Q'、'R'、'S'] - > T
- ['C', 'D'] - > E
- ['O'] - > P
- ['N', 'O', 'P'] - > Q
- ['D'、'E'、'F'、'G'、'H'] - > I
- ['X'] - > Y
- ['K'] - > L
- ['M'] - > N
- ['R'] - > T
- ['K'] - > L
- ['E', 'F', 'G'] - > H
- ['Q'] - > R
- ['Q', 'R', 'S'] - > T
モデルはランダムに生成されたサブシーケンスから文字を完全に学習しているわけではありませんが、かなり良い仕事をしていることがわかります。モデルはまだ調整されていないため、より多くのトレーニングやより大きなネットワーク、あるいはその両方が必要になる場合があります(読者のための演習として残されています)。これは、アドホッククエリを処理できるため、上記で学んだ「各バッチのすべてのシーケンシャル入力例」アルファベットモデルの素晴らしく自然な拡張ですが、今回は任意のシーケンス長(最大長)です。 要約する この記事では、KerasのLSTM Recurrent Neural Networksとそれらが状態の管理方法を発見しました。具体的には、次のことを学びました。 1.単一の文字から文字予測のための素朴なLSTMネットワークを開発する方法。 2。サンプルのタイムステップのシーケンスを学習するように素朴なLSTMを構成する方法。 3.手動で管理する状態を手動で管理することにより、サンプル間のシーケンスを学習するようにLSTMを構成する方法。 |