Learning Objectives
- 算出一條直線的間隔,說明支援向量機要最大化什麼
- 說明支援向量是什麼,以及 C 怎麼影響間隔與支援向量的數量
- 用加一個特徵的例子說明核函數的想法,並說明 RBF 核的 gamma 怎麼影響過擬合
- 說明 SVC 為什麼要先標準化,並用 pipeline 寫出來
可以分開的直線有很多條,選哪一條?
兩群點可以用一條直線分開時,這樣的直線通常有無限多條。第 7 課的羅吉斯迴歸會找到其中一條,但有些直線貼著某一邊的點經過,新的資料只要稍微偏一點就分錯了。
支援向量機(support vector machine,SVM)的想法很直接:選離兩邊最近的點都盡量遠的那一條。這個距離叫間隔(margin)。Corinna Cortes 與 Vladimir Vapnik 在 1995 年的論文《Support-Vector Networks》提出了這個方法的軟間隔版本,也就是今天常用的形式。
一條直線寫成 w·x + b = 0。類別標成 +1 與 −1 時,一個點到直線的(帶正負的)距離是:
距離 = y × (w·x + b) / ||w||
答對時是正的,分錯邊時是負的。一條直線的好壞,就看所有點裡最小的那個距離。
逐步看:算三條直線的間隔
六個點,兩個類別。三條候選直線都能把它們分開,但離兩邊的距離不同。
只有最靠近邊界的點說了算
間隔最大的直線只由離它最近的那幾個點決定,這些點叫支援向量(support vector)。把其他點移動一點或刪掉(只要沒有越過間隔),直線完全不會變。
SVC 把 w·x + b 的大小調整成間隔邊上的點剛好等於 ±1,這時候兩條虛線(等於 +1 與 −1)之間的間隔寬度是 2 / ||w||。所以最大化間隔等於讓 ||w|| 盡量小。
訓練好之後:
| 屬性 | 內容 |
|---|---|
svm.support_ |
支援向量在訓練資料裡的編號 |
svm.support_vectors_ |
支援向量本身 |
svm.n_support_ |
每個類別有幾個 |
svm.coef_、svm.intercept_ |
w 與 b(只有線性核才有 coef_) |
逐步看:SVC 找到的邊界與支援向量
40 個用亂數產生的點,兩群分得開。看最後的圖:實線是邊界、虛線是間隔、圈起來的是支援向量。
分不開怎麼辦:軟間隔與 C
真實資料常常分不開,或者為了一個離群點,把間隔擠得很窄也不值得。軟間隔允許有些點進入間隔、甚至分錯邊,但要付出代價。C 決定這個代價有多高:
| C | 模型的態度 | 間隔 | 支援向量 | 風險 |
|---|---|---|---|---|
| 小 | 寬容,寧可有點犯錯也要間隔寬 | 寬 | 多 | 欠擬合 |
| 大 | 嚴格,盡量不犯錯 | 窄 | 少 | 過擬合 |
間隔裡面的點和分錯邊的點都算支援向量,所以間隔越寬,支援向量越多。SVC 的預設是 C=1.0。
逐步看:C 怎麼影響間隔
200 個點、兩群有重疊。看輸出的欄位:間隔寬度、支援向量數、訓練與測試準確率。
核函數:把資料送到分得開的空間
有些資料用直線怎麼分都不行,例如一圈包著一圈。但如果多加一個特徵 x1² + x2²(到原點距離的平方),內圈的值都小、外圈的值都大,在三維空間裡用一個平面就能切開。
支援向量機的訓練與預測,最後都只用到兩筆資料的內積。所以不必真的算出高維的特徵,只要有一個函數能直接算出「兩筆資料在高維空間裡的內積」就夠了,這個函數叫核函數(kernel)。
最常用的是 RBF 核(SVC 的預設):
K(a, b) = exp(−gamma × ||a − b||²)
兩個點越近,值越接近 1;越遠越接近 0,可以當成「相似度」。預測時,模型把新資料和每個支援向量比相似度,加權投票。
逐步看:同心圓,直線不行,核函數可以
用 make_circles 產生一圈包一圈的資料。比較三種做法的測試準確率。
gamma 與標準化
RBF 核有兩個要一起調的超參數:C 與 gamma。gamma 控制一個訓練點影響的範圍:
| gamma | 影響範圍 | 邊界 | 風險 |
|---|---|---|---|
| 小 | 大 | 平滑,接近直線 | 欠擬合 |
| 大 | 小 | 彎彎曲曲,包住每個點 | 過擬合 |
SVC 的預設 gamma="scale",也就是 1 / (特徵數 × X.var()),會依資料的變異數調整。
RBF 核裡面有距離,所以支援向量機和 k 近鄰一樣受尺度影響。make_pipeline 可以把標準化和模型接在一起,fit 時只用訓練資料算平均與標準差,比較不會寫錯。
逐步看:gamma 的影響與標準化
前半段用 make_moons 看 gamma;後半段用 students.csv(程式產生的模擬資料,不是任何學校的真實資料)看標準化。
什麼時候用支援向量機
- 適合:資料量不大(幾千到幾萬筆)、特徵不少、邊界可能彎曲;文字分類也常用線性核。
- 不適合:資料量很大時,訓練時間會快速增加(
SVC的文件提醒,超過數萬筆可以考慮LinearSVC等其他做法)。 - 要注意:
SVC預設不輸出機率(probability=False),要機率就設probability=True,訓練會變慢;模型也不像決策樹那樣能直接讀懂。 C與gamma要一起調,常見做法是用交叉驗證搜尋(第 14 課)。
實作:寫一個算間隔的函數
完成 margin(w, b, X, y):回傳所有點裡最小的帶正負距離 y × (w·x + b) / ||w||。結果是負的表示有點分錯邊。
小測驗:支援向量機
下面程式會印出什麼?
import numpy as np
w = np.array([3.0, 4.0])
b = -2.0
x = np.array([1.0, 1.0])
print((x @ w + b) / np.linalg.norm(w))
訓練好線性 SVM 之後,刪掉一個離邊界很遠、不是支援向量的訓練點再重新訓練,邊界會怎樣?
把 C 從 100 調到 0.01,通常會發生什麼?
RBF 核的 SVM 訓練準確率 0.99、測試準確率 0.84,幾乎每個訓練點都是支援向量。優先試什麼?
下面哪一種寫法比較不容易造成資料洩漏?
重點整理
- 支援向量機找間隔最大的邊界;間隔寬度是
2 / ||w||。 - 邊界只由支援向量決定,其他點對它沒有影響。
- C:小=寬容、間隔寬、支援向量多;大=嚴格、間隔窄。預設
C=1.0。 - 核函數不必真的算高維特徵,就能得到彎曲的邊界。RBF 核(預設)的 gamma 越大,邊界越彎、越容易過擬合。
- 裡面有距離,所以要標準化;用
make_pipeline(StandardScaler(), SVC())。 - 下一課換成文字資料:用字出現的次數與條件機率做分類。