1.4 支持向量機?
支持向量機(SVMs)是一種用于分類、回歸和異常檢測的有監督學習方法。
支持向量機的優點有:
在高維空間里也非常有效 對于數據維度遠高于數據樣本量的情況也有效 在決策函數中使用訓練集的子集(也稱為支持向量),因此也是內存高效利用的。 通用性:可以為決策函數指定不同的核函數。已經提供了通用核函數,但也可以指定自定義核函數。
支持向量機的缺點包括:
scikit-learn中的支持向量機-同時支持稠密(dense)(numpy.ndarray和可通過numpy.asarray轉換)和稀疏(sparse)(任意的scipy.sparse
)樣本向量作為輸入。然而,若要使用支持向量機對稀疏數據進行預測,則必須在這樣的數據上進行擬合。要獲得最佳性能, 可使用C_ordered的numpy.ndarray對象(稠密 dense),或者使用數據類型是dtype=float64
的 scipy.sparse.csr_matrix
對象(稀疏 sparse)。
1.4.1 分類
SVC、NuSVC和LinearSVC能夠在數據集上執行多分類。
SVC 和 NuSVC是相似的方法, 但是接受的參數的集合稍微不同并且有不同的數學公式(看這部分 數學公式)。另一方面,
LinearSVC
是使用線性核的支持向量分類的的另一種實現, 適用于線性核的情況。注意,LinearSVC不接受關鍵字內核,因為假設是線性的。它還缺少 SVC 和 NuSVC 的一些屬性,如Support_
。
和其他分類器一樣, 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])
SVMs的決策函數依賴于訓練數據的某些子集,被稱為支持向量。這些支持向量的一些特性可以在屬性 support_vectors_
, support_
和 n_support
中找到:
>>> # 獲取支持向量
>>> clf.support_vectors_
array([[0., 0.],
[1., 1.]])
>>> # 獲取支持向量的索引
>>> clf.support_
array([0, 1]...)
>>> # 獲取每個類中支持向量的個數
>>> clf.n_support_
array([1, 1]...)
Examples:
1.4.1.1 多分類
SVC和NuSVC 為實現多分類的使用了“一對一”的方法(Knerr等人,1990年)。如果n_class是類別的數量,那么 n_class * (n_class - 1) / 2
分類器會被重構,每個分類器都是使用兩類數據進行訓練。為了提供與其他分類器一致的接口, decision_function_shape
選項允許將“一對一”分類器的結果轉換為形狀是(n_samples, n_classes)
的決策函數。
>>> 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] # 4 classes: 4*3/2 = 6
6
>>> clf.decision_function_shape = "ovr"
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes
4
另一方面,LinearSVC實現了“one-vs-the-rest”的多類策略,從而訓練了n_class(n 類別)個模型。如果只有兩個類,則只訓練一個模型:
>>> lin_clf = svm.LinearSVC()
>>> lin_clf.fit(X, Y)
LinearSVC()
>>> dec = lin_clf.decision_function([[1]])
>>> dec.shape[1]
4
有關決策函數的完整描述,請參見數學公式 Mathematical formulation。
請注意,LinearSVC還實現了另一種多分類策略,即由Crammer和Singerr [16]提出的所謂的多分類支持向量機,它使用的選項是multiclass=‘sammer_singer’
。此方法是一致的,對于one-vs-rest 分類準確度一般是不夠的。在實踐中,one-vs-rest分類通常是首選的,因為結果大多是差不多的,但運行時間要少得多。
對于“one-vs-rest”的LinearSVC, 它的屬性coef_
and intercept_
分別擁有的形狀是 [n_class, n_features]
和[n_class]
。系數的每一行對應于n_class這么多個“one-vs-rest”分類器中的一個, 在按照 'one'類 的排序下, 和intercepts相似。
在“one-vs-one” 的 SVC
情況下, 屬性的布局更復雜一些。在具有線性核的情況下, 屬性 coef_
和intercept_
分別具有[n_class * (n_class - 1) / 2, n_features]
和 [n_class * (n_class - 1) / 2]
的形狀。這與上面描述的LinearSVC的布局類似,每一行現在對應于二分類器。0到n的類排序是 “0 vs 1”, “0 vs 2” , … “0 vs n”, “1 vs 2”, “1 vs 3”, “1 vs n”, . . . “n-1 vs n”。
dual_coef_
的形狀是 [n_class-1, n_SV]
, 有點難以理解這個布局。它的列是對應 n_class * (n_class - 1) / 2
這么多個 “one-vs-one”分類器中的任意一個。每個支持向量都使用在n_class-1個分類器中。
可以通過一個例子更清楚地說明這一點:
考慮一個三分類的問題, 0類有三個支持向量, 并且1類和2類分別有兩個支持向量和。對于每一個支持向量來說, 都有兩個對偶系數。我們稱類和中支持向量的系數為, 對偶系數dual_coef_
如下:

