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

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

ディープラーニングを学び始めると、必ず行列とベクトルが出てきます。そして多くの人が、ここでつまずきます。

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

この記事では、その答えを実際の手計算で示します。ただし、いきなり完全な形は扱いません。二段階に分けます。

第1ステップでは、バイアスを外します。式は z = Wx だけです。行列積、ReLU、転置による逆伝播。この三つの骨格に集中します。

第2ステップで、バイアスを戻します。何が変わり、何が変わらないのかを確認します。

数値は、すべて電卓なしで計算できるように選びました。第1ステップの計算は、更新の直前まですべて整数です。紙とペンだけで最後まで追えます。

結論

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

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

二つ目は、逆伝播が転置で説明できるからです。順伝播で W を掛けたなら、逆伝播では W^{T} を掛けます。この対応を知っていれば、層ごとに式を暗記する必要がありません。

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

以下、手を動かして確認していきます。

なぜバイアスなしから始めるのか

いきなり z = Wx + b を扱うと、初学者は三つのことを同時に処理することになります。

  • 行列積の計算
  • ベクトルの加算
  • 逆伝播での二つの勾配

バイアスを外せば、z = Wx だけになります。覚えることが減り、行列積そのものの働きが見えやすくなります。

そして重要なのは、逆伝播の骨格がバイアスの有無で変わらないことです。転置が現れる理由も、アダマール積を使う理由も、バイアスとは無関係です。

つまり、バイアスは後から足せる部品です。先に骨格を組み、後から部品を付ける。この順序が理解を早めます。

なお、バイアスがないと表現力は落ちます。入力が0のとき出力も必ず0になるためです。原点を通る直線しか引けない、と考えてください。この制約は第2ステップで解消します。

準備 ReLUという活性化関数

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

定義

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

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

微分

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

正なら1、負なら0です。u = 0 では厳密には微分できませんが、実装上は0または1と決めて扱います。

門番だと思ってください

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

正の値が来たら、そのまま通します。負の値が来たら、通行止めにして0にします。

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

この対応が、計算を極端に単純にしています。指数関数が一切出てこないため、手計算に向いています。

第1ステップ バイアスなしで骨格をつかむ

使用するネットワーク

入力が2つ、中間層が2つ、出力が1つの構成です。バイアスはありません。

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

記号の意味

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

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

初期値

すべて整数にしました。

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

W_1 = \begin{pmatrix} 1 & 2 \\ 2 & -1 \end{pmatrix}, \quad W_2 = \begin{pmatrix} 1 & 2 \end{pmatrix}

正解値は次の通りです。

t = 8

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

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

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

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

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

行列で書けば、次の一行です。

z_1 = W_1 x

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

計算します。

z_1 = \begin{pmatrix} 1 & 2 \\ 2 & -1 \end{pmatrix} \begin{pmatrix} 2 \\ 1 \end{pmatrix}

1行目です。

1 \times 2 + 2 \times 1 = 2 + 2 = 4

2行目です。

2 \times 2 + (-1) \times 1 = 4 - 1 = 3

結果は次の通りです。

z_1 = \begin{pmatrix} 4 \\ 3 \end{pmatrix}

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

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

手順2 ReLUを通す

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

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

a_1 = \begin{pmatrix} 4 \\ 3 \end{pmatrix}

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

ここで、行列積との違いを押さえてください。ReLUは各要素に独立して適用されます。1番目の出力は1番目の入力にしか依存しません。

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

手順3 出力を求める

\hat{y} = W_2 a_1

計算します。

1 \times 4 = 4

2 \times 3 = 6

\hat{y} = 4 + 6 = 10

手順4 損失を求める

二乗誤差を使います。

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

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

計算します。

\hat{y} - t = 10 - 8 = 2

L = \frac{1}{2} \times 2^2 = \frac{1}{2} \times 4 = 2

損失は2です。正解が8なのに10を出力したので、誤差が出ています。

手順5 出力層の誤差

ここから逆伝播です。求めたいのは、次の二つです。

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

それぞれ、その重みを少し増やしたとき損失がどれだけ変わるかを表します。値が正なら減らせばよく、負なら増やせばよい、という指針になります。

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

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} を付けた理由が、ここで分かります。

出力層は恒等関数なので、次のようになります。

\delta_2 = 10 - 8 = 2

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

手順6 出力層の重みの勾配

\hat{y} = W_2 a_1 でしたので、次のようになります。

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

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

