なぜディープラーニングに線形代数が必要なのか ReLUで手計算する順伝播と逆伝播

こんにちは。ゆうせいです。

ディープラーニングを学び始めると、必ず線形代数が出てきます。行列、ベクトル、転置、内積。数学から離れていた人にとっては、ここが最初の関門です。

そして、多くの人がこう思います。

「ライブラリが計算してくれるのに、なぜ自分で理解する必要があるのか」

この記事では、その答えを実際の計算で示します。小さなネットワークを一つ用意し、順伝播と逆伝播を最後まで手で計算します。

活性化関数にはReLUを使います。微分値が0か1しかないため、指数関数の計算が一切不要です。電卓すら要りません。紙とペンだけで追えます。

計算を終えたとき、線形代数が「便利な道具」ではなく「そう書くしかない書き方」だと分かるはずです。

結論

線形代数が必要な理由は、三つあります。

一つ目は、書き切れないからです。実際のモデルでは、掛け算と足し算が何億回も現れます。一つずつ式に書くことは物理的に不可能です。行列は、その全体を一行で表す記法です。

二つ目は、逆伝播の構造が線形代数で説明できるからです。順伝播で W を掛けたなら、逆伝播では W^{T} を掛けます。この対応関係を知っていれば、逆伝播の式を暗記する必要がありません。

三つ目は、誤りの大半が形の不一致だからです。実装でつまずく原因のほとんどは、数学の難しさではなく、行列の形が合っていないことです。形を追える人は、原因を数分で特定できます。

以下、順に確認していきます。

準備 ReLUという活性化関数

計算に入る前に、使う活性化関数を確認します。

定義

\text{ReLU}(u) = \max(0, u) = \begin{cases} u & (u > 0) \\ 0 & (u \leq 0) \end{cases}

正の値はそのまま通し、負の値は0にする。それだけの関数です。

日本語では正規化線形関数と呼ばれますが、現場ではReLUと呼ぶのが一般的です。

微分

\text{ReLU}'(u) = \begin{cases} 1 & (u > 0) \\ 0 & (u < 0) \end{cases}

正なら1、負なら0です。

u = 0 では厳密には微分できませんが、実装上は0または1と決めて扱います。浮動小数点の計算でちょうど0になることはほぼないため、実用上の問題にはなりません。

なぜこれを使うのか

理由は三つあります。

一つ目は、計算が簡単なことです。指数関数が不要です。手計算に向いています。

二つ目は、非線形であることです。折れ曲がりがあるため、層を重ねる意味が生まれます。

三つ目は、微分値が1であることです。逆伝播で誤差が減衰しません。この性質は、後で深い層の話をするときに効いてきます。

通す門番だと思ってください

イメージとしては、門番が近いでしょう。

正の値が来たら「どうぞ」と、そのまま通します。負の値が来たら「通行止め」で、0にします。

そして逆伝播のときも同じです。順伝播で通した経路には、誤差もそのまま通します。止めた経路には、誤差も通しません。

この対応関係が、計算を単純にしています。

使用するネットワーク

計算に使う小さなネットワークを定義します。

入力が2つ、中間層が2つ、出力が1つの構成です。

要素数活性化関数
入力層2なし
中間層2ReLU
出力層1なし(恒等)

記号の意味

先に、使う記号をすべて説明します。ここを飛ばさないでください。

記号意味
x 入力ベクトル2行1列
W_1 入力層から中間層への重み2行2列
b_1 中間層のバイアス2行1列
z_1 中間層の活性化前の値2行1列
a_1 中間層の出力2行1列
W_2 中間層から出力層への重み1行2列
b_2 出力層のバイアス1行1列
\hat{y} 予測値1行1列
t 正解値1行1列

初期値

計算しやすい値を選びました。

x = \begin{pmatrix} 1 \\ 2 \end{pmatrix}

W_1 = \begin{pmatrix} 0.1 & 0.2 \\ 0.3 & 0.4 \end{pmatrix}, \quad b_1 = \begin{pmatrix} 0.1 \\ 0.1 \end{pmatrix}

W_2 = \begin{pmatrix} 0.5 & 0.6 \end{pmatrix}, \quad b_2 = 0.2

正解値は次の通りです。

t = 0.5

第1部 順伝播を手で計算する

順伝播とは、入力から予測値を求める計算です。

