9.4 多類別支援向量機

📖 ISLP §9.4 📄 pp. 383–384 ★★☆ 中等 ⏱️ 約 25 分鐘
SVM 多類別分類 One-vs-One One-vs-All sklearn 多數決
← 9.3 支援向量機 (SVM) 📑 課程首頁 9.5 SVM 與邏輯回歸的關係 →

從二元到多元:SVM 的擴展

截至目前,我們所討論的 SVM 都是在二元分類的情境下運作的——把資料分成兩類(+1 和 −1)。但在真實世界中,很少有問題只需要分兩類。手寫數字辨識有 10 個類別、花卉品種分類有 3 個類別、新聞文章分類可能有幾十個主題。

現在的問題是:SVM 的天生結構——尋找一個「分隔超平面」——並不自然地推廣到多類別情境。你沒辦法只用一條線把三類資料同時分開。幸運的是,統計學家發展出了兩種巧妙的策略來解決這個問題:一對一(One-Versus-One,OvO)一對多(One-Versus-All,OvA)

ISLP §9.4, pp. 383–384 · 課本原文:James, Witten, Hastie, Tibshirani (2023) An Introduction to Statistical Learning with Applications in Python · 相關文獻:Hsu & Lin (2002) "A Comparison of Methods for Multiclass Support Vector Machines", IEEE Transactions on Neural Networks
核心概念:既然一個 SVM 只能處理二元分類,那就用「多個 SVM 的組合」來解決多元問題。就像一個人無法同時判斷「這是貓、狗、還是鳥」,但可以問他 10 個「這是 A 還是 B」的問題,最後投票決定。這就是 OvO 和 OvA 的精髓。

策略一:一對一(One-Versus-One, OvO)

運作原理

假設我們有 \(K\) 個類別(\(K > 2\))。OvO 的做法是:針對每一對類別都訓練一個 SVM。總共會訓練 \(\binom{K}{2} = \frac{K(K-1)}{2}\) 個分類器。

以三個類別(A、B、C)為例:

當一個新的測試觀察值 \(x^*\) 進來時,讓所有 \(\binom{K}{2}\) 個分類器都對它進行分類,然後統計每個類別獲得的「票數」。最終分類結果就是得票最多的類別——這就是所謂的多數決(majority voting)

\[\text{最終類別} = \arg\max_k \sum_{j=1}^{\binom{K}{2}} \mathbf{1}\{ \text{SVM}_j \text{ 將 } x^* \text{ 分給類別 } k \}\]
(9.26 等價表述 — OvO 多數決)

Python 實作:用 sklearn 展示 OvO

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 生成三類別資料(方便視覺化)
X, y = make_blobs(n_samples=300, centers=3, cluster_std=1.8,
                  random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42)

# sklearn 的 SVC 預設使用 OvO(decision_function_shape='ovo')
svm_ovo = SVC(kernel='rbf', C=1.0, gamma='scale',
              decision_function_shape='ovo', random_state=42)
svm_ovo.fit(X_train, y_train)
y_pred = svm_ovo.predict(X_test)
acc_ovo = accuracy_score(y_test, y_pred)

print(f"OvO 分類準確率:{acc_ovo:.4f}")
print(f"訓練了 {svm_ovo.n_support_.sum()} 個支援向量")
print(f"每類支援向量數:{svm_ovo.n_support_}")

# 視覺化決策邊界
h = 0.05
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))
Z = svm_ovo.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

fig, ax = plt.subplots(figsize=(8, 6))
ax.contourf(xx, yy, Z, alpha=0.3, cmap='viridis')
scatter = ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train,
                     cmap='viridis', edgecolors='white', s=40)
ax.scatter(svm_ovo.support_vectors_[:, 0], svm_ovo.support_vectors_[:, 1],
           s=120, facecolors='none', edgecolors='red', linewidth=1.5,
           label='Support Vectors')
ax.set_xlabel('特徵 1 (X₁)')
ax.set_ylabel('特徵 2 (X₂)')
ax.set_title(f'SVM 一對一 (OvO) — 三類別決策邊界\n準確率 = {acc_ovo:.3f}')
ax.legend()
plt.tight_layout()
plt.show()
OvO 分類準確率:0.9778 訓練了 49 個支援向量 每類支援向量數:[21 12 16]

策略二:一對多(One-Versus-All, OvA)

運作原理

OvA(也稱為 One-Versus-Rest,OvR)採用不同的策略:對每個類別 k,訓練一個「k 對其他所有類別」的 SVM。總共只需要訓練 \(K\) 個分類器。

同樣以三類別(A、B、C)為例:

每個 SVM 給出一個決策函數值 \(f_k(x^*) = \beta_{0k} + \beta_{1k}x^*_1 + \cdots + \beta_{pk}x^*_p\),表示「觀察值屬於類別 k 的信心程度」(分數越高,越有信心它屬於 k 而非其他類別)。最終選擇信心分數最高的那個類別。

\[\text{最終類別} = \arg\max_k \; \big( \beta_{0k} + \beta_{1k}x^*_1 + \beta_{2k}x^*_2 + \cdots + \beta_{pk}x^*_p \big)\]
(課本 §9.4.2 — OvA 決策規則)

Python 實作:OvA 與 OvO 對比

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
from sklearn.svm import SVC
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

# 同樣的三類別資料
X, y = make_blobs(n_samples=300, centers=3, cluster_std=1.8,
                  random_state=42)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42)

