リカレントニューラルネットワークの分析を深く理解する

リカレントニューラルネットワークの分析を深く理解する

[[211637]]

リカレント ニューラル ネットワーク (RNN) は、レイヤー内に重み付けされた接続が含まれるニューラル ネットワークの一種です (接続が後続のレイヤーにのみフィードされる従来のフィードフォワード ネットワークとは対照的です)。 RNN にはループが含まれているため、新しい入力を処理しながら情報を保存できます。このメモリにより、時系列データなど、事前の入力を考慮する必要があるタスクに適しています。このため、現在のディープラーニング ネットワークはすべて RNN に基づいています。このチュートリアルでは、RNN の背後にあるアイデアを探り、RNN をゼロから実装してシーケンス データの予測を実行します。

ニューラル ネットワークは、高度に接続された処理要素 (ニューロン) のネットワークに基づいて入力を出力にマッピングする計算構造です。ニューラル ネットワークの簡単な紹介については、パーセプトロン (ニューラル ネットワークの構成要素) とバックプロパゲーション学習機能を備えた多層パーセプトロンを分析する私の別のチュートリアル「ニューラル ネットワークの詳細」をお読みください。

前回のチュートリアルでは、フィードフォワード ネットワーク トポロジについて説明しました。このトポロジ (下の図を参照) では、入力ベクトルを隠れ層を介してネットワークに送り込み、最終的に出力を得ることができます。このネットワークでは、入力は決定論的に出力にマッピングされます(入力が適用されるたびに)

ただし、時系列データを扱っていると仮定しましょう。単一のデータ ポイントだけを単独で使用しても、重要なプロパティ (データ シリーズは変化しているか? 増加しているか? 減少しているかなど) が欠けているため、完全には役に立ちません。文字または単語がネットワーク入力を表す自然言語処理アプリケーションを考えてみましょう。単語を理解することを考えるとき、文脈の中での文字が重要になります。これらの入力は単独では役に立ちませんが、以前に起こったことのコンテキストに置かれた場合のみ役に立ちます。

時系列データへのアプリケーションでは、入力履歴を考慮できる新しいタイプのトポロジが必要です。ここで RNN が登場します。 RNN はフィードバックを通じて内部メモリを維持できるため、一時的な動作をサポートします。次の例では、隠し層の出力が隠し層に再度適用されます。ネットワークはフィードフォワードのままですが (入力は最初に隠し層に適用され、次に出力層に適用されます)、RNN はコンテキスト ノード (後続の入力の隠し層に影響します) を介して内部状態を維持します。

RNN はネットワークの一種ではなく、さまざまな問題を解決するトポロジの集合です。再帰型ネットワークの重要な側面は、十分な層とノードがあればチューリング完全であり、つまり、計算可能な任意の関数を実装できることです。

RNN アーキテクチャ

RNN は 1980 年代に導入され、過去の入力の記憶を保持する能力により、ニューラル ネットワークに新たな問題領域が生まれました。使用できるアーキテクチャのいくつかを見てみましょう。

ホップフィールド

ホップフィールド ネットワークは連想記憶の一種です。入力パターンを指定すると、その入力に最も類似したパターンを取得します。この関連付け(入力と出力のつながり)は、人間の脳の働きに似ています。ホップフィールド ネットワークは、人間が記憶の一部を与えられたときにその記憶を完全に思い出すことができる仕組みと同様に機能します。

ホップフィールド ネットワークは本質的にバイナリであり、個々のニューロンはオン (アクティブ) またはオフ (非アクティブ) のいずれかになります。すべてのニューロンは、重み付けされた接続を通じて他のすべてのニューロンに接続されます (下の図を参照)。各ニューロンは入力と出力の両方の役割を果たします。初期化時に、部分的なパターンがネットワークにロードされ、ネットワークが収束するまで(収束します)各ニューロンが更新されます。出力は収束時に提供されます(ニューロンの状態)。