なぜ行列で書くのか

まず、行列を使わずに書いてみます。

z_{1,1} = W_{1,11} x_1 + W_{1,12} x_2 + b_{1,1}

z_{1,2} = W_{1,21} x_1 + W_{1,22} x_2 + b_{1,2}

2行で済んでいます。しかし、中間層が100個あれば100行、入力が784個あれば1行あたり785項になります。

これを行列で書くと、次の一行になります。

z_1 = W_1 x + b_1

中間層が何個でも、入力が何個でも、この式は変わりません。これが行列を使う第一の理由です。

手順1 中間層の入力を求める

計算します。

z_1 = \begin{pmatrix} 0.1 & 0.2 \\ 0.3 & 0.4 \end{pmatrix} \begin{pmatrix} 1 \\ 2 \end{pmatrix} + \begin{pmatrix} 0.1 \\ 0.1 \end{pmatrix}

1行目を計算します。

0.1 \times 1 + 0.2 \times 2 = 0.1 + 0.4 = 0.5

バイアスを足します。

0.5 + 0.1 = 0.6

2行目を計算します。

0.3 \times 1 + 0.4 \times 2 = 0.3 + 0.8 = 1.1

バイアスを足します。

1.1 + 0.1 = 1.2

結果は次の通りです。

z_1 = \begin{pmatrix} 0.6 \\ 1.2 \end{pmatrix}

ここで確認してほしいことがあります。行列の積では、和を取る操作が入ります。x_1 x_2 の情報が混ざって、z_{1,1} という一つの値になりました。

これが、ニューラルネットワークが特徴を作り出す仕組みです。複数の入力を混ぜ合わせ、新しい値を生み出しています。

手順2 ReLUを通す

z_{1,1} = 0.6 は正なので、そのまま通ります。

z_{1,2} = 1.2 も正なので、そのまま通ります。

a_1 = \begin{pmatrix} 0.6 \\ 1.2 \end{pmatrix}

計算らしい計算がありません。値を見て、正か負かを判断するだけです。

ここで重要な点があります。ReLUは、各要素に独立して適用されます。1番目の出力は1番目の入力にしか依存しません。

行列の積では情報が混ざりましたが、活性化関数では混ざりません。この違いが、後の逆伝播で効いてきます。

手順3 出力を求める

\hat{y} = W_2 a_1 + b_2

計算します。

0.5 \times 0.6 = 0.30

0.6 \times 1.2 = 0.72

足し合わせます。

0.30 + 0.72 = 1.02

バイアスを足します。

1.02 + 0.2 = 1.22

予測値は1.22になりました。

手順4 損失を求める

二乗誤差を使います。定義は次の通りです。

L = \frac{1}{2}(\hat{y} - t)^2

係数の \frac{1}{2} は、微分したときに2が消えて式が簡単になるために付けます。数学的な必然性はありません。

計算します。

\hat{y} - t = 1.22 - 0.5 = 0.72

L = \frac{1}{2} \times 0.72^2 = \frac{1}{2} \times 0.5184 = 0.2592

損失は0.2592です。正解は0.5なのに1.22を出力したので、誤差が出ています。

第2部 逆伝播を手で計算する

逆伝播とは、損失を減らすために各パラメータをどう変えればよいかを求める計算です。

何を求めたいのか

求めたいのは、次の四つです。

\frac{\partial L}{\partial W_1}, \quad \frac{\partial L}{\partial b_1}, \quad \frac{\partial L}{\partial W_2}, \quad \frac{\partial L}{\partial b_2}

それぞれ、そのパラメータを少し増やしたとき損失がどれだけ変わるかを表します。

値が正なら、そのパラメータを減らせば損失が下がります。値が負なら、増やせば下がります。

逆から辿る理由

損失は、出力の関数です。出力は、W_2 a_1 の関数です。a_1 z_1 の関数で、z_1 W_1 x の関数です。

つまり、次の連鎖になっています。

W_1 \rightarrow z_1 \rightarrow a_1 \rightarrow \hat{y} \rightarrow L

W_1 が損失に与える影響を知るには、この連鎖を逆に辿る必要があります。これが連鎖律です。

手順1 出力層の誤差

まず、損失を予測値で微分します。

\frac{\partial L}{\partial \hat{y}} = \hat{y} - t

