のんびり亀エンジニアの勉強日記

勉強したこと調べたことをゆるくまとめていきます。

特異値分解をnumpyで実装してみる

特異値分解

特異値分解とは

固有値分解は正方行列にしか適用できませんでした。
それを任意のサイズの行列に適用できるように拡張したのが特異値分解と呼ばれるものです。

 m\times n行列 {\bf A}を下記のように3つの行列に分解することを特異値分解といいます。

 \displaystyle
{\bf A}{\bf x} = {\bf U}{\bf \Sigma}{\bf T}

ここで、 {\bf U} {\bf V}は直行行列、 {\bf \Sigma}は行列 {\bf A}の特異値 \sigma_iを降順にi行i列に並べて残りを0にした行列です。

 \displaystyle
{\bf \Sigma} = 
\begin{bmatrix}
\sigma_0 & 0 & \cdots & 0 \\
0 & \sigma_1 & \cdots & 0 \\
\vdots & \vdots &  & \vdots \\
0 & 0 & \cdots & \sigma_n \\
\vdots & \vdots &  & \vdots \\
0 & 0 & \cdots & 0 \\
\end{bmatrix}

ここで特異値とは、 {\bf A}{\bf A}^T固有値平方根で求められる値のことです。
つまり、正方行列にしか適用できなかった固有値分解を任意のサイズの行列で疑似的にできるように拡張したのが特異値分解と呼ばれる手法になります。

numpyでの実装

numpyのnp.linalg.svdメソッドを使うことで特異値分解をすることができます。
ちなみに \Sigmaは対角成分(特異値)のみが返されるので、元の行列に復元したいときなどはnp.diagを使うなどして対角行列に戻す必要があります。

import numpy as np

A = np.array([[1, 3, 4, 2], [1, -1, 2, -5], [-2, 2, -1, 3]])
U, S, VT = np.linalg.svd(A)

print("U")
print(U)
print("Sigma")
print(S)
print("V^T")
print(VT)
print("")

print("AA^Tの固有値の平方根")
eigen_val, eigen_vec = np.linalg.eig(np.dot(A, A.T))
print(np.sqrt(eigen_val))

実行結果はこちら。

U
[[-0.33922653 -0.93622648 -0.09168066]
 [ 0.7463714  -0.32718841  0.57954937]
 [-0.57258632  0.1281707   0.80976366]]
Sigma
[ 6.99488133  5.27073893  1.5135872 ]
V^T
[[ 0.22192192 -0.4159075   0.10127734 -0.87607905]
 [-0.28833837 -0.42217034 -0.85897888  0.02807978]
 [-0.7476666   0.5053795  -0.01148765 -0.43064399]
 [-0.55552344 -0.62720389  0.50176311  0.21504133]]

AA^Tの固有値の平方根
[ 6.99488133  5.27073893  1.5135872 ]

特異値がちゃんと {\bf A}{\bf A}^T固有値平方根になっていることが分かります。