1.4. 支援向量機#
支援向量機(SVMs)是一組用於分類、迴歸和離群值偵測的監督式學習方法。
支援向量機的優點是
在高維空間中有效。
在維度數量大於樣本數量的情況下仍然有效。
在決策函數中使用訓練點的子集(稱為支持向量),因此也具有記憶體效率。
多功能:可以為決策函數指定不同的核函數。提供了常見的核函數,但也可以指定自定義核函數。
支援向量機的缺點包括
scikit-learn 中的支援向量機支援密集(numpy.ndarray
和可透過 numpy.asarray
轉換為該格式)和稀疏(任何 scipy.sparse
)樣本向量作為輸入。但是,要使用 SVM 對稀疏資料進行預測,必須先在此類資料上進行擬合。為了獲得最佳效能,請使用 C 順序的 numpy.ndarray
(密集)或 scipy.sparse.csr_matrix
(稀疏)且 dtype=float64
。
1.4.1. 分類#
SVC
、NuSVC
和 LinearSVC
是能夠在資料集上執行二元和多類別分類的類別。

SVC
和 NuSVC
是類似的方法,但接受的參數集略有不同,並且具有不同的數學公式(請參閱數學公式部分)。另一方面,LinearSVC
是針對線性核的情況,支援向量分類的另一種(更快的)實作。它也缺少 SVC
和 NuSVC
的某些屬性,例如 support_
。LinearSVC
使用 squared_hinge
損失,並且由於其在 liblinear
中的實作,它也會對截距進行正規化(如果考慮)。但是,可以透過仔細微調其 intercept_scaling
參數來減少此影響,這允許截距項與其他特徵相比具有不同的正規化行為。因此,分類結果和分數可能與其他兩個分類器不同。
與其他分類器一樣,SVC
、NuSVC
和 LinearSVC
接受兩個陣列作為輸入:一個形狀為 (n_samples, n_features)
的陣列 X
,其中包含訓練樣本,以及一個形狀為 (n_samples)
的類別標籤(字串或整數)陣列 y
。
>>> from sklearn import svm
>>> X = [[0, 0], [1, 1]]
>>> y = [0, 1]
>>> clf = svm.SVC()
>>> clf.fit(X, y)
SVC()
擬合後,模型可用於預測新值
>>> clf.predict([[2., 2.]])
array([1])
SVM 的決策函數(在數學公式中詳述)取決於訓練資料的某個子集,稱為支持向量。這些支持向量的某些屬性可以在屬性 support_vectors_
、support_
和 n_support_
中找到。
>>> # get support vectors
>>> clf.support_vectors_
array([[0., 0.],
[1., 1.]])
>>> # get indices of support vectors
>>> clf.support_
array([0, 1]...)
>>> # get number of support vectors for each class
>>> clf.n_support_
array([1, 1]...)
範例
1.4.1.1. 多類別分類#
SVC
和 NuSVC
實作了用於多類別分類的「一對一」方法。總共會建立 n_classes * (n_classes - 1) / 2
個分類器,並且每個分類器都會訓練來自兩個類別的資料。為了提供與其他分類器一致的介面,decision_function_shape
選項允許將「一對一」分類器的結果單調轉換為形狀為 (n_samples, n_classes)
的「一對多」決策函數,這是參數的預設設定(預設值 = ‘ovr’)。
>>> X = [[0], [1], [2], [3]]
>>> Y = [0, 1, 2, 3]
>>> clf = svm.SVC(decision_function_shape='ovo')
>>> clf.fit(X, Y)
SVC(decision_function_shape='ovo')
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 6 classes: 4*3/2 = 6
6
>>> clf.decision_function_shape = "ovr"
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes
4
另一方面,LinearSVC
實作「一對多」多類別策略,因此訓練 n_classes
個模型。
>>> lin_clf = svm.LinearSVC()
>>> lin_clf.fit(X, Y)
LinearSVC()
>>> dec = lin_clf.decision_function([[1]])
>>> dec.shape[1]
4
請參閱數學公式,以取得決策函數的完整描述。
多類別策略的詳細資訊#
請注意,LinearSVC
也實作了一種替代的多類別策略,也就是 Crammer 和 Singer 所提出的多類別 SVM [16],可透過使用選項 multi_class='crammer_singer'
來啟用。實務上,通常偏好使用一對其餘(one-vs-rest)分類,因為結果大多相似,但運行時間明顯較少。
對於「一對其餘」(one-vs-rest)的 LinearSVC
,屬性 coef_
和 intercept_
的形狀分別為 (n_classes, n_features)
和 (n_classes,)
。係數的每一列對應到 n_classes
個「一對其餘」分類器中的一個,截距也類似,順序依據「一」的類別。
對於「一對一」(one-vs-one)的 SVC
和 NuSVC
,屬性的佈局稍微複雜一些。對於線性核函數的情況,屬性 coef_
和 intercept_
的形狀分別為 (n_classes * (n_classes - 1) / 2, n_features)
和 (n_classes * (n_classes - 1) / 2)
。這與上面描述的 LinearSVC
的佈局類似,只是現在每一列都對應到一個二元分類器。從類別 0 到 n 的順序為「0 對 1」、「0 對 2」、…「0 對 n」、「1 對 2」、「1 對 3」、「1 對 n」、…「n-1 對 n」。
dual_coef_
的形狀為 (n_classes-1, n_SV)
,其佈局有點難以理解。這些列對應到 n_classes * (n_classes - 1) / 2
個「一對一」分類器中任何一個所涉及的支援向量。每個支援向量 v
在比較 v
的類別與另一個類別的 n_classes - 1
個分類器中都有一個對偶係數。請注意,這些對偶係數中的一些(但不是全部)可能為零。每列中的 n_classes - 1
個條目是這些對偶係數,依反對類別排序。
透過一個範例或許可以更清楚地說明:考慮一個三類別問題,其中類別 0 有三個支援向量 \(v^{0}_0, v^{1}_0, v^{2}_0\),類別 1 和 2 則分別有兩個支援向量 \(v^{0}_1, v^{1}_1\) 和 \(v^{0}_2, v^{1}_2\)。對於每個支援向量 \(v^{j}_i\),都有兩個對偶係數。讓我們將支援向量 \(v^{j}_i\) 在類別 \(i\) 和 \(k\) 之間的分類器中的係數稱為 \(\alpha^{j}_{i,k}\)。那麼 dual_coef_
看起來會像這樣
\(\alpha^{0}_{0,1}\) |
\(\alpha^{1}_{0,1}\) |
\(\alpha^{2}_{0,1}\) |
\(\alpha^{0}_{1,0}\) |
\(\alpha^{1}_{1,0}\) |
\(\alpha^{0}_{2,0}\) |
\(\alpha^{1}_{2,0}\) |
\(\alpha^{0}_{0,2}\) |
\(\alpha^{1}_{0,2}\) |
\(\alpha^{2}_{0,2}\) |
\(\alpha^{0}_{1,2}\) |
\(\alpha^{1}_{1,2}\) |
\(\alpha^{0}_{2,1}\) |
\(\alpha^{1}_{2,1}\) |
類別 0 的支援向量的係數 |
類別 1 的支援向量的係數 |
類別 2 的支援向量的係數 |
範例
1.4.1.2. 分數和機率#
SVC
和 NuSVC
的 decision_function
方法會針對每個樣本給出每個類別的分數(或在二元情況下,每個樣本一個分數)。當建構函式選項 probability
設定為 True
時,會啟用類別成員機率估計(來自方法 predict_proba
和 predict_log_proba
)。在二元情況下,機率會使用 Platt 縮放 [9] 來校準:對 SVM 的分數進行邏輯回歸,透過對訓練資料的額外交叉驗證進行擬合。在多類別情況下,則根據 [10] 進行擴展。
注意
相同的機率校準程序可透過 CalibratedClassifierCV
用於所有估計器(請參閱機率校準)。在 SVC
和 NuSVC
的情況下,此程序已內建於底層使用的 libsvm 中,因此它不依賴 scikit-learn 的 CalibratedClassifierCV
。
Platt 縮放中涉及的交叉驗證對於大型資料集來說是一項耗時的操作。此外,機率估計可能與分數不一致
分數的「argmax」可能不是機率的 argmax
在二元分類中,即使
predict_proba
的輸出小於 0.5,樣本仍可能被predict
標記為屬於正類別;類似地,即使predict_proba
的輸出大於 0.5,樣本也可能被標記為負類別。
Platt 的方法也已知存在理論問題。如果需要信心分數,但這些分數不必是機率,則建議將 probability=False
,並使用 decision_function
而不是 predict_proba
。
請注意,當 decision_function_shape='ovr'
且 n_classes > 2
時,與 decision_function
不同,predict
方法預設不會嘗試打破平局。您可以設定 break_ties=True
,使 predict
的輸出與 np.argmax(clf.decision_function(...), axis=1)
相同,否則將始終返回平局類別中的第一個類別;但請記住,這會帶來額外的計算成本。有關平局打破的範例,請參閱SVM 平局打破範例。
1.4.1.3. 不平衡問題#
在希望給予某些類別或某些個別樣本更多權重時,可以使用參數 class_weight
和 sample_weight
。
SVC
(但不包括 NuSVC
) 在 fit
方法中實作了 class_weight
參數。它是一個形式為 {class_label : value}
的字典,其中 value 是一個大於 0 的浮點數,將類別 class_label
的參數 C
設定為 C * value
。 下圖說明了不平衡問題的決策邊界,包含和不包含權重校正的情況。

