6.1 子集選擇

📖 ISLP §6.1 📄 pp. 239–248 ★★★★☆ ⏱️ 約 35 分鐘
變數選擇 模型比較 AIC BIC Cp 逐步迴歸 過擬合 偏差-變異取捨
← 5.3 Lab: 交叉驗證與 Bootstrap 📑 課程首頁 6.2 收縮方法 →

為什麼需要選擇變數?

想像你要搬家——你有一整間房子的家具,但新家只有一半大。你會怎麼做?當然是挑最重要的帶走。統計學習中的「子集選擇」(subset selection) 就是這個道理:從 \(p\) 個可能的預測變數中,挑出真正有用的那一組。

為什麼不全部留著?因為多餘的變數不但不會幫忙,還會增加模型複雜度、放大變異數、導致過擬合。好的模型不是變數最多的模型,而是在測試資料上表現最好的模型

🎯 核心目標:從 \(p\) 個預測變數中選出一個子集,使得模型在未見過的測試資料上有最小的預測誤差——而不僅僅是在訓練資料上看起來漂亮。
James, Witten, Hastie, Tibshirani (2023) An Introduction to Statistical Learning with Python, Chapter 6.1, pp. 239–248.

6.1.1 最佳子集選擇 (Best Subset Selection)

概念:暴力解法,全部試過

最佳子集選擇的邏輯非常直覺:把所有可能的變數組合都試一遍,挑最好的。就像去 buffet 每一道菜都吃一口,再決定最喜歡的組合。

具體來說,對於 \(p\) 個預測變數,我們要擬合所有包含恰好 \(k\) 個變數的模型(\(k = 0, 1, \dots, p\)),總共有 \(2^p\) 個可能的模型。演算法如下:

Algorithm 6.1:最佳子集選擇

1. 令 \(\mathcal{M}_0\) 為虛無模型(無任何預測變數),只預測樣本平均值。
2. 對 \(k = 1, 2, \dots, p\):
  (a) 擬合所有 \(\binom{p}{k}\) 個包含恰好 \(k\) 個變數的模型。
  (b) 選出 RSS 最小(或 \(R^2\) 最大)的模型,稱為 \(\mathcal{M}_k\)。
3. 從 \(\mathcal{M}_0, \dots, \mathcal{M}_p\) 中,用驗證集誤差、\(C_p\)、AIC、BIC 或 adjusted \(R^2\) 選出最終模型。
⚠️ 關鍵陷阱:如果只用 RSS 或 \(R^2\) 來選最終模型,你永遠會選到包含所有變數的模型(因為訓練誤差隨變數增加而單調遞減)。但我們要的是測試誤差最小的模型,不是訓練誤差最小的!這就是為什麼步驟 3 必須使用 \(C_p\)、AIC、BIC、adjusted \(R^2\) 或交叉驗證。

計算瓶頸:\(2^p\) 的詛咒

最佳子集選擇的問題是計算量隨 \(p\) 指數成長。當 \(p = 10\),約需考慮 1,024 個模型;當 \(p = 20\),超過一百萬個;當 \(p = 40\),超過一兆個——即使用最快的電腦也跑不動。

# 最佳子集選擇的計算複雜度說明
import numpy as np

# 示範 2^p 的爆炸性成長
for p in [5, 10, 15, 20, 30, 40]:
    n_models = 2**p
    print(f"p = {p:2d}: 需要考慮 {n_models:>15,} 個模型")
# p =  5: 需要考慮              32 個模型
# p = 10: 需要考慮           1,024 個模型
# p = 15: 需要考慮          32,768 個模型
# p = 20: 需要考慮       1,048,576 個模型
# p = 30: 需要考慮   1,073,741,824 個模型
# p = 40: 需要考慮 1,099,511,627,776 個模型

用 Credit 資料集示範

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
import pandas as pd
from itertools import combinations
from ISLP import load_data
from sklearn.linear_model import LinearRegression

# 載入 Credit 資料集
credit = load_data('Credit')
# 只取數值型變數 + 將 region 轉為 dummy(仿課本 Figure 6.1)
X_full = pd.get_dummies(credit.drop('Balance', axis=1), drop_first=True)
y = credit['Balance']
p = X_full.shape[1]