ホップフィールド ネットワークは、複数のパターンを学習 (ヘブビアン学習経由) することができ、入力にノイズがある場合でも最も近いパターンを思い出すように収束します。ホップフィールド ネットワークは、時間領域の問題を解決するのには適していませんが、再帰的な問題を解決するのには適しています。

単純再帰ネットワーク

単純再帰型ネットワークは、ネットワークに状態を導入するステートフル レイヤーを含む、再帰型ネットワークの一般的なクラスです。 。状態レイヤーは次のステージへの入力に影響を与えるため、時間の経過とともに変化するデータ パターンに適用できます。

状態はさまざまな方法で適用できますが、2 つの一般的なアプローチは Elman ネットワークと Jordan ネットワークです (下の図を参照)。エルマン ネットワークでは、隠し層は過去の入力のメモリを保持するコンテキスト ノード状態層にデータを供給します。下の図に示すように、以前の隠し層の結果のメモリを保持するコンテキスト ノードのセットがあります。もう一つの一般的なトポロジーは、Jordan ネットワークです。ジョーダン ネットワークは、隠し層の履歴を保持する代わりに、出力層を状態層に格納する点で異なります。

Elman ネットワークと Jordan ネットワークは標準的なバックプロパゲーションによってトレーニングすることができ、それぞれシーケンス認識と自然言語処理に応用できます。ここでは 1 つの状態レイヤーのみが紹介されていることに注意してください。ただし、状態レイヤーの出力が後続の状態レイヤーへの入力として機能する状態レイヤーをさらに追加できることは容易にわかります。このチュートリアルでは、Elman ネットワークのセクションでこの概念について説明します。

その他のネットワーク

再帰型ネットワークの研究は止まらず、今日では再帰型アーキテクチャが時系列データの処理の標準となっています。深層学習における Long Short-Term Memory (LSTM) 方式は、畳み込みネットワークに適用され、生成された言語を通じて画像やビデオの内容を記述します。 LSTM には忘却ゲートが含まれており、これにより個々のニューロンを「トレーニング」して、どの情報が重要か、その情報をどれだけの期間重要に保つかを学習できます。 LSTM は、重要なイベント間の間隔が長いデータを処理できます。

もう一つの一般的なアーキテクチャは、Gated Recurrent Unit (GRU) と呼ばれます。 GRU は LSTM の最適化であり、必要なパラメーターとリソースが少なくなります。

RNN トレーニング アルゴリズム

RNN は履歴情報を時間的または順次的に組み込む特性があるため、独自のトレーニング アルゴリズムを備えています。勾配降下法は、RNN 重み最適化 (重みに対する誤差導関数に比例して重みを調整することで誤差を最小化する) にうまく適用されています。よく使われる手法は、Back Propagation Through Time (BPTT) です。これは、シーケンス内の各要素の累積エラーの重み更新を合計し、重みを更新することで重み更新を適用します。大きな入力シーケンスの場合、この動作により重みが消失または爆発する可能性があります (消失勾配問題または爆発勾配問題として知られています)。この問題を解決するために、BPTT とリアルタイム再帰学習などの他のアルゴリズムを組み合わせたハイブリッド アプローチがよく使用されます。

他のトレーニング方法も、進化する RNN に適用できます。進化アルゴリズム (遺伝的アルゴリズムやシミュレーテッド アニーリングなど) を適用して候補となる RNN の集団を進化させ、それらの適応度 (つまり、特定の問題を解決する能力) に応じてそれらを再結合することができます。解への収束は保証されていませんが、収束は RNN 進化を含むさまざまな問題にうまく適用できます。

RNN の便利な用途の 1 つは、シーケンスの予測です。次の例では、少数の語彙に基づいて単語の最後の文字を予測する RNN を構築します。単語を RNN に入力し、一度に 1 文字ずつ読み込むと、ネットワークの出力は予測された次の文字を表します。

遺伝的アルゴリズムフロー

