Microsoft-cognitive-toolkit-recurrent-neural-network

提供:Dev Guides
移動先:案内検索

CNTK-リカレントニューラルネットワーク

ここで、CNTKでリカレントニューラルネットワーク(RNN)を構築する方法を理解しましょう。

前書き

ニューラルネットワークで画像を分類する方法を学びました。これは、ディープラーニングの象徴的な仕事の1つです。 しかし、ニューラルネットワークが優れており、多くの研究が行われているもう1つの分野は、リカレントニューラルネットワーク(RNN)です。 ここでは、RNNとは何か、および時系列データを処理する必要があるシナリオでRNNをどのように使用できるかについて説明します。

リカレントニューラルネットワークとは

リカレントニューラルネットワーク(RNN)は、時間をかけて推論できる特殊な種類のNNとして定義できます。 RNNは主に、時間とともに変化する値に対処する必要があるシナリオで使用されます。 時系列データ。 よりよく理解するために、通常のニューラルネットワークとリカレントニューラルネットワークを少し比較してみましょう。

  • ご存知のように、通常のニューラルネットワークでは、1つの入力しか提供できません。 これにより、予測が1つだけになるように制限されます。 例として、通常のニューラルネットワークを使用してテキストジョブを翻訳できます。
  • 一方、リカレントニューラルネットワークでは、単一の予測をもたらす一連のサンプルを提供できます。 つまり、RNNを使用すると、入力シーケンスに基づいて出力シーケンスを予測できます。 たとえば、翻訳タスクでRNNを使用して成功した実験がかなりあります。

リカレントニューラルネットワークの使用

RNNはいくつかの方法で使用できます。 それらのいくつかは次のとおりです-

単一の出力を予測する

手順を詳しく説明する前に、RNNがシーケンスに基づいて単一の出力を予測する方法について、基本的なRNNがどのように見えるかを見てみましょう。

単一出力

上の図でできるように、RNNには入力へのループバック接続が含まれており、値のシーケンスをフィードすると、シーケンス内の各要素がタイムステップとして処理されます。

さらに、ループバック接続のため、RNNは生成された出力をシーケンスの次の要素の入力と組み合わせることができます。 このようにして、RNNは予測を行うために使用できるシーケンス全体にわたってメモリを構築します。

RNNで予測を行うには、次の手順を実行します。

  • 最初に、初期の非表示状態を作成するには、入力シーケンスの最初の要素をフィードする必要があります。
  • その後、更新された非表示状態を生成するには、初期の非表示状態を取得し、それを入力シーケンスの2番目の要素と組み合わせる必要があります。
  • 最後に、最終的な非表示状態を生成し、RNNの出力を予測するには、入力シーケンスの最後の要素を取得する必要があります。

このようにして、このループバック接続の助けを借りて、RNNに時間の経過とともに発生するパターンを認識するように教えることができます。

シーケンスの予測

上記のRNNの基本モデルは、他のユースケースにも拡張できます。 たとえば、これを使用して、単一の入力に基づいて一連の値を予測できます。 このシナリオでは、RNNで予測を行うために、次の手順を実行できます-

  • 最初に、初期の非表示状態を作成し、出力シーケンスの最初の要素を予測するには、入力サンプルをニューラルネットワークにフィードする必要があります。
  • その後、更新された非表示状態と出力シーケンスの2番目の要素を生成するには、初期の非表示状態を同じサンプルと組み合わせる必要があります。
  • 最後に、非表示の状態をもう一度更新し、出力シーケンスの最終要素を予測するために、サンプルをもう一度フィードします。

シーケンスの予測

シーケンスに基づいて単一の値を予測する方法、および単一の値に基づいてシーケンスを予測する方法を見てきました。 次に、シーケンスのシーケンスを予測する方法を見てみましょう。 このシナリオでは、RNNで予測を行うために、次の手順を実行できます-

  • まず、初期の非表示状態を作成し、出力シーケンスの最初の要素を予測するには、入力シーケンスの最初の要素を取得する必要があります。
  • その後、非表示状態を更新して出力シーケンスの2番目の要素を予測するには、初期の非表示状態を取得する必要があります。
  • 最後に、出力シーケンスの最終要素を予測するには、更新された非表示状態と入力シーケンスの最終要素を取得する必要があります。

