L1.1 15 分鐘閱讀 2026年9月

L1.1 十個房仲各看一百間房:Bias 與 Variance

本篇重用M1.0兩台體重計:Gaussian 的線性組合·M1.1從鞋子猜身高:Conditional Expectation·M1.2天氣預報該報幾度:MSE 的最小值是 Conditional Expectation

起點:十個房仲,同一間房

台北有十位資深房仲,本領一樣、方法一樣。差別只在他們各自看過的一百間房子不同——同一個市場裡隨機抽的一百間。

現在把同一間房子帶去給十個人估。你會得到十個數字。

問題一:這十個數字散得多開? 如果從 2,700 萬到 3,100 萬,那表示「他看過哪一百間」對答案影響很大——換一份資料就換一個答案

問題二:這十個數字的平均,會偏嗎? 如果十個人平均起來是 2,880 萬而真實價值是 2,900 萬,那有一個 20 萬的系統性偏差——這個偏差不會因為找第十一個房仲而消失,因為它是「這套方法」本身的偏差,不是誰的運氣。

兩個問題是獨立的。一個方法可以很穩定但一直偏(十個人都說 2,600 萬);也可以平均起來很準但每個人差很多(平均 2,900 但散佈 ±400 萬)。兩種都不好,但要修的東西完全不同。

這一篇把 背考古題的學生:過擬合與欠擬合 裡定性的「過擬合/欠擬合」換成三個可以分別算出數字的量。

十個房仲對同一間房的估價,會散得多開?
平均起來會偏嗎?
這兩個問題為什麼一定要分開問?

課堂提問Q1

「十個房仲」在數學上是什麼?期望是對什麼取的?把 E[(Yf^(x))2]\mathbb E[(Y-\hat f(x))^2] 拆成三塊——每一塊分別對應上面的哪個問題?

先想一想,再展開看整理後的答案

十個房仲 = 十份訓練資料。 訓練集 DD 本身是隨機的(從 ppnn 筆),所以 f^D\hat f_D 是一個隨機函數:換一份資料就換一個模型。房仲的「本領」是演算法(假設空間+損失+最佳化),十個人共用;不同的是餵給它的資料。

期望對兩件事取。 固定一個查詢點 xxYY 的隨機(同一個 xx 的實際成交價本來就有波動)與 DD 的隨機(抽到哪一百間)。兩者獨立。

三項分解。fˉ(x)=ED[f^D(x)]\bar f(x)=\mathbb E_D[\hat f_D(x)](十個房仲的平均答案,1010\to\infty 的版本),f(x)=E[Yx]f^\star(x)=\mathbb E[Y\mid x]M1.2 說的最佳答案):

ED,Y[(Yf^D(x))2]=Var(Yx)不可約+(fˉ(x)f(x))2bias2 :問題二+ED[(f^D(x)fˉ(x))2]variance:問題一.\mathbb E_{D,Y}\big[(Y-\hat f_D(x))^2\big] =\underbrace{\mathrm{Var}(Y\mid x)}_{\text{不可約}} +\underbrace{(\bar f(x)-f^\star(x))^2}_{\text{bias}^2\ \text{:問題二}} +\underbrace{\mathbb E_D\big[(\hat f_D(x)-\bar f(x))^2\big]}_{\text{variance:問題一}}.

bias 是「平均起來偏多少」,variance 是「換一份資料會抖多少」,不可約是「同一個 xxYY 本身就會變」。 三項都非負,所以每一項都是總誤差的下界之一——想要總誤差小,三項都得小。

三塊,各由不同的旋鈕控制

  • 不可約誤差 E[Var(YX)]\mathbb E[\mathrm{Var}(Y\mid X)]:由問題本身決定。換模型沒用,加資料沒用。唯一能動它的方式是換問題——加入新的特徵,讓原本「隨機」的部分變成可預測的(房子的漏水史、賣方的急迫程度)。這一項在共用 toy 裡正好是 0.152=0.02250.15^2=0.0225
  • bias²:由假設空間決定。F\mathcal F 裡最好的那個離真相多遠,這是 L0 的 approximation error 在單點上的樣子。加資料壓不動它——十萬個只會畫直線的房仲,平均起來還是一條直線。
  • variance:由假設空間的大小與資料量的比值決定。空間越大、資料越少,換一份資料換一個模型的程度越劇烈。這一項是 L0 的 estimation error,而且加資料對它非常有效

三個旋鈕解釋了為什麼「模型不夠好」需要先診斷。同樣的症狀(測試誤差高),bias 主導時要加容量,variance 主導時要加資料或減容量,不可約主導時該做的是接受它並停止投入——很多專案在這一格燒掉幾個月。

一個立刻可用的推論。十個房仲的平均比任何單一房仲穩,但如果他們都只會畫直線,平均起來還是直線。這是 bagging、random forest、以及深度學習裡權重平均之所以有效、也之所以有極限的原因。

平均多個模型(ensembling)只壓 variance,不壓 bias。

