誤差逆伝播法の数式の説明なんて世の中にたくさんあると思うが、理解のために自分でもまとめる。
特に添字などのミスがあると思うので、見つけ次第修正する。
誤差逆伝播の計算 (1)#
問題設定#
入力を第 0 層、出力を第 L 層とする。ニューラルネットワークはよく次のようなグラフで描かれる。
円がノードを表す。ノードに入っていく矢印が入力、出ていく矢印が出力を表す。

第 l 層 j 番目のノードの出力を yjl とおく (注意: この記事では yjl の l は添字を表すものとする。累乗ではない。これから現れる変数についても同様)。これはある関数 fl を用いて以下の式で表される。fl は活性化関数と呼ばれる。
yjl=fl(ujl) ただし、
ujl は前の層の出力を用いて計算される線形和で、以下のように定義される。
ujl=i∑wijlyil−1 このような、線形和を取って f を適用するという流れは次のようなグラフで描かれる。

この ∑∣f のノードがたくさん集まって第 l 層を形成している。
損失関数 E は、重みwijl についての関数である。これは出力値 yiL と教師データ y~i との違いを測る尺度であるから、yiL の関数でもある。
例えば、以下の二乗誤差は損失関数の一種である。
E=21i∑(yiL−y~i)2 定義中に wijl が含まれていないじゃないか、と思うかもしれないが、yiL の定義中に wijL が含まれている。さらにその中の yiL−1 中に wijL−1 が含まれている。以下同様にして wijl は E の中に含まれている。
いま、E を最小化するような wijl を求めたい。これには確率的勾配法が利用できるが、そのために偏微分 ∂wijl∂E を計算する必要がある。以降、これをどう計算するかという話を展開していく。
出力層#
l=L のときを考える。まず E は y1L,y2L,…, についての関数だから、連鎖律より、
∂wijL∂E=j′∑∂yj′L∂E∂wijL∂yj′L と変形できる。ところが、yj’L=fL(uj’L) は uj’L についての関数より、再び連鎖律から、
∂wijL∂yj′L=∂uj′L∂yj′L∂wijL∂uj′L=fL′(uj′L)∂wijL∂i′∑wi′j′yi′L−1=fL′(uj′L)yiL−1δj,j′ と書ける。ここで、fL’ は fL の導関数を表し、δj,j’ はクロネッカーのデルタを表す。
結局、式 (1) の∑j’ の部分が消えて、
∂wijL∂E=j′∑∂yj′L∂EfL′(uj′L)yiL−1δj,j′=∂yjL∂EfL′(ujL)yiL−1 が得られる。このように j’=j の項しか残らないのは、wijL が E(y1L,y2L,…) の引数のうち yjL にしか関わってこないからなのだが、一応このことを丁寧に計算で示した。
今後の計算のために、以下の量を定義する。
δjl:=∂ujl∂E=∂yjl∂EfL′(ujl) これを用いると、
∂wijL∂E=δjLyiL−1 と表せる。
中間層#
l=L のときを考える。E は y1L,y2L,… の関数であるが、その各々 yjL=fL(∑iwijLyiL−1) は y1L−1,y2L−1,… の関数である。
従って、E は y1L−1,y2L−1,… の関数でもある。以下同様にして、E は y1l,y2l,… の関数であることが分かる。よって、連鎖率より、
∂wijl∂E=j′∑∂yj′l∂E∂wijl∂yj′l と書ける。出力層のときとまったく同じ議論により、これは、
∂wijl∂E=∂yjl∂Efl′(ujl)yil−1=δjlyil−1 となる。ここで、δjl は
δjl=∂yjl∂Efl′(ujl) であるから、これを計算するためには∂yjl∂E を計算する必要がある。 E は y1l+1,y2l+2,… の関数でもあるから、連鎖律より、
∂yjl∂E=k∑∂ykl+1∂E∂yjl∂ykl+1 が成り立つ。さらに ykl+1 は ukl+1 の関数であるから、連鎖律より、
∂yjl∂E=k∑∂ykl+1∂E∂ukl+1∂ykl+1∂yjl∂ukl+1 となる。ここで、
∂ukl+1∂ykl+1∂yjl∂ukl+1=fl+1′(ukl+1)=∂yjl∂j′∑wj′kl+1yj′l=wjkl+1 であるから、式(2)は、
∂yjl∂E=k∑∂ykl+1∂Efl+1′(ukl+1)wjkl+1=k∑δkl+1wjkl+1 となる。以上より求めたかった δjl は、
δjl=∂yjl∂Efl′(ujl)=k∑δkl+1wjkl+1fl′(ujl) と書ける。すなわち、δjl を計算するためには δkl+1 を計算しておけば良い。
ここまでのまとめ#
さて、いままでの計算をまとめると、1≤l≤L について、
∂wijl∂E=δjlyil−1 と表せる。ただし δjl は、
δjl=⎩⎨⎧∂yjL∂EfL′(ujL)k∑δkl+1wjkl+1fl′(ujl)(l=L)(l=L) で表される。
ベクトル表記にする#
第 l 層についての δjl を並べたベクトルを δl で表す。同様に、ベクトル yl,ul を定義する。
前提として、ul、yl は次のように計算できる。これはいわゆる順伝播である。
ulyl=(Wl)⊤yl−1=f(ul) ただし、関数 f とベクトル x に対し、 f(x) は x の各成分に f を適用したベクトルを表す。
最初に、δl の計算式を導く。l=L のとき、
δL=(δ1Lδ2L⋯)=(∂y1L∂EfL′(u1L)∂y2L∂EfL′(u2L)⋯)=∂yL∂E∗fL′(uL) とすればよい。
ただし、任意のベクトルx,y について、x∗y は成分ごとの積をとったベクトルを表す。
l=L のとき、Wl+1=(wijl+1)ij を P×Q 行列とする(注意: この記事では Wl の l は累乗ではなく添字を表すものとする)。
ここでは,P は入力の次元、 Q は出力の次元を表していることに注意。このとき、
δl=∑kQδkl+1w1kl+1fl′(u1l)∑kQδkl+1w2kl+1fl′(u2l)⋮∑kQδkl+1wPkl+1fl′(uPl)=w11l+1w21l+1⋮wP1l+1⋯⋯⋱⋯w1Ql+1w2Ql+1⋮wPQl+1δ1l+1fl′(u1l)δ2l+1fl′(u2l)⋮δPl+1fl′(uPl)=Wl+1δl+1∗fl′(ul) ∂wijl∂E については、
∂wijl∂E=yl−1(δl)⊤ と表せる。
まとめ#
前提として、以下の順伝播の式で yl、ul が計算できているものとする。
ulyl=(Wl)⊤yl−1=f(ul) 誤差逆伝播の式は次のように書ける。
∂Wl∂E=yl−1(δl)⊤ δl={∂yL∂E∗fL′(uL)Wl+1δl+1∗fl′(ul)(l=L)(l=L) 誤差逆伝播の計算 (2)#
前節では「δjl+1 を使って δjl を求める」という流れを導出したが、
式変形を睨むと、「∂yjl+1∂E を使って ∂yjl∂E を求める」という流れでも良いことが分かる。
それを確かめるために、δjl+1 という表記をやめ、式を見直してみる。
まず、1≤l≤L について、前節の議論により、
∂wijl∂E=∂yjl∂Efl′(ujl)yil−1 が成り立つ。∂yjl∂E をどう求めるかを考える。
l=L の場合は、単に損失関数 E の定義に従って計算すれば良い (どんな損失関数を選ぶかで形が違ってくる)。l=L の場合、前節の中間層の計算を追ってみると、
∂yjl∂E=k∑∂ykl+1∂Efl+1′(ukl+1)wjkl+1 と計算できる。すなわち、ykl+1 による偏微分を使って第 ykl による偏微分が計算できることが分かる。式 (4) には第 l+1 層に関連する量しか含まれていないから、この計算は l+1 層にて事前に行っておけばよい。もちろん、第 l 層の立場からすれば、第 l−1 層についての以下の事前計算を行うことになる。
∂yjl−1∂E=k∑∂ykl∂Efl′(ukl)wjkl まとめると、第 l 層で行う処理は次の2つである。
- 式 (3)に従って ∂wijl∂E を計算する。
- 1つ前の層の計算のために、式 (5) を使って E の yil−1 による偏微分を計算しておく。
最初からこちらの手法で解説している文献もある。
計算の分離#
重み付き線形和を求めるだけの層、活性化関数を求めるだけの層に分ける。

前者は全結合層と呼ばれている。Affine Layer 、Dense Layer とも言われる。後者は活性化関数に応じて層の名前が付く。例えばシグモイド関数なら Sigmoid Layer と呼ばれ、ReLU関数なら ReLU Layer と呼ばれる。
kerasやChainer、PyToachなどのニューラルネットワークのライブラリでは、このように層を分けている。その理由はおそらく使いやすさ・分かり易さの観点からだと思われる。
後々計算すると分かるが、分けることによって式が綺麗にまとまる。また活性化関数の層を分けておけば、全結合層以外の層とも組み合わせられる。
以下では、それぞれの層について ∂wijl∂E, ∂yjl−1∂E の計算方法を記す。
活性化関数を求める層#
yil−1 を入力、yil=f(yil−1) (i=1,2,…) を出力とする層を考える。
まず、∂wijl∂E を計算する必要はない。なぜなら、重み wijl が存在しないからである。よって、yil−1 による微分のみ計算する。
連鎖律より、
∂yjl−1∂E=k∑∂ykl∂E∂yjl−1∂ykl yil は yil−1 のみの関数であることに注意して、
∂yjl−1∂E=k∑∂ykl∂Efl′(yjl−1)δk,j=∂yjl∂Efl′(yjl−1) と計算される。
ベクトルで表記すると、
∂yl−1∂E=∂yl∂E∗fl′(yl−1) 全結合層#
yil−1 を入力、yil=∑jwijyjl−1 (i=1,2,…) を出力とする層を考える。
面倒なので計算を端折る。要するにこの線形和は、以前の議論で導いた2つの式 (3)、(5)
∂wijl∂E∂yjl−1∂E=∂yjl∂Efl′(ujl)yil−1=k∑∂ykl∂Efl′(ukl)wjkl において、活性化関数が恒等関数 f(x)=x となるものである。よって、
∂wijl∂E∂yjl−1∂E=∂yjl∂Eyil−1=k∑∂ykl∂Ewjkl が得られる。
ベクトルで表記すると、
∂Wl∂E∂yl−1∂E=yl−1(∂yl∂E)⊤=Wl∂yl∂E ミニバッチ学習#
いままで確率的勾配法で話を進めてきたが、ミニバッチ勾配法の場合はどのようになるのだろうか。
計算のためには、今までの表記を少し改め、損失関数をミニバッチ用に変える必要がある。
今まで出力値を yil と表していたが、これを n 番目の入力データに対する出力値 ynil に書き直す。
まず、ミニバッチとする訓練データの個数を N とする。その入力を y10,y20,…,yN0 とする。
各ノードに対する出力値は y1l,y2l,…,yNl と書ける。
ここで、第 l 層のノードの個数を Dl とし、ynl=(yn1l,yn2l,…,ynDll)⊤
とした。
また、
Yl=(y1ly2l⋯yNl) と書くことにする。
n 番目の入力データに対する損失関数をEnとする。例えば二乗誤差なら、
En=21i∑(yniL−y~ni)2 となる。
N 個の訓練データに対する損失関数を、各々のデータの損失関数の平均で表す。
E=N1n=1∑NEn さて wijl についての偏微分をどう計算するかだが、これは偏微分の線形性から、
∂wijl∂E=N1n=1∑N∂wijl∂En が成り立つことに注目すれば良い。En の計算は今までの議論と何ら変わりない。
あとはそれぞれの層について、順伝播・逆伝播がどう表現できるのかについて考える。
誤差逆伝播の計算 (1) の場合#
n 番目のデータに対する第 l 層の線形和を unl とおく。Yl の定義とほぼ同様にして、
Ul=(u1lu2l⋯uNl) を定義する。同様に、
Δl=(δ1lδ2l⋯δNl) を定義する。
順伝播#
Ul=(u1lu2l⋯uNl)=((Wl)⊤y1l−1(Wl)⊤y2l−1⋯(Wl)⊤yNl−1)=(Wl)⊤Yl−1 Yl=(f(u1l)f(u2l)⋯f(uNl))=f(Ul) ただし、関数 f と行列 A に対し、 f(A) は A の各成分に f を適用した行列を表す。
逆伝播#
式 (6) が Wl=(wijl)ij の各成分について成り立つから、
∂Wl∂E=N1n=1∑N∂Wl∂En と変形できる。これより、
∂Wl∂E=N1n=1∑N∂Wl∂En=N1n=1∑Nynl−1(δnl)⊤=N1(y1l−1y2l−1⋯yNl−1)(δ1l−1)⊤(δ2l−1)⊤⋮(δNl−1)⊤=N1Yl−1(Δl)⊤ Δl の計算については、l=L のとき、
ΔL=(δ1Lδ2L⋯δNL)=(∂y1L∂E∗fL′(u1L)∂y2L∂E∗fL′(u2L)⋯∂yNL∂E∗fL′(uNL))=∂YL∂E∗fL′(UL) ただし、任意の行列A,B について、A∗B は成分ごとの積をとった行列を表す。
l=L のとき、
Δl=(δ1lδ2l⋯δNl)=(Wl+1δ1l+1∗fl′(u1l)Wl+1δ2l+1∗fl′(u2l)⋯Wl+1δNl+1∗fl′(uNl))=Wl+1Δl+1∗fl′(Ul) と計算できる。このように、ベクトルだった部分がそのまま行列になったかのような形となる。実際、N=1 としたらベクトルの表現に戻ることが分かるだろう。
誤差逆伝播の計算 (2) の場合#
活性化関数の層#
順伝播については、
Yl+1=(y1l+1y2l+1⋯yNl+1)=(f(y1l)f(y2l)⋯f(yNl))=f(Yl) で計算すれば良い。
逆伝播については次のように式が変形できる。
∂Yl−1∂E=(∂y1l∂E∂y2l∂E⋯∂yNl∂E)=(∂y1l∂E∗fl′(y1l−1)∂y2l∂E∗fl′(y2l−1)⋯∂yNl∂E∗fl′(yNl−1))=∂Yl∂E∗fl′(Yl) ベクトルが行列にすり替わっただけで、式の形はほとんど変わらない。
全結合層#
順伝播については、
Yl+1=(y1l+1y2l+1⋯yNl+1)=((Wl)⊤y1l(Wl)⊤y2l⋯(Wl)⊤yNl)=(Wl)⊤Yl で計算できる。
逆伝播の式を導出する。Wl については、
∂Wl∂E=N1n=1∑N∂Wl∂En=N1n=1∑Nynl−1(∂ynl∂E)⊤=N1Yl(∂Yl∂E)⊤ となる。Yl−1 については、
∂Yl−1∂E=(∂y1l−1∂E∂y2l−1∂E⋯∂yNl−1∂E)=(Wl∂y1l∂EWl∂y2l∂E⋯Wl∂yNl∂E)=Wl∂Yl∂E どちらも、ベクトルで表現した場合とあまり変わらないことが分かる。
まとめと感想#
自分でまとめてみると、難しい計算はしていないことが実感できた。
出てくる文字の数が多かったり、添字がややこしかったり、どの変数について連鎖律を使うのかという点がやや技巧的だったりはした。
ここまで式をまとめられれば、プログラムの実装ができそう。それは別の記事でまとめたい。
誤差逆伝播法の導出を通して、ニューラルネットワークが少し身近に感じられるようになった。
ただし、誤差逆伝播法は必要だが、これ自体がニューラルネットワークの本質であると勘違いしないようにしなければならないだろう (あくまで計算テクニックの1つ、という認識を持っている)。
本当に考えなければならないのは、どんなネットワークを作るか、どんな入力形式にするか、だと思う。
とはいえ自分はまだ入門者なので、確かなことは言えないが…。