2018-10-31こしあんTweetPocketTensorFlowなど分散共分散行列の計算関数が用意されていない場合は、分散共分散行列や相関行列を計算する際に自分で関数を定義しなければいけません。派生させて計算する方法を理論を中心に見ていきます。Augmentation(Qiitaで書きました)」が必要になったからです。AugmentationならNumpyで書けばよくね?」と思われるかもしれませんが、実はNumpyで書くと分散共分散行列の計算がボトルネックになって訓練速度が遅く(1.5倍~2倍レベル)なってしまいました。Augmentationの記事ではないので詳細は別の機会にしますが、ちょっとCPU(Numpy)でやらせるにはハードな行列計算をGPUでブーストできるTensorFlowのレイヤーで定義しようというのがきっかけです。TensorFlowにはcov関数がない分散共分散行列の計算は、Numpyではnp.cov()という関数を使えば一発で計算してくれます。Irisデータセットのカラム間の共分散を計算するコードは次のとおりです。0.58241432]]今回はカラム(Irisだったらガクの長さや花弁の幅といった列の方向)間で集計しましたが、Numpyでは列の方向と行の方向の両方で集計できるようになっています。引数の「rowvar=False」は「行方向ではない、つまり列方向での分散を計算するよ」という意味です。TensorFlowでは2018年10月時点でnp.covに相当する、分散共分散行列の計算関数がありません。Augmentationだけじゃなくて、レイヤーの途中で主成分分析やカーネル法で多分使うと思うんですがなぜないんでしょうね。Propの定義の都合上なのかな(適当な想像)。数式で見てみましょう。\tag{1}$$で表されます。ここでCov(X)は分散共分散行列を表します。$E[\cdot]$は期待値を表す関数。$\mu$は平均のベクトルです。\tag{2}$$とかける順序を逆に書いているサイトも多いです。どっちが正しくてどっちが間違いということはなくて、Xの定義によりけりなんですよね。4)となり、Xは150×4の行列であることがわかります。(2)式の計算方法で計算すると、期待値の中身は150×4の行列と4×150の行列の積になるので、150×150の行列になってしまいます。(1)式の計算方法で計算すると、4×150と150×4の行列の積なので、期待された4×4の行列ができます。数学のページではないので、Xの行列の定義とかは厳密にしませんが(ぶっちゃけ自分がそういうのできるほど知識がない)、状況に応じて変わるよということを言いたかっただけです。np.covのrowvarをTrueとするか、Falseとするかのような違いです。(2)の式も間違いではないですが、今回の記事では(1)の式を使います。標準化や正規化(どちらも英語ではnormalization)と言われる操作とは異なることです。後で説明しますが、データを標準化してグラム行列を計算すると相関行列となります。分散共分散行列を出したいときは標準偏差で割るのではなく平均だけ引けばいいです。Xが系列間の平均で引かれていた、つまり平均補正済みとしましょう。このときデータの系列間の平均は0になるので、$$\mu^T\mu=0$$となります。Xが平均補正済みだとしたら分散共分散行列は、$$Cov(X)=E[X^TX]$$という簡単な式になります。期待値の中身は前回の記事で見たようにグラム行列なので、こういうが言えます。本当にそうなるかNumpyで確認していきましょう。平均で引かれていたら、分散共分散行列=グラム行列の期待値まずは前回のおさらい。グラム行列とは行列$X$に対して次のような行列積計算を行って得られた行列のことです。10]]例としてはIrisデータセットをそのまま使います。Iris_Xを平均で引きグラム行列の期待値を取ったら、本当にnp.cov()で計算できた分散共分散行列と等しくなるか確認します。rowvar=False))出力は次の通りです。小数第3位ぐらいになってくると誤差で若干変わっていますが、だいたい同じ結果を返しているのが確認できます。追記:誤差ではなくNumpyのcovが不偏分散で計算しているからだそうです。N-1と計算するとあいます。0.58241432]]分散共分散行列と相関行列の関係、グラム行列と相関行列の関係さきほど少し触れた平均で引くだけでなく標準偏差で割られていた場合、つまりデータが標準化されていた場合はどうなるか見てましょう。結論からいうと、グラム行列の期待値は相関行列になります。相関行列というと統計やっていた方ならあーと思い浮かぶと思います。系列間のパラメーターの相関を見るときにとても便利な行列です。Irisの相関行列は次のようになります。]]対角成分は同一系列の相関を取ったものなので必ず1になります。データの傾向見る際は便利なのでぜひ活用してみてください。相関行列と分散共分散行列は別物ではなく、相関行列が分散共分散行列の特殊な場合と考えるほうが自然です。ここで分子は$X_i$と$X_j$の共分散、分母はそれぞれの分散を表します。これを行列表記にしてみましょう。今$\Sigma$を分散共分散行列とし、$P$を相関行列とします。系列数がN個あるものとし(Irisの例ではN=4です)、$\Sigma$と$P$ともにN×N行列で表されます。分散共分散行列の対角成分を$\sigma$で取り出します。本来はこの対角成分はベクトルですが、以下のような1×Nの列行列で定義します。\sigma_{X_N}]$$括弧の中身は要素ごとの分散ですね。これは本来の数学の計算ではないのですが、Pythonではできるブロードキャスティングを使った計算で、$\sigma$をsigmaという変数に入れ、次のコードで求められる行列を$S$とします。sigma)これは相関係数の定義にある分母の値を行列で書き直したものにすぎません。この$S$という行列が定義できれば、相関行列$P$は分散共分散行列$\Sigma$を用いて次のように表されます。$$ここでの演算は要素間の割り算です(もう少し厳密に言うならアダマール積の割り算版)。このSの行列の成分が全て1になれば$P=\Sigma$という式が成立します。ちなみにもっと厳密な数学の定義は、高校数学の美しい物語にある「相関行列の定義と分散共分散行列との関係」をご覧ください。書くのかと思いました。行列Sの成分が全て1になるということは、Xの系列間の分散が全て1になるということなので、「系列間の標準偏差で割る」という補正をしていた場合は、分散共分散行列=相関行列となります。分散共分散行列については、「系列間の平均で引く」という補正をしていた場合は、分散共分散行列=グラム行列の期待値となります。三段論法的な発想により、「データを標準化していたら」グラム行列の期待値は相関行列と等しくなる。これを実際のデータで確認してみます。標準化していたら、相関行列=分散共分散行列=グラム行列の期待値Irisデータで確かめる標準化済みのグラム行列の期待値と、分散共分散行列と、標準化していないときの相関行列が等しくなるのを確認します。rowvar=False)print("実際の相関行列")print(corr)結果は以下の通りです。]]分散共分散行列がちょっと誤差ありますが、グラム行列の期待値と実際の相関行列は完全に同じ値を返していますね。np.corrcoefの実装がこんなのなんでしょう。一般の分散共分散行列から相関行列を求める方法さて、補足ですが、標準化していない一般の分散共分散行列から相関行列を求める方法をコードで確認しておきましょう。rowvar=False)print("実際の相関行列")print(corr)やっていることは先程述べたこととをコードに書き換えたものです。]]一致しているのを確認できましたね。関係のまとめ今までの議論をまとめると次の図になります。これを見るともう少しすっきり理解できるのではないでしょうか。今、実数の行列の話のみ考えるとしましょう。行列で計算できますが、スケールだったり対角要素だったりだんだん縛りが多くなり、より狭い概念となっていきます。分散共分散行列はグラム行列より狭い概念で、「グラム行列計算前に平均で引いていたら」そのグラム行列の期待値が、分散共分散行列と等しくなります。相関行列はもっと狭い概念で、「グラム行列計算前に標準化していたら」、グラム行列の期待値が相関行列と等しくなります。分散共分散行列と相関行列の関係は、先程見たような分散の調整を行ったものですが、やはり相関行列のほうが狭い概念です。期待値操作や、スケール調整が入るため、必ずしもこのような包含図が正しいとは限りませんが、漠然とした理解ではこのような図になります。実際に実装するときはこれだけわかってれば大丈夫です。関数がないときは、前回みたような方法でグラム行列を計算すればよいことがわかりました。あとはデータの標準化次第で、分散共分散行列になるか、相関行列になるかが決まるというわけです。次回は実際にTensorFlowで実装してみます。Commentコメントをキャンセルメールアドレスが公開されることはありません。