計算します。

2 \times 4 = 8

2 \times 3 = 6

\frac{\partial L}{\partial W_2} = \begin{pmatrix} 8 & 6 \end{pmatrix}

なぜ a_1 を掛けるのか。\hat{y} = W_{2,1} a_{1,1} + W_{2,2} a_{1,2} という関係から、W_{2,1} を少し変えたときの \hat{y} の変化は a_{1,1} 倍になるためです。

入ってきた値が大きいほど、その重みの変更が結果に強く響く。当然の話です。

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

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

\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 と同じ形になっています。

計算します。

1 \times 2 = 2

2 \times 2 = 4

\frac{\partial L}{\partial a_1} = \begin{pmatrix} 2 \\ 4 \end{pmatrix}

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

手順8 ReLUを逆に通る

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

\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が各要素に独立して適用されたからです。混ざっていないものは、戻すときも混ぜません。

計算します。

\delta_1 = \begin{pmatrix} 2 \\ 4 \end{pmatrix} \odot \begin{pmatrix} 1 \\ 1 \end{pmatrix} = \begin{pmatrix} 2 \\ 4 \end{pmatrix}

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

ここで、二つの演算の役割分担を整理します。

演算役割
行列積 W^{T}\delta 誤差を前の層の次元へ戻す
アダマール積 \odot 通す経路と止める経路を選別する

混ぜる操作と選別する操作。この二つが交互に現れるのが、ニューラルネットワークの構造です。

手順9 中間層の重みの勾配

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

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

計算します。

\begin{pmatrix} 2 \\ 4 \end{pmatrix} \begin{pmatrix} 2 & 1 \end{pmatrix}

1行目です。

2 \times 2 = 4, \quad 2 \times 1 = 2

2行目です。

4 \times 2 = 8, \quad 4 \times 1 = 4

\frac{\partial L}{\partial W_1} = \begin{pmatrix} 4 & 2 \\ 8 & 4 \end{pmatrix}

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

手順10 重みを更新する

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

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

W_2 を更新します。

1 - 0.01 \times 8 = 1 - 0.08 = 0.92

2 - 0.01 \times 6 = 2 - 0.06 = 1.94

W_1 を更新します。

1 - 0.01 \times 4 = 0.96

2 - 0.01 \times 2 = 1.98

2 - 0.01 \times 8 = 1.92

-1 - 0.01 \times 4 = -1.04

更新後は次の通りです。

W_1 = \begin{pmatrix} 0.96 & 1.98 \\ 1.92 & -1.04 \end{pmatrix}, \quad W_2 = \begin{pmatrix} 0.92 & 1.94 \end{pmatrix}

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

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

z_{1,1} = 0.96 \times 2 + 1.98 \times 1 = 1.92 + 1.98 = 3.90

z_{1,2} = 1.92 \times 2 + (-1.04) \times 1 = 3.84 - 1.04 = 2.80

どちらも正なので、そのまま通ります。

a_1 = \begin{pmatrix} 3.90 \\ 2.80 \end{pmatrix}

出力を計算します。

0.92 \times 3.90 = 3.588

1.94 \times 2.80 = 5.432

\hat{y} = 3.588 + 5.432 = 9.020

損失を計算します。

9.020 - 8 = 1.020

L = \frac{1}{2} \times 1.020^2 = \frac{1}{2} \times 1.0404 = 0.5202

結果をまとめます。

項目更新前更新後
予測値10.0009.020
損失2.00000.5202

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

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

負の値が出るとどうなるか

ここまでは z_1 がどちらも正でした。負になる場合を確認します。

入力だけを次のように変えます。重みは初期値に戻します。

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

順伝播を計算します。

z_{1,1} = 1 \times 1 + 2 \times 3 = 1 + 6 = 7

z_{1,2} = 2 \times 1 + (-1) \times 3 = 2 - 3 = -1

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

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

出力を計算します。

\hat{y} = 1 \times 7 + 2 \times 0 = 7

正解を t = 8 とします。

\delta_2 = 7 - 8 = -1

L = \frac{1}{2} \times (-1)^2 = 0.5

逆伝播です。

\frac{\partial L}{\partial W_2} = -1 \times \begin{pmatrix} 7 & 0 \end{pmatrix} = \begin{pmatrix} -7 & 0 \end{pmatrix}

