SVD:轉一下、拉一下、再轉一下
玩完這關,你手上會有一把「任何矩陣都拆得開」的刀 —— 包含 04 關那把刀啃不動的長方形資料表。
① 這關在解什麼問題
06 關給了你正交矩陣:一個只會轉(可能再翻一次面)、絕對不改變任何長度與夾角的矩陣。 它的欄向量兩兩垂直、每根長度都是 1,所以 QᵀQ = I,逆矩陣直接就是轉置。這一關全程要用它。
但 06 關結尾留了一句沒兌現的話:「秩」告訴你一個矩陣真正有幾層,卻沒告訴你每一層有多重要。 這關就是來補這一刀的。
先講一個具體到你明天上班就會遇到的麻煩。
你手上有一張表:200 個使用者 × 50 部電影的評分。一個 200×50 的數字方陣… 不對, 是長方形。你想問一個很自然的問題:
「這 10000 個數字裡,真正的骨架是什麼?是不是其實只有三、四種『口味』在主導 —— 有人愛動作片、有人愛文藝片、有人只看新片 —— 剩下的都是這幾種口味的加權組合加一點雜訊?」
這個問題 04 關其實回答過一半:特徵向量就是「矩陣的自然方向」。 但那把刀有兩個硬限制,剛好把你擋死:
- 它只吃方陣。特徵向量的定義是 Av = λv —— 等號左右兩邊要能相減,A 送進去的向量跟吐出來的向量就得住在同一個空間,所以 A 一定要是 n×n。 200×50 的評分表把 50 維的東西送進去、吐出 200 維的東西,連寫都寫不出來。
- 就算是方陣也不保證拆得開。04 關那一節你已經玩過: 純旋轉矩陣[[0, −1], [1, 0]]沒有任何一個方向不被轉歪,實特徵向量一根都沒有; 剪切矩陣[[1, 1], [0, 1]]只剩一根,湊不出一組完整的基底。
還有第三個麻煩,來自 05 關。那關的 PCA 是這樣做的:先組共變異數矩陣 C,再對 C 做特徵分解。 C 的尺寸是欄數 × 欄數。欄數 d = 2 的時候是個 2×2,很可愛; 但你拿的是 1536 維的文字 embedding 時 ——
這關給你的工具叫 SVD(Singular Value Decomposition,奇異值分解)。 它一次解決上面三件事:吃任何形狀的矩陣、對任何矩陣都保證存在、而且做 PCA 時根本不用把 C 造出來。
04 的特徵分解是方陣專用、而且會失敗的拆法;SVD 是任何矩陣都成立、永遠不會失敗的拆法。 這關剩下的篇幅只在講一件事:它把矩陣拆成哪三塊、每一塊在幹嘛。
② 從你已經會的東西開始:矩陣對一個圓做的事,永遠只有一種
先把「矩陣」這個詞放一邊。國中你就知道:把一個圓往橫的方向拉長,會變成橢圓。 橢圓有一根長軸、一根短軸,兩根互相垂直。你也知道,把橢圓整個轉個角度,它還是橢圓。
這關要用的第一個事實,就這麼樸素:
拿任何一個 2×2 矩陣 A,把單位圓(半徑 1、圓心在原點的圓)上的每一個點都送進去, 吐出來的形狀一定是一個橢圓(最扁的情況會扁成一根線段,或縮成一個點)。 不會是方形、不會是心形、不會是波浪 —— 只會是橢圓。
為什麼這件事重要?因為橢圓這個東西很好描述,只要三樣資料就講完了:
- 長軸朝哪個方向(一個角度)
- 長軸多長、短軸多長(兩個數字)
- (短軸的方向不用講,它一定垂直於長軸)
而 SVD 就是把這三樣資料,從矩陣 A 裡挖出來,寫成三個矩陣相乘。所以那句標題不是比喻,是字面意思:
下一節就是把這三步一格一格拆給你看。
「矩陣能做的事」比你想像的少很多:對一個圓,它只能轉 + 拉 + 轉。 SVD 就是把這三個動作分別抓出來。
③ 主實驗:把「轉一下、拉一下、再轉一下」一格一格拆開
你會看到什麼:四格圖,從左到右是同一個單位圓被 A 吃掉的三個中間步驟。 每格裡:青色輪廓=目前的形狀、青色小點=圓上 12 個等距的記號 (綠色那顆大的是追蹤點,看它跑到哪就知道形狀被轉了多少)、 紫箭頭 v₁=拉得最兇的那個「輸入方向」、桃箭頭 v₂=垂直於它的另一個方向。
你可以動什麼:四根滑桿是矩陣 A 的四個數字(左上、右上、左下、右下); 下面那排按鈕是幾個經典的 A;按「▶ 播放三步」會依序把四格點亮一次。
要看出什麼:格 1→2 圓還是圓(只轉),格 2→3 圓變成正的橢圓(只沿 x、y 拉), 格 3→4 橢圓還是同樣胖瘦只是轉了個角度(只轉)。 三步走完的結果,跟直接算 A×圓(第 4 格那條綠虛線)完全重合。
三個矩陣現在長什麼樣子
上面滑桿動的時候,下面這三個矩陣就是 SVD 現場算出來的答案。 把它們照順序乘起來,要一個數字不差地變回 A —— 這個「不差」就是最後那行還原誤差在講的事。
④ 三個矩陣各自是誰:Vᵀ 只轉、Σ 只拉、U 只轉
公式長這樣,只有五個符號:
U —— 左邊那個正交矩陣,欄向量叫「左奇異向量」u₁, u₂, …
Σ —— 中間那個對角矩陣(讀作 sigma,大寫),對角線上放奇異值 σ₁ ≥ σ₂ ≥ … ≥ 0。
VT —— 右邊那個正交矩陣的轉置(T = transpose,把列變欄)。V 的欄向量叫「右奇異向量」v₁, v₂, …
Vᵀ 跟 U:正交矩陣 = 只轉不變形
06 關的正交矩陣,用一句話複述: 它的每一根欄向量長度都是 1、而且兩兩垂直,所以它作用在任何向量上都不會改變長度、也不會改變兩個向量之間的夾角。 它能做的只有「把整個空間剛體地轉一個角度」(外加可能翻一次面,像照鏡子)。
兩個副作用你會一直用到:
- QTQ = I —— 轉置就是逆矩陣。要把它做的事反轉回去,不用解方程式,把矩陣翻過來就好。
- 它不會製造也不會消滅「大小」 —— 所以 A 把東西放大縮小的能耐,全部都在中間的 Σ 裡,一分不漏。
Σ:對角矩陣 = 只沿著座標軸拉
對角矩陣就是「只有左上到右下那條斜線上有數字,其他格子全是 0」的矩陣。它作用起來蠢得可愛:
奇異值 σᵢ 是什麼
σᵢ(sigma,小寫)= 第 i 個方向的拉伸倍率。具體來說: 輸入方向 vᵢ 被 A 吃進去之後,會變成長度 σᵢ、方向 uᵢ 的向量。寫成一行就是
兩個規定,記住就好:
- σᵢ 一律 ≥ 0。它是「長度」,長度沒有負的。想要「反方向」?那個負號被吸收進 uᵢ 裡了。
- σ₁ ≥ σ₂ ≥ … 由大到小排。這是慣例,也是低秩近似能成立的原因 —— 排好之後「前 k 層」自動就是「最重要的 k 層」。
跟 04 關的 λ 對比
| 特徵值 λ(04 關) | 奇異值 σ(這關) | |
|---|---|---|
| 定義式 | Av = λv | Av = σu |
| 進去 / 出來的方向 | 必須同一根 | 可以是兩根不同的 |
| 可以是負的嗎 | 可以(負 = 方向翻過去) | 不行,一律 ≥ 0 |
| 可以是複數嗎 | 可以(代表這動作在轉) | 不行,一律是實數 |
| 保證存在嗎 | 不保證 | 保證,任何矩陣都有 |
σ 和 λ 是兩種不同的數字,只有在一種情況下會撞在一起:A 本身是對稱且半正定(例如共變異數矩陣 C)。 那時候 σᵢ = λᵢ。其他時候它們沒有簡單關係 —— 例如剪切矩陣 [[1, 1], [0, 1]] 的 λ 是 1 和 1, σ 卻是 1.618 和 0.618。(上面按「剪切」按鈕就看得到。)
本頁符號速查
| 符號 | 唸法 | 它是什麼 |
|---|---|---|
| A | A | 你要拆的那個矩陣。這關的互動裡是 2×2(四根滑桿),實務上是 m×n 的資料表。 |
| m, n | m、n | A 的列數與欄數。m×n 就是「m 列 n 欄」,例:1000 個人 × 20 個欄位。 |
| U | U | 左邊的正交矩陣。欄向量 u₁, u₂ 是「輸出端」的方向。 |
| Σ | 大寫 sigma | 對角矩陣,只有對角線有值,放的是 σ₁, σ₂, … |
| V | V | 右邊的正交矩陣。欄向量 v₁, v₂ 是「輸入端」的方向。 |
| VT | V transpose | V 的轉置(列欄互換)。因為 V 是正交的,VT 同時也是 V 的逆矩陣。 |
| σi | 小寫 sigma i | 第 i 個奇異值=第 i 個方向的拉伸倍率,≥ 0,由大到小排。 |
| ui, vi | u i、v i | 第 i 根左 / 右奇異向量,長度都是 1。下標 i 就是「第幾根」。 |
| λ | lambda | 04 關的特徵值。這關只在 ⑦ 拿來對照。 |
| k | k | 低秩近似時「保留幾層」的那個數字。k = 1 就是只留最重要的一層。 |
| C | C | 03 關的共變異數矩陣,C = XTX /(n−1)。 |
| X | X | 去中心化之後的資料表(每一列一個樣本,每一欄一個變數,欄平均已經是 0)。 |
| I | I | 單位矩陣:對角線全 1、其他全 0,乘上去等於什麼都沒做。 |
| rank | rank / 秩 | 06 關的秩:這個矩陣真正有幾層獨立的東西。= 非零 σ 的個數。 |
SVD 把一個矩陣的能耐切成三份:方向資訊全在 U 和 V 裡,大小資訊全在 Σ 裡。 而且因為 U、V 是正交的,這種切法不會有任何遺漏或重複。
⑤ 換個讀法:把矩陣拆成一層一層疊起來
「A = UΣVᵀ」是矩陣乘矩陣的寫法,看久了還是有點抽象。同一件事有第二種寫法, 這種寫法才是 SVD 在真實系統裡真正被用的樣子:
先搞懂 uvᵀ 是什麼:直立的乘橫躺的
這是全關最容易卡住的一步,所以慢慢來,先用四個小數字。
你在 01 關學的內積,寫成矩陣是 uᵀv:橫躺的乘直立的, (1×2) × (2×1),結果是一個數字。
現在把順序倒過來,uvᵀ:直立的乘橫躺的,(2×1) × (1×2),結果是一個 2×2 矩陣。 算法就是小學的乘法表 —— 左邊每一格,乘上上面每一格:
你會看到什麼:一張乘法表。左欄是 u 的兩個分量(直立擺),上排是 v 的兩個分量(橫躺擺), 中間綠色四格就是 uvᵀ 的四個數字。
你可以動什麼:四根滑桿,分別是 u₁、u₂、v₁、v₂ 這四個整數。
要看出什麼:不管你怎麼調,下面那一列永遠是上面那一列的固定倍數(倍數 = u₂/u₁)。 兩列平行 → 這個矩陣的秩永遠是 1 → 它只有「一層」。
一個 uvᵀ = 一張乘法表 = 秩 1 的矩陣 = 「一層」。 它只有 m + n 個自由的數字(u 的 m 個、v 的 n 個),卻攤開成 m×n 格 —— ⑥ 的省空間就是從這裡來的。
回到 A:疊上第二層
下面用的是 ③ 那個 A(同一個,改上面的滑桿這裡會跟著變)。 現在把它拆成兩層,讓你自己決定要疊幾層:
你會看到什麼:單位圓被「目前保留的層數」作用之後的形狀。 紫=第一層 σ₁u₁v₁ᵀ 的貢獻方向、桃=第二層的貢獻方向、 綠虛線=完整的 A 的結果(當作標準答案對照)。
你可以動什麼:兩顆按鈕切「只留第一層」/「兩層都留」。右邊的矩陣數字會同步換。
要看出什麼:只留第一層時,整個圓被壓扁成一根線段(因為秩 1 的矩陣把整個平面壓到一條線上); 加上第二層,線段才長出寬度、變回橢圓、跟綠虛線重合。σ₂ 越小,第一層就越接近完整的 A。
矩陣不是一團 m×n 個彼此無關的數字,它是幾張乘法表疊起來的東西, 而 SVD 告訴你這幾張表分別是什麼、以及誰比較重要。
⑥ 只留前 k 層:低秩近似,以及它到底省了多少
既然層是排好序的,那自然的想法就是:只留前 k 層,後面全部丟掉。 這個動作叫「低秩近似」(low-rank approximation),而且有個很強的保證 —— 在所有秩 ≤ k 的矩陣裡,這樣切出來的那一個離原矩陣最近,沒有更好的答案了。 (這個定理叫 Eckart–Young,這關不證,但你可以放心用。)
下面這張 12×12 的圖案不是隨便來的,是用公式現場生出來的:兩顆高斯亮點 + 一條對角亮帶。
數字越大顏色越深。生成公式寫在檔案裡的 genPattern,你可以改。
你會看到什麼:三張 12×12 的方格圖。左=原圖(青色越深數字越大)、 中=只用前 k 層重建出來的圖、右=誤差圖(紅色越深代表這一格差越多)。 下面那排橫條是 12 個奇異值 σ₁…σ₁₂ 的大小,被保留的是青色、被丟掉的是灰色。
你可以動什麼:一根滑桿,決定保留幾層(k 從 1 到 12)。
要看出什麼:σ 掉得非常快 —— 前兩層就吃掉 95.7% 的能量。 k 拉到 4、5 的時候中間那張圖幾乎跟左邊一樣,但你只存了不到一半的數字。 同時看右邊那欄的帳:k 太大反而比原本更佔空間。
儲存成本怎麼算
這跟 05 關那筆帳是同一個帳法:你不能只算「壓縮後的資料」, 還得把「還原需要的參數」一起算進去,不然那堆數字還原不回來。
保留 k 層:k × (m + n + 1) 個數字 每一層需要:一根 uᵢ(m 個數字)+ 一根 vᵢ(n 個數字)+ 一個 σᵢ(1 個數字)。 k 層就乘上 k。
m = n = 12 的時候:原本 144,每多留一層要多花 25 個 —— 所以 k ≥ 6 就虧本了。 12×12 太小,本來就不是拿來壓縮的尺寸。
真正划算的是細長或巨大的矩陣。把 ① 那張表代進去看:
| 矩陣 | 原本 m×n | k | k(m+n+1) | 剩下 |
|---|---|---|---|---|
| 資料表 1000 × 20 | 20,000 | 3 | 3,063 | 15.3% |
| 評分表 200 × 50 | 10,000 | 8 | 2,008 | 20.1% |
| embedding 100萬 × 1536 | 15.36 億 | 128 | 1.28 億 | 8.3% |
| 這節的 12 × 12 | 144 | 4 | 100 | 69.4% |
低秩近似省的不是「不重要的細節」,是排在後面的那幾層。 它們是不是「不重要」,取決於你接下來要問什麼問題 —— 這句話跟 05 關丟掉次軸那段是同一個道理。
⑦ SVD 跟特徵分解到底什麼關係
先看差在哪
| 特徵分解 A = PDP⁻¹ | SVD A = UΣVᵀ | |
|---|---|---|
| 吃什麼形狀 | 只吃方陣 n×n | 任何 m×n |
| 保證存在嗎 | 不保證(旋轉、剪切就爆) | 保證,永遠存在 |
| 兩組軸正交嗎 | 一般不正交(A 對稱時才正交) | U、V 兩組都正交 |
| 對角線上的值 | λ,可正可負可複數 | σ,一律實數且 ≥ 0 |
| 進去 / 出來同一組軸嗎 | 同一組(P 和 P⁻¹) | 兩組不同(V 進、U 出) |
| 數值穩定嗎 | P⁻¹ 可能病態 | 正交矩陣的逆是轉置,很穩 |
再看它們在哪裡接上:PCA 那個等式
這是全關最實用的一條,把 05 關和這關焊死在一起。設 X 是去中心化之後的資料表 (n 列樣本 × d 欄變數,每一欄平均已經是 0)。03 關的共變異數矩陣就是
現在對 X 直接做 SVD:X = UΣVT。把它代進去:
所以 C = V (Σ²/(n−1)) VT,這正好就是 C 的特徵分解。
1. X 的右奇異向量 vᵢ,就是 C 的特徵向量(= 05 關的主軸)。
2. σᵢ² /(n−1) 就是 C 的特徵值 λᵢ(= 那根主軸上的變異數)。
所以做 PCA 根本不用把 C 造出來,直接對去中心化的 X 做 SVD 就好 —— 這就是 ① 那個 236 萬格中間表可以直接跳過的原因,也是 sklearn 的 PCA 底層真的在做的事。
現場驗算:還是 05 關那 12 個人
你會看到什麼:左邊是 05 關那 12 個人的身高體重散佈圖(青點), 紫箭頭是用 SVD 算出來的 v₁。右邊兩欄數字並排:左欄走 SVD、右欄走 eig(C), 同一列的兩個數字應該一模一樣。
你可以動什麼:兩顆按鈕,切換「有去中心化(正確做法)」和「忘了去中心化」。 切過去的時候鏡頭會拉遠,好讓座標原點進到畫面裡。
要看出什麼:有去中心化時,σ₁²/(n−1) = 205.55、主軸 45.24°, 跟 05 關算出來的 λ₁ 和角度一個小數點都不差。 忘了去中心化的話,v₁ 會整根倒向「原點到資料重心」的方向(約 19.5°), 算出來的東西根本不是 PCA —— 而且沒有任何錯誤訊息。
切到「忘了去中心化」時鏡頭會自動拉遠,因為那時候箭頭是從座標原點 (0, 0) 出發的 —— 不拉遠你根本看不到起點在哪。
| 量 | 走 SVD | 走 eig(C) |
|---|
PCA 和 SVD 不是兩件事,是同一件事的兩種算法。 「對 X 做 SVD」和「對 C 做特徵分解」給你完全一樣的軸, 但前者不用造 C、數值上也穩得多。
⑧ 數學長怎樣:這關的 SVD 是怎麼算出來的
先講白話步驟。要對一個 m×n 的 A 做 SVD,只要你手上有「對稱矩陣的特徵分解」這一個工具就夠了:
// 1. 造出 AᵀA。它一定是 n×n 的對稱矩陣,而且半正定(特徵值不會是負的)。
const B = matMul(transpose(A), A);
// 2. 對 B 做特徵分解。因為 B 對稱,特徵向量保證兩兩垂直 → 這組就是 V。
const { values, vectors } = eig(B); // values 由大到小
// 3. 奇異值 = 特徵值開根號。(B 半正定,所以根號裡不會是負的)
const sigma = values.map(Math.sqrt);
// 4. 由 A vᵢ = σᵢ uᵢ 反推 U:把 vᵢ 送進 A,再除以長度。
const U = vectors.map((v, i) =>
sigma[i] > 1e-12 ? scale(matVec(A, v), 1 / sigma[i])
: anyUnitVectorPerpendicularToPrevious() // σ = 0 的守衛
);
第 4 步那個守衛不是選配。σᵢ = 0 表示 A 把 vᵢ 這個方向整個壓成 0, 這時候 A vi / σi 是 0 除以 0,算不出方向 —— 但 U 又必須是完整的正交矩陣,所以隨便補一根垂直於前面所有 uⱼ 的單位向量就行(反正它會被 σ = 0 乘掉)。 按上面的「壓扁(秩 1)」按鈕就是在測這條路徑。
符號版:
「造 AᵀA 再分解」在教學上最清楚,在數值上卻是最爛的做法之一:
平方一次,條件數就平方一次,小的 σ 會被浮點誤差直接吃掉。
真正的函式庫(LAPACK 的 dgesdd、numpy 的 np.linalg.svd)走的是
Golub–Kahan 雙對角化,全程不碰 AᵀA。
這一頁是教材,不是函式庫 —— 所以我用清楚的寫法,並且在頁面上印出還原誤差讓你自己檢查。
實測 2×2 的還原誤差在 1e−15 這個量級,教學用途完全夠。
這關的兩段實作,都寫在這個檔案裡:
svd2(A)—— 2×2 版本,直接呼叫LAB.M.eig2拿 V,再由 A vᵢ 反推 U。 技巧:σᵢ 不用 √λ 而是直接取 |A vᵢ| 的長度,這樣還原誤差恆為浮點級別。jacobiEig(S)—— n×n 對稱矩陣的特徵分解(LAB.M只有 2×2 版)。 用 Jacobi 旋轉法:反覆挑一個非對角元素,用一個平面旋轉把它轉成 0,掃到全部非對角元素都夠小為止。 ⑥ 的 12×12 就是它算的,約 40 行,程式碼裡有逐行註解。
⑨ 工程師的「原來如此」
2006 年 Netflix 開了一百萬美金獎金,要人把電影推薦的預測誤差降 10%。 最後贏的那一套,核心就是這關的第二種寫法。
把「使用者 × 電影」的評分表當成 A,做 SVD 之後 A ≈ σ₁u₁v₁ᵀ + σ₂u₂v₂ᵀ + …。 這時候每一層有了具體意義:v₁ 是一組「電影的口味權重」,u₁ 是一組「使用者對這種口味的偏好強度」。 你沒有告訴系統什麼叫動作片,但第一層自己長出來的 v₁ 就會在動作片上給高分 —— 這就是「隱因子」(latent factor)這個詞的由來:沒人標註,是從資料的骨架裡掉出來的。
預測某人對某片的評分,就是把那個人的隱因子向量跟那部片的隱因子向量做內積。 200×50 的表壓成 8 層之後,你存的不是 10000 個評分,是 2008 個數字 —— 而且空格也能填了 (原表大部分是空的,你當然沒看過那部片;低秩結構會把它補出來)。 實務上因為空格太多,會用 ALS / SGD 只在有評分的格子上擬合,那叫 matrix factorization, 但骨架就是這裡的 uvᵀ 疊加。
把一堆文件做成「文件 × 詞」的次數表(1 萬篇 × 5 萬個詞),這張表大得離譜而且幾乎全是 0。 對它做 truncated SVD 只留前 300 層,你會得到一件很妙的事: 「汽車」和「轎車」這兩個詞,在原表裡是完全不同的兩欄(內積 = 0), 壓到 300 維之後卻靠得很近。
原因就是低秩近似的本質 —— 前 300 層是「所有文件共用的骨架」,
而「汽車」跟「轎車」出現在同一批文件裡,所以它們在骨架上的座標幾乎一樣。
被丟掉的後面幾萬層,才是「這篇剛好用了這個詞」的個別雜訊。
這套東西叫 LSA(Latent Semantic Analysis),是今天所有 embedding 的老祖宗,
sklearn 裡就叫 TruncatedSVD(專門處理稀疏矩陣,不需要去中心化)。
同一招換個場合叫「降噪」:訊號有結構(低秩),雜訊沒有(能量平均散在所有層)。 砍掉小的 σ,砍掉的絕大部分是雜訊。感測器陣列、金融因子模型、影像去雜訊都在用這一手。 至於「拿 SVD 壓縮 JPG」那個經典教學例子 —— 它能跑,但沒人真的用, 因為 JPEG 的 DCT + 量化在同樣品質下小得多。SVD 在壓縮界的真實戰場是 「大模型權重矩陣的低秩壓縮」(LoRA 那一票)。
⑩ 踩坑
σ 很小 ≠ 那一層沒用。σ 衡量的是「這一層貢獻了多少能量」,不是「這一層有多少資訊價值」。 在異常偵測裡,訊號正好躲在最小的那幾層 —— 前面幾層是所有人都一樣的共同行為, 「這個帳號跟大家不一樣」這件事只會出現在殘差裡。 同理,05 關那個「同身高下偏壯偏瘦」也是躲在次軸。 丟哪幾層,取決於你要問什麼,不是取決於 σ 的大小。
SVD 不唯一,符號可以整根翻。如果 (uᵢ, vᵢ) 是一組解,那 (−uᵢ, −vᵢ) 也是 —— 兩個負號在 σᵢuᵢvᵢᵀ 裡乘掉了,A 一模一樣。所以換個函式庫、換個版本、換台機器, 你算出來的 v₁ 可能整根乘上 −1,而且不會有任何錯誤訊息。
後果很實際:你存下來的隱因子分數整欄變號,下游那條「分數 > 0 就標成 A 類」的規則會全部反過來。
解法跟 05 關講的一樣:fit 完立刻做符號規範化
(例如強迫某個指定分量為正),並且把它跟模型一起存起來。
另外,當 σ₁ ≈ σ₂(兩層一樣重)時更糟:那時候 v₁、v₂ 可以在它們張成的平面裡任意旋轉,
不只是翻號而已。這關的「純旋轉」按鈕就是這種情況(σ₁ = σ₂ = 1)。
忘了去中心化,你算的就不是 PCA。「X 的右奇異向量 = C 的特徵向量」這個等式, 前提是 X 每一欄的平均都是 0。沒去中心化的話,XᵀX 裡混進了「重心離原點多遠」這件事, 而且它通常大到把真正的主軸壓掉 —— v₁ 會直接指向重心方向。 ⑦ 那個「忘了去中心化」按鈕按下去就看得到:45.24° 變成 19.46°。
反例是 LSA:處理稀疏的詞頻矩陣時故意不去中心化,因為去中心化會把稀疏矩陣變稠密,
1 萬 × 5 萬的表直接爆記憶體。所以 sklearn 才要分成 PCA(會去中心化)
和 TruncatedSVD(不會)兩個類別 —— 不是重複造輪子,是兩個不同的東西。
完整 SVD 在大矩陣上很貴。m×n 的完整 SVD 大約要 O(mn·min(m,n)) 的運算量, 而且會吐出一個 m×m 的 U —— 100 萬列的資料表,那個 U 是 10¹² 個數字,你根本存不下。
但你要的通常只有前 k 層。所以實務上用:numpy.linalg.svd(A, full_matrices=False)(只算細長版)、
scipy.sparse.linalg.svds(A, k=50)(只算前 50 個)、
或 randomized SVD(先隨機投影到 k+p 維再做,快一個數量級)。
最省的還是 04 關那個「乘乘乘」的 power iteration ——
只想要第一層的話,它幾十次迭代就給你了。
⑪ 這關回答了什麼
SVD 到底在拆什麼?
拆一個矩陣「做動作的方式」。任何矩陣對一個圓做的事都只有三步: 轉一下(Vᵀ)→ 沿兩根垂直軸拉一下(Σ)→ 再轉一下(U)。 SVD 就是把這三步分別算出來。
換個角度看,它把矩陣拆成一疊薄片:A = σ₁u₁v₁ᵀ + σ₂u₂v₂ᵀ + …, 每張薄片是一個秩 1 的「乘法表」,σ 由大到小告訴你哪張比較重要。 這兩種讀法是同一個等式。
奇異值 σ 跟特徵值 λ 差在哪?
定義式差一個字:λ 是 Av = λv(進去出來同一根方向), σ 是 Av = σu(進去出來可以是不同方向)。就因為這一個字, σ 不需要 A 是方陣。
性質上:λ 可以是負的、可以是複數、可能根本不存在;σ 一律是 ≥ 0 的實數,而且一定存在。 兩者只在「A 對稱且半正定」時會相等。完整對照看 ④ 那張表。
為什麼說「任何矩陣都有 SVD」?憑什麼這麼強?
因為它把難題轉嫁給了一個一定成立的定理。看 ⑧ 的算法: SVD 不直接處理 A,而是先造 AᵀA。不管 A 是什麼形狀、多歪多爛, AᵀA 一定是對稱的方陣((AᵀA)ᵀ = AᵀA,兩行就證完),而且半正定 (因為 vᵀAᵀAv = |Av|² ≥ 0,是個平方,不可能是負的)。
而「實對稱矩陣一定可以正交對角化、特徵值一定是實數」是譜定理保證的,沒有例外。 所以 V 一定存在、λ 一定 ≥ 0、σ = √λ 一定算得出來。 SVD 的「永遠成立」是從對稱矩陣借來的。
低秩近似到底丟了什麼?
丟了「排在第 k 名之後的那幾層」,也就是那些 σ 比較小的 uvᵀ 薄片。 丟掉之後的誤差有個乾淨的公式:剩餘誤差的平方 = 被丟掉那些 σ 的平方和。 ⑥ 那張誤差圖畫的就是這個。
但「σ 小」只代表能量小,不代表沒價值。異常偵測、殘差分析要的正好是被丟掉的那部分 (坑 1)。判斷能不能丟的標準永遠是: 你接下來要回答的問題,會不會剛好落在你丟掉的那幾層上。
什麼時候該用 SVD,什麼時候用特徵分解?
用 SVD:矩陣是長方形的(資料表、評分表、詞頻表);要做低秩近似 / 降維 / 降噪; 要算偽逆、最小平方、條件數;矩陣可能奇異或病態。簡單講 —— 不確定的時候用 SVD,它不會失敗。
用特徵分解:你要問的問題本身就是「這個方陣重複作用很多次會怎樣」—— 馬可夫鏈的穩態、微分方程、PageRank、矩陣冪(04 關那個乘乘乘)。 這些問題只有 λ 回答得了,因為要的就是「方向不變、只縮放」這件事。 另外,A 是對稱矩陣時(共變異數矩陣、Gram 矩陣),特徵分解又快又穩,沒必要繞去 SVD。
SVD 跟 PCA 是同一件事嗎?
幾乎是,但差一個步驟:去中心化。 PCA = 先把每一欄減掉自己的平均,再對結果做 SVD,然後把右奇異向量當主軸。 少了「減平均」這一步,你算的就只是 SVD,不是 PCA(坑 3 那個 19.46° 就是現場示範)。
接上去之後兩者完全等價:X 的右奇異向量 vᵢ = C 的特徵向量、 σᵢ²/(n−1) = λᵢ。⑦ 拿 05 關那 12 個人現場驗算過: σ₁ = 47.5504 → 47.5504²/11 = 205.5489,跟 05 關的 λ₁ 一個小數點都不差。
實務上一律走 SVD 那條路:不用造 C(省掉 d×d 的中間表)、數值上穩得多,
這也是 sklearn PCA 底層真正在做的事。
U、Σ、V 的尺寸到底各是多少?(長方形的時候很容易搞混)
A 是 m×n 的話:U 是 m×m、Σ 是 m×n、V 是 n×n,這叫「完整 SVD」。 Σ 是長方形的對角矩陣 —— 對角線上有 min(m,n) 個 σ,其他全是 0。
但那些 0 對應的 U 欄向量乘出來也是 0,存了沒用。所以實務上都用「精簡 SVD」:
U 是 m×r、Σ 是 r×r、V 是 n×r,r = min(m, n)。
1000×20 的表,完整版的 U 是 1000×1000(一百萬個數字),精簡版只有 1000×20。
numpy 裡就是 full_matrices=False 這個參數。
到這裡,「一個矩陣把圓拉成什麼形狀的橢圓」你已經拆得乾乾淨淨了。 但整關我們都在看形狀 —— 從來沒把它拿來用。
下一關反過來:機率統計 05 關把共變異數矩陣 C 畫出來的那顆橢圓當成一把尺。 同樣是「離重心 3 公分」,在資料鋪得很開的方向上是家常便飯,在資料很緊的方向上就是異常。 這把尺叫馬氏距離,而它量出來的等距線,就是這關那顆橢圓。