RNNの働き

再帰型ニューラルネットワーク(RNN)の動作を理解するには、まずネットワークの再帰層がどのように機能するかを理解する必要があります。 それでは、最初に、eが標準の繰り返しレイヤーで出力を予測する方法について説明します。

標準RNNレイヤーを使用した出力の予測

前に説明したように、RNNの基本層はニューラルネットワークの通常の層とはかなり異なります。 前のセクションでは、RNNの基本的なアーキテクチャも図で示しました。 初めてのステップインシーケンスの非表示状態を更新するには、次の式を使用できます-

Rnn Layer

上記の方程式では、初期の非表示状態と重みのセットの間の内積を計算することにより、新しい非表示状態を計算します。

次のステップでは、現在のタイムステップの非表示状態が、シーケンスの次のタイムステップの初期非表示状態として使用されます。 そのため、2番目のタイムステップの非表示状態を更新するには、次のように最初のタイムステップで実行された計算を繰り返すことができます-

First Step

次に、以下のようにシーケンスの3番目と最後のステップの非表示状態を更新するプロセスを繰り返すことができます-

Last Step

そして、上記のすべてのステップをシーケンスで処理したら、次のように出力を計算できます-

出力の計算

上記の式では、3番目の重みのセットと最後のタイムステップからの非表示の状態を使用しました。

Advanced Recurrent Units

基本的な再発層の主な問題は、勾配の消失問題であり、このため、長期的な相関関係の学習にはあまり適していません。 簡単に言えば、基本的なリカレント層は長いシーケンスをうまく処理しません。 これが、より長いシーケンスでの作業に非常に適した他のいくつかの反復レイヤータイプが次の理由である-

Long-Short Term Memory(LSTM)

Long-Short Term Memory(LSTM)

長期短期記憶(LSTM)ネットワークはHochreiter&Schmidhuberによって導入されました。 これは、基本的なリカレントレイヤーに長い間覚えておくという問題を解決しました。 LSTMのアーキテクチャは、上記の図に示されています。 ご覧のとおり、入力ニューロン、メモリセル、出力ニューロンがあります。 消失する勾配の問題に対処するために、長期短期メモリネットワークは明示的なメモリセル(以前の値を格納)と次のゲートを使用します-

  • ゲートを忘れる-名前が示すように、以前の値を忘れるようにメモリセルに通知します。 メモリセルは、ゲートまで値を保存します。 「ゲートを忘れる」はそれらを忘れるように言います。
  • 入力ゲート −名前が示すように、新しいものをセルに追加します。
  • 出力ゲート −名前が示すように、出力ゲートは、セルから次の非表示状態にベクトルを渡すタイミングを決定します。

Gated Recurrent Units(GRU)

Gated Recurrent Units(GRUs)

*Gradient recurrent units* (GRUs)は、LSTMネットワークのわずかなバリエーションです。 ゲートが1つ少なく、LSTMとは少し配線が異なります。 そのアーキテクチャを上の図に示します。 入力ニューロン、ゲートメモリセル、出力ニューロンがあります。 Gated Recurrent Unitsネットワークには次の2つのゲートがあります-

ゲートの更新 −次の2つのことを決定します−

  • 最後の状態から保持する必要がある情報の量はどれくらいですか?
  • 前のレイヤーからどのくらいの量の情報を入れるべきですか?

*リセットゲート *−リセットゲートの機能は、LSTMネットワークの忘却ゲートの機能とよく似ています。 唯一の違いは、場所が少し異なることです。

長期短期メモリーネットワークとは対照的に、Gated Recurrent Unitネットワークはわずかに高速で実行が容易です。

RNN構造の作成

