NumPy から直接 RNN を作成するにはどうすればいいですか?

NumPy から直接 RNN を作成するにはどうすればいいですか?

成熟した Tensorflow および PyTorch フレームワークを使用して再帰ニューラル ネットワーク (RNN) を実装することで、このテクノロジを使用するハードルが大幅に下がりました。

しかし、初心者にとってはこれでは十分ではありません。事実を知るだけでは十分ではなく、その背後にある理由も知る必要があります。

[[347983]]

低レベルの間違いを避けるために、しっかりとした理論的基礎を築き、RNN を使用してより実用的な問題を解決します。

そこで、考えるべき興味深い質問があります。

Tensorflow のようなフレームワークを使用せずに、Numpy のみを使用して RNN を構築するにはどうすればよいですか?

わからなくても心配しないでください。ここにチュートリアルがあります: Numpy を使用して NLP 用の RNN をゼロから構築します。

RNN 構築プロセスを案内します。

初期化パラメータ

従来のニューラル ネットワークとは異なり、RNN には次の 3 つの重みパラメーターがあります。

入力重み、内部状態重み、出力重み

まず、上記の 3 つのパラメータをランダムな値で初期化します。

その後、単語埋め込み次元(word_embedding dimension)と出力次元(output dimension)がそれぞれ 100 と 80 に初期化されます。

出力次元は、語彙内に存在する一意の単語ベクトルの合計数です。

  1. 隠し次元= 100           
  2. output_dim = 80 # これは語彙内のユニークな単語の合計です 
  3. 入力重み= np.random.uniform(0, 1, (hidden_​​dim,hidden_​​dim))  
  4. 内部状態の重み= np .random.uniform(0,1, (hidden_​​dim, hidden_​​dim))  
  5. 出力重み= np .random.uniform(0,1, (出力次元、隠し次元))

変数 prev_memory は internal_state (前のシーケンスのメモリ) を参照します。

他のパラメータにも初期化値が与えられます。

input_weight 勾配、internal_state_weight 勾配、output_weight 勾配は、それぞれ dU、dW、dV と名付けられます。

変数 bptt_truncate は、バックプロパゲーション中にネットワークがバックトラックしなければならないタイムスタンプの数を表します。これは、勾配消失の問題を克服するために行われます。

  1. prev_memory = np.zeros ((hidden_​​dim,1))  
  2. 学習率= 0.0001  
  3. ネポック= 25                   
  4. T = 4 # シーケンスの長さ 
  5. bptt_切り捨て= 2     
  6. dU = np.zeros (入力重み.shape)  
  7. dV = np .zeros(出力の重み.shape)  
  8. dW = np.zeros (内部状態の重み.shape)

前方伝播

出力ベクトルと入力ベクトル

たとえば、「私は遊ぶのが好きです」という文があります。語彙が次のようになっているとします。

I はインデックス 2 にマップされ、同様にインデックス 45 に、はインデックス 10 に、** はインデックス 64 に、句読点 .** はインデックス 1 にマップされます。

入力から出力までに何が起こるかを示すために、まず各単語の単語埋め込みをランダムに初期化します。

  1. 入力文字列= [2,45,10,65]  
  2. 埋め込み= [] # これは各単語の埋め込みを含む文の埋め込みリストです 
  3. iが範囲(0,T)内にある場合:  
  4. x = np.random.randn (隠し次元、1)  
  5. 埋め込み.append(x)

入力が完了したので、出力を検討する必要があります。

このプロジェクトでは、RNN ユニットは入力を受け取った後、次に最も可能性の高い単語を出力します。

RNN をトレーニングするために使用され、t+1 番目の単語が出力として与えられたときに t 番目の単語を入力として受け取ります。たとえば、RNN ユニットが「like」という単語を出力する場合、与えられた入力単語は「I」です。

現在、入力は埋め込みベクトルの形式になっており、損失関数を計算するために必要な出力形式はワンホットエンコードされたベクトルです。

これは、ニューラル ネットワークが単一の例文からのみ学習し、初期入力がその文の最初の単語であるため、最初の単語を除く入力文字列内のすべての単語に対して実行されます。

RNNのブラックボックス計算

重みパラメータが決まり、入力と出力がわかったので、順方向伝播の計算を開始できます。

ニューラル ネットワークのトレーニングには、次の計算が必要です。

で:

U は入力重み、W は内部状態重み、V は出力重みを表します。

入力重みはinput(x)で乗算され、内部状態の重みは前の層のアクティベーション(prev_memory)で乗算されます。

