終於來到 SVM,這也是本系列介紹 Machine Learning 中分類演算法的最後一個,當然在機器學習中還有很多的監督式分類演算法,我個人認為相對沒我介紹的這幾個經典,就留給讀者自行學習。從明天開始到進入樹模型之前,我會補充一下,模型 Validation Index 的內容 (用來衡量模型結果好不好),因為前面飆的有點快,後來有發現這部分也很重要,預計會花 2 ~ 3 天的篇幅來介紹。
我們就進入正題,支援向量機 (Support Vector Machine) 是一種監督式學習演算法,泛指支援向量機演算法框架,透過在特徵空間中尋找最能分隔不同類別的超平面 (hyperplane),並最大化分類邊界 (margin),可應用於:
但是回歸的部分非常少用到 Support Vector Regression 本系列就不說明這塊;至於異常檢測的應用又稱 OneClass SVM 目前沒有規劃,這是一種無監督式學習的技術,專門在做 Anomaly Detection 的任務,因為本系列規劃在樹模型介紹完成後,會進入深度學習篇章,所以 OneClass SVM 的部分如果後續有篇幅的話會再補充,如果沒有也請讀者自行學習;所以本篇會以 SVM 應用在分類任務 (Support Vector Classification) 上來詳細說明。
在詳細介紹 SVM 之前,要先說明一下 SVM 到底要解決什麼問題,我們先回到 Day 5 介紹的 Logistic Regression,假設同一組數據做分類,可能會發生以下狀況,我們先看到 Logistic Regression 的部分,大家會發現看起來分類正確,但是那條線怎麼切得怪怪的,這也是 Logistic Regression 的問題,會造成模型泛化性不夠好,因為 Logistic Regression 對於這部分沒有進行處理。
接下來才有 SVM 登場的機會,就是針對 Logistic Regression 的問題進行解決,就可以很明顯的對比出來,下面這條線明顯漂亮很多,所以模型的泛化性也比較好。
我必須先聲明 SVM 的原理,因為 SVM 他也不是透過梯度下降來學習的,是直接透過一些數學原理把超平面算出來,目前我也不會,所以我只能講個大概,以及後面樹模型的極限梯度提升樹 (XGBoost),我這人認為這兩個算法的數學原理是超難的那種,只能交由讀者自行努力。
回到正題,SVM 是如何運作的,它的核心概念就是想辦法找出一條線 (或高維空間的超平面) ,這條線是離最近資料點最遠的分隔線 (最大化 margin) 能把兩類資料清楚分開,如下圖:
在進入求超平面之前,先來談一下最大化 Margin,Margin 是兩類資料「最近一筆」資料點到分隔線的距離,但是因為真實數據一定存在噪音,所以會有兩種區分,從上面這幾張圖就可以看出來,真實的數據集根本不會長這樣,一定會有噪音,如果無法容忍這種噪音,一定會導致演算法停不下來,所以這個 margin 會有兩種區分:
回到上面那張圖,中間那條紅色的就是超平面,求解的核心流程如下:
以上就是超平面的求解過程,聽不懂沒關係來日方長,先說一點,SVM 是有約束的 convex QP 問題,沒有封閉解,但也不靠傳統梯度下降,而是靠核技巧 (Kernel Trick): 隱式地將整個特徵空間投影到一個更高維度的特徵空間中,讓非線性可分的資料變得線性可分,來看看這個連結 YouTube: SVM with polynomial kernel visualization 暸解一下,SVM 是怎麼運作的。
接下來談談 kernel trick,首先 SVM 的 kernel trick 分為三種模式:
回到剛剛的 YouTube 連結,這種隱式升維的動作,在 kernel='linear' 是不會做的,那麼大家一定會有疑問,那這三個 kernel 怎麼選? 只能從資料判斷,我先不負責任的說就先使用 RBF 就對了,如何判斷?
如果你的資料長以下這樣,直接用 linear 就可以了
但是真實世界中,資料圖大概會長下面這樣,就只能用 'poly' 或 'rbf'了。
圖片來源: 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 增加而增強 | 通常更強且可應對複雜非線性 |
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 %
Support Vector Machine 的價值,在於它透過支持向量專注於邊界附近的「關鍵少數樣本」,並透過核技巧靈活應對線性與非線性問題。今天的 Digits 多分類案例,雖然不像真實業務資料那麼雜亂,但仍呈現了幾個值得記住的重點: