8
8

More than 5 years have passed since last update.

特異値分解と固有値分解が同じ結果を出していることの確認(Rのsvd、eigen関数)

Last updated at Posted at 2017-07-18

ある行列A(サンプルN×変数 D)があったとして、
$A = U \Sigma V^T$
のように行列分解するのが、特異値分解(SVD)。

Aの共分散行列
$S = \frac{1}{N-1}A^T A$
に対して、
$SV = V\Lambda$
となるような、固有ベクトル$V$を探す、すなわち、
$S = V\Lambda V^T$
のように行列分解するのが、固有値分解(EVD、Vは特異値分解のものと同じ)。

$S = \frac{1}{N-1}A^T A = \frac{1}{N-1}(U \Sigma V^T)^T U \Sigma V^T = \frac{1}{N-1}V \Sigma U^T U \Sigma V^T = \frac{1}{N-1}V \Sigma^2 V^T$
となることから、

SVDの特異値とEVDの固有値には以下のような関係があるはず。
$\frac{1}{N-1}\Sigma^2 = \Lambda$

というのは、数式上ではわかるが、実際にRの関数が同じ値を返すか不安だったので、確認してみた。

準備

まずデータを用意する。

# 10サンプル × 15次元のデータとする
set.seed(123)
A <- matrix(runif(150), nrow=10)
A <- scale(A, scale=FALSE) # あらかじめmean-centeringはしておく

続いて、SVD、EVDを実行

# SVD
res.svd <- svd(A)
# EVD
S <- t(A) %*% A / (10 - 1)
res.evd <- eigen(S)

固有値は同じか

まずは、SVDが返す特異値の自乗$\Sigma^2$と、EVDが返す固有値$\Lambda$が同じかを調べた

RではSVDはsvd関数、EVDはeigen関数があるので、これらを使った

# SVDの特異値^2 = EVDの固有値
plot(1:10, res.svd$d^2/(10 - 1), col="black", type="l", ylim=c(0,4), xlim=c(1,10), main="Eigen Value")
par(new=TRUE)
plot(1:10, res.evd$values[1:10], col="blue", type="l", ylim=c(0,4), xlim=c(1,10), ann=FALSE)
legend("topright", legend=c("SVD", "EVD"), col=c("black", "blue"), pch=16)

Rplot.png

かなり一致しているのが見える

固有ベクトルは同じか

次に、固有ベクトル$V$がsvdとeigen関数で同じかを調べた

固有ベクトルは長さ1のベクトルなので、同じ方向を向いているほど、内積が1に近づく

180度反対の向きのものも正解なので、内積の絶対値を確認した

evec.svd <- res.svd$v
evec.evd <- res.evd$vectors[,1:10]
image(abs(t(evec.svd) %*% evec.evd), xlab="SVD", ylab="EVD", main="Eigen Vector")

Rplot.png

最後が怪しいが、それ以外は一致した

ローディングは同じか

最後に、長さの違うもう一つの固有ベクトル$U$が同じになるかを調べた

SVDでは、
$A = U \Sigma V^T$
の$U$ですでに計算してある。

EVDでは、データ行列$A$と固有ベクトル$V$と、固有値$\Lambda$しかないが、$A$に$V$をかければ、$U$がもとまる

$A V = U \Sigma$

ただし、$\Sigma$分の重さがかかってるので、長さを1にして$\Sigma$を取り除く

loading.evd <- A %*% evec.evd
loading.svd <- res.svd$u
apply(loading.evd, 2, function(x){norm(x, "2")}) # この固有ベクトルにはまだ、固有値分の重みがかかっている
apply(loading.svd, 2, function(x){norm(x, "2")}) # SVDのVはΣと分離されているからOk
for(i in 1:ncol(loading.evd)){
    loading.evd[, i] <- loading.evd[, i] / norm(loading.evd[, i], "2")
}
image(abs(t(loading.evd) %*% loading.svd), xlab="EVD", ylab="SVD", main="Loading")

Rplot.png

やはり、最後(10番目の固有ベクトル)が何かおかしい

固有ベクトルなら、互いに直行しているため、内積が0になる

そのため、SVDの$U$と、EVDの$U$で各々内積をみた

image(crossprod(loading.svd), main="SVD")
image(crossprod(loading.evd), main="EVD")

Rplot*-1.png
Rplot*-0.png

これを見る限り、EVDの10番目の軸が他の軸と直行していない

このように、原因はよくわからないが、eigen関数の高次の部分が不安定になる事例が見受けられた

8
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8