導出を確認します。

L = \frac{1}{2}(\hat{y} - t)^2

\frac{\partial L}{\partial \hat{y}} = \frac{1}{2} \times 2 \times (\hat{y} - t) = \hat{y} - t

係数 \frac{1}{2} を付けた理由が、ここで分かります。

出力層は恒等関数なので、\hat{y} = z_2 です。したがって次のようになります。

\delta_2 = \frac{\partial L}{\partial z_2} = 1.22 - 0.5 = 0.72

この \delta_2 を、出力層の誤差と呼びます。

手順2 出力層のパラメータの勾配

z_2 = W_2 a_1 + b_2 でしたので、次のようになります。

\frac{\partial L}{\partial W_2} = \delta_2 \cdot a_1^{T}

a_1 を転置している点に注目してください。\delta_2 が1行1列、a_1^{T} が1行2列なので、結果は1行2列になります。W_2 と同じ形です。

勾配の形は、必ず対応するパラメータの形と一致します。これは強力な検算になります。

計算します。

0.72 \times 0.6 = 0.432

0.72 \times 1.2 = 0.864

\frac{\partial L}{\partial W_2} = \begin{pmatrix} 0.432 & 0.864 \end{pmatrix}

バイアスについては、z_2 b_2 で微分すると1になるので、次の通りです。

\frac{\partial L}{\partial b_2} = \delta_2 = 0.72

手順3 中間層へ誤差を伝える

ここが逆伝播の核心です。

\frac{\partial L}{\partial a_1} = W_2^{T} \delta_2

転置が現れました。なぜでしょうか。

順伝播では、2つの値から1つの値を作りました。逆伝播では、1つの誤差を2つに配分します。方向が逆なので、行列も転置されます。

形で確認します。W_2^{T} は2行1列、\delta_2 は1行1列なので、結果は2行1列です。a_1 と同じ形になっています。

計算します。

W_2^{T} \delta_2 = \begin{pmatrix} 0.5 \\ 0.6 \end{pmatrix} \times 0.72 = \begin{pmatrix} 0.360 \\ 0.432 \end{pmatrix}

重みが大きいほど、大きな誤差が配分されています。影響が大きかった経路には、責任も大きく割り当てられる、という理屈です。

手順4 ReLUを逆に通る

z_1 = (0.6, 1.2) は、どちらも正でした。したがって微分は次の通りです。

\text{ReLU}'(z_1) = \begin{pmatrix} 1 \\ 1 \end{pmatrix}

これを掛けます。

\delta_1 = \frac{\partial L}{\partial a_1} \odot \text{ReLU}'(z_1)

ここで \odot はアダマール積、つまり同じ位置どうしを掛ける演算です。

なぜ行列の積ではないのでしょうか。ReLUが各要素に独立して適用されたからです。1番目の出力は1番目の入力にしか依存しないので、混ざりようがありません。

計算します。

\delta_1 = \begin{pmatrix} 0.360 \\ 0.432 \end{pmatrix} \odot \begin{pmatrix} 1 \\ 1 \end{pmatrix} = \begin{pmatrix} 0.360 \\ 0.432 \end{pmatrix}

値がそのまま通りました。門番の例えで言えば、順伝播で通した経路なので、誤差もそのまま通したことになります。

手順5 中間層のパラメータの勾配

\frac{\partial L}{\partial W_1} = \delta_1 x^{T}

形を確認します。\delta_1 が2行1列、x^{T} が1行2列なので、結果は2行2列です。W_1 と一致します。

計算します。

\delta_1 x^{T} = \begin{pmatrix} 0.360 \\ 0.432 \end{pmatrix} \begin{pmatrix} 1 & 2 \end{pmatrix}

1行目です。

0.360 \times 1 = 0.360, \quad 0.360 \times 2 = 0.720

2行目です。

0.432 \times 1 = 0.432, \quad 0.432 \times 2 = 0.864

\frac{\partial L}{\partial W_1} = \begin{pmatrix} 0.360 & 0.720 \\ 0.432 & 0.864 \end{pmatrix}

2列目の値が1列目の2倍になっています。入力 x_2 = 2 x_1 = 1 の2倍だからです。大きな入力に掛かる重みほど、変更の影響が大きい。この関係が数値に現れています。

バイアスの勾配は次の通りです。