把總誤差拆成三塊,再算出 variance 的量級

固定 xx,寫 f^=f^D(x)\hat f=\hat f_D(x)fˉ=ED[f^]\bar f=\mathbb E_D[\hat f]f=E[Yx]f^\star=\mathbb E[Y\mid x]。把 Yf^Y-\hat f 拆成三段:

Yf^=(Yf)噪聲+(ffˉ)常數+(fˉf^)均值為 0.Y-\hat f=\underbrace{(Y-f^\star)}_{\text{噪聲}}+\underbrace{(f^\star-\bar f)}_{\text{常數}}+\underbrace{(\bar f-\hat f)}_{\text{均值為 }0}.

平方後取期望。三個交叉項都是零:第一段對 YY 的條件期望為零且與 DD 獨立;第三段對 DD 的期望為零;第二段是常數。於是

E[(Yf^)2]=Var(Yx)+(fˉf)2+VarD(f^).\mathbb E\big[(Y-\hat f)^2\big]=\mathrm{Var}(Y\mid x)+(\bar f-f^\star)^2+\mathrm{Var}_D(\hat f).

這與 天氣預報該報幾度:MSE 的最小值是 Conditional Expectation 的三行證明是同一個技巧(拆成「均值為零的部分」加「常數部分」,交叉項消失),只是那裡對 YY 分解,這裡多了一層對 DD 的分解。

線性模型有一個乾淨的量級公式。設計矩陣固定、噪聲變異數 σ2\sigma^2、參數個數 k=d+1k=d+1(本篇的 pp 一律留給分布),最小平方估計在訓練點上的平均 variance 是

1niVar(f^(xi))=σ2kn.\frac1n\sum_i \mathrm{Var}\big(\hat f(x_i)\big)=\frac{\sigma^2 k}{n}.

兩個依賴一目了然:參數越多,variance 線性上升;資料越多,線性下降。 這是「nn 要跟 kk 一起長」這句老話的來源,也是下面實測會驗證的公式。

注意這個分解只對平方損失這麼漂亮

三項分解依賴平方損失的代數(交叉項為零)。換成 0–1 損失或 cross-entropy,「bias」與「variance」仍然可以定義(Domingos 2000 給了一個統一的框架),但它們不再單純相加:分類問題裡 variance 有時幫得上忙——一個平均起來偏向錯誤類別的預測,只要抖得夠厲害,仍然可能有一半的時間落在正確的那一邊。

所以在分類任務上,請把 bias–variance 當成思考工具(是「方法本身偏了」還是「換份資料就變」)而不是可以逐項相加的帳。要量化,直接量你在意的那個指標在多次重抽下的平均與散佈——這也正是 同一個實驗跑三次結果都不同,該報哪一個? 要做的事。

回到情境:三種處方,與「加資料」的極限

回到十個房仲。如果十個人的估價彼此差很多(高 variance):他們的方法對「看過哪一百間」太敏感。修法是讓每個人多看一些(加資料)、或讓方法更受限(減容量、加正則化),或把十個人的答案平均起來(ensembling)。

如果十個人很一致但一起偏低(高 bias):多找一百個房仲、每人多看一千間,都不會改善。要換方法——加入他們現在沒在看的特徵,或允許更豐富的調整規則。

如果十個人一致、也不偏,但每次的實際成交價本來就在 ±5% 之間跳(不可約):這是市場本身的隨機。此時該做的是改變目標——報一個區間而不是一個數字,或者去找能解釋那 5% 的新資訊。

這一切依賴什麼。 一,十份資料真的是獨立同分佈抽的;若十個房仲其實共用一半的案例,variance 會被低估。二,這裡的 fˉ\bar f 是「無限多份資料的平均模型」,實務上只能用有限次重抽估計,重抽次數不夠時 bias 與 variance 的估計本身就有 variance。三,平方損失(見上面的 Details)。

回到情境:把三塊量出來

固定 30 個位置(只重抽噪聲,這樣分解裡沒有「設計也在變」這個額外來源),每個 degree 重抽 300 份資料:

import numpy as np, warnings; warnings.filterwarnings("ignore")
from numpy.polynomial import Polynomial
from ml_toy import truth, NOISE

xs, ys = truth()
x = np.linspace(0, 1, 30)                              # 固定的 30 個位置
Y = np.sin(2*np.pi*x) + NOISE*np.random.default_rng(0).standard_normal((300, 30))

for d in (1, 2, 3, 5, 7, 9, 12):
    P = np.array([Polynomial.fit(x, yy, d)(xs) for yy in Y])   # 300 條擬合曲線
    bias2 = ((P.mean(0) - ys)**2).mean()                        # 平均曲線離真值多遠
    var   = P.var(0).mean()                                     # 每條離平均曲線多遠
    print(f"degree {d:2d}   bias² {bias2:.4f}   variance {var:.4f}"
          f"   noise {NOISE**2:.4f}   總和 {bias2+var+NOISE**2:.4f}")
