ゼロから作るDeep Learning3 フレームワーク編を読む その⑦ステップ41
はじめに
シリーズ記事の続編で前記事はこちら↓です。 cha-kabu.hatenablog.com
ですが、今回の内容的にはこちら↓の記事との情報の関連性が高いです。 cha-kabu.hatenablog.com
ステップ41 行列の積
行列積を計算するMatMulクラス、そしてそのインスタンス化とforward処理を行ってくれるmatmul関数を実装します。実装コード自体は今までの延長で難しいことは無いのですが、行列積の逆伝播を理解するのが数学苦手にはしんどいです…魔のステップ37に引き続き、適宜書籍よりも低いレベルから情報をまとめていきます。
参考にしたサイト様
以下の先人たちのまとめを参考にさせて頂きました。本記事で紹介する逆伝播の勾配算出方法はどのページとも異なるのですが、とても参考になりました。なお、本記事に誤った情報があった場合は当然ながらすべて私の理解力の無さが原因であり、引用先の皆様の責任は一切ありません。
諸注意
まず、以降の説明で混乱しないために以下の用語を抑えておいてください。
出力1つ | 出力複数 | |
---|---|---|
入力1つ | (スカラ値)関数 | ベクトル値関数 |
入力複数 | 多変数(スカラ値)関数 | 多変数ベクトル値関数 |
ステップ37のまとめでも、一応多変数ベクトル値関数の微分までをまとめました。今回は行列積なので行列の行列微分…ではありません。というのも未だに良く分かってないのですが、どうやら「行列の行列微分」ってもの自体が存在しないみたいなんですよね(参考:おしえてgoo)。
「行列積の微分なんだから行列を行列で微分できないとダメじゃん!」と思っていたのですが、先にネタバレをしてしまうと、「行列積の微分そのもの」に注目するのではなく、「最終出力(スカラ)を微分するとはどういうことか?」から考えていって逆伝播時に下流に流す勾配を求めます。図にすると以下の感じです。(matmulは行列積を行う関数、太字の大文字は行列を表します。)
今までの逆伝播実装では、その時点での勾配を直接(数式的に)求め、上流から流れてくる勾配と掛け合わせて下流に流す実装を行ってきました。のを直接求める方法です。一方、今回はこれまでのものとは違い、逆算的に(?)はどうやって表すことができるか?に向かって数式を組み立てていく方法になります。
その際重要になってくるのが連鎖律です。スカラ値関数の連鎖律は今までも多用してきたので問題ないと思いますが、合成関数に多変数関数やベクトル値関数が含まれる場合の連鎖律についてまとめた後、逆伝播計算について考えていきます。
なお、以降ベクトルや行列を使った表現が出てきますがそれぞれ幾何的なイメージ(ベクトルは矢印、とか)は持たない方が納得しやすいと思います。単純に、「ベクトルや行列の形でまとめて書いた方がスッキリ書けるからそうしているだけ」で、表記のために使っていると割り切らないと頭がごちゃごちゃになっていきます。自分は「行列とベクトルの積は線形写像だからこの計算の意味は…」とか考え始めてドツボに嵌りました。
目次的なもの
諸注意で記載した通り、連鎖律→逆伝播を実際にやってみる流れでまとめていきます。
多変数関数の連鎖律
逆伝播を実際にやってみる
多変数関数の連鎖律
まずは連鎖律についてまとめます。序盤のステップでも連鎖律自体は使われており、見た目同じなので混乱は少ないと思います。ただし、序盤で出ていた連鎖律は単変数関数のものでした。多変数関数になると形は似ているのですが、「総和をとる」点が異なってきます。
簡単な具体例
まずは証明抜きに簡単な二変数関数の連鎖律を例を見ていきます。をとの関数、をの関数、をの関数とします。数式で表すと以下の通りです。
この時、をで偏微分した値は以下の数式で表すことができます。
更に具体的な例で本当に正しそうか見てみます。例えばとして、となる場合を考えます。このを数式的にで偏微分するのは簡単ですね。になります。この結果を先ほどの連鎖律を使って出した結果と見合わせます。
確かにあっていそうです。
一般化
先ほどの例は二変数に限ったものでした。これを三変数、四変数、…の時にも適用できるように一般化すると、以下の様に表せられます。
変数がいくつになっても良い様に、多くの変数をで表しているのが文字に慣れていないと混乱しますが、先ほどの二変数の例で言うとがに、がに対応しています。先ほどの例からが消えてが増えたわけではないのでご注意ください。別々のアルファベットで表そうとしても最大26文字で使い切ってしまうので、仕方なく添え字で区別し、添え字を使うことで総和記号を使って短く書けるようになっているだけです。
こちらの証明については難しいので諦めた長くなるのでこちらの九州大学の講義ノートなどでご確認ください。
なお、式を見て「二変数関数の例のにあたる様な、と対になる変数はどこにいったの?」と混乱してしまう人もいるかも知れません。これは式ではについての偏微分のみを問題にしており、変数は単なる定数(で偏微分すると0)と見なせるからです。について同じことをやりたければ、式のをと置き換えるだけです。
特殊形
さて、式はある特殊な条件下では実はもっと簡単な形で書くことができます。特殊な条件とは、はの関数、はの関数、…といった様にそれぞれのが一つの変数しか持たないときです。具体例で見た方が分かりやすいと思うので、がそれぞれの一つだけを変数に持つ場合を考えます。
結局残るのは黒字部分になります。そう考えると結局0になる部分も含めて総和をとる必要はないので式のは無くすことができ、以下の様に書き換えられます。
は一般的な表記ではないかと思いますが、ここでは「だけを変数にもつ関数」の意味で使用しています。
こちらの特殊形はあくまで特殊形で、公式というよりかは式の条件を指定しただけのものなのですが、後で出てきますので覚えておいてください。
式の表現を変えてみる
ここまでのところで多変数関数の連鎖律について学びましたのでもう具体的に逆伝播を考えても良いのですが、少し脱線して「これまでの式って書き換えることができるよね」という話です。逆伝播を考える際にも出てくると言えば出てきますが、文字いっぱいで辛ければ飛ばしてください。
式を見直してみると、ベクトルや行列、その内積を使って表記ができることに気づきます。式を見るよりも展開して書いてみた方が分かりやすいかと思いますので、書き下してみます。なお、こちらでは書籍に合わせて出力を行ベクトルとして表し、かつ転置の記号は付けていません。しかしDeepLearning以外の文脈で多変数関数の連鎖律を調べると、出力を列ベクトルで表記していることがほとんどです。結果が縦に並んでいるか横に並んでいるかだけの違いで本質的には同じですが、数式はそれらのものとは異なりますのでご注意ください。
また、変数をで表していると数に限りがあるので、ここからはの形で表すことにします。
画像内最後の数式に注目頂きたいのですが、この式は参照として記載している多変数ベクトル値関数を全微分した式ととても似た形をしています。ただし、(出力を行ベクトルで表したので)右辺の成分が全微分のときのヤコビ行列×変化量の形から、変化量×ヤコビ行列の形に逆転しています。出力を列ベクトルに合わせるとこの逆転は元に戻るのですが、同時にヤコビ行列(緑字部分)は転置の形になります。
出力も多変数だったら(多変数ベクトル値関数の連鎖律)
これまで見てきた数式展開では、出力はスカラ値であることを前提としていました。出力も多変数、すなわちが多変数ベクトル値関数の場合にどうなるかを見てみます。こちらの数式は書き下すと大変なので、コンパクトにまとめます。
黄色のところは参考までに記載しています。赤の行列を、青を、緑をと呼ぶならば、成分の計算方法はの1行目成分と緑の行列の1列目成分の内積で表せられます。
逆伝播を実際にやってみる
少し脱線しましたがいよいよ行列積の逆伝播について考えていきます。
本記事で今まで使用してきた記号体系と変わってしまいますが、書籍と表記を合わせて以下の逆伝播を考えます。ただし行列とベクトルの表記については一部書籍に従わず、行列は大文字の太字、ベクトルは小文字の太字で表すことにします。
※書籍ではがベクトルの時のことを先に考えていますが、行列について考えれば網羅できるので本記事では省略します。
目標は図の中のとがどう計算できるかを考えることです。については、今回上流の計算が無いので実際の値は分かりませんが、実際に逆伝播するときには上流の計算は終わっているはずので既知と仮定します(やを表す計算式の中に残っても良い)。または最終出力のスカラです。大文字ですが行列ではないのでご注意ください。
前準備①について
逆伝播の前に、順伝播の時に計算されるとは何なのか、具体的に見ておきます。順伝播の図からとの積であることは明らかですが、その各要素の計算は以下の様になります(意味ありげに書いていますが、ただの行列積です)。
行列の積の定義から明らかですが、例えばの計算はの1行目成分との一列目成分の内積で計算され、以下の様に表されます。
これを一般化すると、の成分は以下の様に計算されます。
はの中にある変数なので段々数を増やしてループしたくなりますが、今回はの影響は受けない添え字ですのでご注意ください。をどれかに定めるとそれと一緒に決定的に決まる変数です。の影響を受けるのはの列番号との行番号で、それぞれ常に同じ値となります。また、仮定された行列のサイズよりの最大値はです。
前準備②この記事だけで使う記法の確認
後々の説明を考え、他ではあまり見ないオリジナルの記法を用いたいと思います。行列の1行目成分(行ベクトル)を、の形で「太小文字に行番号を表す添え字」で表します。今回出てくる記号について具体的に書くと以下図の通りです。
前準備はここまでです。それではとをどうやって求めるのか、個別に見ていきましょう。
方向に流れる勾配
を求めていきます。スカラの行列微分ですので、サイズはと同じになります。この後どうやってこれを求めていくかですが、の一要素での偏微分について考えていき、後でそれをに拡張します。
と添え字が変数の状態だと分かりづらくなってしまうので、下図の通りについて具体的に見ていって、後から一般化したいと思います。
まず、3つを代表してでを偏微分する―が少し動くとはどう変わるのかを求める―際の連鎖律を考えます。連鎖律を考える際、途中でが何かしらの形で中継地点として出てくるのは想像できるかと思いますが、はにどのような影響を与えているのでしょうか?
ここで前準備のところで導出した、を思い出してください。の(列方向成分)については総和をとるので多くのに関わりそうですが、の行方向成分がであれば、の行方向成分もで決まります。つまり、が影響を与えるのは、の1行目成分のみということが分かります。そう考えると連鎖律の特殊形が使え(総和記号が不要)、他は以下の様に表すことができます。
続いて引き続きを代表に、連鎖律の際右辺が何なのかについて考えます。の1行目成分をで偏微分していますが、そもそもはどのように求められるものだったでしょうか。各要素をの時に限って考えることになるので、で求められます。そしてそれぞれについて偏微分するとペアになるだけが残ることになり、になります。少しややこしいのでこのことを書き下してみます。
ということで、他も同様に考えて各連鎖律の式は以下の様にアップデートできます。
良い感じにの各要素が分かってきたので、具体的に行列に落とし込んでみます。繰り返しになりますがはスカラを行列で偏微分しているので、スケールはであることを思い出しておいてください。
なんだか凶悪な行列に変化しましたね…しかし、実はこれでもうほぼ完成です!変形後の行列の各要素を見てみます。の次元数は、の列数に該当するのでです。そしては偏微分になっているので分かりにくいですがスカラのベクトル微分なので要はベクトルで、次元数はの列数に該当するのでこちらもです。となると、凶悪に見える各要素はただの次元数のベクトルどうしの内積でしかなく、行列積で表せそうです!パズルの様にどういう行列積で表せられるか考え(慣れてたら一瞬で分かるのでしょうが…)、書いてみます。
だいぶスッキリしました!のサイズは、のサイズはなので行列積が成り立つ条件も満たしています。さらには上流から流れてくる勾配なので既知、も順伝播の時に使用するものを転置しただけなので既知でしたので、無事方向の下流に計算可能な勾配を流すことができることが分かりました。
方向に流れる微分
続いてを求めますが、求め方としてはと同様の考えで求めることができます。しかしまた同じような話を書いてもしょうが無いので、少しズルをします。先ほど求めた通り、です。また、であり、転置の性質よりです。これを求めたものに対応させて置き換えて求めます。図で書くと以下の様な感じです。
こちらも既知の成分で表すことができました!
最後に
以上でステップ41のまとめ終了です。ステップ37と合わせて1週間くらい時間かかりました…個人的な一番の学びは、「テンソル微分は意味は考えずに"そう表せるからそうしてるだけ"と考えた方が分かりやすい」です。なんちゃら写像やらなんりゃら座標やらの知識を中途半端に掻い摘んで沼に嵌っていきました…次のステップからはスピード上げていきたいところです。