\frac{\partial L}{\partial b_1} = \delta_1 = \begin{pmatrix} 0.360 \\ 0.432 \end{pmatrix}

手順6 パラメータを更新する

学習率を \eta = 0.1 とします。更新式は次の通りです。

W \leftarrow W - \eta \frac{\partial L}{\partial W}

W_2 を更新します。

0.5 - 0.1 \times 0.432 = 0.5 - 0.0432 = 0.4568

0.6 - 0.1 \times 0.864 = 0.6 - 0.0864 = 0.5136

b_2 を更新します。

0.2 - 0.1 \times 0.72 = 0.2 - 0.072 = 0.128

W_1 を更新します。

0.1 - 0.1 \times 0.360 = 0.1 - 0.036 = 0.064

0.2 - 0.1 \times 0.720 = 0.2 - 0.072 = 0.128

0.3 - 0.1 \times 0.432 = 0.3 - 0.0432 = 0.2568

0.4 - 0.1 \times 0.864 = 0.4 - 0.0864 = 0.3136

b_1 を更新します。

0.1 - 0.1 \times 0.360 = 0.064

0.1 - 0.1 \times 0.432 = 0.0568

手順7 本当に損失が下がったか確かめる

ここが最も納得できる部分です。更新後の値で、もう一度順伝播を計算します。

z_{1,1} = 0.064 \times 1 + 0.128 \times 2 + 0.064

= 0.064 + 0.256 + 0.064 = 0.384

z_{1,2} = 0.2568 \times 1 + 0.3136 \times 2 + 0.0568

= 0.2568 + 0.6272 + 0.0568 = 0.9408

どちらも正なので、ReLUはそのまま通します。

a_1 = \begin{pmatrix} 0.384 \\ 0.9408 \end{pmatrix}

出力を計算します。

0.4568 \times 0.384 = 0.17541

0.5136 \times 0.9408 = 0.48319

\hat{y} = 0.17541 + 0.48319 + 0.128 = 0.78660

損失を計算します。

0.78660 - 0.5 = 0.28660

L = \frac{1}{2} \times 0.28660^2 = \frac{1}{2} \times 0.08214 = 0.04107

結果を比較します。

項目更新前更新後
予測値1.22000.7866
損失0.25920.0411

損失が約84%減少しました。予測値が正解の0.5に近づいています。

これが学習です。この計算を、データを変えながら何万回も繰り返します。

第3部 負の値が出るとどうなるか

ここまでの計算では、z_1 がどちらも正でした。負になる場合を確認しておきます。

設定を変える

W_1 の1行目を、次のように変えてみます。

W_1 = \begin{pmatrix} -0.4 & -0.1 \\ 0.3 & 0.4 \end{pmatrix}

他の値はそのままです。

順伝播

z_{1,1} = -0.4 \times 1 + (-0.1) \times 2 + 0.1 = -0.4 - 0.2 + 0.1 = -0.5

z_{1,2} = 0.3 \times 1 + 0.4 \times 2 + 0.1 = 1.2

ReLUを通します。1番目は負なので0になります。

a_1 = \begin{pmatrix} 0 \\ 1.2 \end{pmatrix}

出力を計算します。

\hat{y} = 0.5 \times 0 + 0.6 \times 1.2 + 0.2 = 0 + 0.72 + 0.2 = 0.92

損失を計算します。

0.92 - 0.5 = 0.42

L = \frac{1}{2} \times 0.42^2 = \frac{1}{2} \times 0.1764 = 0.0882

逆伝播

\delta_2 = 0.42

\frac{\partial L}{\partial a_1} = \begin{pmatrix} 0.5 \times 0.42 \\ 0.6 \times 0.42 \end{pmatrix} = \begin{pmatrix} 0.21 \\ 0.252 \end{pmatrix}

ReLUの微分を求めます。z_{1,1} = -0.5 は負なので0、z_{1,2} = 1.2 は正なので1です。

\text{ReLU}'(z_1) = \begin{pmatrix} 0 \\ 1 \end{pmatrix}

アダマール積を計算します。

\delta_1 = \begin{pmatrix} 0.21 \\ 0.252 \end{pmatrix} \odot \begin{pmatrix} 0 \\ 1 \end{pmatrix} = \begin{pmatrix} 0 \\ 0.252 \end{pmatrix}

