9.2 機器學習中的迭代訓練#
概念說明#
機器學習的訓練是本書迭代理論的最大現代應用:反覆「取一批資料 → 算損失梯度 → 更新參數」,直到模型夠好。但它與教科書最佳化有本質差異——目標變了。數值迭代求的是精確解,訓練求的是泛化好的解。這改寫了收斂的意義:
- 隨機性取代精確性:資料太大,全量梯度( \(O(N)\) )不可行,改用 mini-batch 的隨機梯度(SGD): \(g_k = \nabla \ell(\mathbf{x}_k; \text{batch})\) ,其期望值等於真梯度,但帶方差。收斂理論:凸問題 \(O(1/\sqrt{k})\) 、強凸 \(O(1/k)\) ,且梯度噪聲像「退火」——步長越大擾動越強,反而有助跳出尖銳極小值(好的泛化往往在平坦區)。
- 學習率排程是新的收斂控制:固定步長下 SGD 只能收斂到解的鄰域(噪聲地板);要真正收斂需步長衰減( \(\alpha_k \propto 1/k\) 、餘弦退火、階梯式)。暖身(warmup)避免初期大梯度爆走。
- 早停取代收斂判據:監控驗證集指標,不再改善就停——因為訓練損失持續下降可能正是過擬合(把訓練集背下來)。這在數值計算世界是異端(誤差越小越好),在機器學習是常識。
- ** epoch、batch、步的帳目**:一個 epoch 掃完整訓練集;總更新次數 = epoch 數 × (N / batch size)。batch 大→梯度準但每步貴;batch 小→梯度噪但走得多。現代大型訓練用「線性縮放規則」調 batch 與學習率。
- 超參數搜尋也是迭代:5.2 節的 Adam 把每參數步長自動化後,剩餘超參(學習率、權重衰減)用網格或貝葉斯最佳化迭代搜尋——迭代中的迭代。
與前章的銜接:訓練大模型的線性層(如推論時的 LoRA、或推薦系統的交替最小平方 ALS)內部就是第 4 章的迭代線性求解;第 5 章的動量、Adam、L-BFGS 直接上場。全書的迭代工具箱在此會師。
生活類比#
學做菜:每次只看一頁食譜(mini-batch)就下廚改進,不必讀完整本(全量梯度);偶爾讀錯了(噪聲)反而逼你學會通則而非背菜色(泛化);味道不再進步就收手(早停),繼續練只會變成「只合自家口味」。
演算法步驟#
標準訓練迴圈(以邏輯迴歸為例):
- 初始化參數 \(\theta\) 、學習率、epoch 數、batch size。
- 對每個 epoch:洗牌資料,切成 batch。
- 對每個 batch:前向算損失 → 反向算梯度 → 更新 \(\theta\) (SGD/Adam)。
- 每 epoch 後在驗證集評估;指標不再改善(patience 次內)則早停。
- 回傳驗證最佳時的參數。
Python 範例#
用 NumPy 從零訓練邏輯迴歸(含 mini-batch、Adam、早停):
import numpy as np
rng = np.random.default_rng(0)
N, D = 4000, 20
X = rng.normal(size=(N, D))
w_true = rng.normal(size=D)
p = 1 / (1 + np.exp(-(X @ w_true)))
y = (rng.random(N) < p).astype(float)
Xtr, Xva, ytr, yva = X[:3200], X[3200:], y[:3200], y[3200:]
def loss(w, X, y):
z = X @ w
return np.mean(np.logaddexp(0, z) - y * z) # 數值穩定的 BCE
def grad(w, X, y):
return X.T @ (1/(1 + np.exp(-(X @ w))) - y) / len(y)
def train(lr=0.05, bs=64, epochs=200, patience=10):
w = np.zeros(D)
m, v = np.zeros(D), np.zeros(D)
best, best_w, wait, t = np.inf, w.copy(), 0, 0
for ep in range(1, epochs + 1):
idx = rng.permutation(len(Xtr))
for s in range(0, len(idx), bs):
b = idx[s:s+bs]; t += 1
g = grad(w, Xtr[b], ytr[b])
m = 0.9*m + 0.1*g; v = 0.999*v + 0.001*g*g
w -= lr * (m/(1-0.9**t)) / (np.sqrt(v/(1-0.999**t)) + 1e-8)
vl = loss(w, Xva, yva)
if vl < best - 1e-5:
best, best_w, wait = vl, w.copy(), 0
else:
wait += 1
if wait >= patience:
break
acc = np.mean(((Xva @ best_w) > 0) == yva)
return best_w, ep, acc
w, ep, acc = train()
print(f"epoch {ep} 早停, 驗證準確率 {acc:.3f}, "
f"‖w - w*‖ = {np.linalg.norm(w - w_true):.3f}")
複雜度與收斂性分析#
每步成本 \(O(bs \cdot D)\) ;一個 epoch \(O(N \cdot D)\) 。理論保證(凸):SGD/Adam 達 \(\varepsilon\) 精度需 \(O(1/\varepsilon^2)\) 步;深度的非凸世界無一般保證,實務以驗證指標為準。關鍵工程變數:batch size(梯度方差 \(\propto 1/bs\) )、學習率與排程(主導收斂速度與穩定性)、早停 patience(防過擬合)。與 5 章理論的對照:教科書說「收斂到駐點」,ML 說「停在驗證損失的谷」——兩者可能不是同一點,這是迭代思維在 ML 最重要的修正。
練習#
- 固定 epochs = 50,掃 batch size ∈ {1, 16, 64, 512},記錄每 epoch 時間與最終驗證損失,解釋權衡。
- 把 Adam 換成 SGD+動量,加入餘弦退火學習率,比較收斂曲線。
- 觀察早停:把訓練集人為縮小(過擬合風險高),畫訓練/驗證損失雙曲線,標出早停點。