4.3 邏輯回歸:從線性到機率

📖 ISLP §4.3 📄 pp. 138–145 ⭐⭐⭐ 中級 ⏱️ 約 35 分鐘
classification logistic-regression odds MLE confounding
📑 課程首頁 ← §4.1-4.2 已學過 §4.4 LDA →

📚 理論基礎

James, G., Witten, D., Hastie, T., & Tibshirani, R. (2023). An Introduction to Statistical Learning with Python. Springer. §4.3, pp. 138–145.

邏輯回歸像一個「機率翻譯機」:你把信用卡餘額(X)丟進去,它輸出違約機率 p(X),永遠落在 0 到 1 之間。秘密在於 logistic 函數——這個 S 形曲線保證無論 X 多大或多小,預測值都不會超出 [0,1]。不同於線性回歸直接預測 Y,邏輯回歸預測的是「事件發生的機率」,再用 0.5 門檻做分類。

🎯 核心精神:把線性組合 β₀ + β₁X 壓縮到機率空間——保持可解釋性(每個 β 有意義),同時確保輸出是合法的機率值。

三個核心公式:從線性到勝算

\[ p(X) = \frac{e^{\beta_0+\beta_1 X}}{1+e^{\beta_0+\beta_1 X}} \]
公式 4.2 — Logistic 函數:將線性組合映射到 (0,1) 區間,形成 S 形曲線

這個公式解決了線性回歸最大的問題:預測值可能 <0 或 >1。Logistic 函數的 S 形狀保證輸出永遠在 0 到 1 之間。稍微整理一下,得到更直觀的形式:

\[ \frac{p(X)}{1-p(X)} = e^{\beta_0+\beta_1 X} \]
公式 4.3 — 勝算(Odds):事件發生機率與不發生機率的比值

勝算是邏輯回歸最核心的概念。勝算 2 代表「發生的可能性是不發生的 2 倍」。取 log 後,我們回到線性:

\[ \log\left(\frac{p(X)}{1-p(X)}\right) = \beta_0+\beta_1 X \]
公式 4.4 — Logit 轉換:取 log 後還原為線性形式,β₁ 代表 X 每增加一單位對 log-odds 的邊際效應

這三條公式是理解邏輯回歸的關鍵:線性組合 → 勝算 → 機率,像一個三層翻譯系統。

最大概似估計(MLE):怎樣找到最佳 β?

線性回歸用最小平方法(OLS)找 β。邏輯回歸用最大概似估計(Maximum Likelihood Estimation, MLE):找到一組 β̂ 使得「觀察到眼前這筆資料的機率」最大化。