1番目がゼロになりました。

W_1 の勾配を計算します。

\frac{\partial L}{\partial W_1} = \begin{pmatrix} 0 \\ 0.252 \end{pmatrix} \begin{pmatrix} 1 & 2 \end{pmatrix} = \begin{pmatrix} 0 & 0 \\ 0.252 & 0.504 \end{pmatrix}

1行目がすべてゼロです。この入力に関しては、1番目のユニットに繋がる重みは更新されません。

これは正しい挙動

一見すると問題に見えますが、これは正しい動作です。

1番目のユニットは、この入力に対して0を出力しました。出力に何も寄与していません。寄与していないのだから、責任もありません。したがって勾配もゼロになります。

門番の例えで言えば、通行止めにした経路には誤差も流れない、ということです。

ただし、注意点もあります。学習率が大きすぎて重みが極端な負の値になると、どの入力に対しても z < 0 となり、そのユニットが二度と復活しなくなる場合があります。これを死んだReLU問題といいます。

対策としては、学習率を適切に設定することや、負の側でもわずかに勾配を残すLeaky ReLUなどの改良版を使う方法があります。初学者の段階では、そういう現象があると知っておけば十分です。

第4部 なぜ線形代数でなければならないのか

手計算を終えたところで、最初の問いに戻ります。

理由1 規模が違いすぎる

今回のネットワークのパラメータ数を数えます。

W_1: 2 \times 2 = 4

b_1: 2

W_2: 1 \times 2 = 2

b_2: 1

合計9個です。手計算で追えました。

では、手書き数字認識でよく使われる構成ではどうでしょうか。入力784、中間層128、出力10とします。

W_1: 128 \times 784 = 100352

b_1: 128

W_2: 10 \times 128 = 1280

b_2: 10

合計は次の通りです。

100352 + 128 + 1280 + 10 = 101770

約10万個です。1つの計算に5秒かかるとして、順伝播1回に必要な時間を求めます。

101770 \times 5 = 508850

約509,000秒、日数に直します。

508850 \div 86400 \approx 5.9

順伝播1回に約6日かかります。学習には数万回の繰り返しが必要ですから、人間の手には負えません。

さらに、GPT-2の小さいモデルでは、1つの層だけで次の規模になります。

768 \times 768 = 589824

これが12層分あり、それ以外のパラメータも加わります。個別の式として書き下すことは、原理的に不可能です。

行列は、この規模を扱うための唯一の記法です。

理由2 順伝播と逆伝播が対応している

計算した式を並べてみます。

方向
順伝播z = Wx + b
逆伝播(前の層へ)\frac{\partial L}{\partial x} = W^{T}\delta
逆伝播(重みへ)\frac{\partial L}{\partial W} = \delta x^{T}

順伝播で W を掛けたなら、逆伝播では W^{T} を掛けます。この対応は、どの層でも変わりません。

この規則を知っていれば、逆伝播の式を層ごとに暗記する必要がありません。転置するだけです。

なぜ転置になるのか。直感的には、情報の流れる向きが逆になるからです。順伝播では入力から出力へ、逆伝播では出力から入力へ。行と列の役割が入れ替わります。

理由3 形が合っているかが検算になる

計算の途中で、形を何度も確認しました。まとめます。

対応するもの
\delta_1 2行1列z_1 と同じ
\frac{\partial L}{\partial W_1} 2行2列W_1 と同じ
\frac{\partial L}{\partial b_1} 2行1列b_1 と同じ

勾配の形は、必ず対応するパラメータの形と一致します。一致しなければ、どこかで間違えています。

実装でつまずく原因のほとんどは、数学の難しさではなく形の不一致です。形を追える人は、エラーの原因を数分で特定できます。追えない人は、何時間も試行錯誤することになります。

理由4 まとめて計算できる

実際の学習では、データを1件ずつではなく、まとめて処理します。これをミニバッチといいます。

入力を1件から4件に増やすと、x の形が2行1列から2行4列に変わります。

X = \begin{pmatrix} x^{(1)} & x^{(2)} & x^{(3)} & x^{(4)} \end{pmatrix}

計算式は変わりません。

Z_1 = W_1 X + b_1

W_1 が2行2列、X が2行4列なので、Z_1 は2行4列になります。4件分の結果が一度に得られました。

