SGD vs Batch vs Mini-Batch

Vibe Prompt

「幫我比較 Batch GD、SGD、Mini-Batch SGD 在線性回歸上的收斂速度,畫出損失曲線。」

import numpy as np

def batch_gd(X, y, lr=0.01, epochs=100):
    m, n = X.shape
    w = np.zeros(n)
    losses = []
    for _ in range(epochs):
        pred = X @ w
        loss = np.mean((pred - y)**2)
        losses.append(loss)
        grad = (2/m) * X.T @ (pred - y)
        w -= lr * grad
    return w, losses

def sgd(X, y, lr=0.01, epochs=100):
    m, n = X.shape
    w = np.zeros(n)
    losses = []
    for _ in range(epochs):
        idx = np.random.randint(m)
        xi, yi = X[idx:idx+1], y[idx:idx+1]
        pred = xi @ w
        loss = np.mean((X @ w - y)**2)
        losses.append(loss)
        grad = 2 * xi.T @ (pred - yi)
        w -= lr * grad
    return w, losses

def minibatch_sgd(X, y, lr=0.01, epochs=100, batch_size=32):
    m, n = X.shape
    w = np.zeros(n)
    losses = []
    for _ in range(epochs):
        idx = np.random.choice(m, batch_size, replace=False)
        Xb, yb = X[idx], y[idx]
        pred = Xb @ w
        loss = np.mean((X @ w - y)**2)
        losses.append(loss)
        grad = (2/len(idx)) * Xb.T @ (pred - yb)
        w -= lr * grad
    return w, losses

# 測試
np.random.seed(42)
X = np.random.randn(1000, 5)
true_w = np.array([3, -2, 1, 0.5, -1])
y = X @ true_w + np.random.randn(1000) * 0.1

w_batch, _ = batch_gd(X, y)
w_sgd, _ = sgd(X, y)
w_mini, _ = minibatch_sgd(X, y)

print(f"真實權重: {true_w}")
print(f"Batch GD: {np.round(w_batch, 3)}")
print(f"SGD:      {np.round(w_sgd, 3)}")
print(f"Mini-Batch:{np.round(w_mini, 3)}")

深入理解:三種變體的收斂特性

收斂路徑比較

| 特性 | Batch GD | SGD | Mini-Batch SGD | |------|:--------:|:---:|:--------------:| | 每次資料量 | $m$(全部) | 1 | 32-256 | | 每步計算成本 | $O(mn)$ | $O(n)$ | $O(bn)$ | | 收斂穩定性 | 最穩定 | 最不穩定 | 中等 | | 逃脫局部極小 | ✗ | ✓ | ✓ | | 平行化效率 | 高 | 低 | 高 | | 泛化能力 | 較差(易 overfit) | 較好 | 較好 |

學習率排程 (Learning Rate Scheduling)

SGD 需要隨著訓練進行降低學習率,常見策略:

def learning_rate_decay(initial_lr, epoch, decay_type='step'):
    if decay_type == 'step':
        # 每 30 個 epochs 降為 1/10
        return initial_lr * (0.1 ** (epoch // 30))
    elif decay_type == 'exp':
        # 指數衰減
        return initial_lr * (0.95 ** epoch)
    elif decay_type == 'cosine':
        # 餘弦退火
        return 0.5 * initial_lr * (1 + np.cos(np.pi * epoch / 100))

Batch Size 的影響

| Batch Size | 梯度雜訊 | 收斂速度 | 記憶體 | |:----------:|:--------:|:--------:|:-----:| | 1 (SGD) | 高 | 每步快,整體慢 | 低 | | 32-64 | 中 | 最佳平衡點 | 中 | | 128-256 | 低 | 每步慢,整體快 | 高 | | 1024+ (Large Batch) | 極低 | 可能泛化差 | 極高 |


實戰:收斂速度比較

import matplotlib.pyplot as plt

# 執行三種方法並記錄損失
w_batch, losses_batch = batch_gd(X, y, lr=0.01, epochs=100)
w_sgd, losses_sgd = sgd(X, y, lr=0.01, epochs=100)
w_mini, losses_mini = minibatch_sgd(X, y, lr=0.01, epochs=100, batch_size=32)

plt.figure(figsize=(10, 6))
plt.plot(losses_batch, label='Batch GD', linewidth=2)
plt.plot(losses_sgd, label='SGD (noisy)', alpha=0.7)
plt.plot(losses_mini, label='Mini-Batch (32)', linewidth=2)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('三種 GD 變體的收斂曲線比較')
plt.legend()
plt.yscale('log')
plt.grid(True)
plt.show()

print(f"Batch GD 最終損失: {losses_batch[-1]:.6f}")
print(f"SGD 最終損失: {losses_sgd[-1]:.6f}")
print(f"Mini-Batch 最終損失: {losses_mini[-1]:.6f}")

實務建議

  1. 預設使用 Mini-Batch SGD:batch size 32-128 是通用起點
  2. SGD 適合小資料集:< 10,000 筆資料,可以接受完整批次
  3. 學習率排程至關重要:Step Decay 或 Cosine Annealing 是常見選擇
  4. Large Batch 需要特殊處理:> 1024 時需用 Linear Scaling Rule 調整學習率
  5. Shuffle 資料很重要:每個 epoch 前隨機打亂資料,避免學到順序偏差

關鍵要點

  • ✅ Batch GD:穩定但慢,適合小資料集
  • ✅ SGD:快速但雜訊大,能逃脫局部極小值
  • ✅ Mini-Batch SGD:速度與穩定的最佳平衡
  • ✅ 學習率排程是 SGD 族系成功的關鍵
  • ✅ Batch Size 32-128 是實務上最常見的選擇


梯度下降實戰要點

梯度下降是機器學習中最核心的最佳化演算法。理解它的變體(SGD、Momentum、Adam)對於訓練深度學習模型至關重要。

核心概念

  • 梯度方向是函數增加最快的方向
  • 學習率決定每一步的大小
  • SGD 用隨機樣本近似梯度
  • Momentum 加速收斂
  • Adam 結合 Momentum + RMSProp

SGD 的收斂分析

SGD 的梯度有隨機性,不像 Batch GD 那樣穩定收斂。它的收斂行為需要從期望角度分析。

收斂定理

$$E[L(w_{t+1})] \leq L(w_t) - \eta |\nabla L(w_t)|^2 + \eta^2 \sigma^2$$

  • $\eta |\nabla L|^2$:預期改善項
  • $\eta^2 \sigma^2$:雜訊項

這告訴我們:學習率不能太大(否則雜訊項主導),也不能太小(否則改善太慢)。

Learning Rate Scheduling

實務上不該用固定學習率:

  • Step Decay:每 N 個 epoch 將學習率減半
  • Exponential Decay:$\eta_t = \eta_0 \times e^{-kt}$
  • Cosine Annealing:餘弦退火

下一章預告:自動微分 AutoDiff

SGD 需要計算梯度。手動推導梯度既繁瑣又容易錯——下一章的自動微分讓框架幫你算。

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!