前面學的 logistic regression 是「判別模型」——直接學 \( \Pr(Y=k|X=x) \)(看到 x 時,它是哪一類?)。而 generative model 走另一條路:先學每一類的「樣子」\( f_k(x) \),再用貝氏定理反推分類機率。
生活化比喻:判別模型像你去餐廳,看一眼菜就判斷「這是川菜還是日料」。生成模型則是你先學「川菜的辣椒+花椒是長這樣」、「日料的擺盤+醬油是長這樣」,再問新菜比較像誰。
LDA 假設所有類別共享同一個共變異數矩陣 \( \mathbf{\Sigma} \),但每個類有自己的平均 \( \boldsymbol{\mu}_k \)。這導致決策邊界是線性的。
\[ \delta_k(x) = x^T \mathbf{\Sigma}^{-1} \boldsymbol{\mu}_k - \frac{1}{2} \boldsymbol{\mu}_k^T \mathbf{\Sigma}^{-1} \boldsymbol{\mu}_k + \log \pi_k \]分類時選 \( \delta_k(x) \) 最大的 k。當 \( p \)(變數數量)很大時,LDA 需要估計 \( p(p+1)/2 \) 個共變異數參數。
QDA 放寬假設:每個類有自己的共變異數矩陣 \( \mathbf{\Sigma}_k \)。這讓決策邊界變成二次曲線,可以捕捉更複雜的分類邊界。
\[ \delta_k(x) = -\frac{1}{2}(x-\boldsymbol{\mu}_k)^T \mathbf{\Sigma}_k^{-1}(x-\boldsymbol{\mu}_k) - \frac{1}{2}\log|\mathbf{\Sigma}_k| + \log \pi_k \]但代價是參數量暴增:QDA 要估 \( K \times p(p+1)/2 \) 個參數,樣本不夠時會 overfit。
Naive Bayes 做一個大膽假設:給定類別後,所有 predictor 互相獨立。也就是 \( f_k(x) = \prod_{j=1}^p f_{kj}(x_j) \)。這個假設幾乎在所有真實資料中都是「錯的」,但實戰上效果出奇地好。
為什麼?因為即使它對 \( f_k(x) \) 的估計有偏差,分類時只在乎誰的 \( \delta_k(x) \) 最大,不求精確機率。bias-variance trade-off 再次發揮威力:引入偏差,換取極低 variance。
| LDA | QDA | Naive Bayes | |
|---|---|---|---|
| 假設 | 各類共用 Σ | 各類獨立 Σk | predictors 獨立 |
| 邊界形狀 | 線性 | 二次曲線 | 非線性 |
| 參數量 | \( \frac{p(p+1)}{2} + Kp \) | \( K\cdot\frac{p(p+1)}{2} + Kp \) | 最低 |
| 適合情境 | K 多、n 中等 | 各類形狀差異大 | p 很大、n 很小 |
| 預測速度 | 快 | 中等 | 最快 |
# Google Colab / 本機通用
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
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis, QuadraticDiscriminantAnalysis
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, classification_report, roc_curve, auc
# ── 載入 Default 資料 ──
from ISLP import load_data
Default = load_data('Default')
X = pd.get_dummies(Default[['student', 'balance', 'income']], drop_first=True)
y = (Default['default'] == 'Yes').astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)
# ── LDA ──
lda = LinearDiscriminantAnalysis()
lda.fit(X_train, y_train)
lda_pred = lda.predict(X_test)
print("=== LDA 混淆矩陣 ===")
print(confusion_matrix(y_test, lda_pred))
# ── QDA ──
qda = QuadraticDiscriminantAnalysis()
qda.fit(X_train, y_train)
qda_pred = qda.predict(X_test)
print("\n=== QDA 混淆矩陣 ===")
print(confusion_matrix(y_test, qda_pred))
# ── Naive Bayes ──
nb = GaussianNB()
nb.fit(X_train, y_train)
nb_pred = nb.predict(X_test)
print("\n=== Naive Bayes 混淆矩陣 ===")
print(confusion_matrix(y_test, nb_pred))
# ── ROC 曲線比較 ──
lda_prob = lda.predict_proba(X_test)[:,1]
qda_prob = qda.predict_proba(X_test)[:,1]
nb_prob = nb.predict_proba(X_test)[:,1]
plt.figure(figsize=(8,6))
for probs, label, color in [(lda_prob,'LDA','#1a73e8'),(qda_prob,'QDA','#0d904f'),(nb_prob,'Naive Bayes','#e37400')]:
fpr, tpr, _ = roc_curve(y_test, probs)
plt.plot(fpr, tpr, label=f'{label} (AUC={auc(fpr,tpr):.3f})', color=color, linewidth=2)
plt.plot([0,1],[0,1],'k--',alpha=0.3)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve: LDA vs QDA vs Naive Bayes')
plt.legend()
plt.savefig('/tmp/roc_comparison.png', dpi=120, bbox_inches='tight')
plt.close()
print("\n✅ ROC 圖已存為 /tmp/roc_comparison.png")
| 方法 | 判別函數 | 核心假設 |
|---|---|---|
| LDA | \( \delta_k(x) = x^T \Sigma^{-1} \mu_k - \frac{1}{2}\mu_k^T \Sigma^{-1}\mu_k + \log\pi_k \) | \( \Sigma_k = \Sigma \; \forall k \) |
| QDA | \( \delta_k(x) = -\frac{1}{2}\log|\Sigma_k| - \frac{1}{2}(x-\mu_k)^T\Sigma_k^{-1}(x-\mu_k) + \log\pi_k \) | 各類 \( \Sigma_k \) 不同 |
| Naive Bayes | \( \delta_k(x) = \log\pi_k + \sum_{j=1}^p \log f_{kj}(x_j) \) | predictors 獨立 |
📚 James, Witten, Hastie, Tibshirani (2023) An Introduction to Statistical Learning with Python, §4.4, p153–160.
📚 Hastie, Tibshirani, Friedman (2009) The Elements of Statistical Learning, §4.3.