示例 |
---|
在iris數據集中繪制不同的SVM分類器 |
1.4.1.2 分數和概率
SVC 和 NuSVC 的decision_function
決策函數給出了每個樣本的每類分數(或者是一個二分類情況下給每一個樣本一個簡單的分數), 當把參數probability
設置為True
的時候, 就會啟用類成員概率估計(可從方法predict_proba
和predict_log_proba
獲得)。在二分類情況下, 概率用Platt scaling進行標準化 [9]:對支持向量機的分數進行Logistic回歸,通過對訓練數據的額外交叉驗證來擬合。在多類的情況下,這會擴展為per [10]。
注意:所有的估計器都可以通過
CalibratedClassifierCV
(看 Probability calibration)來使用相同的概率校準過程。在SVC和NuSVC的情況下,在底層中是使用libsvm中構建的,因此它不依賴Scikit-Learning的類CalibratedClassifierCV
。
Platt校準所涉及的交叉驗證對于大型數據集來說是一項昂貴的操作。此外,概率估計可能與分數不一致:
分數的 “argmax” 可能不是概率的 “argmax” 在二分類中,即使 predict_proba
的輸出小于0.5,樣本也可以被預測為屬于正類;同樣,即使predict_proba
的輸出大于0.5,它也可以被標記為負類。
Platt這個方法也有理論上的問題。如果需要置信度分數,但這些不一定是概率,那么最好設置probability=False
,使用decision_function
來代替 predict_proba
。
請注意,當DecisionFunctionShape=‘ovr’
和n_class>2
時,與decision_function
不同的是,預測方法在默認情況下不會試圖中斷聯系。您可以為predict
的輸出設置break_ties=True
,使其與np.argmax(clf.Decisionfunction(...),axis=1)
相同,否則始終會返回綁定類中的第一個類;但是要記住,它是有計算成本的。參看 支持向量機的練習。
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
是在fit方法中通過參數 sample_weight
對單個樣本的權重。類似于class_weight
, 這將把第i個樣例的參數c
設置為c * sample_weight[i]
, 這將鼓勵分類器正確的獲取這些樣本。下圖說明了樣本加權對決策邊界的影響。圓圈的大小與樣本權重成正比:

