iT邦幫忙

2025 iThome 鐵人賽

DAY 10
0
AI & Data

30 天入門常見的機器學習演算法系列 第 10

(Day 10) 支援向量機 (Support Vector Machine)

  • 分享至 

  • xImage
  •  

終於來到 SVM,這也是本系列介紹 Machine Learning 中分類演算法的最後一個,當然在機器學習中還有很多的監督式分類演算法,我個人認為相對沒我介紹的這幾個經典,就留給讀者自行學習。從明天開始到進入樹模型之前,我會補充一下,模型 Validation Index 的內容 (用來衡量模型結果好不好),因為前面飆的有點快,後來有發現這部分也很重要,預計會花 2 ~ 3 天的篇幅來介紹。

我們就進入正題,支援向量機 (Support Vector Machine) 是一種監督式學習演算法,泛指支援向量機演算法框架,透過在特徵空間中尋找最能分隔不同類別的超平面 (hyperplane),並最大化分類邊界 (margin),可應用於:

  • 分類 (Classification)
  • 回歸 (Regression)
  • 異常檢測 (Anomaly Detection)

但是回歸的部分非常少用到 Support Vector Regression 本系列就不說明這塊;至於異常檢測的應用又稱 OneClass SVM 目前沒有規劃,這是一種無監督式學習的技術,專門在做 Anomaly Detection 的任務,因為本系列規劃在樹模型介紹完成後,會進入深度學習篇章,所以 OneClass SVM 的部分如果後續有篇幅的話會再補充,如果沒有也請讀者自行學習;所以本篇會以 SVM 應用在分類任務 (Support Vector Classification) 上來詳細說明。

SVM 解決了什麼問題?

在詳細介紹 SVM 之前,要先說明一下 SVM 到底要解決什麼問題,我們先回到 Day 5 介紹的 Logistic Regression,假設同一組數據做分類,可能會發生以下狀況,我們先看到 Logistic Regression 的部分,大家會發現看起來分類正確,但是那條線怎麼切得怪怪的,這也是 Logistic Regression 的問題,會造成模型泛化性不夠好,因為 Logistic Regression 對於這部分沒有進行處理。

Image_2025-08-08_13-21-09.png
圖片來源: https://b5031631512567.medium.com/logistic-regression-羅吉斯回歸-support-vector-machine-svm-做a-b分類-82aa5e5edaf8

接下來才有 SVM 登場的機會,就是針對 Logistic Regression 的問題進行解決,就可以很明顯的對比出來,下面這條線明顯漂亮很多,所以模型的泛化性也比較好。

Image_2025-08-08_13-21-23.png

圖片來源: https://b5031631512567.medium.com/logistic-regression-羅吉斯回歸-support-vector-machine-svm-做a-b分類-82aa5e5edaf8

模型介紹

模型邏輯與核心概念

運作原理

我必須先聲明 SVM 的原理,因為 SVM 他也不是透過梯度下降來學習的,是直接透過一些數學原理把超平面算出來,目前我也不會,所以我只能講個大概,以及後面樹模型的極限梯度提升樹 (XGBoost),我這人認為這兩個算法的數學原理是超難的那種,只能交由讀者自行努力。

回到正題,SVM 是如何運作的,它的核心概念就是想辦法找出一條線 (或高維空間的超平面) ,這條線是離最近資料點最遠的分隔線 (最大化 margin) 能把兩類資料清楚分開,如下圖:

Image_2025-08-08_13-33-52.png
圖片來源: https://b5031631512567.medium.com/logistic-regression-羅吉斯回歸-support-vector-machine-svm-做a-b分類-82aa5e5edaf8

在進入求超平面之前,先來談一下最大化 Margin,Margin 是兩類資料「最近一筆」資料點到分隔線的距離,但是因為真實數據一定存在噪音,所以會有兩種區分,從上面這幾張圖就可以看出來,真實的數據集根本不會長這樣,一定會有噪音,如果無法容忍這種噪音,一定會導致演算法停不下來,所以這個 margin 會有兩種區分:

  • Hard Margin: 不引入損失因子,但是會無法處理資料存在噪音
  • Soft Margin: 引入損失因子,允許少量誤分類,並在目標函數中加入懲罰