degree  1   bias² 0.2004   variance 0.0016   noise 0.0225   總和 0.2245
degree  2   bias² 0.2004   variance 0.0022   noise 0.0225   總和 0.2251
degree  3   bias² 0.0048   variance 0.0028   noise 0.0225   總和 0.0301
degree  5   bias² 0.0000   variance 0.0041   noise 0.0225   總和 0.0266
degree  7   bias² 0.0000   variance 0.0053   noise 0.0225   總和 0.0278
degree  9   bias² 0.0000   variance 0.0069   noise 0.0225   總和 0.0294
degree 12   bias² 0.0000   variance 0.0095   noise 0.0225   總和 0.0320

兩欄的方向完全相反,一如理論。 bias² 從 0.20040.2004 一路掉到 0.00000.0000d5d\ge5 之後,多項式已經足以逼近 sin\sin 到量測不出的程度);variance 從 0.00160.0016 單調爬到 0.00950.0095。總和的谷底在 d=5d=50.02660.0266),與上一篇 U 形的谷底一致——U 形不是一個經驗現象,它是這兩欄相加的結果。

三個細節值得停一下:

  • d=1d=1d=2d=2 的 bias² 完全相同(0.20040.2004:二次項對 sin(2πx)\sin(2\pi x)[0,1][0,1] 上幫不上忙,所以「加容量」在這一步只買到 variance、沒買到 bias。加錯方向的容量比不加更糟。
  • d5d\ge5 之後 bias² 是 0.00000.0000:這時再減 degree 只會傷害,再加 degree 只買 variance。這一格的正確判斷是「停」,而不是繼續調。
  • 不可約誤差那一欄從頭到尾是 0.02250.0225:它不是被模型影響的量。總和永遠 >0.0225>0.0225——這條地板線在讀任何訓練曲線時都該先畫出來。

驗證量級公式。 理論說 variance σ2(d+1)/n\approx\sigma^2(d+1)/n。固定 d=9d=9,改變 nn

for n in (30, 100, 300):
    x = np.linspace(0, 1, n)
    Y = np.sin(2*np.pi*x) + NOISE*np.random.default_rng(0).standard_normal((300, n))
    P = np.array([Polynomial.fit(x, yy, 9)(xs) for yy in Y])
    print(f"degree 9, n={n:3d}:  variance {P.var(0).mean():.4f}"
          f"   (理論 σ²(d+1)/n = {NOISE**2*10/n:.4f})")
degree 9, n= 30:  variance 0.0069   (理論 σ²(d+1)/n = 0.0075)
degree 9, n=100:  variance 0.0021   (理論 σ²(d+1)/n = 0.0022)
degree 9, n=300:  variance 0.0008   (理論 σ²(d+1)/n = 0.0007)

三個數字都對到個位數的百分比。這條公式因此可以當成一個預算工具:想把 variance 減半,要嘛砍一半參數,要嘛加一倍資料。

加一倍資料只買得到 variance 那一項,bias 與不可約誤差不動。

刻意違反:把噪聲關掉。σ=0\sigma=0,不可約誤差變 00、公式預測 variance 也變 00。但這個結論只在「資料位置固定」時成立——若每次重抽連 xx 的位置一起重抽(更接近真實的重新蒐集資料),高次多項式仍然抖得很厲害,因為設計本身的隨機也是 variance 的來源,而它不隨 σ\sigma 消失。這解釋了上一篇的觀察:噪聲為零時仍然可以過擬合。做這個分解時把「什麼在重抽」講清楚,比記住公式更重要。

拖旋鈕,看三塊怎麼此消彼長

互動 demo:三塊各自由不同的旋鈕控制:加資料壓 variance、換假設空間壓 bias、雜訊那塊誰也動不了。

先消化一下

想一想

一個團隊把同一個架構用五個不同的隨機種子各訓一次,測試準確率是 71%71\%83%83\%76%76\%85%85\%74%74\%。他們決定回報最高的 85%85\%。除了誠實性以外,這組數字在診斷上告訴你什麼?

想一想

在表裡 d5d\ge5 之後 bias² 都是 0.00000.0000。有人主張「所以應該用 d=12d=12,反正 bias 已經最小」。最合適的評論是:

想一想

「加一倍資料」對三項的影響是:

參考文獻

  1. Geman, S., Bienenstock, E., Doursat, R. Neural Networks and the Bias/Variance Dilemma. Neural Computation 1992.(把分解系統地用在學習演算法上的經典來源。)
  2. Hastie, T., Tibshirani, R., Friedman, J. The Elements of Statistical Learning, 2nd ed. Springer 2009, Ch. 7.3.(分解、線性模型的 σ2p/n\sigma^2p/n、以及它與模型選擇準則的關係。)
  3. Domingos, P. A Unified Bias-Variance Decomposition and its Applications. ICML 2000.(0–1 損失等非平方損失下的推廣,以及 variance 在分類裡為何可能有益。)