# OvA 模式(decision_function_shape='ovr')
svm_ova = SVC(kernel='rbf', C=1.0, gamma='scale',
              decision_function_shape='ovr', random_state=42)
svm_ova.fit(X_train, y_train)
y_pred_ova = svm_ova.predict(X_test)
acc_ova = accuracy_score(y_test, y_pred_ova)

# OvO 模式(對照組)
svm_ovo = SVC(kernel='rbf', C=1.0, gamma='scale',
              decision_function_shape='ovo', random_state=42)
svm_ovo.fit(X_train, y_train)
y_pred_ovo = svm_ovo.predict(X_test)
acc_ovo = accuracy_score(y_test, y_pred_ovo)

print(f"OvA 準確率:{acc_ova:.4f}  |  OvO 準確率:{acc_ovo:.4f}")
print(f"OvA 支援向量總數:{svm_ova.n_support_.sum()}")
print(f"OvO 支援向量總數:{svm_ovo.n_support_.sum()}")

# 並排比較決策邊界
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
h = 0.05
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))

for idx, (svm, name, acc) in enumerate(
    [(svm_ova, 'OvA (一對多)', acc_ova),
     (svm_ovo, 'OvO (一對一)', acc_ovo)]):
    Z = svm.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
    ax = axes[idx]
    ax.contourf(xx, yy, Z, alpha=0.3, cmap='viridis')
    ax.scatter(X_train[:, 0], X_train[:, 1], c=y_train,
               cmap='viridis', edgecolors='white', s=30)
    ax.set_title(f'{name}\n準確率 = {acc:.3f}')
    ax.set_xlabel('特徵 1')
    ax.set_ylabel('特徵 2')

plt.tight_layout()
plt.show()
OvA 準確率:0.9778 | OvO 準確率:0.9778 OvA 支援向量總數:49 OvO 支援向量總數:49
📊 OvO vs OvA 的選擇建議
sklearn 的 SVC 預設使用 OvO(decision_function_shape='ovo'),因為在實務上 OvO 通常更受歡迎——每個子分類器只處理兩個類別,資料量較小、訓練較快。但 OvA 只需 K 個分類器(vs OvO 的 K(K−1)/2 個),當類別數很大時 OvA 反而更有效率。sklearn 也提供 LinearSVC 預設使用 OvA,適合大規模線性分類。

兩種策略的比較

✅ OvO(一對一)優點

⚠️ OvO(一對一)缺點

✅ OvA(一對多)優點

⚠️ OvA(一對多)缺點

特性OvO(一對一)OvA(一對多)
分類器數量\(\binom{K}{2} = K(K-1)/2\)\(K\)
K=4 時6 個 SVM4 個 SVM
K=10 時45 個 SVM10 個 SVM
K=100 時4,950 個 SVM ❌100 個 SVM ✓
決策機制多數決投票最大信心分數
每子分類器資料量僅含兩個類別(較少)含所有 K 個類別(較多)
類別不平衡敏感度低(配對比較)高(少數類 vs 多數類)
sklearn 預設SVC(kernel='rbf')LinearSVC

🎯 應用場景:手寫數字辨識(MNIST)

MNIST 手寫數字辨識有 10 個類別(0–9)。使用 OvO 策略需要訓練 \(\binom{10}{2} = 45\) 個 SVM,每個只區分兩個數字(例如「3 vs 7」)。對一個新圖片,45 個 SVM 各自投票,最終以多數決決定是哪個數字。sklearn 的 SVC 搭配 RBF 核心在 MNIST 上可達 ~98% 準確率。

📊 應用場景:文件主題分類

新聞文章需要被分類到 20 個主題(政治、體育、科技……)。使用 OvA 策略只需 20 個 SVM——遠比 OvO 的 190 個來得實際。每個 SVM 判斷「這篇文章是否屬於類別 k」。最終選出信心分數最高的類別作為文章主題。

🧬 應用場景:基因表達資料的癌症亞型分類

基因表達資料通常有數千個特徵但只有幾百個樣本(高維度低樣本問題)。癌症亞型可能有 3–5 種。OvO 在這裡表現良好,因為高維度下的配對比較比 OvA 的全體對抗更能捕捉細微的類別差異。

💡 Hermes 架構啟發:策略模式(Strategy Pattern)的統計學根源
OvO 和 OvA 是統計學中的「組合策略」——將複雜的多類問題分解為多個簡單的二元問題,再以投票或計分機制整合結果。這和軟體架構中的 Strategy Pattern 以及多模型集成(Ensemble)有相同的設計精神。在 Hermes 的多 Agent 系統中,當單一 Agent 無法處理複雜任務時,可以借鏡 OvO 的「配對比較 + 多數決」模式:讓多個專門的副手各自從不同角度判斷,主 Agent 彙總投票進行最終決策。這比單一全能 Agent 更穩健,因為每個專門 Agent 只需要處理自己擅長的二元決策。這就是 Divide and Conquer 在 AI 架構中的力量。
當一個工具天生只能處理二元問題時,聰明的做法不是換工具,而是把多元問題拆成許多個二元問題,再用組合的方式解決。 — §9.4 核心精神 · OvO / OvA 的設計哲學
← 9.3 支援向量機 (SVM) 📑 課程首頁 9.5 SVM 與邏輯回歸的關係 →