7.1 SVD 概念與幾何意義#
本節目標#
- 理解為什麼「不是所有矩陣都可對角化」,但任何矩陣都有 SVD
- 掌握奇異值分解 \(A = U\Sigma V^T\) 的幾何意義:旋轉 → 軸向伸縮 → 旋轉
- 看懂奇異值就是「變換把單位圓變成橢圓」時的伸縮量
- 了解四個基本子空間如何被 SVD 統一描述
- 分辨 SVD 與特徵分解的關係(對稱矩陣時二者一致)
動機:對角化不是萬能的#
第 6 章我們學過特徵分解:若 \(A\) 有 \(n\) 個線性獨立的特徵向量,就能寫成
但這是有條件的。像這個矩陣:
它的特徵值只有 \(1\) ,特徵向量只有一個方向( \(x\) 軸方向)。它不可對角化—— 你找不到足夠的「不變方向」來拆解它。
更根本的問題是:特徵分解只對方陣有定義。一張 \(1000 \times 500\) 的圖片、 一份 \(10000 \times 200\) 的資料表,都不是方陣,特徵分解直接出局。
SVD 的承諾:拋棄「找不變方向」的想法,改問「變換把單位圓拉成什麼形狀」。 這樣一問,任何矩陣——方陣、長方陣、可逆、不可逆——都有了乾淨的分解。
import numpy as np
A = np.array([[1., 1.],
[0., 1.]])
U, s, Vt = np.linalg.svd(A)
print(s) # [1.618 0.618]——奇異值永遠存在,即使不能對角化
核心定理:任何矩陣都能分解#
奇異值分解(Singular Value Decomposition):任何 \(m \times n\) 實矩陣 \(A\) 都可寫成
- \(V\) : \(n \times n\) 正交矩陣(旋轉),欄是 \(A^T A\) 的特徵向量,稱右奇異向量
- \(\Sigma\) : \(m \times n\) 對角矩陣,對角線上是奇異值 \(\sigma_1 \ge \sigma_2 \ge \cdots \ge 0\)
- \(U\) : \(m \times m\) 正交矩陣(旋轉),欄是 \(AA^T\) 的特徵向量,稱左奇異向量
三個因子各有清楚的身分:兩個旋轉,夾一個軸向伸縮。
幾何意義:單位圓的三步旅程#
把 \(V^T \Sigma U^T\) 反過來讀(因為作用在向量上是 \(Ax = U\Sigma V^T x\) ), 任何線性變換都等價於三步:
- \(V^T\) :先旋轉,把「好方向」轉到座標軸上
- \(\Sigma\) :沿座標軸分別伸縮 \(\sigma_1, \sigma_2, \ldots\) 倍
- \(U\) :再旋轉到最終位置
用 NumPy 驗證分解確實還原矩陣:
import numpy as np
A = np.array([[3., 1.],
[2., 4.]])
U, s, Vt = np.linalg.svd(A)
Sigma = np.zeros_like(A)
Sigma[:2, :2] = np.diag(s)
print(np.allclose(U @ Sigma @ Vt, A)) # True
動手畫:單位圓 → 橢圓#
單位圓上所有點經 \(A\) 變換後,形成一個橢圓;橢圓的半軸長正是奇異值。 我們用 linalg_viz 工具箱把三步驟畫出來:
import numpy as np
import matplotlib.pyplot as plt
from linalg_viz import plot_transform_steps # 本書視覺化工具箱
A = np.array([[3., 1.],
[2., 4.]])
U, s, Vt = np.linalg.svd(A)
fig = plot_transform_steps(A, circle=True,
titles=["原單位圓", "步驟1:$V^T$ 旋轉",
"步驟2:$\\Sigma$ 伸縮", "步驟3:$U$ 旋轉(橢圓)"])
plt.show()
若暫時沒有工具箱,也可以手動畫出橢圓與其主軸:
import numpy as np
import matplotlib.pyplot as plt
A = np.array([[3., 1.],
[2., 4.]])
U, s, Vt = np.linalg.svd(A)
theta = np.linspace(0, 2*np.pi, 200)
circle = np.vstack([np.cos(theta), np.sin(theta)]) # 2 x 200 個圓上的點
ellipse = A @ circle # 經 A 變換後的點
plt.figure(figsize=(5, 5))
plt.plot(circle[0], circle[1], "--", alpha=0.4, label="單位圓")
plt.plot(ellipse[0], ellipse[1], label="$A$ 作用後")
# 橢圓主軸 = 奇異向量 × 奇異值
for i in range(2):
v = s[i] * U[:, i]
plt.arrow(0, 0, v[0], v[1], head_width=0.3, color=f"C{i+1}",
label=f"$\\sigma_{i+1}={s[i]:.2f}$")
plt.axis("equal"); plt.legend(); plt.show()
你會看到:兩支箭頭互相垂直,長度恰為 \(\sigma_1, \sigma_2\) 。 這就是 SVD 的幾何全貌——奇異值是伸縮量,奇異向量是伸縮的方向。
四個基本子空間,一次講完#
第 4 章的四個基本子空間(行空間、零空間、列空間、左零空間), 在 SVD 眼中只是一個分解的四個角落。設 \(A\) 為 \(m \times n\) 、秩為 \(r\) :
| 子空間 | 由哪些奇異向量張成 | 對應奇異值 |
|---|---|---|
| 行空間 \(C(A)\) | \(U\) 的前 \(r\) 欄 | \(\sigma_1,\ldots,\sigma_r > 0\) |
| 左零空間 \(N(A^T)\) | \(U\) 的後 \(m-r\) 欄 | 奇異值為 \(0\) |
| 列空間 \(C(A^T)\) | \(V\) 的前 \(r\) 欄 | \(\sigma_1,\ldots,\sigma_r > 0\) |
| 零空間 \(N(A)\) | \(V\) 的後 \(n-r\) 欄 | 奇異值為 \(0\) |
一句話總結:** \(V\) 把整個空間分成「會被伸縮的」與「會被壓扁的」兩群方向; \(U\) 接住伸縮的結果**。秩 \(r\) 就是「非零奇異值的個數」。
A = np.array([[1., 2.],
[2., 4.],
[3., 6.]]) # 秩 1:三行都成比例
s = np.linalg.svd(A, compute_uv=False)
print(s) # [7.416... 0.000...]——第二個奇異值是 0,秩 = 1
print(np.linalg.matrix_rank(A))
數值上我們不檢查「是否恰好等於 0」,而是看它是否小於機器精度等級的門檻—— np.linalg.matrix_rank 內部正是這樣做的。
SVD 與特徵分解的關係#
兩者常被混淆,用一句話劃清界線:
特徵分解問「哪些方向經變換後不變?」(限方陣、且要有足夠特徵向量) SVD 問「哪些方向被伸縮最多?」(任何矩陣都行)
它們透過 \(A^T A\) 相連:
即: \(V\) 的欄是 \(A^TA\) 的特徵向量, \(U\) 的欄是 \(AA^T\) 的特徵向量, 而奇異值是這些對稱矩陣特徵值的平方根: \(\sigma_i = \sqrt{\lambda_i}\) 。
特別地,當 \(A\) 是對稱矩陣時,特徵分解與 SVD 本質上是同一件事 (奇異值 \(=\) 特徵值的絕對值):
S = np.array([[2., 1.],
[1., 2.]]) # 對稱矩陣
w = np.linalg.eigvalsh(S) # 特徵值:[1, 3]
s = np.linalg.svd(S, compute_uv=False)
print(np.sort(np.abs(w)), np.sort(s)) # [1. 3.] [1. 3.] 一致!
所以可以把 SVD 想成特徵分解的超集合: 對稱矩陣的優雅結構,被 SVD 搬到了「所有矩陣」身上。
小結與練習#
本節重點
- 不可對角化的矩陣、長方矩陣,都仍有 SVD: \(A = U\Sigma V^T\)
- 幾何上,任何線性變換 \(=\) 旋轉( \(V^T\) ) \(+\) 軸向伸縮( \(\Sigma\) ) \(+\) 旋轉( \(U\) )
- 奇異值 \(\sigma_i\) 是單位圓被變換成橢圓後的半軸長,也是 \(\|A\|_2\)
- 秩 \(=\) 非零奇異值個數;四個基本子空間由 \(U\) 、 \(V\) 的欄直接給出
- 對稱矩陣的 SVD 與特徵分解一致: \(\sigma_i = |\lambda_i|\)
練習
- 對 \(A = \begin{bmatrix} 0 & 2 \\ 0 & 0 \end{bmatrix}\) 手算 SVD(提示:先算 \(A^TA\) ),再用 NumPy 驗證。這個矩陣不可對角化,但 SVD 存在嗎?
- 寫一個函數
circle_to_ellipse(A),回傳單位圓經 \(A\) 變換後橢圓的半軸長與主軸方向,並對三個隨機 \(2\times 2\) 矩陣畫圖檢查主軸是否正交。 - 用
np.linalg.svd計算 \(\|A\|_2\) (最大奇異值)與 Frobenius 範數 \(\sqrt{\sum \sigma_i^2}\) ,並和np.linalg.norm(A, 2)、np.linalg.norm(A, 'fro')比對。 - 構造一個秩為 1 的 \(5 \times 3\) 隨機矩陣(外積法:
np.outer(u, v)),確認它只有一個非零奇異值。