RNN の例を見る前に、遺伝的アルゴリズムの背後にあるプロセスを見てみましょう。遺伝的アルゴリズムは、自然選択のプロセスにヒントを得た最適化手法です。下の図に示すように、アルゴリズムは、見つけるべきソリューションのパラメータをエンコードする候補ソリューションのランダムな集団 (染色体と呼ばれる) を作成します。作成された後、集団の各メンバーは対応する質問に対してテストされ、適合値が割り当てられます。次に、集団から親染色体(適応度の高いもの)が特定され、次の世代のために娘染色体が作成されます。娘染色体の生成では、遺伝的演算子が適用されます(各親染色体から要素を取得する(交差と呼ばれる)ことや、娘染色体にランダムな変化を導入する(突然変異と呼ばれる)ことなど)。適切な候補ソリューションが見つかるまで、このプロセスは新しい集団で再び開始されます。

染色体グループを使用したニューラルネットワークの表現

染色体は集団のメンバーとして定義され、解決すべき特定の問題のコードを含んでいます。進化する RNN のコンテキストでは、染色体は下の図に示すように RNN の重みで構成されます。

各染色体には、重みごとに 16 ビットの値が含まれています。この値 (0 ~ 65535) を、範囲の半分を減算し、0.001 を掛けて重みに変換します。つまり、このエンコーディングでは、-32.767 から 32.768 までの値を 0.001 単位で表すことができます。

集団から染色体を取り出して RNN を生成するプロセスは、染色体から変換された重みを使用してネットワークの重みを初期化することとして定義するだけで済みます。この場合、重みは 233 になります。

RNN を使用した文字の予測

それでは、ニューラル ネットワークにおける文字の使用について見ていきましょう。ニューラル ネットワークは数値を扱うため、文字をネットワークに入力するには何らかの形式の表現が必要です。この例では、ワンホットエンコーディングを使用しました。ワンホット エンコーディングは、文字をベクトルに変換し、ベクトルには 1 つの要素のみが設定されます。このエンコーディングにより、数学的に使用できる固有の機能が作成されます。たとえば、表現の各文字には、ネットワークに適用される独自の重みがあります。この実装では文字をワンホットエンコーディングで表現していますが、自然言語処理アプリケーションでも同様に単語を表現します。次の図は、この例で使用されるワンホット ベクトルとテストに使用される語彙を示しています。

これで、RNN が文字を処理できるようにするエンコーディングができました。それでは、RNN のコンテキストで文字がどのように処理されるかを見てみましょう。次の図は、文字予測のコンテキストにおける Elman スタイルの RNN を示しています (文字 b を表すワンホット ベクトルを入力)。テスト単語の各文字をワンホット コードにエンコードし、それをネットワークへの入力として送ります。次に、ネットワークはフィードフォワード方式で実行され、出力は勝者総取り方式で解析され、ワンホット ベクトルを定義する勝利要素 (この場合は文字 a) が決定されます。この実装では、単語の最後の文字のみがチェックされ、他の文字は検証では無視され、適合度の計算は実行されません。

シンプルなエルマンスタイルのRNN実装

遺伝的アルゴリズムを使用してトレーニングされた Elman スタイルの RNN のサンプル実装を見てみましょう。この実装の Linux ソース コードは GitHub にあります。実装は 3 つのファイルで構成されます。

  • main.cはメインループ、テスト関数、集団の適応度を取得するための関数を提供します。
  • ga.cは遺伝的アルゴリズムの機能を実装する
  • rnn.cは実際のRNNを実装する

ここでは、遺伝的アルゴリズム プロセスと RNN 評価機能という 2 つのコア機能に焦点を当てます。

RNN の中核は、RNN ネットワークの実行を実装する RNN_feed_forward 関数にあります (以下のコードを参照)。この機能は、上図に示すネットワークと同様に 3 つのステージに分割されます。最初の段階では、入力層とコンテキスト層を組み合わせた隠れ層の出力が計算されます (各層には独自の重みセットがあります)。指定された単語をテストする前に、コンテキスト ノードは 0 に初期化されています。第二段階では、出力層の出力を計算します。このステップでは、各隠れ層のニューロンに独自の重みが組み込まれます。 ***、第 3 段階では、*** コンテキスト レイヤー ニューロンを 2 番目のコンテキスト レイヤー ニューロンに伝播し、隠しレイヤーの出力を *** コンテキスト ノードに伝播します。このステップでは、ネットワークに 2 つのメモリ レイヤーを実装します。

