當完美擬合是個陷阱
在這一階裡你已學會解最小平方問題:方程式比未知數多、沒有精確解,於是你把 b 投影到 A 的行空間上,最小化殘差範數 ||A x - b||_2。到目前為止,一切都在推你盡量貼近資料。這篇要給你一個轉折:有時貼得太近,恰恰是最不該做的事。你拿到的資料幾乎從不是純訊號——它帶著量測雜訊、捨入,以及取樣的偶然。一個追逐每一個小擺動的模型,是在忠實地複製雜訊,而雜訊不會重演。
過度擬合就是這個失敗的名字。想像十個帶雜訊的點,大致落在一條平緩的直線上。一條直線給出合理的擬合,殘差適中。但一個 9 次多項式能精確地穿過全部十個點——殘差為零,在訓練點上滿分。然而在點與點之間它上下劇烈擺盪,要它預測第十一個點時就束手無策。在你手上的資料上殘差最小的模型,並不是最能描述產生這些資料的那個過程的模型。
偏差、變異,與病態的基底
替這兩個彼此競爭的誤差取名會很有幫助。偏差是模型太僵硬、抓不住真實形狀所造成的誤差——硬把一條直線套到曲線上,無論給多少資料都會落空。變異則是模型太靈活、會去遷就這一份資料裡那一份特定雜訊所造成的誤差;重新蒐集一次資料,高變異的擬合會長得完全不同。偏差是固執地錯;變異是不穩定地對。偏差—變異權衡就是這個躲不掉的張力:簡單的模型偏差大、變異小,豐富的模型反過來,而甜蜜點落在兩者之間。
多項式迴歸把這個陷阱變得具體,也把它和整條階梯的數值穩定主題綁在一起。要擬合一個 d 次多項式,你會建一個 Vandermonde 矩陣,它的各行是在你的取樣點上算出的 1, t, t^2, ..., t^d。在等距點上,隨著 d 增大這些行幾乎變得平行——高次冪看起來都差不多——於是矩陣變得極度病態。條件數在大約 8 到 10 次時就會衝破 10^8,而回想那條經驗法則:條件數接近 10^k 會吃掉你 16 位雙精度數字中的大約 k 位。擬合係數變得巨大、正負號交替震盪,正是過度擬合烙進線性代數裡的典型指紋。
脊迴歸/Tikhonov:懲罰過大的係數
核心想法在這裡,而且簡單到令人卸下心防。過度擬合表現為巨大、震盪的係數。那就改變你在最小化的東西:不要只看殘差 ||A x - b||_2^2,再加上一個對解大小的懲罰,lambda 乘以 ||x||_2^2。你現在要的是一個既貼近資料、係數又小的擬合,旋鈕 lambda 決定你各自有多在乎。這就是脊迴歸,又稱 Tikhonov 正則化,是治療不適定或過度擬合最小平方問題的主力療法。
ordinary least squares: minimize ||A x - b||^2 ridge / Tikhonov: minimize ||A x - b||^2 + lambda * ||x||^2 normal equations: A^T A x = A^T b (lambda = 0) ridge equations: (A^T A + lambda I) x = A^T b (lambda > 0)
看看那個 lambda*I 對線性代數做了什麼。樸素的正規方程解 A^T A x = A^T b,而你在第 3 篇看過它為什麼危險:A^T A 把條件數平方,於是一個只是略微病態的 A,會變成數值上毫無希望的 A^T A。脊迴歸把系統改成 (A^T A + lambda I) x = A^T b。在對角線加上 lambda,會把 A^T A 的每個特徵值都抬高 lambda,包括那些原本造成爆炸的、危險地接近零的特徵值。條件數下降、矩陣變得能安全求逆、狂野的係數也被拉住——這一切都來自對角線上一個精挑細選的數字。
一個誠實的提醒,與這條階梯教過的一切一致。緊湊形式 (A^T A + lambda I) 是理解脊迴歸最乾淨的寫法,但 A^T A 依然把條件數平方,所以要算得準,你其實不會真的去組它。穩定的做法和普通最小平方一樣:把問題堆疊起來,用以 SVD 或 QR 為基礎的方法求解,那永遠不會建出 A^T A。用對角線的抬升來理解脊迴歸;用穩定的分解來計算它。
SVD 揭露的正則化真相
上一階的奇異值分解,毫不含糊地展示了正則化到底在做什麼。寫 A = U S V^T。由偽逆造出的最小平方解,會把資料的每個分量除以一個奇異值 sigma_i。當某個 sigma_i 很小時,除以它會讓那個分量爆炸——而小的 sigma_i 恰好坐在最糟的雜訊上頭,所以你是在放大垃圾。這就是用 SVD 的眼睛看過度擬合:小奇異值把資料裡的小誤差變成解裡的巨大擺動。
脊迴歸用一個過濾因子來修正這件事。樸素解把每個 SVD 分量乘以 1/sigma_i;脊迴歸改成乘以 sigma_i / (sigma_i^2 + lambda)。當 sigma_i 相對於 sqrt(lambda) 很大時,這幾乎就是 1/sigma_i——強而可信的方向幾乎原封不動地通過。當 sigma_i 很小時,這個因子平滑地縮向 sigma_i / lambda,趨於零而不是爆炸。正則化就是一個溫柔的調光開關,把那些被雜訊主宰的方向恰如其分地、依其不可靠的程度淡出。
它較粗暴的表親是截斷 SVD:不去平滑地淡出小奇異值,而是直接丟掉每個 sigma_i 低於門檻的方向,其餘的原樣保留。那是個硬截斷,而非脊迴歸的軟漸變,而且它直接連回數值秩的概念——你只留下 SVD 認為高於雜訊底線的方向。兩種方法用不同口音說著同一件事:相信大的奇異值,懷疑小的。
選 lambda,以及帶著走的東西
整個方法繫於一個數字 lambda,那你該怎麼選它?你不能從訓練殘差讀出它,因為在那裡 lambda = 0 永遠勝出——零懲罰依定義給出最小殘差,而那正是你想逃離的過度擬合。誠實的答案是直接估計泛化誤差。標準工具是交叉驗證:保留一部分資料,在其餘資料上用一系列 lambda 值擬合,再在保留的那部分上量測誤差。選那個保留誤差最小的 lambda。你是讓沒見過的資料、而不是殘差,來當裁判。
- 選一組候選的 lambda 值,通常以對數間隔排列,比如從 10^-6 到 10^2,因為正確的尺度事先很少知道。
- 對每個 lambda,在一個訓練子集上擬合脊迴歸解,再在一個擬合從未見過的保留驗證子集上量測它的誤差。
- 把驗證誤差對 lambda 畫出來:lambda 先馴服變異時誤差下降,接著 lambda 太大、過度平滑而把偏差找回來時誤差上升——一條 U 形曲線。
- 選 U 形底部的那個 lambda,再用該值在全部資料上重新擬合。那個底部就是你偏差—變異的甜蜜點。
從這一階帶走三個想法。第一,擬合的目標從不是最小的訓練殘差——而是在你沒見過的資料上最小的誤差,光追殘差會直接走進過度擬合。第二,正則化是解藥:脊迴歸/Tikhonov 加上 lambda*||x||_2^2,SVD 把它揭露為一個過濾器,淡出那些不可靠的小奇異值方向,抬高條件數、收縮狂野的係數,代價是一點偏差。第三,這裡每個數值答案都是近似的——沒有精確的 lambda、沒有精確的模型,只有一個由誠實驗證選出、站得住腳的平衡。你進入這一階時能在最小平方意義下解 A x = b;你離開時懂得什麼時候不要把它解到底。