示例 |
---|
支持向量機:不平衡類的分離超平面 加權樣本 |
1.4.2 回歸
支持向量分類方法可以推廣到解決回歸問題。這種方法稱為支持向量回歸。
支持向量分類(如上所述)生成的模型僅依賴于訓練數據的一個子集,因為構建模型的成本函數(cost funtion)并不關心超出邊際(margin)的訓練點。類似地,支持向量回歸模型僅依賴于訓練數據的一個子集,因為成本函數忽略了預測接近目標對象的樣本。
支持向量回歸有三種不同的實現方式:SVR、NuSVR和LinearSVR。LinearSVR提供了比SVR更快的實現,但只考慮了線性內核,而NuSVR實現的方式與SVR和LinearSVR略有不同。有關更多詳細信息,詳細信息請參見Implementation details。
與分類的類一樣,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])
示例 |
---|
基于線性和非線性核的支持向量回歸(SVR) |
1.4.3 密度估計,異常檢測
類OneClassSVM實現了一種用于離群點檢測的單類支持向量機.
關于OneClassSVM的描述與使用, 請看 Novelty and Outlier Detection。
1.4.4 復雜度
支持向量機是一種強大的工具,但是它們的計算和存儲需求隨著訓練向量的增加而迅速增加。支持向量機的核心是二次規劃問題(QP),它將支持向量從其余的訓練數據中分離出來。在實踐中(數據集相關), 會根據 libsvm 的緩存的效率,在和之間基于 libsvm 的縮放操作才會調用這個 QP 解析器。如果數據是非常稀疏的,則應該用樣本向量中非零特征的平均數量來替換 。
對于線性情況,通過 liblinear實現的 LinearSVC
算法比基于 libsvm的SVC
算法要高效得多,并且可以線性地擴展到數百萬個樣本和/或特征。
1.4.5 實用技巧
避免數據復制:對于SVC、SVR、NuSVC和NuSVR,如果傳遞給某些方法的數據不是行優先級(C-ordered)的和雙重精度的,則在調用底層C實現之前將復制它。您可以通過檢查給定的numpy數組的
flags
屬性來檢查它是否是行優先級的。對于
LinearSVC
(和LogisticRegression
),作為numpy數組傳遞的任何輸入都將被復制并轉換為 liblinear 庫內部稀疏數據表示(雙精度浮點數和非零部分的int 32索引)。如果您想在擬合一個大型的線性分類器, 但是又不打算以一個稠密的行優先級的雙精度數組作為輸入, 那我們建議您使用SGDClassifier
類來代替。目標函數可以配置為幾乎與LinearSVC
模型相同。內核緩存大小:對于SVC、SVR、NuSVC和
NuSVR
,內核緩存的大小對大規模問題的運行時間有很大的影響。如果您有足夠的可用RAM,建議將cache_size
設置為比默認值200(MB)更高的值,例如500(MB)或1000(MB)。設置C:
C
默認為1
,這是一個合理的默認選擇。如果觀測數據有大量的噪聲,你應該調小它:調小C對應于增強的正則化。當C值較較大時,
LinearSVC
和LinearSVR
對C不太敏感,預測結果在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())
? 有關縮放和標準化的更多詳細信息,請參閱 Preprocessing data一節。
對于參數
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(loss='l2', penalty='l1', dual=False)
使用L1正則化得到一個稀疏解,即有一部分特征權重不是0,并且對決策函數有貢獻。增加C
將生成一個更復雜的模型(更多的特征被選擇)。可以使用l1_min_c
去計算C
值, 這個C
值會產生一個'null'模型(所有的權重都是0)。
1.4.6 核函數
核函數可以是以下任意一個:
線性核(linear):
多項式核(polynomial): , 其中是有參數
degree
指定, 是由coef0
指定。徑向基核rbf: , 其中是有參數
gamma
指定, 必須大于0。sigmoid核:, 其中是由參數
coef0
指定。
參數kernel
能夠指定不同的核:
>>> linear_svc = svm.SVC(kernel='linear')
>>> linear_svc.kernel
'linear'
>>> rbf_svc = svm.SVC(kernel='rbf')
>>> rbf_svc.kernel
'rbf'
1.4.6.1 RBF核的參數
當使用徑向基(RBF)核訓練支持向量機時,必須考慮兩個參數: C
和 gamma
。參數C, 是所有支持向量機核共有參數, 決策面簡單時,能夠將分類錯誤的訓練樣本改變。一個低的C
能夠使決策面平滑, 與此同時, 一個高的C
旨在能夠正確類所有訓練樣本。gamma
定義了單個訓練樣本有多大的影響力。一個大的gamma
, 其附近的其他樣本一定會被影響到。
正確選擇C
和gamma
是支持向量機性能的關鍵。建議使用sklearn.model_selection.GridSearchCV
與 C
和gamma
以指數間隔來選取一個好的數值。
示例:
1.4.6.2 自定義核
你可以定義自己的核, 可以通過給一個 python 函數作為核,或者通過計算一個Gram矩陣。
帶有自定義內核的分類器的行為方式與任何其他分類器相同, 以下除外:
support_vectors_
現在是為空的,只有支持向量的索引存儲在支持support_
中。fit()
方法中的第一個參數的引用(不是一個副本)為了未來的引用會被儲存起來。如果那個數組在fit()
和predict()
之間的操作過程中被改變了, 你將得到意料之外的結果。
1.4.6.2.1 使用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)
示例 |
---|
使用自定義核的支持向量機 |
1.4.6.2.2 使用Gram矩陣
你可以通過使用kernel='precomputed'
來預先計算核。然后給fit()
和predict()
方法傳遞Gram矩陣而不是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')
>>> # 線性核計算
>>> gram_train = np.dot(X_train, X_train.T)
>>> clf.fit(gram_train, y_train)
SVC(kernel='precomputed')
>>> # 在訓練樣本上做預測
>>> gram_test = np.dot(X_test, X_train.T)
>>> clf.predict(gram_test)
array([0, 1, 0])
1.4.7 數學公式
支持向量機在高維或無限維空間中構造超平面或超平面集,可用于分類、回歸或其他任務。從直覺上看, 一個好的分離的實現方案是, 超平面距離訓練數據中任意一類的的樣本的距離達到最大(也被稱為 函數間距), 因為通常來講, 間距越大, 分類器的泛化誤差就越小。下圖顯示了一個線性可分問題的決策函數,邊界有三個樣本,稱為“支持向量”(“support vectors”):
通常,當問題不是線性可分時,支持向量是邊界內的樣本。
我們推薦[13]和[14]作為支持向量機的理論和實際應用的良好參考。
1.4.7.1 SVC
給定訓練向量, =1, ..., n, 在兩個類中, 和一個向量, 我們的目標是找到 和 對于給定的預測對大多數樣本判斷正確。
SVC主要能解決以下問題:
直覺上講, 我們要最大化間距(margin)(等價于最小化), 同時對于分類錯誤的樣本或者在間距邊界內部的樣本會招致懲罰。理想情況下, 對于所有樣本來說的值都應該1, 這會是一個完美的預測。但是問題通常并不總是與超平面完全分離,所以我們允許一些樣本離它們正確的邊緣邊界有一個距離。懲罰項C
控制著這個懲罰的強度,因此,作為一個正則化參數的倒數(見下面的注釋)。
對原始問題的對偶問題是:
其中是一個全1的向量, 是一個 x 的半正定矩陣, , 其中是內核。稱為對偶系數,它們的上界是。這種對偶形示強調了這樣一個事實,即訓練向量可通過核函數?隱式映射到一個更高(可能無限)維的空間:參見內核技巧。
一旦優化問題得到解決,給對于給定樣本的決策函數的輸出將變成:
預測的類對應于它的符號。我們只需要在支持向量(即位于邊緣范圍內的樣本)上求和,因為對于其他樣本,對偶系數為零。
可以通過以下屬性訪問這些參數: dual_coef_
可以訪問, support_vectors_
可以訪問支持向量intercept_
可以訪問截距項。
注意:
從libsvm和 liblinear 導出的支持向量機模型使用C作為正則化參數,而大多數其他估計器使用alpha。兩個模型正則化量之間的嚴謹等價取決于模型所優化的特定的目標函數。比如:當所使用的估計器是
sklearn.linemodel.Ridge
回歸時, 它們之間的關系是:
1.4.7.2 LinearSVC
原始問題可以等價地表示為:
其中, 我們用到了合頁損失 (hinge loss)。這是LinearSVC直接優化的形式,但與對偶形式不同的是,這種形式不涉及樣本之間的內積,因此不能應用著名的內核技巧。這就是為什么LinearSVC只支持線性核(?是標識函數)的原因。
1.4.7.3 NuSVC
-SVC公式[15]是C-SVC的重新參數化,因此在數學上是等價的。
我們引入了一個新的參數(來代替C)來控制支持向量的數目和邊際誤差:∈(0,1)是
我們引入了一個新的參數ν(而不是C)來控制支持向量和邊緣誤差的數目:ν是邊際誤差分數的上界和支持向量分數的下界。邊際誤差對應于位于邊際邊界錯誤一側的樣本:它要么被錯誤分類,要么被正確地分類,但不位于邊際之外。
1.4.7.4 SVR
給定訓練向量, =1, ...,n, 并且向量, 能夠解決以下主要問題:
在這里, 我們懲罰的樣本是那些預測值偏離他們真實目標至少的樣本。這些樣本的懲罰是通過或者, 這取決于他們的預測值是在ε上方還是下方。
對偶問題是:
其中是所有為1的向量, 是一個 x 的半正定矩陣,是核, 這里,訓練向量通過函數?隱式映射到一個更高(可能無限)的維空間。
它的預測是:
可以通過下面的屬性訪問這些參數:dual_coef_
能夠訪問不同的, support_vectors_
可以訪問支持向量, intercept_
可以訪問截距項。
1.4.7.5 LinearSVR
原始問題可以等價地表述為:
這里我們使用了epsilon-insensitive損失, 即小于ε的誤差被忽略了。這是LinearSVR直接優化的方式。
1.4.8 實現細節
在內部,我們使用 libsvm [12] 和 liblinear [11]來處理所有的計算。這些庫使用C和Cython包裝。有關所使用算法的實現和詳細信息,請參閱各自的論文。
參考:
[9] Platt “Probabilistic outputs for SVMs and comparisons to regularized likelihood methods”.
[10] Wu, Lin and Weng, “Probability estimates for multi-class classification by pairwise coupling”, JMLR 5:975-1005, 2004.
11(1, 2) Fan, Rong-En, et al., “LIBLINEAR: A library for large linear classification.”, Journal of machine learning research 9.Aug (2008): 1871-1874.
12(1,2) Chang and Lin, LIBSVM: A Library for Support Vector Machines.
[13] Bishop, Pattern recognition and machine learning, chapter 7 Sparse Kernel Machines
[14] “A Tutorial on Support Vector Regression”, Alex J. Smola, Bernhard Sch?lkopf - Statistics and Computing archive Volume 14 Issue 3, August 2004, p. 199-222.
[15] Sch?lkopf et. al New Support Vector Algorithms
[16] Crammer and Singer On the Algorithmic Implementation ofMulticlass Kernel-based Vector Machines, JMLR 2001.