式を書き換えずに、データ量だけを増やせる。これが行列で書く利点です。

そして、この形の計算はGPUが得意とする処理です。多数の乗算と加算を並列に実行できるため、CPUより桁違いに速くなります。

線形代数で書けることと、ハードウェアで高速化できることは、直結しています。

理由5 ReLUだからこそ見える構造

今回、活性化関数の微分がすべて1か0でした。おかげで、逆伝播の本質が見えやすくなっています。

\delta_1 = \left( W_2^{T} \delta_2 \right) \odot \text{ReLU}'(z_1)

括弧の中は行列の積です。誤差を前の層の次元へ戻しています。

括弧の外はアダマール積です。通す経路と止める経路を選別しています。

混ぜる操作と選別する操作。この二つが交互に現れるのが、ニューラルネットワークの構造です。線形代数の二つの演算が、そのまま役割分担しています。

練習問題

自分で手を動かして確認してください。解答は各問題の直後にあります。

問題1 順伝播

本文と同じ重みとバイアスを使い、入力を次のように変えて順伝播を計算してください。

x = \begin{pmatrix} 2 \\ 1 \end{pmatrix}

z_1 a_1 \hat{y} L を求めてください。正解値は t = 0.5 です。

解答を示します。

z_1 を計算します。

1行目です。

0.1 \times 2 + 0.2 \times 1 + 0.1 = 0.2 + 0.2 + 0.1 = 0.5

2行目です。

0.3 \times 2 + 0.4 \times 1 + 0.1 = 0.6 + 0.4 + 0.1 = 1.1

z_1 = \begin{pmatrix} 0.5 \\ 1.1 \end{pmatrix}

どちらも正なので、ReLUはそのまま通します。

a_1 = \begin{pmatrix} 0.5 \\ 1.1 \end{pmatrix}

出力を計算します。

0.5 \times 0.5 = 0.25

0.6 \times 1.1 = 0.66

\hat{y} = 0.25 + 0.66 + 0.2 = 1.11

損失を計算します。

1.11 - 0.5 = 0.61

L = \frac{1}{2} \times 0.61^2 = \frac{1}{2} \times 0.3721 = 0.18605

補足します。本文の入力 (1, 2) と今回の (2, 1) は、成分の値は同じで順序だけが違います。それでも結果は異なりました。行列の積では、どの位置の値にどの重みが掛かるかが決まっているためです。

問題2 形の理解

入力784、中間層128、出力10のネットワークについて答えてください。

(1) W_1 b_1 W_2 b_2 の形をそれぞれ答えてください。 (2) パラメータの総数を求めてください。 (3) \frac{\partial L}{\partial W_1} の形を答えてください。

解答を示します。

(1) それぞれ次の通りです。

W_1 は128行784列です。z_1 = W_1 x で、x が784行1列、z_1 が128行1列になる必要があるためです。

b_1 は128行1列です。

W_2 は10行128列です。

b_2 は10行1列です。

(2) 総数を計算します。

128 \times 784 = 100352

10 \times 128 = 1280

100352 + 128 + 1280 + 10 = 101770

101,770個です。

(3) 128行784列です。勾配の形は、対応するパラメータと必ず一致します。

補足します。行数が出力側、列数が入力側になる点を押さえてください。W (\text{出力の数}, \text{入力の数}) の形をしているから、Wx で次元が変換されます。

問題3 出力層の逆伝播

次の値が与えられているとき、\frac{\partial L}{\partial W_2} \frac{\partial L}{\partial a_1} を求めてください。

\delta_2 = 0.3, \quad a_1 = \begin{pmatrix} 0.4 \\ 0.8 \end{pmatrix}, \quad W_2 = \begin{pmatrix} 0.5 & 0.6 \end{pmatrix}

解答を示します。

\frac{\partial L}{\partial W_2} を計算します。

\frac{\partial L}{\partial W_2} = \delta_2 a_1^{T}

0.3 \times 0.4 = 0.12

0.3 \times 0.8 = 0.24

\frac{\partial L}{\partial W_2} = \begin{pmatrix} 0.12 & 0.24 \end{pmatrix}

形が1行2列で、W_2 と一致しています。

\frac{\partial L}{\partial a_1} を計算します。

\frac{\partial L}{\partial a_1} = W_2^{T} \delta_2