開始する前に、データソースからの出力について予測を行う前に、RNNを構築する必要があります。RNNの構築は、前のセクションで通常のニューラルネットワークを構築したのとまったく同じです。 以下は、1つをビルドするためのコードです。

from cntk.losses import squared_error
from cntk.io import CTFDeserializer, MinibatchSource, INFINITELY_REPEAT, StreamDefs, StreamDef
from cntk.learners import adam
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
BATCH_SIZE = 14* 10
EPOCH_SIZE = 12434
EPOCHS = 10

複数の層をステーキング

CNTKで複数の再発層をスタックすることもできます。 たとえば、次のレイヤーの組み合わせを使用できます

from cntk import sequence, default_options, input_variable
from cntk.layers import Recurrence, LSTM, Dropout, Dense, Sequential, Fold
features = sequence.input_variable(1)
with default_options(initial_state = 0.1):
   model = Sequential([
      Fold(LSTM(15)),
      Dense(1)
   ])(features)
target = input_variable(1, dynamic_axes=model.dynamic_axes)

上記のコードでわかるように、CNTKでRNNをモデル化するには、次の2つの方法があります-

  • まず、リカレントレイヤーの最終出力のみが必要な場合、 Fold レイヤーをGRU、LSTM、RNNStepなどのリカレントレイヤーと組み合わせて使用​​できます。
  • 次に、別の方法として、 Recurrence ブロックを使用することもできます。

時系列データを使用したRNNのトレーニング

モデルを構築したら、CNTKでRNNをトレーニングする方法を見てみましょう-

from cntk import Function
@Function
def criterion_factory(z, t):
   loss = squared_error(z, t)
   metric = squared_error(z, t)
   return loss, metric
loss = criterion_factory(model, target)
learner = adam(model.parameters, lr=0.005, momentum=0.9)

データをトレーニングプロセスに読み込むには、一連のCTFファイルからシーケンスを逆シリアル化する必要があります。 次のコードには create_datasource 関数があります。これは、トレーニングデータソースとテストデータソースの両方を作成するのに役立つユーティリティ関数です。

target_stream = StreamDef(field='target', shape=1, is_sparse=False)
features_stream = StreamDef(field='features', shape=1, is_sparse=False)
deserializer = CTFDeserializer(filename, StreamDefs(features=features_stream, target=target_stream))
   datasource = MinibatchSource(deserializer, randomize=True, max_sweeps=sweeps)
return datasource
train_datasource = create_datasource('Training data filename.ctf')#we need to provide the location of training file we created from our dataset.
test_datasource = create_datasource('Test filename.ctf', sweeps=1) #we need to provide the location of testing file we created from our dataset.

データソース、モデル、損失関数を設定したので、トレーニングプロセスを開始できます。 これは、前のセクションで基本的なニューラルネットワークを使用した場合と非常によく似ています。

progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
   features: train_datasource.streams.features,
   target: train_datasource.streams.target
}
history = loss.train(
   train_datasource,
   epoch_size=EPOCH_SIZE,
   parameter_learners=[learner],
   model_inputs_to_streams=input_map,
   callbacks=[progress_writer, test_config],
   minibatch_size=BATCH_SIZE,
   max_epochs=EPOCHS
)

次のような出力が得られます-

出力−

average  since  average  since  examples
loss      last  metric  last
------------------------------------------------------
Learning rate per minibatch: 0.005
0.4      0.4    0.4      0.4      19
0.4      0.4    0.4      0.4      59
0.452    0.495  0.452    0.495   129
[…]

モデルの検証

RNNで実際に予測することは、他のCNKモデルで予測を行うことと非常に似ています。 唯一の違いは、単一のサンプルではなくシーケンスを提供する必要があることです。

今、私たちのRNNは最終的にトレーニングで行われているので、次のようにいくつかのサンプルシーケンスを使用してテストすることでモデルを検証できます-

import pickle
with open('test_samples.pkl', 'rb') as test_file:
test_samples = pickle.load(test_file)
model(test_samples) * NORMALIZE

出力−

array([[dtype=float32)