\frac{\partial L}{\partial a_1} = \begin{pmatrix} 1 \\ 2 \end{pmatrix} \times (-1) = \begin{pmatrix} -1 \\ -2 \end{pmatrix}

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

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

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

\delta_1 = \begin{pmatrix} -1 \\ -2 \end{pmatrix} \odot \begin{pmatrix} 1 \\ 0 \end{pmatrix} = \begin{pmatrix} -1 \\ 0 \end{pmatrix}

W_1 の勾配を計算します。

\begin{pmatrix} -1 \\ 0 \end{pmatrix} \begin{pmatrix} 1 & 3 \end{pmatrix} = \begin{pmatrix} -1 & -3 \\ 0 & 0 \end{pmatrix}

2行目がすべて0になりました。この入力に関しては、2番目のユニットに繋がる重みは更新されません。

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

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

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

第1ステップのまとめ

覚えるべき式は、四つだけです。

場面
順伝播z = Wx
出力層の誤差\delta = \hat{y} - t
前の層へ伝えるW^{T}\delta
重みの勾配\delta x^{T}

そして活性化関数を通るときは、アダマール積で \text{ReLU}' を掛けます。

これが骨格です。層が何層あっても、この繰り返しです。

第2ステップ バイアスを加える

骨格が分かったので、部品を足します。

何が変わるのか

順伝播の式が変わります。

z_1 = W_1 x + b_1

\hat{y} = W_2 a_1 + b_2

そして、勾配を求める対象が二つ増えます。

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

変わらないものもあります。転置による誤差の伝播も、アダマール積も、重みの勾配の式も、まったく同じです。バイアスは足し算なので、掛け算の構造には影響しません。

バイアスの勾配は驚くほど簡単

z = Wx + b b で微分します。

\frac{\partial z}{\partial b} = 1

b の係数が1だからです。したがって、連鎖律から次のようになります。

\frac{\partial L}{\partial b} = \delta

誤差そのものです。新しい計算は不要です。

直感的にも自然です。バイアスは、そのユニットの出力を直接持ち上げたり下げたりする量です。誤差が2あれば、バイアスを2の方向に動かせばよい。それだけの話です。

なぜバイアスが必要なのか

第1ステップのネットワークには、構造的な制約がありました。

x = 0 を入れると、必ず z = 0 になります。したがって出力も0です。入力が0のときの出力を、0以外にできません。

一次関数で言えば、切片が0に固定された状態です。原点を通る直線しか引けません。

バイアスは、この切片にあたります。加えることで、ReLUが折れ曲がる位置も動かせるようになります。表現できる関数の幅が、大きく広がります。

初期値

第1ステップと同じ重みに、バイアスを足します。

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

W_1 = \begin{pmatrix} 1 & 2 \\ 2 & -1 \end{pmatrix}, \quad b_1 = \begin{pmatrix} 1 \\ 2 \end{pmatrix}

W_2 = \begin{pmatrix} 1 & 2 \end{pmatrix}, \quad b_2 = 1

正解値は次の通りです。

t = 14

順伝播

W_1 x の部分は、第1ステップと同じです。

W_1 x = \begin{pmatrix} 4 \\ 3 \end{pmatrix}

バイアスを足します。

z_1 = \begin{pmatrix} 4 \\ 3 \end{pmatrix} + \begin{pmatrix} 1 \\ 2 \end{pmatrix} = \begin{pmatrix} 5 \\ 5 \end{pmatrix}

どちらも正なので、そのまま通ります。

a_1 = \begin{pmatrix} 5 \\ 5 \end{pmatrix}

出力を計算します。

1 \times 5 = 5

2 \times 5 = 10

\hat{y} = 5 + 10 + 1 = 16

損失を計算します。

16 - 14 = 2

L = \frac{1}{2} \times 4 = 2

逆伝播

出力層の誤差です。

\delta_2 = 2

出力層の勾配を求めます。

\frac{\partial L}{\partial W_2} = \delta_2 a_1^{T} = 2 \times \begin{pmatrix} 5 & 5 \end{pmatrix} = \begin{pmatrix} 10 & 10 \end{pmatrix}

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

バイアスの勾配は、誤差をそのまま書き写すだけです。

中間層へ誤差を伝えます。

\frac{\partial L}{\partial a_1} = W_2^{T} \delta_2 = \begin{pmatrix} 1 \\ 2 \end{pmatrix} \times 2 = \begin{pmatrix} 2 \\ 4 \end{pmatrix}