レイヤー間で使用される活性化関数は tanh です。

  1. tanh_activation(Z)を定義します:  
  2. return (np.exp(Z)-np.exp(-Z))/(np.exp(Z)-np.exp(-Z)) # これはtanh関数で、np.tanh(Z)と書くこともできます。  
  3. softmax_activation(Z)を定義します:  
  4. e_x = np.exp (Z - np.max(Z)) # これはソフトマックス関数のコードです 
  5. e_x / e_x.sum(= 0 )を返す
  1. Rnn_forward(入力埋め込み、入力重み、内部状態重み、事前メモリ、出力重み)を定義します。  
  2. 転送パラメータ= []  
  3. W_frd = np .dot(内部状態の重み、前のメモリ)  
  4. U_frd = np .dot(入力重み、入力埋め込み)  
  5. 合計= W_frd + U_frd  
  6. ht_activated = tanh_activation (sum_s)
  7. yt_unactivated = np .asarray(np.dot(output_weights, tanh_activation(sum_s)))  
  8. yt_activated =ソフトマックス活性化(yt_unactivated)  
  9. forward_params.append([W_frd、U_frd、sum_s、yt_unactivated]) をフォワードします。  
  10. ht_activated、yt_activated、forward_params を返す

損失関数の計算

損失関数は、次の式で表わされるクロスエントロピー損失関数を使用します。

  1. def calculate_loss(出力マッパー、予測出力):  
  2. 合計損失= 0    
  3. レイヤー損失= []  
  4. for y,y_ in zip(output_mapper.values(),predicted_output): # この for ループ計算は最初の方程式用で、各タイムスタンプの損失が計算されます
  5.  損失= -sum(y[i]*np.log2(y_[i])、iが範囲(len(y))内にある場合 
  6. 損失損失= 損失 / float(len(y))
  7.   レイヤー損失.append(損失)  
  8. for i in range(len(layer_loss)): #これは、考慮されるすべてのタイムスタンプに対して計算された合計損失です。  
  9. 合計損失合計損失= 合計損失 + レイヤー損失[i]  
  10. total_loss/float(len(predicted_output)) を返す

最も重要なのは、上記のコードの 5 行目を確認することです。

ご存知のとおり、ground_truth output(y) は [0, 0, …., 1, …0] の形式であり、predicted_output(y^hat) は [0.34, 0.03, …, 0.45] の形式であるため、そこから総損失を推測するには損失が単一の値である必要があります。

これを行うには、sum 関数を使用して、特定のタイムスタンプにおける y ベクトルと y^hat ベクトルの各値の誤差の合計を取得します。

total_loss はモデル全体の損失です (すべてのタイムスタンプを含む)。

バックプロパゲーション

バックプロパゲーションの連鎖律:

上の図に示すように:

コストは誤差を表し、y^hat から y までの差を表します。

Cost は関数の出力であるため、活性化 a によって反映される変化は dCost/da で表されます。

実際には、これはアクティブ化されたノードの観点からの変更(エラー)値を意味します。

同様に、a の z に対する変化は da/dz として表され、z の w に対する変化は dw/dz として表されます。

最終的に、私たちが気にするのは、重みの変化(誤差)がどれだけ大きいかということです。

重量とコストには直接的な関係がないため、期間中の相対的な変化値を直接乗算することができます(上記の式に示すように)。

RNNのバックプロパゲーション

RNN には 3 つの重みがあるため、3 つの勾配が必要です。 input_weights(dLoss/dU)、internal_state_weights(dLoss/dW)、およびoutput_weights(dLoss/dV)の勾配。

これら 3 つのグラデーションの連鎖は次のように表すことができます。

dLoss/dy_unactivated コードは次のとおりです。

  1. def delta_cross_entropy(予測出力、オリジナルt出力):  
  2. li = []  
  3. grad =予測出力   
  4. for i,l in enumerate(original_t_output): # インデックスの値が 1 かどうかを確認し、1 の場合は、predicted_ouput リストから同じインデックス値を取得し、そこから 1 を減算します。
  5. l == 1の場合:  
  6. # grad = np .asarray(np.concatenate( grad, axis = 0 ))  
  7. 等級[i] - = 1  
  8.   卒業証書を返す

2 つの勾配関数を計算します。1 つは multiplication_backward、もう 1 つは additional_backward です。

multiplication_backward の場合、2 つのパラメータが返されます。1 つは重みに関する勾配 (dLoss/dV) であり、もう 1 つはチェーン勾配です。チェーン勾配は、別の重みの勾配を計算するためのチェーンの一部になります。

addition_backward の場合、導関数を計算するときに、加算関数 (ht_unactivated) 内の個々のコンポーネントの導関数は 1 になります。たとえば、dh_unactivated / dU_frd = 1 (h_unactivated = U_frd + W_frd) であり、dU_frd / dU_frd の導関数は 1 です。

したがって、勾配を計算するには、これら 2 つの関数だけが必要です。 multiplication_backward 関数はベクトルのドット積を含む方程式に使用され、addition_backward は 2 つのベクトルの加算を含む方程式に使用されます。

  1. def multiplication_backward(重み,x,dz):  
  2. 勾配の重み= np .array(np.dot(np.asmatrix(dz),np.transpose(np.asmatrix(x))))  
  3. チェーン勾配= np .dot(np .transpose(重み),dz)  
  4. gradient_weight、chain_gradient を返す 
  5. def add_backward(x1,x2,dz): # この関数はht_unactivated関数の導関数を計算するためのものです 
  6. dx1 = dz * np.ones_like(x1)  
  7. dx2 = dz * np.ones_like(x2)  
  8. dx1、dx2を返す 
  9. tanh_activation_backward(x,top_diff)を定義します。  
  10. 出力= np .tanh(x)  
  11. 戻り値 (1.0 - np.square(出力)) * top_diff

これまで、RNN のバックプロパゲーションを分析して理解してきました。現在、RNN は単一のタイムスタンプに機能を実装しており、これを使用してすべてのタイムスタンプの勾配を計算できます。

以下のコードに示すように、forward_params_t は特定の時間ステップにおけるネットワークのフォワードパラメータを含むリストです。

変数 ds は重要な部分です。このコード行は、バックプロパゲーション中に必要な情報を抽出するのに役立つ、以前のタイムスタンプでの隠し状態を考慮に入れます。

  1. def single_backprop(X,input_weights,internal_state_weights,output_weights,ht_activated,dLo,forward_params_t,diff_s,prev_s): # そこにあるすべてのデータのすべてのパラメータ値をインスライドします
  2. W_frd = forward_params_t [0][0]  
  3. U_frd =転送パラメータt [0][1]  
  4. ht_unactivated = forward_params_t [0][2]  
  5. yt_unactivated = forward_params_t [0][3]  
  6. dV、 dsv =乗算_後方(出力重み、ht_activated、dLo)  
  7. ds = np .add(dsv,diff_s) # メモリの切り捨てに使用 
  8. dadd = tanh_activation_backward (ht_unactivated、ds)  
  9. dmulw、 dmulu = add_backward (U_frd、W_frd、dadd)  
  10. dW、 dprev_s =乗算後方(内部状態の重み、prev_s、dmulw)  
  11. dU, dx = multiplication_backward (input_weights, X, dmulu) #入力重み 
  12. 戻り値 (dprev_s, dU, dW, dV)

RNN の場合、勾配消失の問題のため、元のバックプロパゲーションの代わりに切り捨てられたバックプロパゲーションが使用されます。

この手法では、現在のセルは 1 つのタイムスタンプだけを参照するのではなく、k 個のタイムスタンプのみを参照します。ここで、k は参照する前のセルの数を表します。

  1. def rnn_backprop(埋め込み、メモリ、出力t、dU、dV、dW、bptt_truncate、入力重み、出力重み、内部状態重み):  
  2. 4 = 4です   
  3. # 最初のタイムスタンプからバックプロパゲーションを開始します。  
  4. tが範囲内(4)の場合:
  5.   prev_s_t = np .zeros((hidden_​​dim,1)) #最初のタイムスタンプには前のメモリがないので必須。
  6.  
  7. diff_s = np .zeros((hidden_​​dim,1)) # これは、前のレベルから以前の情報を復元するための切り捨ての目的で使用されます 
  8. 予測=メモリ["yt" + str(t)]  
  9. ht_activated =メモリ["ht" + str(t)]  
  10. forward_params_t =メモリ["params" + str(t)]  
  11. dLo = delta_cross_entropy (predictions,output_t[t]) #特定のタイムスタンプの損失導関数 
  12. dprev_s、dU_t、dW_t、 dV_t = single_backprop (埋め込み[t]、入力重み、内部状態重み、出力重み、ht_activated、dLo、前方パラメータt、diff_s、prev_s_t)
  13.   prev_s_t = ht_activated    
  14. = t -1  
  15. dLo = np .zeros((output_dim,1)) #ここでは、変換された情報には必要ないため、損失微分は 0 になります。  
  16. # 次のコードは、切り捨てられた bptt と各タイムスタンプ用です。
  17.   iが範囲(t-1,max(-1,t-bptt_truncate),-1)内である場合:
  18.   forward_params_t =メモリ["params" + str(i)]  
  19. ht_activated =メモリ["ht" + str(i)]  
  20. prev_s_i = np .zeros((hidden_​​dim,1)) i == 0 の場合、それ以外の場合は memory["ht" + str(prev)]  
  21. dprev_s、dU_i、dW_i、 dV_i = single_backprop (埋め込み[t]、入力重み、内部状態重み、出力重み、ht_activated、dLo、forward_params_t、dprev_s、prev_s_i)
  22. dU_t += dU_i # ルックバック時の以前の勾配を現在の時間シーケンスに追加する 
  23. dW_t += dW_i  
  24. dV += dV_t  
  25. dU += dU_t  
  26. dW += dW_t  
  27. リターン(dU、dW、dV)

体重更新

バックプロパゲーションを使用して勾配を計算したら、バッチ勾配降下法によって重みを更新することが必須です。

  1. gd_step(学習率、dU、dW、dV、入力重み、内部状態重み、出力重み)を定義します。  
  2. 入力重み- =学習率* dU  
  3. 内部状態の重み- =学習率* dW  
  4. 出力重み- =学習率* dV  
  5. input_weights、internal_state_weights、output_weights を返す

トレーニングシーケンス

上記の手順をすべて完了したら、ニューラル ネットワークのトレーニングを開始できます。

トレーニングに使用される学習率は静的ですが、ステップ減衰などの動的な方法を使用して学習率を変更することもできます。

  1. def train(T, 埋め込み, output_t, output_mapper, 入力重み, 内部状態重み, 出力重み, dU, dW, dV, 事前メモリ,学習率= 0.001 , nepoch = 100 ,評価損失後= 2 ):
  2.  損失= []  
  3. 範囲内のエポック(nepoch)の場合:  
  4. if(エポック% evaluate_loss_after == 0):  
  5. 出力文字列、メモリ= full_forward_prop (T、埋め込み、入力重み、内部状態の重み、前のメモリ、出力重み)  
  6. 損失= calculate_loss (出力マッパー、出力文字列)  
  7. 損失.append(損失)  
  8. 時刻= datetime.now ().strftime('%Y-%m-%d %H:%M:%S')  
  9. print("%s:エポック後の損失=%d: %f" % (time,epoch,loss))  
  10. sys.stdout.flush()  
  11. dU、dW、 dV = rnn_backprop (埋め込み、メモリ、出力t、dU、dV、dW、bptt_truncate、入力重み、出力重み、内部状態重み)  
  12. 入力重み、内部状態重み、出力重み= sgd_step (学習率、dU、dW、dV、入力重み、内部状態重み、出力重み)  
  13. リターンロス
  1. 損失=トレーニング(T、埋め込み、出力 t、出力マッパー、入力重み、内部状態の重み、出力重み、dU、dW、dV、事前メモリ、学習率= 0.0001 nepoch = 10 評価損失後= 2 )

おめでとう!これで、リカレント ニューラル ネットワークをゼロから構築できました。

次に、LSTM や GRU などの高度なアーキテクチャに移ります。

<<:  ボストン・ダイナミクスのロボット犬はまもなく腕が生え、走って充電できるようになる

>>:  産業用 AI が将来、精製業界にどのような力を与えるか

ブログ    
ブログ    

推薦する

...

人工知能では顔と性格の違いは分からない

中国の研究チームは、女性の外見だけに基づいてその性格特性を予測できる人工知能プログラムを立ち上げたと...

自動運転はAIの今後の発展の鍵となるのか?

現地時間10月12日、テスラのCEO、イーロン・マスク氏はツイッターで「約束通り、完全自動運転機能(...

...

...

MITとHKUは、Transformerを超える精度を持つ物理モデルに基づく視覚推論フレームワークを提案

[[437809]]動的視覚推論、特にオブジェクト間の物理的な関係についての推論は、コンピューター ...

人工知能チップの過去、現在、そして未来

AIは現在ニュースでよく取り上げられています。現在、AIは医療診断、新しい化学物質の合成、群衆の中に...

俳優の顔の交換、AIデート、モザイク除去…2020年のAI界の注目トピックトップ10を振り返る

[[373822]] 2020年が終わりを迎えました。今年、人工知能(AI)分野は浮き沈みに富み、常...

「トランスフォーマー チャレンジャー」マンバはMacBookでも動く! GitHub は半日で 500 以上のスターを獲得しました

「トランスフォーマーの挑戦者」MambaがMacBookで実行できるようになりました!誰かが Git...

顔認識会社Clearviewのソースコードがサーバーの設定ミスにより公開される

この記事はLeiphone.comから転載したものです。転載する場合は、Leiphone.com公式...

メタバースがますます熱を帯びる中、開発者はどのような主要テクノロジーを掘り下げていくべきでしょうか?

「メタバース」という概念は昨年、海外で爆発的に広まりました。国内の専門家も、我が国の関連技術の開発...

...

小売業界のトレンド: 人工知能からクーポンコードまで

テクノロジーによりシステム効率が大幅に向上し、ビジネス運営のコスト効率と時間効率が向上しました。テク...

...

「統合インテリジェンス」について語るサンダーソフトはスマートカーの時代をリードする

[51CTO.comからのオリジナル記事] 5G+AIoTを中核とするさまざまなインテリジェント技術...