SVC
、NuSVC
、SVR
、NuSVR
、LinearSVC
、LinearSVR
和 OneClassSVM
也透過 sample_weight
參數在 fit
方法中實作了個別樣本的權重。 與 class_weight
類似,這會將第 i 個樣本的參數 C
設定為 C * sample_weight[i]
,這會鼓勵分類器正確分類這些樣本。 下圖說明了樣本權重對決策邊界的影響。圓圈的大小與樣本權重成正比。

範例
1.4.2. 迴歸#
支持向量分類的方法可以擴展來解決迴歸問題。這種方法稱為支持向量迴歸。
支持向量分類(如上所述)產生的模型僅依賴於訓練數據的子集,因為建構模型的成本函數不在乎位於邊界之外的訓練點。類似地,支持向量迴歸產生的模型僅依賴於訓練數據的子集,因為成本函數會忽略預測值接近目標值的樣本。
支持向量迴歸有三種不同的實作方式:SVR
、NuSVR
和 LinearSVR
。LinearSVR
提供了比 SVR
更快的實作,但僅考慮線性核,而 NuSVR
實作的公式與 SVR
和 LinearSVR
略有不同。由於在 liblinear
中的實作,如果考慮截距,LinearSVR
也會對截距進行正規化。 然而,可以透過仔細調整其 intercept_scaling
參數來減少此效果,這使得截距項與其他特徵相比具有不同的正規化行為。 因此,分類結果和分數可能與其他兩個分類器不同。 有關更多詳細資訊,請參閱 實作細節。
與分類類別一樣,fit 方法將向量 X、y 作為參數,只是在這種情況下,y 預期具有浮點數值而不是整數值
>>> from sklearn import svm
>>> X = [[0, 0], [2, 2]]
>>> y = [0.5, 2.5]
>>> regr = svm.SVR()
>>> regr.fit(X, y)
SVR()
>>> regr.predict([[1, 1]])
array([1.5])
範例
1.4.3. 密度估計,新穎性偵測#
OneClassSVM
類別實作了用於離群值偵測的單類 SVM。
有關 OneClassSVM 的描述和用法,請參閱 新穎性和離群值偵測。
1.4.4. 複雜性#
支持向量機是強大的工具,但它們的計算和儲存需求會隨著訓練向量的數量快速增加。 SVM 的核心是一個二次規劃問題 (QP),它將支持向量與其餘訓練數據分開。 libsvm 的實作所使用的 QP 求解器介於 \(O(n_{features} \times n_{samples}^2)\) 和 \(O(n_{features} \times n_{samples}^3)\) 之間,具體取決於 libsvm 快取在實務中的使用效率(取決於資料集)。如果數據非常稀疏,則應將 \(n_{features}\) 替換為樣本向量中非零特徵的平均數量。
對於線性情況,LinearSVC
中 liblinear 實作所使用的演算法比其基於 libsvm 的 SVC
對應物有效得多,並且可以幾乎線性地擴展到數百萬個樣本和/或特徵。
1.4.5. 實用技巧#
避免資料複製:對於
SVC
、SVR
、NuSVC
和NuSVR
,如果傳遞給某些方法的數據不是 C 順序連續且雙精度,則會在呼叫底層 C 實作之前複製它。您可以檢查給定的 numpy 陣列是否為 C 連續,方法是檢查其flags
屬性。對於
LinearSVC
(和LogisticRegression
),任何以 numpy 陣列形式傳遞的輸入都將被複製並轉換為 liblinear 內部稀疏數據表示形式(雙精度浮點數和非零元件的 int32 索引)。 如果您想在不複製密集 numpy C 連續雙精度陣列作為輸入的情況下擬合大規模線性分類器,我們建議改用SGDClassifier
類別。 可以將目標函數配置為與LinearSVC
模型幾乎相同。核心快取大小:對於
SVC
、SVR
、NuSVC
和NuSVR
,核心快取的大小對於較大的問題的執行時間有很大的影響。 如果您有足夠的 RAM 可用,建議將cache_size
設定為高於預設值 200(MB) 的值,例如 500(MB) 或 1000(MB)。設定 C:預設情況下,
C
為1
,這是一個合理的預設選擇。 如果您有很多雜訊觀測值,您應該降低它:降低 C 對應於更多的正規化。LinearSVC
和LinearSVR
在C
變大時對其不太敏感,並且預測結果在達到某個閾值後會停止改善。 同時,較大的C
值將需要更多的訓練時間,有時甚至長達 10 倍,如 [11] 所示。支援向量機演算法不是尺度不變的,因此強烈建議您縮放您的數據。 例如,將輸入向量 X 上的每個屬性縮放到 [0,1] 或 [-1,+1],或將其標準化為具有平均值 0 和變異數 1。 請注意,必須將相同的縮放應用於測試向量才能獲得有意義的結果。 這可以通過使用
Pipeline
輕鬆完成>>> from sklearn.pipeline import make_pipeline >>> from sklearn.preprocessing import StandardScaler >>> from sklearn.svm import SVC >>> clf = make_pipeline(StandardScaler(), SVC())
有關縮放和標準化的更多詳細信息,請參閱 預處理數據 部分。
關於
shrinking
參數,引用 [12]:「我們發現,如果迭代次數很多,則收縮可以縮短訓練時間。但是,如果我們鬆散地解決最佳化問題(例如,通過使用較大的停止容忍度),則不使用收縮的程式碼可能會更快」。NuSVC
/OneClassSVM
/NuSVR
中的參數nu
近似於訓練錯誤和支持向量的分數。在
SVC
中,如果數據不平衡(例如,許多正樣本和少數負樣本),請設定class_weight='balanced'
和/或嘗試不同的懲罰參數C
。底層實作的隨機性:
SVC
和NuSVC
的底層實作僅使用隨機數產生器來混洗數據以進行機率估計(當probability
設定為True
時)。 此隨機性可以使用random_state
參數控制。 如果probability
設定為False
,則這些估計器不是隨機的,並且random_state
對結果沒有影響。OneClassSVM
的底層實作與SVC
和NuSVC
的實作類似。 由於沒有為OneClassSVM
提供機率估計,因此它不是隨機的。LinearSVC
的底層實作使用隨機數產生器,以在使用對偶座標下降法擬合模型時選擇特徵(即當dual
設定為True
時)。 因此,對於相同的輸入數據,結果略有不同並不罕見。 如果發生這種情況,請嘗試使用較小的tol
參數。 此隨機性也可以使用random_state
參數控制。 當dual
設定為False
時,LinearSVC
的底層實作不是隨機的,並且random_state
對結果沒有影響。使用
LinearSVC(penalty='l1', dual=False)
提供的 L1 懲罰會產生稀疏解,也就是說,只有一部分特徵權重不為零,並有助於決策函數。 增加C
會產生更複雜的模型(選擇更多特徵)。 使用l1_min_c
可以計算出產生「空」模型(所有權重等於零)的C
值。
1.4.6. 核心函數#
核心函數 可以是以下任何一種
線性:\(\langle x, x'\rangle\)。
多項式:\((\gamma \langle x, x'\rangle + r)^d\),其中 \(d\) 由參數
degree
指定,\(r\) 由coef0
指定。rbf:\(\exp(-\gamma \|x-x'\|^2)\),其中 \(\gamma\) 由參數
gamma
指定,必須大於 0。sigmoid \(\tanh(\gamma \langle x,x'\rangle + r)\),其中 \(r\) 由
coef0
指定。
不同的核心由 kernel
參數指定
>>> linear_svc = svm.SVC(kernel='linear')
>>> linear_svc.kernel
'linear'
>>> rbf_svc = svm.SVC(kernel='rbf')
>>> rbf_svc.kernel
'rbf'
另請參閱 核心近似 以尋找使用 RBF 核心的解決方案,該解決方案速度更快且可擴展性更高。
1.4.6.1. RBF 核心的參數#
當使用徑向基函數 (RBF) 核心訓練 SVM 時,必須考慮兩個參數:C
和 gamma
。 參數 C
是所有 SVM 核心的通用參數,它在訓練範例的錯誤分類和決策面的簡潔性之間進行權衡。 較低的 C
使決策面平滑,而較高的 C
旨在正確分類所有訓練範例。 gamma
定義單個訓練範例的影響程度。 gamma
越大,其他範例必須越接近才能受到影響。
正確選擇 C
和 gamma
對於 SVM 的效能至關重要。 建議使用 GridSearchCV
,其中 C
和 gamma
以指數方式間隔開,以選擇好的值。
範例
1.4.6.2. 自訂核心#
您可以通過將核心作為 Python 函數提供或通過預先計算格拉姆矩陣來定義自己的核心。
使用自訂核心的分類器與任何其他分類器的行為方式相同,只是
欄位
support_vectors_
現在為空,只有支持向量的索引存儲在support_
中為了將來參考,會儲存
fit()
方法中第一個引數的參考(而非副本)。 如果該陣列在fit()
和predict()
的使用之間發生變化,您將會得到意想不到的結果。
使用 Python 函數作為核心#
您可以通過將函數傳遞給 kernel
參數來使用自己定義的核心。
您的核心函式必須接收兩個形狀為 (n_samples_1, n_features)
和 (n_samples_2, n_features)
的矩陣作為引數,並返回一個形狀為 (n_samples_1, n_samples_2)
的核心矩陣。
以下程式碼定義了一個線性核心,並建立一個將使用該核心的分類器實例。
>>> import numpy as np
>>> from sklearn import svm
>>> def my_kernel(X, Y):
... return np.dot(X, Y.T)
...
>>> clf = svm.SVC(kernel=my_kernel)
使用葛蘭姆矩陣#
您可以透過使用 kernel='precomputed'
選項來傳遞預先計算的核心。然後您應該將葛蘭姆矩陣傳遞給 fit
和 predict
方法,而不是傳遞 X。必須提供所有訓練向量和測試向量之間的核心值。
>>> import numpy as np
>>> from sklearn.datasets import make_classification
>>> from sklearn.model_selection import train_test_split
>>> from sklearn import svm
>>> X, y = make_classification(n_samples=10, random_state=0)
>>> X_train , X_test , y_train, y_test = train_test_split(X, y, random_state=0)
>>> clf = svm.SVC(kernel='precomputed')
>>> # linear kernel computation
>>> gram_train = np.dot(X_train, X_train.T)
>>> clf.fit(gram_train, y_train)
SVC(kernel='precomputed')
>>> # predict on training examples
>>> gram_test = np.dot(X_test, X_train.T)
>>> clf.predict(gram_test)
array([0, 1, 0])
範例
1.4.7. 數學公式#
支援向量機在高維或無限維空間中構建一個超平面或一組超平面,這些超平面可用於分類、迴歸或其他任務。直觀地說,好的分離是透過與任何類別的最近訓練資料點具有最大距離的超平面(所謂的函數邊界)來實現的,因為一般來說,邊界越大,分類器的泛化誤差越低。下圖顯示了線性可分離問題的決策函數,邊界上有三個樣本,稱為「支援向量」。

一般來說,當問題不是線性可分離時,支援向量是邊界內的樣本。
我們推薦 [13] 和 [14] 作為 SVM 理論和實務的良好參考資料。
1.4.7.1. SVC#
給定兩個類別中的訓練向量 \(x_i \in \mathbb{R}^p\),i=1,…, n,以及向量 \(y \in \{1, -1\}^n\),我們的目標是找到 \(w \in \mathbb{R}^p\) 和 \(b \in \mathbb{R}\),使得 \(\text{sign} (w^T\phi(x) + b)\) 給出的預測對於大多數樣本都是正確的。
SVC 解決以下原始問題
直觀地說,我們試圖最大化邊界(透過最小化 \(||w||^2 = w^Tw\)),同時在樣本被錯誤分類或位於邊界內時產生懲罰。理想情況下,\(y_i (w^T \phi (x_i) + b)\) 的值對於所有樣本都應該 \(\geq 1\),這表示完美預測。但是,問題通常無法完全用超平面分離,因此我們允許某些樣本與其正確的邊界相距 \(\zeta_i\) 。懲罰項 C
控制此懲罰的強度,因此充當反向正規化參數(請參閱下方的註解)。
原始問題的對偶問題是
其中 \(e\) 是所有 1 的向量,而 \(Q\) 是 \(n\) 乘以 \(n\) 的半正定矩陣,\(Q_{ij} \equiv y_i y_j K(x_i, x_j)\),其中 \(K(x_i, x_j) = \phi (x_i)^T \phi (x_j)\) 是核心。項 \(\alpha_i\) 稱為對偶係數,它們以 \(C\) 為上限。此對偶表示突顯了以下事實:訓練向量透過函數 \(\phi\) 隱式映射到較高(可能是無限)維度的空間中:請參閱 核心技巧。
一旦解決了最佳化問題,決策函數 對於給定樣本 \(x\) 的輸出變成
並且預測的類別對應於其符號。我們只需要對支援向量(即位於邊界內的樣本)進行求和,因為其他樣本的對偶係數 \(\alpha_i\) 為零。
這些參數可以透過屬性 dual_coef_
(包含乘積 \(y_i \alpha_i\))、support_vectors_
(包含支援向量)和 intercept_
(包含獨立項 \(b\))來存取。
注意
雖然從 libsvm 和 liblinear 衍生而來的 SVM 模型使用 C
作為正規化參數,但大多數其他估計器使用 alpha
。兩個模型的正規化量之間的精確等效性取決於模型最佳化的精確目標函數。例如,當使用的估計器為 Ridge
迴歸時,它們之間的關係表示為 \(C = \frac{1}{alpha}\)。
LinearSVC#
1.4.7.2. SVR#
給定訓練向量 \(x_i \in \mathbb{R}^p\),i=1,…, n,以及向量 \(y \in \mathbb{R}^n\),\(\varepsilon\)-SVR 解決以下原始問題
在這裡,我們懲罰其預測與其真實目標相距至少 \(\varepsilon\) 的樣本。這些樣本會根據其預測是位於 \(\varepsilon\) 管之上還是之下,來以 \(\zeta_i\) 或 \(\zeta_i^*\) 的方式懲罰目標。
對偶問題是
其中 \(e\) 是全為 1 的向量,\(Q\) 是一個 \(n\) 乘 \(n\) 的半正定矩陣,\(Q_{ij} \equiv K(x_i, x_j) = \phi (x_i)^T \phi (x_j)\) 是核函數。這裡訓練向量透過函數 \(\phi\) 被隱式地映射到更高維度(可能是無限維)的空間中。
預測公式為
這些參數可以透過屬性 dual_coef_
取得,其中儲存了差值 \(\alpha_i - \alpha_i^*\);透過屬性 support_vectors_
取得支持向量;以及透過屬性 intercept_
取得獨立項 \(b\)。
1.4.8. 實作細節#
在內部,我們使用 libsvm [12] 和 liblinear [11] 來處理所有的計算。這些函式庫使用 C 和 Cython 包裝。關於實作的描述和所使用演算法的細節,請參考它們各自的論文。
參考文獻