ReLUの微分は、どちらも1です。

\delta_1 = \begin{pmatrix} 2 \\ 4 \end{pmatrix} \odot \begin{pmatrix} 1 \\ 1 \end{pmatrix} = \begin{pmatrix} 2 \\ 4 \end{pmatrix}

中間層の勾配を求めます。

\frac{\partial L}{\partial W_1} = \delta_1 x^{T} = \begin{pmatrix} 2 \\ 4 \end{pmatrix} \begin{pmatrix} 2 & 1 \end{pmatrix} = \begin{pmatrix} 4 & 2 \\ 8 & 4 \end{pmatrix}

\frac{\partial L}{\partial b_1} = \delta_1 = \begin{pmatrix} 2 \\ 4 \end{pmatrix}

更新

学習率は \eta = 0.01 です。

W_2 を更新します。

1 - 0.01 \times 10 = 0.90

2 - 0.01 \times 10 = 1.90

b_2 を更新します。

1 - 0.01 \times 2 = 0.98

W_1 を更新します。

1 - 0.04 = 0.96, \quad 2 - 0.02 = 1.98

2 - 0.08 = 1.92, \quad -1 - 0.04 = -1.04

b_1 を更新します。

1 - 0.02 = 0.98

2 - 0.04 = 1.96

検算

更新後の値で、もう一度順伝播します。

z_{1,1} = 0.96 \times 2 + 1.98 \times 1 + 0.98 = 1.92 + 1.98 + 0.98 = 4.88

z_{1,2} = 1.92 \times 2 + (-1.04) \times 1 + 1.96 = 3.84 - 1.04 + 1.96 = 4.76

どちらも正です。

a_1 = \begin{pmatrix} 4.88 \\ 4.76 \end{pmatrix}

出力を計算します。

0.90 \times 4.88 = 4.392

1.90 \times 4.76 = 9.044

\hat{y} = 4.392 + 9.044 + 0.98 = 14.416

損失を計算します。

14.416 - 14 = 0.416

L = \frac{1}{2} \times 0.416^2 = \frac{1}{2} \times 0.173056 = 0.086528

結果をまとめます。

項目更新前更新後
予測値16.000014.4160
損失2.0000000.086528

損失が約96%減少しました。

二つのステップを比べる

同じ形の表で並べます。

項目バイアスなしバイアスあり
順伝播z = Wx z = Wx + b
出力層の誤差\hat{y} - t \hat{y} - t
前の層へW^{T}\delta W^{T}\delta
重みの勾配\delta x^{T} \delta x^{T}
バイアスの勾配なし\delta

追加されたのは最後の一行だけです。骨格は変わっていません。

これが、バイアスを後回しにしてよい理由です。

ブロードキャストとバイアス

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

入力を横に並べる

3件のデータをまとめます。

X = \begin{pmatrix} 2 & 1 & 0 \\ 1 & 3 & 2 \end{pmatrix}

形は2行3列です。列がデータ1件に対応します。

計算式は変わりません。

Z_1 = W_1 X + b_1

形を確認する

W_1 が2行2列、X が2行3列なので、W_1 X は2行3列です。

ところが b_1 は2行1列です。形が違います。どうやって足すのでしょうか。

ブロードキャスト

ここで働くのが、ブロードキャストという仕組みです。b_1 が3列に自動的に複製され、各列に同じ値が足されます。

b_1 を横に並べたものを足す、と考えてください。

\begin{pmatrix} 1 & 1 & 1 \\ 2 & 2 & 2 \end{pmatrix}

なぜこれでよいのでしょうか。バイアスは、そのユニットに固有の値だからです。どのデータを入れても同じ値が足されます。データごとに変わるものではありません。

計算する

W_1 X を求めます。

1列目です。第1ステップで計算した通りです。

\begin{pmatrix} 4 \\ 3 \end{pmatrix}

2列目です。

1 \times 1 + 2 \times 3 = 7

2 \times 1 + (-1) \times 3 = -1

3列目です。

1 \times 0 + 2 \times 2 = 4

2 \times 0 + (-1) \times 2 = -2

まとめます。

W_1 X = \begin{pmatrix} 4 & 7 & 4 \\ 3 & -1 & -2 \end{pmatrix}

バイアスを足します。

Z_1 = \begin{pmatrix} 5 & 8 & 5 \\ 5 & 1 & 0 \end{pmatrix}

ReLUを通します。0以下は0になります。

A_1 = \begin{pmatrix} 5 & 8 & 5 \\ 5 & 1 & 0 \end{pmatrix}

3行目の3列目が0でしたので、そのまま0です。

逆伝播での注意

順伝播で b_1 を3列に複製したのだから、逆伝播では逆の操作をします。つまり、3列分の誤差を足し合わせます。

\frac{\partial L}{\partial b_1} = \sum_{k} \delta_1^{(k)}

k はデータの番号です。行ごとに横方向へ合計する、と考えてください。

複製の逆は合計。この対応は覚えておく価値があります。

なお、平均で割るかどうかは損失関数の定義によります。バッチ全体の平均を損失とするなら、勾配も件数で割ります。合計を損失とするなら割りません。実装のときは、どちらの定義かを確認してください。

落とし穴

ブロードキャストは便利ですが、危険もあります。意図しない形の組み合わせでもエラーが出ず、計算が通ってしまうことがあるためです。

たとえば、2行1列のつもりが1行2列になっていると、縦方向でなく横方向に複製されます。エラーは出ませんが、結果はまったく違うものになります。

計算の前後で形を確認する習慣をつけてください。

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

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

理由1 規模が違いすぎる

第2ステップのネットワークのパラメータ数を数えます。

W_1: 2 \times 2 = 4

b_1: 2

W_2: 1 \times 2 = 2

b_2: 1

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

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

W_1: 100 \times 784 = 78400

b_1: 100

W_2: 10 \times 100 = 1000

b_2: 10

合計を計算します。

78400 + 100 + 1000 + 10 = 79510

約8万個です。1つの計算に5秒かかるとすると、順伝播1回に必要な時間は次の通りです。

79510 \times 5 = 397550

日数に直します。

397550 \div 86400 \approx 4.6

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

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

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

計算した式を並べます。

方向
順伝播z = Wx + b
逆伝播(前の層へ)W^{T}\delta
逆伝播(重みへ)\delta x^{T}
逆伝播(バイアスへ)\delta

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

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

理由3 形が検算になる

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

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

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

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

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

ミニバッチの節で見た通り、データ件数を増やしても式は変わりませんでした。

Z_1 = W_1 X + b_1

X の列数を変えるだけです。

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

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

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

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

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

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

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

混ぜる操作と選別する操作。線形代数の二つの演算が、そのまま役割分担しています。

練習問題

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

問題1 バイアスなしの順伝播

次の設定で、z_1 a_1 \hat{y} L を求めてください。バイアスはありません。

x = \begin{pmatrix} 1 \\ 1 \end{pmatrix}, \quad W_1 = \begin{pmatrix} 1 & 2 \\ 2 & -1 \end{pmatrix}, \quad W_2 = \begin{pmatrix} 1 & 2 \end{pmatrix}, \quad t = 4

解答を示します。

z_1 を計算します。

1行目です。

1 \times 1 + 2 \times 1 = 3

2行目です。

2 \times 1 + (-1) \times 1 = 1

z_1 = \begin{pmatrix} 3 \\ 1 \end{pmatrix}

どちらも正なので、そのまま通ります。

a_1 = \begin{pmatrix} 3 \\ 1 \end{pmatrix}

出力を計算します。

1 \times 3 + 2 \times 1 = 3 + 2 = 5

損失を計算します。

5 - 4 = 1

L = \frac{1}{2} \times 1^2 = 0.5

補足します。本文では x = (2, 1) でしたが、今回は (1, 1) です。入力が変われば、同じ重みでも結果は変わります。学習とは、あらゆる入力に対して誤差が小さくなる重みを探すことです。

問題2 形の理解

入力784、中間層100、出力10のネットワークについて答えてください。バイアスはありません。

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

解答を示します。

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

W_2 は10行100列です。

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

100 \times 784 = 78400

10 \times 100 = 1000

78400 + 1000 = 79400

79,400個です。

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

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

問題3 出力層の逆伝播

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

\delta_2 = 3, \quad a_1 = \begin{pmatrix} 2 \\ 5 \end{pmatrix}, \quad W_2 = \begin{pmatrix} 4 & 1 \end{pmatrix}

解答を示します。

\partial L / \partial W_2 を計算します。

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

3 \times 2 = 6, \quad 3 \times 5 = 15

\frac{\partial L}{\partial W_2} = \begin{pmatrix} 6 & 15 \end{pmatrix}

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

\partial L / \partial a_1 を計算します。

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

4 \times 3 = 12, \quad 1 \times 3 = 3

\frac{\partial L}{\partial a_1} = \begin{pmatrix} 12 \\ 3 \end{pmatrix}

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

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

問題4 ReLUの微分

中間層の値が次の通りのとき、\delta_1 を求めてください。

z_1 = \begin{pmatrix} 3 \\ -2 \\ 5 \\ -1 \end{pmatrix}, \quad \frac{\partial L}{\partial a_1} = \begin{pmatrix} 2 \\ 6 \\ -4 \\ 3 \end{pmatrix}

解答を示します。

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

3 > 0 なので1です。

-2 < 0 なので0です。

5 > 0 なので1です。

-1 < 0 なので0です。

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

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

\delta_1 = \begin{pmatrix} 2 \times 1 \\ 6 \times 0 \\ -4 \times 1 \\ 3 \times 0 \end{pmatrix} = \begin{pmatrix} 2 \\ 0 \\ -4 \\ 0 \end{pmatrix}

補足します。2番目に注目してください。\partial L / \partial a_1 の値が6と最も大きかったにもかかわらず、z が負のため0になりました。

誤差が大きくても、順伝播で寄与していなければ責任を問われない、ということです。

問題5 中間層の勾配

\delta_1 = \begin{pmatrix} 3 \\ -1 \end{pmatrix} x = \begin{pmatrix} 2 \\ 4 \end{pmatrix} のとき、\partial L / \partial W_1 を求めてください。また、2列目が1列目より大きい理由を説明してください。

解答を示します。

\frac{\partial L}{\partial W_1} = \delta_1 x^{T} = \begin{pmatrix} 3 \\ -1 \end{pmatrix} \begin{pmatrix} 2 & 4 \end{pmatrix}

1行目です。

3 \times 2 = 6, \quad 3 \times 4 = 12

2行目です。

-1 \times 2 = -2, \quad -1 \times 4 = -4

\frac{\partial L}{\partial W_1} = \begin{pmatrix} 6 & 12 \\ -2 & -4 \end{pmatrix}

理由を説明します。

2列目は入力 x_2 = 4 に掛かる重みです。z = W_{11} x_1 + W_{12} x_2 という関係から、W_{12} を少し変えたときの z の変化は x_2 倍になります。

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

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

問題6 転置の意味

逆伝播で W^{T} が現れる理由を、形の観点から説明してください。W が3行5列の場合で考えてください。

解答を示します。

W が3行5列のとき、順伝播は次のようになります。

z = Wx

x は5行1列、z は3行1列です。5次元から3次元へ変換しています。

逆伝播では、z の誤差 \delta から x の誤差を求めます。\delta は3行1列、求めたいものは5行1列です。3次元から5次元へ戻す必要があります。

3行1列に掛けて5行1列を作るには、5行3列の行列が必要です。W^{T} がちょうどその形をしています。

W^{T} \delta: (5, 3) \times (3, 1) \rightarrow (5, 1)

補足します。順伝播が次元を減らす方向なら、逆伝播は増やす方向です。行と列の役割が入れ替わるため、転置が現れます。

丸暗記する必要はありません。形を考えれば、転置しか選択肢がないことが分かります。

問題7 バイアスありの逆伝播

次の設定で順伝播を行い、四つの勾配をすべて求めてください。

x = \begin{pmatrix} 1 \\ 1 \end{pmatrix}, \quad W_1 = \begin{pmatrix} 1 & 2 \\ 2 & -1 \end{pmatrix}, \quad b_1 = \begin{pmatrix} 1 \\ 2 \end{pmatrix}

W_2 = \begin{pmatrix} 1 & 2 \end{pmatrix}, \quad b_2 = 1, \quad t = 10

解答を示します。

順伝播から始めます。

W_1 x = \begin{pmatrix} 3 \\ 1 \end{pmatrix}

バイアスを足します。

z_1 = \begin{pmatrix} 3 \\ 1 \end{pmatrix} + \begin{pmatrix} 1 \\ 2 \end{pmatrix} = \begin{pmatrix} 4 \\ 3 \end{pmatrix}

どちらも正なので、そのまま通ります。

a_1 = \begin{pmatrix} 4 \\ 3 \end{pmatrix}

出力を計算します。

1 \times 4 + 2 \times 3 + 1 = 4 + 6 + 1 = 11