隠れ層の活性化関数として tan 関数を使用し、出力層の活性化関数としてシグモイド関数を使用していることに注意してください。 tan 関数は範囲が -1 ~ 1 であるため、隠し層で役立ちます (隠し層からの正と負の両方の出力を使用することもできます)。出力層では、ワンホット活性化ベクトルの最大値に興味があり、その範囲が 0 から 1 であるためシグモイドを使用しました。

  1. void RNN_feed_forward( void )
  2.  
  3. {
  4.  
  5. 整数i、j、k;
  6.  
  7.   
  8.  
  9. // ステージ 1: 隠れ層の出力を計算する
  10.  
  11. ( i = 0 ; i < HIDDEN_NEURONS ; i++ )の場合
  12.  
  13. {
  14.  
  15. 隠された[ i ] = 0.0;
  16.  
  17.   
  18.  
  19. // 入力を組み込みます。
  20.  
  21. ( j = 0 ; j < INPUT_NEURONS+1 ; j++ )の場合
  22.  
  23. {
  24.  
  25. 隠し[ i ] += w_h_i[ i ][ j ] * 入力[ j ];
  26.  
  27. }
  28.  
  29.   
  30.  
  31. // 繰り返しの隠し要素を組み込みます。
  32.  
  33. 隠し[ i ] += w_h_c1[ i ] * コンテキスト1[ i ];
  34.  
  35. 隠し[ i ] += w_h_c2[ i ] * context2[ i ];
  36.  
  37.   
  38.  
  39. // tanh活性化関数を適用します
  40.  
  41. 隠された[ i ] = tanh(隠された[ i ] );
  42.  
  43. }
  44.  
  45.   
  46.  
  47. // ステージ2:出力層の出力を計算する
  48.  
  49. ( i = 0 ; i < OUTPUT_NEURONS ; i++ )の場合
  50.  
  51. {
  52.  
  53. 出力[ i ] = 0.0;
  54.  
  55.   
  56.  
  57. ( j = 0 ; j < HIDDEN_NEURONS+1 ; j++ )の場合
  58.  
  59. {
  60.  
  61. 出力[ i ] += ( w_o_h[ i ][ j ] * 隠し[ j ] );
  62.  
  63. }
  64.  
  65.   
  66.  
  67. // シグモイド活性化関数を適用します
  68.  
  69. 出力[ i ] = シグモイド(出力[ i ] );
  70.  
  71. }
  72.  
  73.   
  74.  
  75. // ステージ3: コンテキストの隠し値を保存する
  76.  
  77. (k = 0; k < HIDDEN_NEURONS+1; k++)の場合
  78.  
  79. {
  80.  
  81. コンテキスト2[k] = コンテキスト1[k];
  82.  
  83. コンテキスト1[k] = 隠し[k];
  84.  
  85. }
  86.  
  87.   
  88.  
  89. 戻る;
  90.  
  91. }

以下のコード例で遺伝的アルゴリズムを実装しました。このコードは 3 つの部分で表示できます。 *** 部分は、集団全体の適応度 (選択プロセス中に使用される) と、集団内で最も適応度の高い染色体を計算します。最も適応した染色体が第 2 部で使用され、この染色体のみが次の集団にコピーされます。これはエリート選択の一種であり、最も適応した染色体が次の集団に複製されるまで維持されます。この集団には 2,000 本の染色体が含まれています。