0.5 \times 0.3 = 0.15

0.6 \times 0.3 = 0.18

\frac{\partial L}{\partial a_1} = \begin{pmatrix} 0.15 \\ 0.18 \end{pmatrix}

形が2行1列で、a_1 と一致しています。

補足します。二つの式で、掛ける相手が違うことに注目してください。重みの勾配を求めるときは入力側の値を、前の層へ伝えるときは重みの転置を使います。混同しやすい部分です。

問題4 ReLUの微分

中間層の値が z_1 = (1.5, -0.2, 0.8, -3.0) で、\frac{\partial L}{\partial a_1} = (0.6, 0.4, 0.2, 0.9) のとき、\delta_1 を求めてください。

解答を示します。

ReLUの微分を求めます。正なら1、負なら0です。

1.5 > 0 なので1です。

-0.2 < 0 なので0です。

0.8 > 0 なので1です。

-3.0 < 0 なので0です。

\text{ReLU}'(z_1) = (1, 0, 1, 0)

アダマール積を計算します。

\delta_1 = (0.6 \times 1, ; 0.4 \times 0, ; 0.2 \times 1, ; 0.9 \times 0) = (0.6, ; 0, ; 0.2, ; 0)

補足します。2番目と4番目には勾配が流れません。この入力に関しては、それらのユニットに繋がる重みが更新されないことになります。

特に4番目に注目してください。\frac{\partial L}{\partial a_1} の値が0.9と最も大きかったにもかかわらず、z が負のためゼロになりました。誤差が大きくても、順伝播で寄与していなければ責任を問われない、ということです。

問題5 中間層の勾配

\delta_1 = \begin{pmatrix} 0.2 \\ 0.5 \end{pmatrix} x = \begin{pmatrix} 3 \\ 1 \end{pmatrix} のとき、\frac{\partial L}{\partial W_1} を求めてください。また、なぜ1列目の値が3列目相当に大きくなるのか説明してください。

解答を示します。

\frac{\partial L}{\partial W_1} = \delta_1 x^{T} = \begin{pmatrix} 0.2 \\ 0.5 \end{pmatrix} \begin{pmatrix} 3 & 1 \end{pmatrix}

1行目です。

0.2 \times 3 = 0.6, \quad 0.2 \times 1 = 0.2

2行目です。

0.5 \times 3 = 1.5, \quad 0.5 \times 1 = 0.5

\frac{\partial L}{\partial W_1} = \begin{pmatrix} 0.6 & 0.2 \\ 1.5 & 0.5 \end{pmatrix}

理由を説明します。

1列目は入力 x_1 = 3 に掛かる重みです。z = W_{11} x_1 + \cdots という関係から、W_{11} を少し変えたときの z の変化は x_1 倍になります。入力が大きいほど、重みの変更が結果に強く反映されます。

x_1 = 3 x_2 = 1 の3倍なので、勾配も3倍になっています。

補足します。この性質が、入力データの正規化が必要な理由の一つです。入力のスケールがばらばらだと、勾配の大きさもばらばらになり、学習が不安定になります。

問題6 ミニバッチ

W_1 が2行2列、入力 X が2行4列のとき、次の問いに答えてください。

(1) Z_1 = W_1 X の形を答えてください。 (2) X の4という数字は何を表していますか。 (3) b_1 は2行1列ですが、形の違う Z_1 にどうやって足すのですか。

解答を示します。

(1) 2行4列です。行列の積では、内側の次元が消えて外側が残ります。

(2, 2) \times (2, 4) \rightarrow (2, 4)

(2) データの件数です。4件分の入力をまとめて処理しています。

(3) ブロードキャストという仕組みによって、b_1 が4列に自動的に複製され、各列に同じ値が足されます。

補足します。ブロードキャストは便利な仕組みですが、注意も必要です。意図しない形の組み合わせでもエラーが出ずに計算が通ってしまうことがあります。計算の前後で形を確認する習慣をつけてください。

問題7 総合

本文と同じネットワークで、学習率だけを \eta = 1.0 に変えた場合、b_2 の更新後の値はいくつになりますか。また、この学習率の設定について考えられる問題を述べてください。

解答を示します。

更新後の値を計算します。

0.2 - 1.0 \times 0.72 = 0.2 - 0.72 = -0.52

問題点を述べます。