誤差を求めます。

\delta_2 = 11 - 10 = 1

出力層の勾配です。

\frac{\partial L}{\partial W_2} = 1 \times \begin{pmatrix} 4 & 3 \end{pmatrix} = \begin{pmatrix} 4 & 3 \end{pmatrix}

\frac{\partial L}{\partial b_2} = 1

誤差を中間層へ伝えます。

\frac{\partial L}{\partial a_1} = \begin{pmatrix} 1 \\ 2 \end{pmatrix} \times 1 = \begin{pmatrix} 1 \\ 2 \end{pmatrix}

ReLUの微分はどちらも1なので、そのままです。

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

中間層の勾配です。

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

\frac{\partial L}{\partial b_1} = \begin{pmatrix} 1 \\ 2 \end{pmatrix}

補足します。バイアスの勾配は、\delta をそのまま書き写すだけです。追加の計算はありません。

また、x = (1, 1) なので W_1 の勾配は各行で同じ値になりました。入力が等しければ、その入力に掛かる重みの勾配も等しくなります。

問題8 ミニバッチでのバイアスの勾配

3件のデータをまとめて処理したところ、次の結果になりました。

X = \begin{pmatrix} 2 & 1 & 0 \\ 1 & 3 & 2 \end{pmatrix}, \quad \Delta_1 = \begin{pmatrix} 1 & 2 & -1 \\ 0 & 3 & 4 \end{pmatrix}

\Delta_1 は3件分の \delta_1 を横に並べたものです。

(1) \partial L / \partial b_1 を求めてください。 (2) \partial L / \partial W_1 を求めてください。 (3) それぞれの形が正しいか確認してください。

解答を示します。

(1) バイアスの勾配は、横方向に合計します。順伝播で複製したので、逆では合計します。

1行目です。

1 + 2 + (-1) = 2

2行目です。

0 + 3 + 4 = 7

\frac{\partial L}{\partial b_1} = \begin{pmatrix} 2 \\ 7 \end{pmatrix}

(2) 重みの勾配は次の式で求めます。

\frac{\partial L}{\partial W_1} = \Delta_1 X^{T}

X^{T} は3行2列です。

X^{T} = \begin{pmatrix} 2 & 1 \\ 1 & 3 \\ 0 & 2 \end{pmatrix}

1行1列目です。

1 \times 2 + 2 \times 1 + (-1) \times 0 = 2 + 2 + 0 = 4

1行2列目です。

1 \times 1 + 2 \times 3 + (-1) \times 2 = 1 + 6 - 2 = 5

2行1列目です。

0 \times 2 + 3 \times 1 + 4 \times 0 = 0 + 3 + 0 = 3

2行2列目です。

0 \times 1 + 3 \times 3 + 4 \times 2 = 0 + 9 + 8 = 17

\frac{\partial L}{\partial W_1} = \begin{pmatrix} 4 & 5 \\ 3 & 17 \end{pmatrix}

(3) \partial L / \partial b_1 は2行1列で、b_1 と一致します。

\partial L / \partial W_1 は2行2列で、W_1 と一致します。

形の計算でも確認できます。

(2, 3) \times (3, 2) \rightarrow (2, 2)

内側の3が消えて、外側が残ります。

補足します。\Delta_1 X^{T} という一つの積の中で、3件分の勾配が自動的に足し合わされています。データ件数を増やしても式は変わりません。

これが、行列で書くことの実用的な利点です。

学習の進め方

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

段階内容
1ベクトルと行列の表記に慣れる
2行列積を手で計算できるようにする
3形の規則を理解する
4バイアスなしで順伝播を計算する
5連鎖律を理解する
6バイアスなしで逆伝播を計算する
7更新後に損失が下がることを確認する
8負の値が出る場合も計算する
9バイアスを加える
10ミニバッチとブロードキャストを理解する

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

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

振り返り

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

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

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

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

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

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

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

今回は二段階で進めました。

第1ステップでは、バイアスを外して骨格だけを見ました。損失が2から0.5202へ、約74%減少しました。

第2ステップでは、バイアスを加えました。追加された式は \partial L / \partial b = \delta の一行だけです。損失は2から0.086528へ、約96%減少しました。

骨格が変わらなかったことが、この構成の要点です。難しく見えるものも、分解すれば単純な部品の組み合わせです。

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

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

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

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

投稿者プロフィール

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

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