遺伝的アルゴリズムの最初の部分では、集団から 2 つの親染色体をランダムに選択し、それらを使用して次の集団の子染色体を作成します。選択アルゴリズムは、いわゆるルーレットホイール選択法に基づいており、染色体はランダムに選択されますが、より一致する親染色体が選択される可能性が高くなります。 2 つの親染色体を選択した後、それらは次の集団の娘染色体に組み換えられます。このプロセスは、交雑(父親の遺伝子を選択して広める)と突然変異(重みをランダムに再定義する)の可能性を意味します。交雑および突然変異の確率は低い (組み換えごとに 1 つの突然変異、交雑はさらに少ない)。

  1. void GA_process_population(符号なし整数ポップ)
  2.  
  3. {
  4.  
  5. ダブル 合計= 0.0;
  6.  
  7. ダブル 最大値= 0.0;
  8.  
  9. intベスト;
  10.  
  11. int i、子;
  12.  
  13.   
  14.  
  15. 最高 = 0;
  16.  
  17. 合計=最大= 人口[ pop ][ best ].fitness;
  18.  
  19.   
  20.  
  21. // 集団全体の適応度を計算する
  22.  
  23. ( i = 1 ; i < POP_SIZE ; i++ )の場合
  24.  
  25. {
  26.  
  27. 合計+= 人口[ pop ][ i ].適応度;
  28.  
  29. 人口[pop][i].fitness > max場合
  30.  
  31. {
  32.  
  33. ベスト = i;
  34.  
  35. 最大値= 人口[ ポップ ][ i ].適応度;
  36.  
  37. }
  38.  
  39. }
  40.  
  41.   
  42.  
  43. // エリート主義者- 最もパフォーマンスの高い染色体を保持します。  
  44.  
  45. 再結合(ポップ、ベスト、ベスト、0、0.0、0.0);
  46.  
  47.   
  48.  
  49. //次の世代を生成します。
  50.  
  51. (子 = 1 ; 子 < POP_SIZE ; 子++ )
  52.  
  53. {
  54.  
  55. 符号なし整数parent1 = select_parent( pop, sum );
  56.  
  57. 符号なし整数parent2 = select_parent( pop, sum );
  58.  
  59.   
  60.  
  61. 再結合(ポップ、親1、親2、子、MUTATE_PROB、CROSS_PROB);
  62.  
  63. }
  64.  
  65.   
  66.  
  67. 戻る;
  68.  
  69. }

サンプル実行

GitHub 上のサンプル ソース コードは、make と入力して ./rnn で実行することで Linux でビルドできます。実行されると、集団がランダムに作成され、テスト語彙全体の最後の文字を正確に予測するソリューションが見つかるまで、またはシミュレーションがソリューションに正しく収束しなくなるまで、いくつかの世代にわたって自然選択が実行されます。成功または失敗は平均適応度によって決定されます。平均適応度が最大適応度の 80% に達した場合、集団は解決策を見つけるのに十分な多様性を欠いており、終了します。

解決策が見つかった場合、コードはテスト語彙全体を公開し、各単語の予測を表示します。染色体の適応度は単語の最後の文字のみに基づいているため、内部の文字は予測されないことに注意してください。次のコードは成功した出力のサンプルを示しています。

  1. $ ./rnn
  2.  
  3. 解決策が見つかりました。
  4.  
  5.   
  6.  
  7. テストベース
  8.  
  9. フェッドb、ゲットs
  10.  
  11. a を与えて b を得た
  12.  
  13. 連邦捜査官は、
  14.  
  15. フェッドe、ゲットd
  16.  
  17.   
  18.  
  19. 検査禁止
  20.  
  21. フェッドb、ゲットs
  22.  
  23. a を与えて b を得た
  24.  
  25. フェッドn、dを得た
  26.  
  27. フェッドe、ゲットd
  28.  
  29.   
  30.  
  31. テストセダン
  32.  
  33. 連邦、連邦
  34.  
  35. フェッドe、ゲットd
  36.  
  37. フェッドd、ゲットs
  38.  
  39. 与えて、nを得た
  40.  
  41.   
  42.  
  43. ...
  44.  
  45.   
  46.  
  47. テスト室
  48.  
  49. 与えたd、得たd
  50.  
  51. フェッドe、nを得た
  52.  
  53.   
  54.  
  55. 腹筋のテスト 
  56.  
  57. 与えて、得た
  58.  
  59. フェッドb、ゲットs
  60.  
  61.   
  62.  
  63. 悲しいテスト
  64.  
  65. 連邦、連邦
  66.  
  67. 与えて、得た

