想像你在一個二維平面上,有藍色和紫色的點各一群。你要用一條直線把它們完美分開——這條線就是超平面的二維版本。在三維空間中,超平面是一個平面;在更高維度,它就是一個 \(p-1\) 維的平坦子空間。
超平面將空間一分為二:一側的點滿足 \(\beta_0 + \beta_1 X_1 + \cdots + \beta_p X_p > 0\),另一側滿足 \(< 0\)。這就是線性決策邊界的本質。超平面的斜率由 \(\beta_1, \ldots, \beta_p\) 決定,而截距 \(\beta_0\) 則控制它偏離原點的距離。
如果一個超平面能將訓練資料的兩個類別完全且正確分到兩側,我們稱它為「分離超平面(separating hyperplane)」。這就像用一條線把紅豆和綠豆完全分開——一顆都不出錯。
更精簡的寫法:將兩邊合併,得到單一條件:
一旦有了分離超平面,分類就非常簡單:對於新測試點 \(x^*\),計算 \(f(x^*) = \beta_0 + \beta_1 x^*_1 + \cdots + \beta_p x^*_p\)。若 \(f(x^*) > 0\) 則歸為類別 1,若 \(f(x^*) < 0\) 則歸為類別 −1。函數值 \(f(x^*)\) 的大小還反映了分類的信心程度——數值離零越遠,表示點離邊界越遠,分類越可靠。
假設你根據郵件的特徵(關鍵字頻率、寄件人信譽、連結數量)訓練了一個分離超平面。當新郵件到達時,\(f(x^*)\) 的值告訴你:它落在邊界左側還是右側,而且數值大小反映了「這封郵件多麼像垃圾郵件」的信心程度。接近 0 的郵件最難判——可能需要人工審查。
問題來了:如果資料是可分離的,那存在無限多個分離超平面!任何一個好的分離線都可以稍微平移或旋轉,仍然完美分離兩類(課本 Figure 9.2 左圖展示了三條這樣的線)。我們該選哪一條?
關鍵術語:
課本 Figure 9.3 展示了這個概念:實線是最大邊界超平面,虛線標出了邊界的寬度,而箭頭指向三個支援向量(兩個藍點和一個紫點)。
要正式定義最大邊界超平面,我們需要解決一個優化問題。令 \(M\) 代表邊界寬度,我們希望最大化 \(M\),同時確保所有點都在正確的一側且距離至少為 \(M\):
讓我們拆解這個看似複雜的公式:
這是一個凸優化問題(二次規劃),可以高效求解。雖然推導過程超出本書範圍,但理解這三個元素的意義就足夠了。
最大邊界分類器有一個致命的限制:資料必須完美可線性分離。如果兩個類別的點有重疊,或存在離群值跨越邊界,就根本不存在任何分離超平面,也就沒有最大邊界分類器。
課本毫不留情地點出:在大多數真實場景中,不存在完美分離。這為下一節(§9.2 支援向量分類器)鋪路——我們需要一個能容忍部分分類錯誤的軟邊界版本。這就是 SVM 演化的關鍵轉折。
以下用 sklearn 的線性 SVM(硬邊界模式 \(C \to \infty\))示範最大邊界分類器的概念,並標出支援向量。
# 9.1 最大邊界分類器 — Python Demo
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)
n = 40
# 類別 1:以 (2, 2) 為中心
X1 = np.random.randn(n, 2) + np.array([2, 2])
y1 = np.ones(n)
# 類別 -1:以 (-2, -2) 為中心
X2 = np.random.randn(n, 2) + np.array([-2, -2])
y2 = -np.ones(n)
X = np.vstack([X1, X2])
y = np.hstack([y1, y2])
# 用 sklearn SVC:設定 C 極大 → 近似硬邊界(無錯誤容忍)
from sklearn.svm import SVC
svm_hard = SVC(kernel='linear', C=1e10)
svm_hard.fit(X, y)
# 提取超平面參數
w = svm_hard.coef_[0]
b = svm_hard.intercept_[0]
print(f"超平面:{w[0]:.3f} * X1 + {w[1]:.3f} * X2 + {b:.3f} = 0")
print(f"支援向量數量:{len(svm_hard.support_)}")
# 可視化
fig, ax = plt.subplots(figsize=(8, 6))
# 繪製資料點
ax.scatter(X1[:, 0], X1[:, 1], c='#58a6ff', edgecolors='k', s=60, label='Class +1')
ax.scatter(X2[:, 0], X2[:, 1], c='#f85149', edgecolors='k', s=60, label='Class -1')
# 標示支援向量
ax.scatter(X[svm_hard.support_, 0], X[svm_hard.support_, 1],
s=200, facecolors='none', edgecolors='#3fb950', linewidths=2,
label=f'Support Vectors ({len(svm_hard.support_)})')
# 繪製決策邊界和邊界線
xx = np.linspace(X[:, 0].min() - 1, X[:, 0].max() + 1, 100)
yy = -(w[0] * xx + b) / w[1]
margin = 1 / np.sqrt(np.sum(w ** 2))
yy_down = yy - np.sqrt(1 + (w[0] / w[1]) ** 2) * margin
yy_up = yy + np.sqrt(1 + (w[0] / w[1]) ** 2) * margin
ax.plot(xx, yy, 'k-', linewidth=2, label='Decision Boundary')
ax.plot(xx, yy_down, 'k--', linewidth=1, alpha=0.7, label='Margin')
ax.plot(xx, yy_up, 'k--', linewidth=1, alpha=0.7)
ax.set_xlabel('X1', fontsize=12)
ax.set_ylabel('X2', fontsize=12)
ax.set_title(f'Maximal Margin Classifier\nMargin Width = {2*margin:.3f}', fontsize=14)
ax.legend(loc='best')
ax.set_aspect('equal')
plt.tight_layout()
plt.savefig('/tmp/islp_9_1_maximal_margin.png', dpi=120, bbox_inches='tight')
plt.show()
print("圖表已儲存至 /tmp/islp_9_1_maximal_margin.png")
| 方法 | 邊界類型 | 對離群值 | 機率輸出 | 適用情境 |
|---|---|---|---|---|
| 最大邊界分類器 | 硬邊界(完美分離) | 極敏感 | ❌ | 完美線性可分、低雜訊 |
| 邏輯回歸(§4.3) | 柔性機率邊界 | 較穩健 | ✅ | 需機率輸出、類別重疊可接受 |
| LDA(§4.4) | 線性判別 | 中等 | ✅ (後驗) | 各類別近似常態分布 |
| KNN(§3.5) | 非線性、非參數 | 穩健 | ✅ (比例) | 無需假設資料分布 |
在疾病篩檢中,我們常用兩個生物標記(如血壓與膽固醇)來區分患者與健康者。最大邊界分類器的邊界寬度對應篩檢的「安全餘裕」——寬邊界意味著正常值和異常值之間的差距很大,偽陽性率低。但如果資料有雜訊(如輕症患者數值與正常人重疊),硬邊界方法就完全無法使用,這正是 §9.2 要解決的問題。
Face ID 系統將你的臉部特徵向量與儲存的模板進行比對。本質上,它在高維特徵空間中建立一個分離超平面來區分「本人」和「非本人」。邊界寬度(margin)對應安全性——寬邊界意味著相似的非本人(如雙胞胎)也無法誤觸解鎖。然而實際系統中使用的是軟邊界版本(支援向量分類器,§9.2),因為臉部表情、光線變化會產生雜訊,無法完美分離。
最大邊界分類器的最大弱點——要求完美分離——直接催生了下一節的支援向量分類器(§9.2)。核心改進是引入「軟邊界」:允許部分點跨越邊界甚至被錯分,透過成本參數 \(C\) 來權衡邊界寬度與錯誤筆數。這是 SVM 從「理想世界的完美分割」走向「現實世界的穩健分類」的關鍵一步。