回到上面那張圖,中間那條紅色的就是超平面,求解的核心流程如下:

  • 設定目標 (最大化 margin)
  • 建立數學模型
    • 設一條線性超平面如下: $w^T x + b = 0$
    • 條件是對所有資料點 $(x_i, y_i)$,滿足: $y_i (w^T x_i + b) \geq 1$
  • 最佳化問題建構 (Primal Form): 因最大化 margin 相當於最小化 $|w|^2$,所以目標函數為 $\min_{w,b} \frac{1}{2} |w|^2 \quad \text{subject to: } y_i (w^T x_i + b) \geq 1 \text{ for all } i$,這是一個凸的二次規劃問題
  • 建立對偶問題 (Dual Form): 透過拉格朗日乘數法,把原問題轉換為對偶形式 (目的是降低變數數量 & 導入 kernel)
    • $\max_{\alpha} \sum_{i=1}^n \alpha_i - \frac{1}{2} \sum_{i,j=1}^n \alpha_i \alpha_j y_i y_j (x_i \cdot x_j)$
    • $\text{subject to: } \sum \alpha_i y_i = 0,\quad \alpha_i \geq 0$
  • 解出 $\alpha$ 值 (支持向量篩選): 用 QP Solver 或 SMO 找出最適的 $\alpha_i$。只有 $\alpha_i > 0$ 的對應 x_i 是 Support Vectors
    • Support Vectors
      • 就是「最靠近」分隔線的那幾筆資料
      • 這幾筆資料會決定整條線的位置 (非 Support Vectors 的點不影響線)
  • 解出超平面參數 $w, b$
    • $w = \sum_{i \in SV} \alpha_i y_i x_i$
    • $b = y_i - w^T x_i \quad (\text{可取任一 support vector 代入算 b})$
  • 得到分類決策函數: $f(x) = \text{sign}(w^T x + b)$

以上就是超平面的求解過程,聽不懂沒關係來日方長,先說一點,SVM 是有約束的 convex QP 問題,沒有封閉解,但也不靠傳統梯度下降,而是靠核技巧 (Kernel Trick): 隱式地將整個特徵空間投影到一個更高維度的特徵空間中,讓非線性可分的資料變得線性可分,來看看這個連結 YouTube: SVM with polynomial kernel visualization 暸解一下,SVM 是怎麼運作的。

Kernel Trick

接下來談談 kernel trick,首先 SVM 的 kernel trick 分為三種模式:

  • kernel='linear': 不會做隱式升維 (不做 Kernel Trick)
  • kernel='poly': 會做隱式升維
  • kernel='rbf': 會做隱式升維 (Sklearn 預設的 Kernel 為 RBF)

回到剛剛的 YouTube 連結,這種隱式升維的動作,在 kernel='linear' 是不會做的,那麼大家一定會有疑問,那這三個 kernel 怎麼選? 只能從資料判斷,我先不負責任的說就先使用 RBF 就對了,如何判斷?

如果你的資料長以下這樣,直接用 linear 就可以了

Image_2025-08-08_13-33-52.png
圖片來源: https://b5031631512567.medium.com/logistic-regression-羅吉斯回歸-support-vector-machine-svm-做a-b分類-82aa5e5edaf8

但是真實世界中,資料圖大概會長下面這樣,就只能用 'poly' 或 'rbf'了。

Image_2025-08-08_14-27-59.png
圖片來源: https://andy6804tw.github.io/crazyai-ml/11.SVM/

至於 'poly' 跟 'rbf' 的區別就不多做贅述,因為太抽象,請讀者找 yt 資源輔助,幫大家整理的表格如下:

Kernel 特徵空間維度 表達能力 解釋方向
Polynomial 有限 (取決於 d) 表達特定階數的多項式 拋物線、曲面等
RBF (Gaussian) 無限維 可擬合任何光滑函數 對每點都有無限的「高斯權重」
項目 Polynomial Kernel RBF Kernel
公式 $(\mathbf{x} \cdot \mathbf{y} + r)^d$ $\exp^{(-\gamma |\mathbf{x} - \mathbf{y}|^2)}$
映射維度 有限且取決於 d 無限維 (無窮多高階特徵)
參數含義 r: 偏移,d: 多項式次數 $\gamma$: 影響距離的縮放
處理方式 模擬高階交互項 模擬「局部範圍的相似性」
非線性表現力 隨 d 增加而增強 通常更強且可應對複雜非線性

模型評估指標

  • Accuracy、Precision、Recall、F1-score: 與一般分類相同
  • ROC-AUC: 適用於二元分類評估
  • Support Vectors 數量: 支援向量數可反映模型複雜度

適用情境

  • 類別邊界明顯、且線性無法直接分隔時,可採用不同核函數
  • 資料維度高且樣本數相對較少時
  • 需要模型具有良好泛化能力,能有效防止過擬合

限制條件

  • 高維空間效能: 在高維度下依然能保持穩定
  • 邊界最大化: 最大化 Margin,有助於提升泛化能力
  • 彈性核方法: 可透過核函數 (kernel) 處理非線性問題
  • 容忍度調控: 透過參數 C 調整對誤分類與 Margin 的容忍度

