支撐向量機(Support Vector Machine, SVM)的目的,是找到一個超平面,以盡可能的將兩類資料分開,並且間隔要愈大愈好。我們先以能完全分開的狀況來說明,以下圖的藍色和橘色兩類為例,黑色和紅色的實線都可以將兩類資料分開,但是紅色線能產生的間隔比較大,所以我們要認為紅色線比黑色線適合。
而在實務上,因為資料可能處在高維空間,因此我們會稱呼上圖的那條實線為「超平面」,而超平面的方程式可以寫成 w⊤x + b = 0,其中 w 是超平面的法向量。因此,某個資料點 xi 與超平面間的有符號距離 di,是:
di = yi(w⊤ * xi + b) / ||w||
其中,yi ∈ {-1, 1} 是 xi 所屬的類別,而有符號距離 di 是用來判斷資料點與超平面的相對位置,與其與資料所屬類別是否一致的,若 di > 0 則代表一致,di < 0 則代表不一致。因此,我們可以構建這樣的最佳化問題:
maxw, b d
subject to yi(w⊤ * xi + b) / ||w|| ≥ d
也就是讓最小的距離 d 盡可能的增大。而如果考慮到實際資料不太容易出現能用超平面完全分開的狀況,則最佳化問題會寫成:
maxw, b, ξ d - C Σξi
subject to yi(w⊤ * xi + b) / ||w|| ≥ d - ξi
其中,ξi 代表資料點 xi 違反間隔的程度,而 C 是用來控制「間隔盡量大」與「違規容忍程度盡量小」這兩者之間權衡的超參數。實際求解這個最佳化問題的方法不在本教材的範圍內,若有興趣可以參考 Lagrange multiplier 與 dual problem 等關鍵字。
然而,在更接近真實的狀況中,資料甚至連用一個超平面大略的分開都不太可能,例如 scikit-learn 的 make_circles 產生的範例資料就是如此。因此,我們需要對空間進行變換,以將座標平方為例,就是將原始空間中,圓形的分類邊界:
w1x12 + w2x22 + b = 0
變成新空間中,直線的分類邊界:
w1z1 + w2z2 + b = 0
而在實務上,我們不必真的將資料轉換到高維空間,而是求取兩兩資料點在新空間中的內積,這樣的做法稱為 kernel method 或者 kernel trick。以把圓形分類邊界變成直線分類邊界來說,若使用 kernel method 求取內積,則可以寫成如下的式子,其中「∘」運算是 hadamard product:
K(xi, xj) = (xi∘xi)⊤(xj∘xj)
上述的 kernel function 在實務上比較少見。常用的 kernel function 有這些:
- Polynomial kernel: K(xi, xj) = (xi⊤xj + c)d,其中的 c 和 d 是超參數。
- RBF (Radial Basis Function) kernel: K(xi, xj) = exp(- || xi - xj ||2 / (2 * σ2)),對應到無限高維的空間,其中σ是超參數。
- Sigmoid kernel: K(xi, xj) = tanh(αxi⊤xj + c),其中的 α 和 c 是超參數。
若有需要,你也可以設計自己的 kernel funtion,只要其所構成的 kernel matrix 是 symmetric positive semi-definite 即可,否則在求取最佳化的計算上可能會不穩定,甚至於無法計算。
我們可以使用 scikit-learn 的 svm.SVC 來幫我們執行 SVM 演算法,使用的資料集是 Wine Data Set,該資料集在 scikit-learn 有內建一版,你可以直接透過 import 相關函式來使用,不必自己下載:
import numpy as np from sklearn.datasets import load_wine from sklearn.model_selection import train_test_split from sklearn.svm import SVC dataset = load_wine() print('Data shapes:', dataset.data.shape, dataset.target.shape) X_train, X_test, y_train, y_test = train_test_split(dataset.data, dataset.target) classes = np.unique(y_train) print('Training data info:', X_train.shape, y_train.shape, classes) print('Test data shapes:', X_test.shape, y_test.shape) model = SVC() model.fit(X_train, y_train) pred = model.predict(X_test) print('Accuracy: {:.2f}%'.format(100*np.mean(pred==y_test)))在上述範例中,若你想要調整超參數,請參照文件;若要改為處理回歸問題,可以使用 sklearn.svm.SVR。