SVD

2.6.2

SVD

最終更新 2020-02-12 読了時間 2 分
まとめ
  • SVD は行列を直交行列と特異値に分解し、低ランク近似を通じて圧縮やノイズ除去を行う。
  • 上位特異値だけを残すことで、情報量の高い成分を抽出できる。
  • PCA との関係が深く、行列分解ベース手法の基盤として重要である。

直感 #

SVDは、行列を『回転・伸縮・回転』に分けて理解する見方です。伸縮量(特異値)の大きい成分だけ残すと、データの骨格を保ったまま次元やノイズを削減できます。

詳細な解説 #


1. 直感:行列を「基本パターン」に分解する #

  • データ行列 \(A\) を「基底ベクトル」と「重み」に分解して表す方法。
  • 画像なら「ぼやけた基本的なパターン」を重ね合わせて元画像を作るイメージ。
  • 特異値が大きい要素から順に「情報量の多いパターン」を表す。

2. 数式でみる SVD #

任意の行列 \(A \in \mathbb{R}^{m \times n}\) は次のように分解できる:

$$ A = U \Sigma V^\top $$

  • \(U \in \mathbb{R}^{m \times m}\):左特異ベクトル(直交行列)
  • \(\Sigma \in \mathbb{R}^{m \times n}\):特異値を対角成分にもつ対角行列
  • \(V \in \mathbb{R}^{n \times n}\):右特異ベクトル(直交行列)

特異値 \(\sigma_1 \ge \sigma_2 \ge \dots\) は「データの強いパターン」順に並ぶ。
上位の特異値だけ残すことで「低ランク近似」ができる。


3. 実験用のデータ(画像) #

「実験」という文字画像をグレースケール化し、行列として扱う。

import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
from scipy import linalg
from PIL import Image

img = Image.open("./sample.png").convert("L").resize((163, 372)).rotate(90, expand=True)
img

4. 特異値分解を実行 #

X = np.asarray(img)
U, Sigma, VT = linalg.svd(X, full_matrices=True)

print(f"A: {X.shape}, U: {U.shape}, Σ:{Sigma.shape}, V^T:{VT.shape}")
A: (163, 372), U: (163, 163), Σ:(163,), V^T:(372, 372)

5. 低ランク近似で画像を復元 #

特異値の上位だけを残すと、元の画像を少ない情報で表現できる。

for rank in [1, 2, 3, 4, 5, 10, 20, 50]:
    U_i = U[:, :rank]
    Sigma_i = np.matrix(linalg.diagsvd(Sigma[:rank], rank, rank))
    VT_i = VT[:rank, :]
    temp_image = np.asarray(U_i * Sigma_i * VT_i)

    plt.title(f"rank={rank}")
    plt.imshow(temp_image, cmap="gray")
    plt.show()

ランクが大きいほど細部が復元され、ランクが小さいほどぼやける。


6. 特異ベクトルの解釈 #

各特異値に対応する \(U, V\) の列は「画像のパターン」を表している。
例えば \(u_1\) は「最も大きな構造」、\(u_2\) は「補助的な構造」を表す。

total = np.zeros((163, 372))
for rank in [1, 2, 3, 4, 5]:
    U_i = U[:, :rank]
    Sigma_i = np.matrix(linalg.diagsvd(Sigma[:rank], rank, rank))
    VT_i = VT[:rank, :]

    if rank > 1:
        for ri in range(rank - 1):
            Sigma_i[ri, ri] = 0

    temp_image = np.asarray(U_i * Sigma_i * VT_i)
    total += temp_image

    plt.figure(figsize=(5, 5))
    plt.suptitle(f"$u_{rank}$ の寄与")
    plt.subplot(211)
    plt.imshow(temp_image, cmap="gray")
    plt.subplot(212)
    plt.plot(VT[0])
    plt.show()

plt.imshow(total)

7. 実務での応用 #

  • 画像圧縮:少数の特異値だけで近似し、容量削減。
  • ノイズ除去:小さい特異値を削除するとノイズが減る。
  • 推薦システム:ユーザー×アイテム行列を分解し、潜在的な好みを抽出。
  • 自然言語処理:LSA(潜在意味解析)で単語文書行列を分解。

発展:SVDと他手法との関係 #

  • 低ランク近似の最適性:ランク\(k\)の近似 \(A_k\) は $$ A_k = \sum_{i=1}^k \sigma_i u_i v_i^\top $$ と表せ、これが Frobenius ノルムで最良の近似になることが知られている。

  • PCAとの関係:データ行列を標準化して SVD を行えば、PCA の主成分と一致する。

  • 計算の工夫:大規模データではランダム化 SVD などを使って効率化。