# 只示範前 100 個組合(實際 2^11 = 2048 太多)
np.random.seed(1)
rss_list = []
r2_list = []
n_combos = min(100, 2**p)
sampled = [np.random.choice(p, size=k+1, replace=False)
           for k in range(p) for _ in range(min(30, n_combos//p))]

for cols in sampled:
    cols = list(set(cols))  # 去重
    if len(cols) == 0:
        continue
    model = LinearRegression().fit(X_full.iloc[:, cols], y)
    y_pred = model.predict(X_full.iloc[:, cols])
    rss = np.sum((y - y_pred)**2)
    tss = np.sum((y - np.mean(y))**2)
    r2 = 1 - rss / tss
    rss_list.append((len(cols), rss))
    r2_list.append((len(cols), r2))

# 找出每個 k 的最佳 RSS(紅色前沿)
rss_df = pd.DataFrame(rss_list, columns=['k', 'RSS'])
r2_df = pd.DataFrame(r2_list, columns=['k', 'R2'])
best_rss = rss_df.groupby('k')['RSS'].min().reset_index()
best_r2 = r2_df.groupby('k')['R2'].max().reset_index()

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4.5))
ax1.scatter(rss_df['k'], rss_df['RSS']/1e6, alpha=0.3, s=15, color='#58a6ff')
ax1.plot(best_rss['k'], best_rss['RSS']/1e6, 'r-o', lw=2, markersize=5, label='Best (red frontier)')
ax1.set_xlabel('Number of Predictors'); ax1.set_ylabel('RSS (×10⁶)')
ax1.set_title('RSS vs Number of Predictors')
ax1.legend(fontsize=8)

ax2.scatter(r2_df['k'], r2_df['R2'], alpha=0.3, s=15, color='#58a6ff')
ax2.plot(best_r2['k'], best_r2['R2'], 'r-o', lw=2, markersize=5, label='Best (red frontier)')
ax2.set_xlabel('Number of Predictors'); ax2.set_ylabel('R²')
ax2.set_title('R² vs Number of Predictors')
ax2.legend(fontsize=8)
plt.tight_layout(); plt.show()
print('Done: Best subset selection demo (like Figure 6.1)')

6.1.2 逐步選擇 (Stepwise Selection)

當 \(p\) 很大時,\(2^p\) 個模型根本跑不動。逐步選擇提供了一條「聰明捷徑」——不全部試,而是在模型空間中進行引導式搜尋。就像逛大賣場:你不會每條走道都走過,而是按照清單有方向地找。

前向逐步選擇 (Forward Stepwise Selection)

零變數開始,每次加入一個「最能改善擬合」的變數,直到所有變數都被納入。

Algorithm 6.2:前向逐步選擇

1. 令 \(\mathcal{M}_0\) 為虛無模型。
2. 對 \(k = 0, \dots, p-1\):
  (a) 考慮所有 \(p-k\) 個「在 \(\mathcal{M}_k\) 基礎上多加入一個變數」的模型。
  (b) 選出 RSS 最小的,稱為 \(\mathcal{M}_{k+1}\)。
3. 從 \(\mathcal{M}_0, \dots, \mathcal{M}_p\) 中用驗證集誤差 / \(C_p\) / AIC / BIC / adjusted \(R^2\) 選出最終模型。

前向逐步選擇只需擬合 \(1 + p(p+1)/2\) 個模型,遠少於 \(2^p\)。當 \(p=20\) 時是 211 個 vs 1,048,576 個。

後向逐步選擇 (Backward Stepwise Selection)

全模型(包含所有 \(p\) 個變數)開始,每次移除一個「最不有用」的變數。

Algorithm 6.3:後向逐步選擇

1. 令 \(\mathcal{M}_p\) 為全模型。
2. 對 \(k = p, p-1, \dots, 1\):
  (a) 考慮所有從 \(\mathcal{M}_k\) 移除一個變數後的 \(k\) 個模型(各含 \(k-1\) 個變數)。
  (b) 選出 RSS 最小的,稱為 \(\mathcal{M}_{k-1}\)。
3. 同上,選出最終模型。
⚠️ 後向選擇的限制:後向選擇要求 \(n > p\)(樣本數大於變數數,否則全模型無法擬合)。而前向選擇可以在 \(n < p\) 的高維情境下使用(最多建構 \(\mathcal{M}_0, \dots, \mathcal{M}_{n-1}\))。

用 sklearn 實作前向與後向選擇

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
import pandas as pd
from ISLP import load_data
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score

# 載入 Credit 資料
credit = load_data('Credit')
X = pd.get_dummies(credit.drop('Balance', axis=1), drop_first=True)
y = credit['Balance']
feat_names = list(X.columns)

def forward_stepwise(X, y, max_features=None):
    """前向逐步選擇:回傳每步選入的變數與對應的模型"""
    n, p = X.shape
    if max_features is None:
        max_features = min(p, n - 1)
    selected = []
    remaining = list(range(p))
    models = []
    scores = []
    for k in range(max_features):
        best_rss = float('inf')
        best_feat = None
        best_model = None
        for j in remaining:
            cols = selected + [j]
            model = LinearRegression().fit(X.iloc[:, cols], y)
            rss = np.sum((y - model.predict(X.iloc[:, cols]))**2)
            if rss < best_rss:
                best_rss = rss
                best_feat = j
                best_model = model
        selected.append(best_feat)
        remaining.remove(best_feat)
        models.append(best_model)
        scores.append(best_rss)
    return selected, models, scores

# 執行前向選擇
sel_idx, models, rss_vals = forward_stepwise(X, y, max_features=10)
print("變數選擇順序(前向):")
for i, idx in enumerate(sel_idx):
    print(f"  Step {i+1}: {feat_names[idx]:<20s} RSS={rss_vals[i]/1e6:.2f}M")

# 視覺化:RSS 下降曲線
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(range(1, len(rss_vals)+1), np.array(rss_vals)/1e6, 'b-o', lw=2, markersize=6)
ax.set_xlabel('Number of Predictors'); ax.set_ylabel('RSS (×10⁶)')
ax.set_title('Forward Stepwise: RSS vs Model Size (Credit Data)')
ax.grid(True, alpha=0.3)
plt.tight_layout(); plt.show()
try:
    from google.colab import drive
    drive.mount('/content/drive')
    DATA_PATH = '/content/drive/MyDrive/ISLP_data/'
except ImportError:
    DATA_PATH = '/tmp/'

import numpy as np
import pandas as pd
from ISLP import load_data
from sklearn.linear_model import LinearRegression

# 後向逐步選擇
credit = load_data('Credit')
X = pd.get_dummies(credit.drop('Balance', axis=1), drop_first=True)
y = credit['Balance']
feat_names = list(X.columns)

def backward_stepwise(X, y):
    """後向逐步選擇"""
    n, p = X.shape
    selected = list(range(p))
    removal_order = []
    rss_vals = []
    for k in range(p, 1, -1):
        worst_rss = float('inf')
        worst_feat = None
        for j in selected:
            cols = [c for c in selected if c != j]
            model = LinearRegression().fit(X.iloc[:, cols], y)
            rss = np.sum((y - model.predict(X.iloc[:, cols]))**2)
            if rss < worst_rss:
                worst_rss = rss
                worst_feat = j
        selected.remove(worst_feat)
        removal_order.append(worst_feat)
        rss_vals.append(worst_rss)
    # 最後剩一個變數
    model = LinearRegression().fit(X.iloc[:, selected], y)
    rss_vals.append(np.sum((y - model.predict(X.iloc[:, selected]))**2))
    return removal_order, rss_vals

removal, rss_bw = backward_stepwise(X, y)
print("變數移除順序(後向):")
for i, idx in enumerate(removal):
    print(f"  Step {i+1}: 移除 {feat_names[idx]:<20s}")
print(f"  最後保留: {[feat_names[s] for s in [c for c in range(X.shape[1]) if c not in removal]]}")

混合方法 (Hybrid Approaches)

混合方法結合了前向與後向的優點:像前向選擇一樣逐步加入變數,但每加入一個新變數後,也檢查是否可以移除已選入但不再有用的變數。這種「邊加邊減」的策略更接近最佳子集選擇,同時保留了計算效率。

6.1.3 選擇最佳模型

子集選擇方法會產出一系列的模型 \(\mathcal{M}_0, \mathcal{M}_1, \dots, \mathcal{M}_p\),現在的問題是:哪一個最好?因為訓練誤差(RSS / \(R^2\))只會隨變數增加而改善,不能用來選。我們需要能估計測試誤差的指標。

\(C_p\)、AIC、BIC 與 Adjusted \(R^2\)

這些指標都在訓練誤差的基礎上加入懲罰項,懲罰模型複雜度(變數太多)。

\(C_p\) 統計量(Mallow's \(C_p\))
\[ C_p = \frac{1}{n}\left(\text{RSS} + 2d\hat{\sigma}^2\right) \] 其中 \(d\) 是預測變數個數,\(\hat{\sigma}^2\) 是誤差變異數的估計(通常用全模型估計)。
選擇 \(C_p\) 最小的模型。
公式 6.2
AIC(Akaike Information Criterion)
對高斯誤差的線性迴歸,AIC 與 \(C_p\) 成比例
\[ \text{AIC} = \frac{1}{n}\left(\text{RSS} + 2d\hat{\sigma}^2\right) \]
公式(省略無關常數)
BIC(Bayesian Information Criterion)
\[ \text{BIC} = \frac{1}{n}\left(\text{RSS} + \log(n) \cdot d \cdot \hat{\sigma}^2\right) \] 由於 \(\log(n) > 2\)(當 \(n > 7\)),BIC 的懲罰比 \(C_p\) 更重,傾向選出更小的模型。
公式 6.3
Adjusted \(R^2\)
\[ \text{Adjusted } R^2 = 1 - \frac{\text{RSS}/(n-d-1)}{\text{TSS}/(n-1)} \] 選擇 adjusted \(R^2\) 最大的模型。注意:與 \(C_p\)、AIC、BIC 相反,這裡是越大越好
公式 6.4
🔄 生活化理解:\(C_p\) 和 AIC 像是「自助餐計價方式」——你可以拿很多道菜(變數),但每多拿一道就要多付 \(2\hat{\sigma}^2\) 元。BIC 更嚴格:每多一道要多付 \(\log(n)\hat{\sigma}^2\) 元,所以人多的時候(\(n\) 大)罰得更重。Adjusted \(R^2\) 則是「滿意度除以花的錢」,你希望每塊錢換到最多滿足感。

四種指標的比較實作

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
import pandas as pd
from ISLP import load_data
from sklearn.linear_model import LinearRegression
from itertools import combinations
from math import comb

credit = load_data('Credit')
X = pd.get_dummies(credit.drop('Balance', axis=1), drop_first=True)
y = credit['Balance']
n, p = X.shape

# 用全模型估計 sigma^2
full_model = LinearRegression().fit(X, y)
sigma2_hat = np.sum((y - full_model.predict(X))**2) / (n - p - 1)
TSS = np.sum((y - np.mean(y))**2)

# 對每個 k 找隨機子集計算 Cp, AIC, BIC, AdjR2
np.random.seed(42)
results = []
for k in range(1, p+1):
    best_cp = float('inf')
    best_bic = float('inf')
    best_adjr2 = -float('inf')
    best_r2 = -float('inf')
    # 隨機取若干組合
    for _ in range(min(200, int(comb(p, k)) if k <= p//2 else 200)):
        cols = list(np.random.choice(p, size=k, replace=False))
        model = LinearRegression().fit(X.iloc[:, cols], y)
        rss = np.sum((y - model.predict(X.iloc[:, cols]))**2)
        r2 = 1 - rss / TSS
        cp = (rss + 2 * k * sigma2_hat) / n
        bic = (rss + np.log(n) * k * sigma2_hat) / n
        adjr2 = 1 - (rss/(n-k-1)) / (TSS/(n-1))
        if cp < best_cp: best_cp = cp
        if bic < best_bic: best_bic = bic
        if adjr2 > best_adjr2: best_adjr2 = adjr2
        if r2 > best_r2: best_r2 = r2
    results.append({'k': k, 'R2': best_r2, 'Cp': best_cp, 'BIC': best_bic, 'AdjR2': best_adjr2})

rdf = pd.DataFrame(results)

fig, axes = plt.subplots(1, 3, figsize=(14, 4))
axes[0].plot(rdf['k'], rdf['Cp'], 'b-o', lw=2, markersize=5)
axes[0].set_xlabel('Number of Predictors'); axes[0].set_ylabel('Cp')
axes[0].set_title('Cp (lower is better)')
axes[0].axvline(rdf.loc[rdf['Cp'].idxmin(), 'k'], color='red', ls='--', alpha=0.7)
axes[0].grid(True, alpha=0.3)

axes[1].plot(rdf['k'], rdf['BIC'], 'orange', marker='s', lw=2, markersize=5)
axes[1].set_xlabel('Number of Predictors'); axes[1].set_ylabel('BIC')
axes[1].set_title('BIC (lower is better)')
axes[1].axvline(rdf.loc[rdf['BIC'].idxmin(), 'k'], color='red', ls='--', alpha=0.7)
axes[1].grid(True, alpha=0.3)

axes[2].plot(rdf['k'], rdf['AdjR2'], 'g-^', lw=2, markersize=5)
axes[2].set_xlabel('Number of Predictors'); axes[2].set_ylabel('Adjusted R²')
axes[2].set_title('Adjusted R² (higher is better)')
axes[2].axvline(rdf.loc[rdf['AdjR2'].idxmax(), 'k'], color='red', ls='--', alpha=0.7)
axes[2].grid(True, alpha=0.3)

plt.tight_layout(); plt.show()
print(f"Cp 建議: {rdf.loc[rdf['Cp'].idxmin(), 'k']} 個變數")
print(f"BIC 建議: {rdf.loc[rdf['BIC'].idxmin(), 'k']} 個變數(傾向更小模型)")
print(f"AdjR² 建議: {rdf.loc[rdf['AdjR2'].idxmax(), 'k']} 個變數")
print(f"R² (僅供參考,不能用來選!): {rdf.loc[rdf['R2'].idxmax(), 'k']} 個變數 (=全模型)")

驗證集與交叉驗證

除了上述間接估計測試誤差的指標,我們也可以直接用驗證集或交叉驗證來估計測試誤差(詳見第五章)。這是最直接的方法:在不同資料分割上評估模型效能,選出平均驗證誤差最小的模型大小。

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
import pandas as pd
from ISLP import load_data
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score

credit = load_data('Credit')
X = pd.get_dummies(credit.drop('Balance', axis=1), drop_first=True)
y = credit['Balance']
n, p = X.shape

# 用前向逐步選擇的順序,對每個 k 做交叉驗證
def forward_order(X, y):
    selected = []
    remaining = list(range(X.shape[1]))
    for _ in range(min(X.shape[1], X.shape[0]-1)):
        best_rss = float('inf')
        best_feat = None
        for j in remaining:
            cols = selected + [j]
            model = LinearRegression().fit(X.iloc[:, cols], y)
            rss = np.sum((y - model.predict(X.iloc[:, cols]))**2)
            if rss < best_rss:
                best_rss = rss
                best_feat = j
        selected.append(best_feat)
        remaining.remove(best_feat)
    return selected

order = forward_order(X, y)
cv_errors = []
for k in range(1, len(order)+1):
    cols = order[:k]
    scores = cross_val_score(LinearRegression(), X.iloc[:, cols], y,
                             cv=5, scoring='neg_mean_squared_error')
    cv_errors.append(-scores.mean())

best_k = np.argmin(cv_errors) + 1
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(range(1, len(cv_errors)+1), cv_errors, 'b-o', lw=2, markersize=6)
ax.axvline(best_k, color='red', ls='--', alpha=0.7,
           label=f'Best k={best_k} (min CV MSE)')
ax.set_xlabel('Number of Predictors'); ax.set_ylabel('5-Fold CV MSE')
ax.set_title('Cross-Validation for Model Size Selection (Credit Data)')
ax.legend(); ax.grid(True, alpha=0.3)
plt.tight_layout(); plt.show()
print(f"交叉驗證建議的最佳模型大小: k = {best_k}")

方法總比較

方法 搜尋的模型數 保證最優? n < p 可用? 計算成本 適用情境
最佳子集 \(2^p\) ✅ 保證 指數級 \(p \leq 30\text{–}40\)
前向逐步 \(1 + p(p+1)/2\) ❌ 不保證 二次方級 高維 (\(p\) 大) 或 \(n < p\)
後向逐步 \(1 + p(p+1)/2\) ❌ 不保證 二次方級 \(n > p\),想從全模型開始刪
混合方法 略多於逐步 ❌ 不保證 視實作而定 略高於逐步 想要接近最佳子集品質但不付全額

模型選擇準則比較

準則 公式結構 懲罰項 選擇方向 理論基礎 傾向
\(C_p\) RSS \(+ 2d\hat{\sigma}^2\) \(2d\hat{\sigma}^2\) ↓ 越小越好 測試 MSE 的不偏估計 中等
AIC 同 \(C_p\)(成比例) \(2d\hat{\sigma}^2\) ↓ 越小越好 最大概似 + 懲罰 中等
BIC RSS \(+ \log(n)d\hat{\sigma}^2\) \(\log(n)d\hat{\sigma}^2\) ↓ 越小越好 貝氏推論 偏向更小模型
Adjusted \(R^2\) \(1 - \frac{\text{RSS}/(n-d-1)}{\text{TSS}/(n-1)}\) 隱含在分母 \(n-d-1\) ↑ 越大越好 啟發式(較不嚴謹) 偏向較大模型
交叉驗證 直接估計測試誤差 無需公式懲罰 ↓ CV 誤差越小越好 實證 資料驅動

子集選擇的優缺點

✅ 優點

❌ 缺點

應用場景

🏥 醫療診斷:找出關鍵生物標記

在基因表達資料中(\(p\) 可能高達數萬個基因,\(n\) 可能只有數百個病人),前向逐步選擇可以從大量基因中篩選出與疾病最相關的少數幾個,建立簡潔且可解釋的診斷模型。BIC 在此情境特別有用,因為它傾向選出更小的模型。

💰 金融風控:信用評分模型

使用 Credit 資料集建立信用卡違約預測模型時,最佳子集選擇(\(p=11\),可行)可以找出最精簡的預測變數組合。模型越簡單,越容易向監管機關解釋、也越不容易過擬合到特定時期的資料。

📈 行銷分析:廣告支出優化

在 Advertising 資料集中,我們想知道 TV、Radio、Newspaper 三種廣告管道哪些真正有效。後向逐步選擇從全模型開始刪除不顯著的變數(如 Newspaper),幫助行銷團隊把預算集中在真正有效的管道上。

🔗 對 Hermes Agent 系統設計的啟發

「子集選擇」對 AI Agent 架構的啟示

1. 工具/技能的「變數選擇」問題:就像統計模型不需要所有預測變數,AI Agent 也不需要載入所有 skill。每次任務前應做「skill subset selection」——根據任務特徵只載入相關的 3-5 個 skill,而非全部 50+ 個。這直接降低 context window 消耗(相當於降低「模型複雜度的懲罰」)。

2. \(C_p\)/BIC 式的自我評估:可以設計一個類似 BIC 的內部指標來評估 skill 的「投資報酬率」——該 skill 被載入的次數 vs 實際被使用的次數 vs 任務成功率的貢獻。長期未被使用或貢獻低的 skill 應該被標記為「待淘汰」——這正是 BIC 懲罰多餘變數的精神。

3. 前向逐步的漸進式擴展:當 Agent 碰到新任務類型時,不該一次載入所有可能的工具,而是像前向逐步選擇一樣——先從最基礎的工具開始,逐步加入一個「最可能改善效能」的新工具,用任務完成度作為「RSS」的類比來決定是否繼續擴展。
用全部變數做出來的模型,是偷看答案的考試成績;用最少變數做出來的模型,才是真正的實力。 — ISLP §6.1 精神:訓練誤差不代表測試誤差,簡約才是王道
← 5.3 Lab: 交叉驗證與 Bootstrap 📑 課程首頁 6.2 收縮方法 →