Skip to content

奇異值分解(Singular Value Decomposition, SVD)筆記

Last Updated on 2024-08-11 by Clay

奇異值分解(Singular Value Decomposition, SVD)是一種將矩陣分解為三個矩陣乘機的方法,揭示了原矩陣的秩(Rank)、資料維度及重要方向,經常用於降維、壓縮和結構分析。

設 A 為一個 m x n 的矩陣,SVD 可以寫為:

其中 的形狀、 的形狀、 的形狀,且:

  • 為實正交矩陣,亦即 ,是 A 的左奇異矩陣
  • 為實正交矩陣,亦即 ,是 A 的右奇異矩陣
  • 為類對角矩陣(因為其並不一定為方陣),只有對角元素可能有非零的數值,其餘元素皆為零;其對角線上的元素是 的奇異值。

其中,上圖的 代表矩陣 (Rank),即矩陣 中線性獨立行或列的最大數目,同時也是奇異值矩陣 非零奇異值的數量,表示著矩陣 中所包含的獨立資訊。

另一方面, 同時也影響了分解後 矩陣的有效範圍。一般來說,當 並且 時,我們可以通過去除 中的某些行列來得到更小的分解結果,節省儲存空間。此類方法在像是 PCA 等資料壓縮(降維)的方法中非常常見。


如何計算 SVD

要計算 SVD,可以遵循以下步驟:

  1. 計算
  2. 求解
  3. 求解
  4. 組合

在過程中,我也會透過 Python 一步步進行驗算。

假設我們有以下矩陣

而我們嘗試將其做 SVD 分解。


1. 計算

接著計算

接著透過 Python 進行驗算:

import numpy as np

A = np.array(
    [
        [3, 2],
        [2, 3],
        [1, 1],
    ],
)

# A^T A
At_A = np.dot(A.T, A)
print("A^T A =\n", At_A)

print("\n")

# A A^T
A_At = np.dot(A, A.T)
print("A A^T =\n", A_At)


Output:

A^T A =
 [[14 13]
 [13 14]]


A A^T =
 [[13 12  5]
 [12 13  5]
 [ 5  5  2]]



2. 求解

求解特徵值(Eigan)

首先,我們通過對矩陣 進行特徵值分解來求解矩陣 和奇異值矩陣

特徵值的計算 滿足以下特徵方程:

所以我們可以計算出一個可供求解 的公式:

最後透過公式解,我們可以得到:


求解奇異值

而特徵值的平方根即為奇異值,之後可用來建構 矩陣:

所以 矩陣為:


求解特徵向量

接著我們可以透過以下方程式求解特徵向量

時:

得到:

將其做正規化,特徵向量為:

時:

同樣將其做正規化:

最後我們可以得到 為:

接著我們同樣透過 Python 驗證:

# Calculate eigenvalues and eigenvectors of `A^T A`
eigvals_AtA, eigvecs_AtA = np.linalg.eig(At_A)

# Caculate singular values
singular_values = np.sqrt(eigvals_AtA)

# Arrange eigenvalues ​​and corresponding eigenvectors in descending order (make sure they are consistent with np.svd)
idx = eigvals_AtA.argsort()[::-1]
eigvals_AtA = eigvals_AtA[idx]
eigvecs_AtA = eigvecs_AtA[:, idx]


print("The Eigenvalues of `A^T A`:\n", eigvals_AtA)
print("\n")

print("The Eigenvectors of `A^T A` (V):\n", V)
print("\n")

print("Singular Values (Σ):\n", singular_values)


Output:

The Eiganvalues of `A^T A`:
[27. 1.]

The Eiganvectors of `A^T A` (V):
[[ 0.70710678 -0.70710678]
 [ 0.70710678 0.70710678]] 

Singular Values (Σ):
[5.19615242 1. ]



3. 求解

求解 和前面的步驟基本上完全一樣,不過由於 的尺寸比較大,所以計算上比較麻煩一點。在這裡,我就由程式幫我計算,偷攋一下。

eigvals_AAt, eigvecs_AAt = np.linalg.eig(A_At)

print("The Eigenvalues of `A A^T`:\n", eigvals_AAt)
print("\n")

print("The Eigenvectors of `A A^T` (U):\n", eigvecs_AAt)


Output:

The Eigenvalues of `A A^T`:
[ 2.70000000e+01 1.00000000e+00 -1.33858608e-16]

The Eigenvectors of `A A^T` (U):
[[ 6.80413817e-01 7.07106781e-01 -1.92450090e-01]
 [ 6.80413817e-01 -7.07106781e-01 -1.92450090e-01]
 [ 2.72165527e-01 2.07171358e-16 9.62250449e-01]]



Step 4. 組合

在我們開始把 組合起來之前,我們需要確保 的方向一致,這是 SVD 分解中的一個重要步驟。

這是因為在計算特徵向量時,特徵向量的方向是任意的。也就是說,如果 是一個特徵向量、那麼 也會同樣是一個特徵向量。

# Get (m, n)
m, n = A.shape
min_dim = min(m, n)

# Get Σ
Sigma = np.zeros((m, n))
Sigma[:min_dim, :min_dim] = np.diag(singular_values)

# Get U, V
U = eigvecs_AAt
V = eigvecs_AtA

# Check the direction
for i in range(min_dim):
    if np.dot(A, V[:, i])[0] * U[0, i] < 0:
        U[:, i] = -U[:, i]

# Reconstruction
A_reconstructed = np.dot(np.dot(U, Sigma), V.T)
print("A_reconstructed:\n", A_reconstructed)


Output:

A_reconstructed:
[[3. 2.]
 [2. 3.]
 [1. 1.]]

結語

本文簡單地探討了奇異值分解(SVD)的實現過程與步驟,闡述了如何計算 , , 等矩陣,並特別關注了 U 和 V 方向一致性的重要性。

這個過程不僅展示了 SVD 的數學原理,也揭示了在實際計算時需要考慮的細節。

理解 SVD 的這些細節對於我們在實際應用中正確使用這一強大工具非常關鍵。現在無論是在資料壓縮、降維、圖像處理還是推薦系統等各種不同的領域,深入理解 SVD 的工作原理都能幫助我們更好地應用和優化相關算法。

尤其像我做 AI 方面相關的研究,越是想要減少儲存空間,大家就越常探討 SVD 的應用;SVD 的重要性可見一斑。


References


Read More

Leave a Reply取消回覆

Exit mobile version