Pythonと機械学習

Pythonも機械学習も初心者ですが、頑張ってこのブログで勉強してこうと思います。

RNN

目次

はじめに

  • RNN(Recurrent Neural Network)は、時間に依存したデータのパターンを学習してくれるネットワークです。

  • 今回はRNNについて計算グラフを用いて行列で順伝播と逆伝播の式を導出してみたいと思います。

  • 計算グラフについては以下過去記事を参照ください。

  • RNNで扱われるデータは、サンプル数とユニット数のインデックスの他に、時間インデックスが増えるのため、3階のテンソルになります。

  • 例えば、ネットワークの \lambda 層目の中間層ユニットのデータ形式\varphi^{(\lambda)}_{itj} となります。(  i がサンプル数、  t が時間、  j がユニット数を表すインデックスです)

  • もちろん、特徴量データや目標値データ(教師データ)も同じように3階のテンソル x_{itj}y_{itj} になります。

  • 行列で表現するときは、3階テンソル \varphi^{(\lambda)}_{itj} のサンプル数とユニット数のインデックスを行列形式にまとめ、時間インデックスだけで \mathbf{\Phi}^{(\lambda)}_{t} と表してやります。

RNNブロックの順伝播

  • 以下の様にRNNブロック(RNNの計算処理をブロックにまとめて表記してます。)に時間依存データ \mathbf{\Phi}^{(\lambda)}_{t} を入力し、 \mathbf{\Phi}^{(\lambda+1)}_{t} が出力される状態を考えていきます。
  • RNNでは出力を再帰的に入力に戻してやります。RNNブロックには、 \mathbf{\Phi}^{(\lambda)}_{t} と前の時間の出力 \mathbf{\Phi}^{(\lambda+1)}_{t-1} が入力される形になります。

  • 紙面上方向が時間の正方向として、全てのデータの入出力をちゃんと書くと以下の様になります。

  • 各時刻 t におけるRNNブロックの中身、つまり \lambda 層から \lambda+1 層への順伝播式は以下のようになっています。

\displaystyle
\mathbf{Z}^{(\lambda)}_{t}
=
\mathbf{\Phi}^{(\lambda)}_{t}\mathbf{W}^{(\lambda)}+
\mathbf{\Phi}^{(\lambda+1)}_{t-1}\mathbf{V}^{(\lambda)}
+\mathbf{b}^{(\lambda)}
\tag{1}


\displaystyle
\mathbf{\Phi}^{(\lambda+1)}_{t}
= f^{(\lambda)}(\mathbf{Z}^{(\lambda)}_{t})
\tag{2}

  • 前の時間 t-1 からの出力 \mathbf{\Phi}^{(\lambda+1)}_{t-1} に重み \mathbf{V}^{(\lambda)} をかけて足しこむところ以外は通常の全結合と同じです。

  • 重み \mathbf{W}^{(\lambda)}\mathbf{V}^{(\lambda)}閾値 \mathbf{b}^{(\lambda)} は、時間に依存しない値であることに注意してください。

  • 時間インデックスのMaxを T とすると t=1 から T まで順番に、全ての時間 t において \mathbf{\Phi}^{(\lambda+1)}_{t} を求めていきます。(ただし \mathbf{\Phi}^{(\lambda+1)}_{0}=0 にしておきます。)

  • RNNブロックの中身を計算グラフで表現すると以下の様になります。

RNNブロックの逆伝播

  • 逆伝播時は、順伝播の出力 \mathbf{\Phi}^{(\lambda+1)}_{t} が時間 t+1 のRNNブロックへ分岐しているので、時間 t+1 からの逆伝播も考慮に入れる必要があります。以下計算グラフの赤線で示しています。(以下の逆伝播の計算グラフは、以前の記事行列演算と計算グラフでまとめた計算グラフの逆伝播のルールに従い機械的に書いたものです。)
  • 上記の計算グラフより、 \frac{\partial L}{\partial \mathbf{\Phi}^{(\lambda+1)}_{t}} から \frac{\partial L}{\partial \mathbf{\Phi}^{(\lambda)}_{t}} までの逆伝播式は、

