はじめに
機械学習の勉強をしていると,次のようなベクトルや行列を使った公式達を使わなければならない場面が出てくると思います.機械学習の本の巻末に書いてあることが多いと思います.(これらはPattern Recognition and Machine Learning (Bishop著, 2006)の巻末に載っている公式です)
\frac{\partial}{\partial\boldsymbol{x}}(\boldsymbol{x}^\mathrm{T}\boldsymbol{a})
=\boldsymbol{a} \\
\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B})=\boldsymbol{B}^\mathrm{T} \\
\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B}\boldsymbol{A}^\mathrm{T})
=\boldsymbol{A}(\boldsymbol{B}+\boldsymbol{B}^\mathrm{T})
単純な公式はちょっと考えたら分かるけれど,複雑な公式になってくると理解するのに時間がかかります.とにかく沢山公式が出てきて,まあ覚えなくてもいいやと思っていると,いざ自力で式変形していった時に,あれ,こんな時どの公式使えばいいんだっけ?となって躓いてしまいます.これは,"行列表記"のままではとても式変形しづらく,"テンソル表記"に変換して計算してから"行列表記"に戻すことでほとんどの公式は覚えなくて良いことに気付くと簡単に解決できます.
基本的なことのような気もするのですが,案外きちんとまとまってる記事がなくて少し苦労したのでまとめてみました.また,微分の定義に複数の流儀があることも分かりました.
この記事で,行列やベクトルの公式に苦しめられてきた人達が私の経験したような苦労を回避できれば嬉しいです.
※結果だけ知りたい人は公式の導出まとめを読んでください.
こんな人向け
基本的な行列,ベクトル,微分に関することは知っているが,冒頭のことで困っている人.
表記
太字でない小文字と大文字(例えば $a$ , $A$)はスカラー,太字の小文字(例えば $\boldsymbol{a}$)はベクトル,太字の大文字(例えば $\boldsymbol{A}$)は行列として書いています.
ベクトルは縦ベクトルで表記することが多いので,ここでもそうします.こんな風に書かれていることが多いと思います.
\boldsymbol{a}={}^t\!\,[a_1, a_2, ...,a_n]=[a_1, a_2, ...,a_n]^\mathrm{T}
また,行列$\boldsymbol{A}$の$(i, j)$成分を$A_{ij}$または$(\boldsymbol{A})_{ij}$と書きます.
行列$\boldsymbol{A}^\mathrm{T}$の$(i, j)$成分を$A^\mathrm{T}_{ij}$または$(\boldsymbol{A}^\mathrm{T})_{ij}$と書きます.
積を各成分で表すための"しりとり"に慣れる
行列$\boldsymbol{A}$と行列$\boldsymbol{B}$の積の$(i, j)$成分は,
(\boldsymbol{A}\boldsymbol{B})_{ij}=\sum_k A_{ik} B_{kj}
になります.この形を見てもらったら分かると思うのですが,右辺を$k$について**"しりとり"**して,"しりとり"できなかった添え字をそのまま残すと左辺になります.この考え方はとても役に立ちます.積をとる行列の数が増えても同じです.
(\boldsymbol{A}\boldsymbol{B}\boldsymbol{C})_{ij}=\sum_{k,l}A_{ik}B_{kl}C_{lj}
(※分かる人だけ 「アインシュタインの縮約を使えば和の記号を書かなくていいじゃないか」という声が聞こえてきそうですが,機械学習の式変形ではアインシュタインの縮約を使うとかえってややこしくなる場合が多い気がしているので,あえて用いていません.)
この"しりとり"はベクトルが入ってきた時に少しややこしくなりますが,ベクトルは縦ベクトルで表す,ということを思い出すと,ベクトルの第$i$成分を,あたかも行列の$(i, 1)$成分であるかのように扱うと行列と同じことになります.
\left(\boldsymbol{A}\boldsymbol{b}\right)_i = \sum_j A_{ij}b_j
と書く時は,慣れるまでは心の中では
\left(\boldsymbol{A}\boldsymbol{b}\right)_{i1} = \sum_j A_{ij}b_{j1}
という気持ちで書きます.しりとりできなかった$(i, 1)$がそのまま両端に残っているのが見てとれると思います.
最終的に縦ベクトルにする時は,$i$のようにしりとりできなかった添え字を左端に残すことを意識すると使いやすいです.
転置記号が入ってきた時には,次のように考えます.
\begin{align}
(\boldsymbol{A}^\mathrm{T}\boldsymbol{B})_{ij}
&=\sum_k A^\mathrm{T}_{ik} B_{kj} \\
&=\sum_k A_{ki} B_{kj} \\
\end{align}
ベクトルの時も同じです.
\boldsymbol{b}^\mathrm{T}\boldsymbol{A}\boldsymbol{c}=\sum_{i,j} b_i A_{ij}c_j
も,慣れるまでは心の中では
\begin{align}
\boldsymbol{b}^\mathrm{T}\boldsymbol{A}\boldsymbol{c}
&=\sum_{i,j} b^\mathrm{T}_{1i} A_{ij} c_{j1}\\
&=\sum_{i,j} b_{i1} A_{ij} c_{j1} \\
\end{align}
という気持ちで書きます.
(定義) 行列,ベクトルによる微分
公式の導出に行く前に,行列,ベクトルによる微分の定義を確認しておきましょう.
英語版Wikipediaのページ(Matrix calculus)によれば,大きく分けてNumerator-layout notation(分子に従う流儀)と,Denominator-layout notation(分母に従う流儀)という2つの流儀があり,どちらが良いということも無く,文献によっては混合して使われることもあるそうです.また,このページには行列やベクトルによる微分の公式が両方の流儀で大量にまとめられています.
ここではPattern Recognition and Machine Learningの流儀に従いましょう.
スカラーのベクトルによる微分は,第$i$成分が次で与えられる縦ベクトルとします.(Denominator-layout notation)
\left(\frac{\partial x}{\partial\boldsymbol{a}}\right)_i
=\frac{\partial x}{\partial a_i}
ベクトルのベクトルによる微分は,$(i, j)$成分が次で与えられる行列とします.(Numerator-layout notation)
\left(\frac{\partial\boldsymbol{a}}{\partial\boldsymbol{b}}\right)_{ij}
=\frac{\partial a_i}{\partial b_j}
スカラーの行列による微分は,$(i, j)$成分が次で与えられる行列とします.(Denominator-layout notation)
\left(\frac{\partial x}{\partial\boldsymbol{A}}\right)_{ij}
=\frac{\partial x}{\partial A_{ij}}
公式を導出する手順
行列やベクトルの公式を導出するには,以下のステップを踏みます.
- "行列表記"から"テンソル表記"に変換する
- "テンソル表記"で微分等の計算を行う
- "テンソル表記"から"行列表記"に変換する
公式
\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B})=\boldsymbol{B}^\mathrm{T} \\
を導出する流れを追っていきましょう.もっと複雑な公式の場合も手順は同じです.
"行列表記"から"テンソル表記"に変換する
適切な言葉が見つからないのですが(既にあったら誰か教えてください),ここでは
\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B})
のような表記を**”行列表記”**と呼びます.スカラーの行列による微分なので行列ですが,これを先程の"しりとり"を使って各成分ごとについて考えて,トレースも和の記号で表すと,この行列の$(i, j)$成分は,
\frac{\partial}{\partial A_{ij}}\sum_{k,l} A_{lk}B_{kl}
となります.このような,行列やベクトルの各成分を用いて行列やベクトルの各成分を表した表式をここでは**”テンソル表記”**と呼びます."テンソル表記"のメリットは,各成分で表しているので,順番を自由に入れ替えて良いという点です.行列では一般に$\boldsymbol{A}\boldsymbol{B}\neq\boldsymbol{B}\boldsymbol{A}$ですが,例えば
(\boldsymbol{A}\boldsymbol{B})_{ij}=\sum_{k}A_{ik}B_{kj}=\sum_k B_{kj}A_{ik}
という$A$と$B$の入れ替えが可能です.これによって計算の自由度が高くなります.また,登場するのも全部スカラーなので,覚えにくいベクトルや行列による微分の公式を覚えなくて良いです.
"テンソル表記"で微分等の計算を行う
この"テンソル表記"で微分の計算を行いましょう.
\frac{\partial}{\partial A_{ij}}\sum_{k,l} A_{lk}B_{kl}
の偏微分を計算すると,
\sum_{k,l}\delta_{il}\delta_{jk}B_{kl}
となります.ここで$\delta_{ij}$はクロネッカーのデルタ記号で,
\delta_{ij}=
\begin{cases}
1 & (i=j) \\
0 & (i\neq j)
\end{cases}
です.$i=l$かつ$j=k$の時だけ,$\frac{\partial A_{lk}}{\partial A_{ij}}=1$で,それ以外は$0$ということを示しています.$k,l$について和をとると結局,
B_{ji}
となります.これ以上変形できないので"テンソル表記"での計算は終了です.
"テンソル表記"から"行列表記"に変換する
$(i, j)$成分が$B_{ji}(=(\boldsymbol{B}^\mathrm{T})_{ij})$となりました.この"テンソル表記"を"行列表記"に戻すと,
\boldsymbol{B}^\mathrm{T}
となります.目的の公式が得られました.
公式の導出まとめ
まとめると次のようになります.
\begin{align}
\left(\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B})\right)_{ij}
&=\frac{\partial}{\partial A_{ij}}\sum_{k,l} A_{lk}B_{kl} \\
&=\sum_{k,l}\delta_{il}\delta_{jk}B_{kl} \\
&=B_{ji} \\
&=(\boldsymbol{B}^\mathrm{T})_{ij}
\end{align}
より,
\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B})
=\boldsymbol{B}^\mathrm{T}
です.
他の公式の導出
\begin{align}
\left(\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B}\boldsymbol{A}^\mathrm{T})\right)_{ij}
&=\frac{\partial}{\partial A_{ij}}\sum_{k,l,m} A_{kl}B_{lm}A^\mathrm{T}_{mk} \\
&=\frac{\partial}{\partial A_{ij}}\sum_{k,l,m} A_{kl}B_{lm}A_{km} \\
&=\sum_{k,l,m}\delta_{ik}\delta_{jl}B_{lm}A_{km}
+\sum_{k,l,m}A_{kl}B_{lm}\delta_{ik}\delta_{jm} \\
&=\sum_m B_{jm}A_{im}+\sum_l A_{il}B_{lj} \\
&=\sum_m A_{im}B^\mathrm{T}_{mj}+\sum_l A_{il}B_{lj} \\
&=(\boldsymbol{A}\boldsymbol{B}^\mathrm{T}+\boldsymbol{A}\boldsymbol{B})_{ij} \\
&=\left(\boldsymbol{A}(\boldsymbol{B}+\boldsymbol{B}^\mathrm{T})\right)_{ij} \\
\end{align}
より,
\frac{\partial}{\partial\boldsymbol{A}}\mathrm{Tr}(\boldsymbol{A}\boldsymbol{B}\boldsymbol{A}^\mathrm{T})
=\boldsymbol{A}(\boldsymbol{B}+\boldsymbol{B}^\mathrm{T})
です.
関連サイト
オンライン計算機
英語版Wikipediaのページ(Matrix calculus)にはなんと,オンラインで行列の微分を計算できるページへのリンクが張られていました.(NIPS 2018)
2018年8月22日現在,スカラーの行列による微分の時はDenominator-layout notationで出力され,それ以外はNumerator-layout notationで出力されるそうです(作者の方にお聞きしました).
LaTeX形式でもPython形式でも出力できるようです.
これでもう怖いもの無しですね.