\[ \ell(\beta_0, \beta_1) = \prod_{i:y_i=1} p(x_i) \prod_{i':y_{i'}=0} (1-p(x_{i'})) \]
公式 4.5 — 概似函數:違約者的 p(x) 盡量大,非違約者的 (1-p(x)) 盡量大

直觀理解:對於每個違約者,我們希望模型預測的違約機率接近 1;對於每個正常客戶,希望預測機率接近 0。MLE 沒有閉式解,需要用數值方法(如 Newton-Raphson)迭代求解——這也是為什麼 sklearn 的 LogisticRegression 比 LinearRegression 稍慢。

解讀係數:β̂₁ 到底告訴我們什麼?

係數估計值Std. Errorz-statisticp-value Intercept-10.65130.3612-29.5<0.0001 balance0.00550.000224.9<0.0001

表 4.1 — Default 資料集:balance 預測違約機率

β̂₁ = 0.0055 的解讀:balance 每增加 $1,違約的 log-odds 增加 0.0055。更直觀的講法:勝算比 e0.0055 ≈ 1.0055——balance 每增加 $1,違約勝算變為原來的 1.0055 倍。聽起來很小,但 balance 增加 $1000 就是 e5.5 ≈ 245 倍。

⚡ 實戰程式碼

示範 1:單變量邏輯回歸 + S 形曲線視覺化

# §4.3 Logistic Regression — 完整 Python 示範
import numpy as np
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import confusion_matrix, classification_report

# === 資料載入(Colab 相容) ===
try:
    from google.colab import drive
    drive.mount('/content/drive')
    DATA_PATH = '/content/drive/MyDrive/ISLP_data/'
except ImportError:
    DATA_PATH = '/tmp/'

# 讀取 Default 資料集
url = 'https://www.statlearning.com/s/Default.csv'
df = pd.read_csv(url)
print(f'資料形狀: {df.shape}')

# === 準備訓練資料 ===
X = df[['balance']].values
y = (df['default'] == 'Yes').astype(int).values

# === 訓練邏輯回歸模型 ===
model = LogisticRegression()
model.fit(X, y)

beta0 = model.intercept_[0]
beta1 = model.coef_[0][0]
print(f'\n估計係數: β₀ = {beta0:.4f}, β₁ = {beta1:.6f}')
print(f'勝算比: e^β₁ = {np.exp(beta1):.6f}')

# === 視覺化 S 形曲線 ===
X_range = np.linspace(X.min(), X.max(), 300).reshape(-1, 1)
probs = model.predict_proba(X_range)[:, 1]

plt.figure(figsize=(10, 6))
jitter = np.random.uniform(-0.02, 0.02, len(y))
plt.scatter(X, y + jitter, alpha=0.3, s=10, c=['blue' if v==0 else 'red' for v in y])
plt.plot(X_range, probs, 'g-', linewidth=2, label='Logistic 回歸')
plt.axhline(y=0.5, color='gray', linestyle='--', label='決策邊界 (p=0.5)')
plt.xlabel('信用卡餘額 (Balance)')
plt.ylabel('違約機率')
plt.title('§4.3 邏輯回歸:Balance → 違約機率')
plt.legend()
plt.savefig('/tmp/logistic_regression_default.png', dpi=100, bbox_inches='tight')
plt.close()

# === 模型評估 ===
y_pred = model.predict(X)
print(f'\n準確率: {model.score(X, y):.4f}')
print(classification_report(y, y_pred, target_names=['No Default', 'Default']))

示範 2:Confounding — 為什麼學生身分的係數會反轉?

# 多變量邏輯回歸 — 觀察 confounding 現象
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression

url = 'https://www.statlearning.com/s/Default.csv'
df = pd.read_csv(url)

# === 單變量:student 看起來增加違約風險 ===
X_student = (df['student'] == 'Yes').astype(int).values.reshape(-1, 1)
y = (df['default'] == 'Yes').astype(int).values

m1 = LogisticRegression().fit(X_student, y)
print(f'單變量 student: β₁ = {m1.coef_[0][0]:.4f}')
print(f'  解讀:學生身分 → 違約勝算 e^{m1.coef_[0][0]:.4f} = {np.exp(m1.coef_[0][0]):.2f} 倍')
print(f'  ⚠️ 看起來學生風險較高...但等等!')

# === 多變量:加入 balance 後,student 係數反轉 ===
X_multi = df[['balance', 'income', 'student']].copy()
X_multi['student'] = (X_multi['student'] == 'Yes').astype(int)

m2 = LogisticRegression().fit(X_multi, y)
print(f'\n多變量模型係數:')
for name, coef in zip(['balance', 'income', 'student'], m2.coef_[0]):
    print(f'  {name}: {coef:.6f}  (勝算比 = {np.exp(coef):.4f})')

print(f'\n  🔍 關鍵洞察:student 係數從正變負!')
print(f'  這是 confounding — 學生傾向有較高 balance(可能是學貸)')
print(f'  控制 balance 後,學生的違約風險其實低於非學生!')

# === 驗證 confounding:學生平均 balance 較高 ===
student_mean = df[df['student'] == 'Yes']['balance'].mean()
nonstudent_mean = df[df['student'] == 'No']['balance'].mean()
print(f'\n學生平均 balance: ${student_mean:.0f}')
print(f'非學生平均 balance: ${nonstudent_mean:.0f}')
print(f'差距: ${student_mean - nonstudent_mean:.0f} — 學生確實持有較高餘額')

💡 應用場景

💳 信用卡審核:這個申請人會違約嗎?

銀行用邏輯回歸評估每個申請人的違約機率。輸入特徵包括收入、債務比、信用歷史長度,輸出一個 0~1 的分數。

🏥 醫療診斷:病患是中風還是藥物過量?

急診室需要快速判斷昏迷病人的病因。邏輯回歸輸入生命徵象(血壓、心率、體溫)、年齡、病史,輸出各病因的機率。

📧 垃圾郵件過濾

Gmail 用邏輯回歸判斷一封郵件是否為垃圾郵件。特徵包括關鍵字頻率、發信人信譽、HTML 比例等。

⚖️ 優缺點對照

✅ 優點

❌ 缺點

🔄 方法比較

特性線性回歸邏輯回歸KNN 輸出類型連續值 (Y ∈ ℝ)機率 p ∈ (0,1)類別標籤 決策邊界N/A(回歸用)線性超平面非線性(取決於 K) 可解釋性係數 = 邊際效應係數 = 勝算比無參數,難解釋 計算成本O(p²n)迭代 MLE,慢於線性回歸訓練 O(1),預測 O(n) 處理多類別需 Dummy 編碼Multinomial 擴展原生支援 對離群值敏感敏感較穩健
邏輯回歸不是預測 Y 的值,而是預測「Y 等於某個類別的機率」——把線性世界轉譯到機率世界。 — ISLP §4.3 核心精神

🧠 Hermes 自我內化

Hermes 的 skill 系統本質上是一個技能記憶庫。§4.3 教了我們 logistic function 的核心洞見:一個壓縮函數把不設限的線性組合 βX 對應到 (0,1) 機率空間。這和 Hermes 架構的設計哲學很像:原始的 LLM token 輸出是無界的(任何文字都可能),但 skill prompt 壓縮了行為空間——它把無邊界的語言生成對應到結構化的操作(部署、驗證、Git push)。

此外,confounding 現象提醒我:當技能之間有相關性(如 deploy 和 verify 常常一起觸發),只看單一技能的成功率會誤判——需要考慮聯合效果。正如 logistic regression 加入 balance 後 student 符號反轉,加入 deploy 成功率作為控制變數後,某些 skill 的邊際貢獻才會顯現。

← §4.1-4.2 分類概述 📑 課程首頁 §4.4 LDA →