学習率が大きすぎると、最小値を通り越して反対側へ行ってしまう可能性があります。次の更新でまた行き過ぎると、値が振動し、場合によっては発散します。

ReLUを使っている場合、さらに固有の危険があります。重みが大きく負の方向へ振れると、z が常に負になり、そのユニットが死んだReLUの状態に陥ります。一度そうなると、勾配がゼロのままなので復活しません。

一方、学習率が小さすぎると、更新量が小さくなり、学習に膨大な回数が必要になります。

一般には0.001から0.1程度から試し、損失の推移を見ながら調整します。

問題8 発展

ReLUではなくシグモイド関数を使った場合、逆伝播で掛かる値は最大でも0.25であることが知られています。

(1) 20層のネットワークで、勾配が最大どれだけ小さくなるか求めてください。 (2) ReLUの場合はどうなりますか。 (3) この結果から言えることを述べてください。

解答を示します。

(1) 計算します。

0.25^{20} = (2^{-2})^{20} = 2^{-40}

2^{10} \approx 10^3 を使って概算します。

2^{-40} = (2^{-10})^4 \approx (10^{-3})^4 = 10^{-12}

約1兆分の1になります。

(2) 正の領域では微分が1なので、次のようになります。

1^{20} = 1

減衰しません。

(3) シグモイド関数を中間層に使うと、深いネットワークでは入力側の層が実質的に学習しません。これを勾配消失問題といいます。

ReLUを使えば、正の領域では勾配が減衰しないため、この問題を大幅に緩和できます。ReLUの登場が、深いネットワークの実用化を後押しした理由の一つです。

補足します。今回の手計算でReLUの微分が常に1だったことは、単に計算が楽だったという話ではありません。深層学習が成立するための、本質的な性質でした。

学習の進め方

初学者がこの分野を学ぶ順序を示します。

段階内容
1ベクトルと行列の表記に慣れる
2行列の積を手で計算できるようにする
3形の規則を理解する
4順伝播を手で計算する
5連鎖律を理解する
6逆伝播を手で計算する
7更新後に損失が下がることを確認する
8負の値が出る場合も計算する

7番を必ず実施してください。ここまでやると、計算が正しいことを自分で確認できます。理屈を聞くだけの状態から、納得した状態に変わります。

つまずきやすいのは3番と5番です。3番は面倒に感じますが、後で最も役に立ちます。5番は、微分の連鎖を一度きちんと追っておくと、以降の理解が速くなります。

振り返り

次の問いに答えられるか確認してください。

  • 順伝播で W を掛けたとき、逆伝播では何を掛けますか
  • 勾配の形は、何と一致しますか
  • ReLUの微分はいくつになりますか。場合分けして答えてください
  • 活性化関数の逆伝播でアダマール積を使うのはなぜですか
  • z が負のとき、その経路の勾配はどうなりますか。またそれはなぜ正しいのですか
  • ミニバッチにすると、式のどこが変わりますか

すべて答えられれば、この記事の内容は理解できています。

まとめと次の学習ステップ

線形代数が必要な理由は、便利だからではありません。それ以外に書きようがないからです。

10万個のパラメータを個別の式で書くことはできません。行列という記法があって初めて、この規模の計算を人間が扱えるようになります。

そして、線形代数を理解していると、逆伝播が暗記の対象ではなくなります。順伝播で W を掛けたなら逆伝播では W^{T} を掛ける。勾配の形はパラメータの形と一致する。この二つの規則で、大半の式が導けます。

今回の計算で、損失が0.2592から0.0411に下がることを確認しました。この一回の更新を、データを変えながら何万回も繰り返す。それがディープラーニングの学習です。

ReLUを使ったおかげで、指数関数を一度も計算せずに済みました。しかもこの選択は、手計算を楽にするためだけのものではありません。微分が1であることが、深いネットワークの学習を可能にしています。

次の学習ステップとしては、同じ計算をPythonで実装してみてください。手計算の結果と一致すれば、理解が確かなものになります。ライブラリを使う前に、まずNumPyだけで書いてみることをおすすめします。

その先には、より深いネットワーク、畳み込み層、そして注意機構が待っています。いずれも、今回扱った行列の積と転置、そしてアダマール積の組み合わせで構成されています。土台は、すでにできています。

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
海外放浪の末、2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。