Last Updated on 2024-08-11 by Clay
奇異值分解(Singular Value Decomposition, SVD)是一種將矩陣分解為三個矩陣乘機的方法,揭示了原矩陣的秩(Rank)、資料維度及重要方向,經常用於降維、壓縮和結構分析。
設 A 為一個 m x n 的矩陣,SVD 可以寫為:
其中 為 的形狀、 為 的形狀、 為 的形狀,且:
- 為實正交矩陣,亦即 ,是 A 的左奇異矩陣
- 為實正交矩陣,亦即 ,是 A 的右奇異矩陣
- 為類對角矩陣(因為其並不一定為方陣),只有對角元素可能有非零的數值,其餘元素皆為零;其對角線上的元素是 的奇異值。
其中,上圖的 代表矩陣 的秩(Rank),即矩陣 中線性獨立行或列的最大數目,同時也是奇異值矩陣 中非零奇異值的數量,表示著矩陣 中所包含的獨立資訊。
另一方面, 同時也影響了分解後 、、 矩陣的有效範圍。一般來說,當 並且 時,我們可以通過去除 和 中的某些行列來得到更小的分解結果,節省儲存空間。此類方法在像是 PCA 等資料壓縮(降維)的方法中非常常見。
如何計算 SVD
要計算 SVD,可以遵循以下步驟:
- 計算 和
- 求解 和
- 求解
- 組合 、、
在過程中,我也會透過 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 的重要性可見一斑。