程式實例

import warnings
warnings.filterwarnings("ignore")

import numpy as np
import pandas as pd

from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
from sklearn.metrics import (
    classification_report, accuracy_score, f1_score, confusion_matrix
)
from sklearn.svm import SVC, LinearSVC

# 1) 載入資料(10 類、多分類)
X, y = load_digits(return_X_y=True)

# 2) 訓練/測試分割(分層)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)

# 3) 基線:LinearSVC(速度快)——記得標準化;提高 max_iter 避免收斂警告
linear_clf = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", LinearSVC(C=1.0, random_state=42, max_iter=5000))
])

linear_clf.fit(X_train, y_train)
y_pred_linear = linear_clf.predict(X_test)

linear_acc = accuracy_score(y_test, y_pred_linear)
linear_macro_f1 = f1_score(y_test, y_pred_linear, average="macro")
linear_weighted_f1 = f1_score(y_test, y_pred_linear, average="weighted")

print("=== LinearSVC (baseline) ===")
print("Accuracy:", round(linear_acc, 4))
print("Macro-F1:", round(linear_macro_f1, 4))
print("Weighted-F1:", round(linear_weighted_f1, 4))
print(classification_report(y_test, y_pred_linear, digits=4))

# 4) RBF SVC(非線性)——小網格搜尋;若你覺得慢,可把 probability=False
rbf_pipe = Pipeline([
    ("scaler", StandardScaler()),
    ("clf", SVC(kernel="rbf", probability=True, random_state=42))  # 如需更快:probability=False
])

param_grid = {
    "clf__C": [1, 10, 100],
    "clf__gamma": ["scale", 0.01]
}
rbf_gs = GridSearchCV(
    rbf_pipe, param_grid=param_grid, cv=3,
    scoring="f1_weighted", n_jobs=-1, verbose=0
)
rbf_gs.fit(X_train, y_train)

best_rbf = rbf_gs.best_estimator_
y_pred_rbf = best_rbf.predict(X_test)

rbf_acc = accuracy_score(y_test, y_pred_rbf)
rbf_macro_f1 = f1_score(y_test, y_pred_rbf, average="macro")
rbf_weighted_f1 = f1_score(y_test, y_pred_rbf, average="weighted")

print("\n=== RBF SVC (tuned) ===")
print("Best params:", rbf_gs.best_params_)
print("Accuracy:", round(rbf_acc, 4))
print("Macro-F1:", round(rbf_macro_f1, 4))
print("Weighted-F1:", round(rbf_weighted_f1, 4))
print(classification_report(y_test, y_pred_rbf, digits=4))

# 5) 混淆矩陣(觀察每一類的錯誤分佈)
cm_linear = confusion_matrix(y_test, y_pred_linear)
cm_rbf = confusion_matrix(y_test, y_pred_rbf)

print("\nConfusion Matrix - LinearSVC:\n", cm_linear)
print("\nConfusion Matrix - RBF SVC:\n", cm_rbf)

執行結果

pyenv shell 3.11.7
/Users/twcch/.pyenv/versions/3.11.7/bin/python /Users/twcch/Documents/Dev/Project/ironman_2025/day10.py
twcch@s-MacBook ironman_2025 % pyenv shell 3.11.7
twcch@s-MacBook ironman_2025 % /Users/twcch/.pyenv/versions/3.11.7/bin/python /Users/twcch/Documents/Dev/Project/ironman_2025/day10.py
=== LinearSVC (baseline) ===
Accuracy: 0.9556
Macro-F1: 0.9551
Weighted-F1: 0.9553
              precision    recall  f1-score   support

           0     1.0000    1.0000    1.0000        36
           1     0.9118    0.8611    0.8857        36
           2     1.0000    1.0000    1.0000        35
           3     0.9730    0.9730    0.9730        37
           4     0.9231    1.0000    0.9600        36
           5     0.9737    1.0000    0.9867        37
           6     0.9444    0.9444    0.9444        36
           7     0.9459    0.9722    0.9589        36
           8     0.8857    0.8857    0.8857        35
           9     1.0000    0.9167    0.9565        36

    accuracy                         0.9556       360
   macro avg     0.9558    0.9553    0.9551       360
weighted avg     0.9559    0.9556    0.9553       360


=== RBF SVC (tuned) ===
Best params: {'clf__C': 10, 'clf__gamma': 0.01}
Accuracy: 0.9833
Macro-F1: 0.9832
Weighted-F1: 0.9833
              precision    recall  f1-score   support

           0     1.0000    1.0000    1.0000        36
           1     0.9722    0.9722    0.9722        36
           2     1.0000    1.0000    1.0000        35
           3     1.0000    1.0000    1.0000        37
           4     0.9459    0.9722    0.9589        36
           5     1.0000    1.0000    1.0000        37
           6     0.9730    1.0000    0.9863        36
           7     0.9474    1.0000    0.9730        36
           8     1.0000    0.9429    0.9706        35
           9     1.0000    0.9444    0.9714        36

    accuracy                         0.9833       360
   macro avg     0.9839    0.9832    0.9832       360