下の図は、平均適合度曲線と最適適合度曲線を示しています。各グラフはフィットネス レベルが約 13 から始まることに注意してください。 12 個の単語が d で終わるため、任意の文字列に対して d を発行するネットワークはこのレベルの成功を達成します。 ただし、以前の文字を考慮して特定の語彙を正確に予測するには、これらの重みを調整する必要があります。図に示すように、実行を成功させるには、正常に実行された最後のテスト ケースを予測するために、半数以上の世代が必要です。

興味深いことに、各グラフは、進化生物学における断続平衡と呼ばれる概念を示しています。断続平衡とは、進化上の変化の爆発によって中断される、長期間の静的平衡(全体的な安定)を特徴とする現象です。 1 つのケースでは、この進化の爆発により局所的最小値での停滞がもたらされ、もう 1 つのケースでは、進化は成功します (局所的最大値での停滞)。

結論

従来のニューラル ネットワークは、入力ベクトルを出力ベクトルに決定論的にマッピングできます。多くの問題ではこれが理想的ですが、順次データや時系列データを考慮する必要がある場合、ネットワークに内部メモリを導入すると、出力の決定を行う際に以前のデータを考慮できるようになります。 RNN は従来のフィードフォワード ネットワークにフィードバックを導入し、1 つ以上のレベルのメモリを含めることができるようになります。 RNN は将来のインフラストラクチャを表し、LSTM や GRU などの最も高度なディープラーニング技術に使用されています。

<<:  百度技術委員会の呉華委員長:NLP技術は機械に人間の言語によるコミュニケーション能力を持たせるはずだ

>>:  1 つの記事で RNN (リカレント ニューラル ネットワーク) の基礎を理解する

ブログ    

推薦する

...

IoTとAIを活用して価値を加速させる4つの効果的な方法

Twitter、LinkedIn、そして多くの IoT 関連の Web サイトを見ると、モノのインタ...

...

DeepMind: ビッグモデルのもう一つの大きな欠陥は、正しい答えが事前にわかっていなければ推論を自己修正できないことだ。

大規模言語モデルのもう一つの重大な欠陥が DeepMind によって明らかにされました。 LLM は...

...

ニューラルネットワークの仕組みを1つの記事で学ぶ

出典: getwallpapers.comディープラーニングは機械学習の重要な分野の 1 つです。そ...

OpenAI の予測: スーパーインテリジェンスは 10 年以内に到来する!鄭済集落は人類を救い、4年で同盟を完全に征服した

スーパーAIがみんなを殺す!サム・アルトマン氏は、AI の将来について何度も公に懸念を表明しており、...

ディープラーニングと自動テキスト要約が出会うとき

[[198984]]導入近年のテキスト情報の爆発的な増加により、人々はニュース、ブログ、チャット、レ...

機械学習研究の10年

[[271167]] 10年前のMSRAの夏、私が初めて機械学習の研究に挑戦したとき、科学研究におけ...

...

大規模言語モデルの効率的なパラメータ微調整 - BitFit/Prefix/Prompt 微調整シリーズ

2018年にGoogleはBERTをリリースしました。リリース後すぐに11のNLPタスクで最先端(S...

TikTok買収事件、主要アルゴリズムが焦点に 英国メディア:買収候補は4つの選択肢を提示

ロイター通信が2日報じたところによると、TikTokの買収候補らは、主要アルゴリズムを伴わない買収を...

LLaMA の微調整によりビデオメモリの要件が半分に削減され、清華大学は 4 ビットの最適化を提案

大規模モデルのトレーニングと微調整にはビデオ メモリに対する要件が高く、オプティマイザーの状態は主要...

自動運転データの所有権をめぐる戦い

次のようなシナリオを想像してください。 あなたはレベル3の自動運転機能を備えたAudi A8を所有し...