\displaystyle
\begin{eqnarray*}
\mathbf{\Delta}^{(\lambda)}_{t}
&\equiv&
\frac{\partial L}{\partial \mathbf{Z}^{(\lambda)}_{t}}
\\
&=&
\left(
\frac{\partial L}{\partial \mathbf{\Phi}^{(\lambda+1)}_{t}}
+
\mathbf{\Delta}^{(\lambda)}_{t+1}\mathbf{V}^{(\lambda)T}
\right)
\circ
f^{'(\lambda)}(\mathbf{Z}^{(\lambda)}_{t})
\end{eqnarray*}
\tag{3}


\displaystyle
\frac{\partial L}{\partial \mathbf{\Phi}^{(\lambda)}_{t}}
= \mathbf{\Delta}^{(\lambda)}_{t}\mathbf{W}^{(\lambda)T}
\tag{4}

  • (3)式の  \mathbf{V}^{(\lambda)T} や(4)式の  \mathbf{W}^{(\lambda)T} の右上についている T は転置を表すので気をつけてください。(以降、行列の右上に付いている T は全て転置を表します。時間のMax値の記号 T と被っているので不本意なのですが他に記号が思いつかず...)

  • 逆伝播時は t=T から 1 まで時間をさかのぼって、全ての時間 t において(3)、(4)式を計算していきます。(ただし、 \mathbf{\Delta}^{(\lambda)}_{T+1}=0 にしておきます。)

  • RNNにおけるバックプロパゲーションは、レイヤー間だけではなく、時間軸に沿っても行う為、BPTT(Back Propagation Through Time)と呼ばれています。

  • また計算グラフより、重み・閾値の勾配は以下の様に求まりますが、


\displaystyle
\frac{\partial L}{\partial \mathbf{b}^{(\lambda)}}
= \left[1 \cdots 1\right]\mathbf{\Delta}^{(\lambda)}_{t}
\tag{5}


\displaystyle
\frac{\partial L}{\partial \mathbf{W}^{(\lambda)}}
= \mathbf{\Phi}^{(\lambda)T}_{t}\mathbf{\Delta}^{(\lambda)}_{t}
\tag{6}


\displaystyle
\frac{\partial L}{\partial \mathbf{V}^{(\lambda)}}
= \mathbf{\Phi}^{(\lambda+1)T}_{t-1}\mathbf{\Delta}^{(\lambda)}_{t}
\tag{7}

  • 順伝播時に、重み・閾値が時間に依存しない値だったことを思い出してください。

  • つまり正確に順伝播時の計算グラフを書くと重み \mathbf{W}^{(\lambda)}\mathbf{V}^{(\lambda)}閾値 \mathbf{b}^{(\lambda)} は、各時間のRNNブロックに分岐で入力されている状態になります。

  • 順伝播での分岐は逆伝播では和になるので、(5)、(6)、(7)式を時間で和をとったものが、真の重み・閾値の勾配になります。

\displaystyle
\frac{\partial L}{\partial \mathbf{b}^{(\lambda)}}
=
\sum_{t=1}^{T}
\left[1 \cdots 1\right]\mathbf{\Delta}^{(\lambda)}_{t}
\tag{8}


\displaystyle
\frac{\partial L}{\partial \mathbf{W}^{(\lambda)}}
=
\sum_{t=1}^{T}
\mathbf{\Phi}^{(\lambda)T}_{t}\mathbf{\Delta}^{(\lambda)}_{t}
\tag{9}


\displaystyle
\frac{\partial L}{\partial \mathbf{V}^{(\lambda)}}
=
\sum_{t=1}^{T}
\mathbf{\Phi}^{(\lambda+1)T}_{t-1}\mathbf{\Delta}^{(\lambda)}_{t}
\tag{10}

  • 計算グラフを使うとRNNの複雑な逆伝播もすんなり求めることができますね。

損失関数(2乗誤差)

  • RNNの例題として回帰を扱おうと思うので、回帰における一般的な損失関数である2乗誤差について、順伝播と逆伝播をまとめておこうと思います。

  • RNNで損失関数を計算するときは、時間のインデックスを考慮して時間に関して和を取る必要があります。(分類問題の場合も、各時間における交差エントロピーを算出後、時間について和を取ってやります。)

  • ネットワークの最終出力を  \mathbf{\Phi} ^{(\Lambda)}_{t}=\varphi^{(\Lambda)}_{itj} 、 目標値を  \mathbf{Y}_{t}=y_{itj} とした時、2乗誤差 Lは以下の式で計算されます。


\displaystyle
\begin{eqnarray*}
L
&=& \sum_{t} g(\mathbf{Y}_{t}, \mathbf{\Phi}^{(\Lambda)}_{t})
\\
&=&
\sum_{t} \sum_{i} \sum_{j}
\frac{1}{2}\left(y_{itj} - \varphi_{itj}\right)^{2}
\\
&=&
\sum_{t}
\frac{1}{2}
    \left[
        \begin{array}{c}
            1 & \cdots & 1
        \end{array}
    \right]
\left(
\left(\mathbf{Y}_{t}-\mathbf{\Phi}^{(\Lambda)}_{t}\right)
\circ
 \left(\mathbf{Y}_{t}-\mathbf{\Phi}^{(\Lambda)}_{t}\right)
\right)
    \left[
        \begin{array}{ccc}
            1 \\
            \vdots\\
            1\\
        \end{array}
    \right]
\\
\end{eqnarray*}
\tag{11}

  • 順伝播時の演算を計算グラフで表すと、
  • 逆伝播時は、

\displaystyle
\begin{eqnarray*}
\frac{\partial L}{\partial \mathbf{\Phi}^{(\Lambda)}_{t}}
&=& 
\frac{\partial L}{\partial \varphi^{(\Lambda)}_{itj}}
\\
&=&
\sum_{\tau} \sum_{k} \sum_{l}
\frac{\partial }{\partial \varphi^{(\Lambda)}_{itj}}
\frac{1}{2}\left(y_{k \tau l} - \varphi_{k \tau l}\right)^{2}
\\
&=&
\frac{\partial }{\partial \varphi^{(\Lambda)}_{itj}}
\frac{1}{2}\left(y_{itj} - \varphi^{(\Lambda)}_{itj}\right)^{2}
\\
&=&
- \left(y_{itj} - \varphi^{(\Lambda)}_{itj}\right)
\\
&=&
- \left(\mathbf{Y}_{t} - \mathbf{\Phi}^{(\Lambda)}_{t}\right)
\\
\end{eqnarray*}
\tag{12}

  • 分類でソフトマックス&交差エントロピーを使った時と同じく、誤差は - \left(\mathbf{Y}_{t} - \mathbf{\Phi}^{(\Lambda)}_{t}\right)になります。うまくできてます。

  • 以下、逆伝播時の計算グラフです。

特徴量データと目標値データの時間の個数が合わない場合

  • 例えば10個の時間インデックスを持つ特徴量データから1個の時間インデックスを持つ目標値データを学習させたい場合などもあると思います。

  • ネットワークの入力である特徴量データと、目標値データの時間の個数を必ずしも合わせる必要はないです。

  • 今ネットワークに T 個の特徴量データ \mathbf{X}_{t} (  t=1 \sim T )を入力して、 T 個の出力 \mathbf{\Phi}^{(\Lambda)}_{t} が得られたとします。

  • それに対し学習させたい目標値データ \mathbf{Y}_{t} が、 T-\tau +1 個(  t=\tau \sim T )しかない場合を考えます。

  • この場合の損失関数は、目標値データの個数分だけ時間の和を取る形になります。


\displaystyle
\begin{eqnarray*}
L
&=& \sum_{t=\tau}^{T} g(\mathbf{Y}_{t}, \mathbf{\Phi}^{(\Lambda)}_{t})
\end{eqnarray*}
\tag{13}

  • 逆伝播時の \frac{\partial L}{\partial \mathbf{\Phi}^{(\Lambda)}_{t}} の計算では、t=1 \sim \tau-1 の出力  \mathbf{\Phi}^{(\Lambda)}_{1} \cdots \mathbf{\Phi}^{(\Lambda)}_{\tau-1} が損失値 L に寄与していないため、その微分 \frac{\partial L}{\partial \mathbf{\Phi}^{(\Lambda)}_{1}} \cdots \frac{\partial L}{\partial \mathbf{\Phi}^{(\Lambda)}_{\tau-1}} は全部0になります。

\displaystyle
\frac{\partial L}{\partial \mathbf{\Phi}^{(\Lambda)}_{t}}
= \begin{cases}
    - \left(\mathbf{Y}_{t} - \mathbf{\Phi}^{(\Lambda)}_{t}\right) & (t \geq \tau) \\
    0 & (t<\tau)
  \end{cases}
\tag{14}