想像你是一間服飾店的店長,想知道這個月顧客平均消費金額的「誤差範圍」。傳統做法是重開 1,000 間分店,每間收集資料來算——但這不現實。Bootstrap 的答案是:從既有的一筆資料中,反覆「放回抽樣」,模擬出 1,000 個平行宇宙的資料,再用這些模擬資料來估計誤差範圍。
對線性迴歸係數來說,統計軟體(如 R、statsmodels)會自動輸出標準誤——Bootstrap 在這裡沒有太大加分。但 Bootstrap 真正的威力在於:
你想計算一個複雜投資組合的最大可能損失(VaR)。這個 VaR 的標準誤無法用簡單公式算出來——Bootstrap 就是完美解答:從歷史報酬中重複抽樣,模擬 10,000 種市場情境,直接從情境分佈計算 VaR 的信賴區間。
課本用一個簡單的投資問題來展示 Bootstrap 的動機。假設你想把一筆錢投資到兩種資產 X 和 Y,回報率是隨機變數。你將資金中的比例 \( \alpha \) 投入 X,其餘 \( 1 - \alpha \) 投入 Y。
最佳策略是:選擇 \( \alpha \) 讓整體投資的變異數(風險)最小。也就是最小化:
經過微積分推導,最優解是:
現實中 \( \sigma_X^2, \sigma_Y^2, \sigma_{XY} \) 都是未知的,只能用歷史資料估計:
課本先用 1,000 次真實模擬 來展示「標準答案」:從已知母體(設 \(\sigma_X^2=1, \sigma_Y^2=1.25, \sigma_{XY}=0.5\))重複抽樣 100 對 (X, Y),每次計算 \(\hat\alpha\)——共得 1,000 個 \(\hat\alpha_1, \hat\alpha_2, \ldots, \hat\alpha_{1000}\)。
模擬結果(課本 Figure 5.10 左圖):
但現實中你只有一組資料,無法回到母體重抽——這就是 Bootstrap 登場的時機。
Bootstrap 的做法極其簡潔,像一個「資料重複利用術」:
「放回抽樣」是什麼意思?想像原始資料是三張卡片:🟦 🟩 🟥。你要抽三張出來(n 不變),但每次抽完會把卡片放回去:
原始資料 Z = {🟦, 🟩, 🟥}
↓ 放回抽樣 ↓
Z*₁ = {🟥, 🟥, 🟦} ← 🟥重複了!🟩沒被抽到
Z*₂ = {🟦, 🟦, 🟦} ← 全部一樣也可以
Z*₃ = {🟩, 🟥, 🟦} ← 剛好跟原始一樣(機率很小)
每個 Bootstrap 樣本都是一個「平行宇宙」——資料結構類似,但具體樣本不同。對於每個 Z*ᵦ,計算 \(\hat\alpha^*_{\beta}\)。B 次之後,我們就有 B 個估計值。
換句話說:就是 B 個 Bootstrap 估計值的樣本標準差。課本中,從單一資料集做出的 Bootstrap SE 是 0.087——與用真實母體重複模擬得到的 0.083 非常接近!
課本 Figure 5.10 三部曲:
# %% [markdown]
# ## §5.2 The Bootstrap — Python Demo
# 展示 Bootstrap 如何從單一資料集估計 \(\hat\alpha\) 的標準誤
# %% [markdown]
# ### 0. 環境設定
# %%
try:
from google.colab import drive
drive.mount('/content/drive')
DATA_PATH = '/content/drive/MyDrive/ISLP_data/'
except ImportError:
DATA_PATH = '/tmp/'
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
# %% [markdown]
# ### 1. 模擬母體資料(已知真實參數)
# 設定:σ²_X = 1, σ²_Y = 1.25, σ_XY = 0.5 → 真實 α = 0.6
# %%
# 母體參數
sigma2_X, sigma2_Y, sigma_XY = 1.0, 1.25, 0.5
true_alpha = (sigma2_Y - sigma_XY) / (sigma2_X + sigma2_Y - 2 * sigma_XY)
print(f"真實 α = {true_alpha:.4f}")
# 從母體產生一組樣本(n=100)——這就是我們唯一擁有的「真實資料」
n = 100
cov_matrix = np.array([[sigma2_X, sigma_XY], [sigma_XY, sigma2_Y]])
pop_data = np.random.multivariate_normal([0, 0], cov_matrix, size=n)
X_data, Y_data = pop_data[:, 0], pop_data[:, 1]
# %% [markdown]
# ### 2. 定義 \(\hat\alpha\) 的計算函數
# %%
def estimate_alpha(x, y):
"""從 (X,Y) 樣本估計最優投資比例 α"""
s2_x = np.var(x, ddof=1)
s2_y = np.var(y, ddof=1)
s_xy = np.cov(x, y, ddof=1)[0, 1]
return (s2_y - s_xy) / (s2_x + s2_y - 2 * s_xy)
# 從我們的單一資料集計算 \(\hat\alpha\)
alpha_hat_orig = estimate_alpha(X_data, Y_data)
print(f"單一資料集的 α̂ = {alpha_hat_orig:.4f}")
print(f"與真實值的差距 = {alpha_hat_orig - true_alpha:.4f}")
# %% [markdown]
# ### 3. Bootstrap!從單一資料集重抽 B=1000 次
# %%
B = 1000
alpha_boot = np.zeros(B)
for b in range(B):
# ponytail: naive loop ok for B=1000, n=100; for B>10k use np.random.choice on axis
idx = np.random.choice(n, size=n, replace=True) # 放回抽樣
alpha_boot[b] = estimate_alpha(X_data[idx], Y_data[idx])
boot_mean = np.mean(alpha_boot)
boot_se = np.std(alpha_boot, ddof=1)
print(f"Bootstrap α̂ 平均 = {boot_mean:.4f}")
print(f"Bootstrap SE(α̂) = {boot_se:.4f}")
# %% [markdown]
# ### 4. 繪圖:Bootstrap 分佈 vs 真實 α
# %%
fig, axes = plt.subplots(1, 2, figsize=(12, 4.5))
# 左圖:Bootstrap 分佈的直方圖
axes[0].hist(alpha_boot, bins=30, color='#58a6ff', edgecolor='#0d1117', alpha=0.8)
axes[0].axvline(true_alpha, color='#d2991d', linewidth=2.5, linestyle='-',
label=f'真實 α = {true_alpha:.1f}')
axes[0].axvline(alpha_hat_orig, color='#3fb950', linewidth=2, linestyle='--',
label=f'原始 α̂ = {alpha_hat_orig:.3f}')
axes[0].set_xlabel('α̂', fontsize=12)
axes[0].set_ylabel('次數', fontsize=12)
axes[0].set_title(f'Bootstrap 分佈 (B={B})', fontsize=13)
axes[0].legend(fontsize=10)
axes[0].set_facecolor('#161b22')
axes[0].tick_params(colors='#c9d1d9')
# 右圖:盒鬚圖
bp = axes[1].boxplot(alpha_boot, vert=True, widths=0.5,
patch_artist=True,
boxprops=dict(facecolor='#58a6ff', alpha=0.7),
medianprops=dict(color='white', linewidth=2))
axes[1].axhline(true_alpha, color='#d2991d', linewidth=2, linestyle='-',
label=f'真實 α = {true_alpha:.1f}')
axes[1].set_ylabel('α̂', fontsize=12)
axes[1].set_title(f'Bootstrap 盒鬚圖 (SE ≈ {boot_se:.3f})', fontsize=13)
axes[1].legend(fontsize=10)
axes[1].set_facecolor('#161b22')
axes[1].tick_params(colors='#c9d1d9')
fig.patch.set_facecolor('#0d1117')
plt.tight_layout()
plt.savefig('/tmp/bootstrap_alpha_demo.png', dpi=100, facecolor='#0d1117')
plt.show()
# %% [markdown]
# ### 5. 不同 B 值對 Bootstrap SE 的影響
# %%
B_values = [10, 50, 100, 500, 1000, 5000]
se_values = []
for B_try in B_values:
alphas = np.array([estimate_alpha(
X_data[np.random.choice(n, size=n, replace=True)],
Y_data[np.random.choice(n, size=n, replace=True)]
) for _ in range(B_try)])
se_values.append(np.std(alphas, ddof=1))
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(B_values, se_values, 'o-', color='#58a6ff', linewidth=2, markersize=8)
ax.axhline(0.083, color='#d2991d', linestyle='--', linewidth=1.5,
label='真實 SE ≈ 0.083(模擬)')
ax.set_xlabel('B (Bootstrap 重複次數)', fontsize=12)
ax.set_ylabel('Bootstrap SE', fontsize=12)
ax.set_title('B 值增加 → SE 估計趨於穩定', fontsize=13)
ax.legend(fontsize=10)
ax.set_xscale('log')
ax.set_facecolor('#161b22')
ax.tick_params(colors='#c9d1d9')
fig.patch.set_facecolor('#0d1117')
plt.tight_layout()
plt.savefig('/tmp/bootstrap_B_convergence.png', dpi=100, facecolor='#0d1117')
plt.show()
print("B=10 SE:", f"{se_values[0]:.4f}")
print("B=50 SE:", f"{se_values[1]:.4f}")
print("B=100 SE:", f"{se_values[2]:.4f}")
print("B=500 SE:", f"{se_values[3]:.4f}")
print("B=1000 SE:", f"{se_values[4]:.4f}")
print("B=5000 SE:", f"{se_values[5]:.4f}")
print("\n✅ Bootstrap 完成!SE ≈", f"{boot_se:.4f}(與真實 SE=0.083 非常接近)")
| 方法 | 用途 | 假設需求 | 計算成本 | 適用對象 |
|---|---|---|---|---|
| Bootstrap | 估計標準誤、信賴區間 | 幾乎無(non-parametric) | 中(需 B × 模型重擬合) | 任何估計量 |
| 交叉驗證 | 估計測試誤差 | 資料 i.i.d. | 中高(k × 模型重擬合) | 預測模型 |
| 傳統公式 SE | 估計標準誤 | 需推導出公式(如常態假設) | 極低(一次性計算) | 有閉合解的估計量 |
| 用途 | 建議 B | 說明 |
|---|---|---|
| 初步探索 | 50–200 | 快速得到 SE 的概略值,適合互動式分析 |
| 正式報告 | 1,000–5,000 | SE 估計穩定,課本建議的標準範圍 |
| 信賴區間建構 | 5,000–10,000 | 需要分佈尾端(如 2.5% 和 97.5% 分位數)的穩定估計 |
| 高精度(如 p-value) | 10,000+ | 需要非常精確的尾端機率時 |
臨床試驗中,治療效果的中位數(而非平均數)往往更重要(因為極端值影響)。沒有公式可以直接算中位數的標準誤——Bootstrap 是最可靠的解法。
A/B 測試的 ROI 提升是兩個隨機變數的比值。比值的標準誤沒有簡單公式,Bootstrap 可以直接從歷史轉換率分佈中模擬出 ROI 的信賴區間。
F1-score、AUC-ROC 這類複雜指標的標準誤不容易公式化。Bootstrap 可以從測試集中放回抽樣,建立這些指標的信賴區間,判斷模型改進是否「真的」顯著。
這個設計原則已經體現在 multi-model-code-review skill 中:4 個模型交叉驗證程式碼,≥2 模型共識才標記為通過。這就是 Bootstrap 精神的工程化應用。