weighted avg     0.9839    0.9833    0.9833       360


Confusion Matrix - LinearSVC:
 [[36  0  0  0  0  0  0  0  0  0]
 [ 0 31  0  1  1  0  1  0  2  0]
 [ 0  0 35  0  0  0  0  0  0  0]
 [ 0  0  0 36  0  0  0  1  0  0]
 [ 0  0  0  0 36  0  0  0  0  0]
 [ 0  0  0  0  0 37  0  0  0  0]
 [ 0  1  0  0  0  0 34  0  1  0]
 [ 0  0  0  0  1  0  0 35  0  0]
 [ 0  2  0  0  0  1  1  0 31  0]
 [ 0  0  0  0  1  0  0  1  1 33]]

Confusion Matrix - RBF SVC:
 [[36  0  0  0  0  0  0  0  0  0]
 [ 0 35  0  0  1  0  0  0  0  0]
 [ 0  0 35  0  0  0  0  0  0  0]
 [ 0  0  0 37  0  0  0  0  0  0]
 [ 0  0  0  0 35  0  0  1  0  0]
 [ 0  0  0  0  0 37  0  0  0  0]
 [ 0  0  0  0  0  0 36  0  0  0]
 [ 0  0  0  0  0  0  0 36  0  0]
 [ 0  1  0  0  1  0  0  0 33  0]
 [ 0  0  0  0  0  0  1  1  0 34]]
twcch@s-MacBook ironman_2025 % 

結果評估

  • Linear vs RBF
    • 通常 RBF SVC 的 Macro-F1 / Weighted-F1 / Accuracy 都會勝過 LinearSVC,顯示非線性邊界更貼近資料結構。
    • 如果你的 LinearSVC 與 RBF SVC 表現接近,代表資料邊界偏線性,部署上可選擇更快、更省資源的線性模型。
  • Macro-F1 vs Weighted-F1
    • Macro-F1 (各類等權) 看模型是否犧牲小眾類;
    • Weighted-F1 更接近整體體感。兩者差距大 → 可能忽略某些類別。
  • 混淆矩陣
    • 檢視容易混淆的數字對 (如 9 ↔ 4)。這能指引特徵工程 (例如邊角筆畫強化) 或資料增強 (旋轉、縮放) 方向。

下一步建議

  • 調參擴張: 若時間允許,擴大 C 與 gamma 範圍 (如 C: [1, 10, 100, 300]、gamma: ["scale", 0.03, 0.01]),或改用 HalvingGridSearchCV (需匯入 from sklearn.experimental import enable_halving_search_cv) 加速。
  • 策略比較: 顯式比 OvR 與 OvO (OneVsRestClassifier(SVC(...)) vs 內建 OvO),檢視多分類策略差異。

結語

Support Vector Machine 的價值,在於它透過支持向量專注於邊界附近的「關鍵少數樣本」,並透過核技巧靈活應對線性與非線性問題。今天的 Digits 多分類案例,雖然不像真實業務資料那麼雜亂,但仍呈現了幾個值得記住的重點:

  • 線性 vs 非線性
    • 線性核在資料邊界近似線性時表現不俗,且訓練與推論速度極快。
    • RBF 核在能捕捉複雜邊界的情境下,往往能在 Macro-F1 與 Weighted-F1 上超越線性核,但需要付出更高計算成本。
  • 多分類評估的必要性
    • 單看 Accuracy 容易忽略小眾類別的表現,因此 Macro-F1 與 Weighted-F1 是更可靠的比較基準。
    • 混淆矩陣能幫助發現「最容易混淆的類別對」,指引後續特徵工程或資料增強策略。
  • 部署時的務實取捨
    • 在推論延遲與資源受限的環境中,若線性核與非線性核表現差距不大,應優先考慮線性核或近似方法 (如 SGDClassifier)。
    • 在離線批次運算、且對精度要求極高的情況,RBF 核仍是值得投資的選擇。
      SVM 不是每個專案的萬靈丹,但它在中小型資料集、特徵經過良好縮放與清理的場景下,依然是一個值得建立的穩健基準 (Strong Baseline)。

上一篇
(Day 9) 樸素貝氏分類器 (Naive Bayes Classifier)
下一篇
(Day 11) 二元分類任務驗證指標
系列文
30 天入門常見的機器學習演算法30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言