Python數模筆記-Sklearn(5)支援向量機

youcans發表於2021-05-16

支援向量機(Support vector machine, SVM)是一種二分類模型,是按有監督學習方式對資料進行二元分類的廣義線性分類器。

支援向量機經常應用於模式識別問題,如人像識別、文字分類、手寫識別、生物資訊識別等領域。


1、支援向量機(SVM)的基本原理

SVM 的基本模型是特徵空間上間隔最大的線性分類器,還可以通過核函式方法擴充套件為非線性分類器。

SVM 的分割策略是間隔最大化,通過尋求結構化風險最小來提高模型的泛化能力,實現經驗風險和置信範圍的最小化。SVM 可以轉化為求解凸二次規劃的問題,學習演算法就是求解凸二次規劃的最優化演算法。
=== 關注 Youcans,分享更多原創系列 https://www.cnblogs.com/youcans/ ===

1.1 SVM 線性分類器

線性可分,在二維平面上是指可以用一條直線將兩個點集完全分開,在三維空間上是指可以用一個平面將兩個點集完全分開,擴充套件到多維空間上就是可以用一個超平面完全分割兩個點集。

對於線性可分問題,不是僅存在一個超平面可以完全分割兩個點集,而是存在無窮多個完全可分的超平面。顯然。可以找到兩個這樣的超平面:(1)完全分割兩個點集;(1)兩者相互平行;(2)兩者距離最大(圖中的兩條虛線),這兩個超平面上的樣本點稱為支援向量

樣本集中的樣本被分為兩類,SVM 學習演算法就是尋找最大間隔超平面(maximum-margin hyperplane),使樣本集的兩類資料點以儘可能寬的間隔被超平面分開,其特徵是:(1)兩類樣本被分割在超平面的兩側;(2)兩側距離超平面最近的樣本點到超平面的距離最大。顯然,最大間隔超平面就是上述兩個支援向量超平面的均值。

順便說一句,感知機()就是採用錯誤分類最小的策略求分離超平面,有無窮多個解;線性可分支援向量機以間隔最大化求解最優分離超平面,解是唯一的。

Python數模筆記-Sklearn(5)支援向量機

超平面可以用線性方程來描述:

\[w^Tx + b = 0 \]

尋找最大間隔超平面,可以轉化為凸二次規劃的最優化問題:

\[min\;\frac{1}{2}||W||^2,\quad s.t.\; y_i(w^Tx + b) \geq 1 \]

SKlearn 的 SVM 模組有很多方法,就是針對凸二次規劃問題的最優化的不同演算法,將在後文中介紹。

1.2 從線性可分到線性不可分

除了線性可分,不就是線性不可分嗎?沒錯,但世界是複雜的,也是豐富多彩的,不一定是非黑即白的。

首先,一個線性不可分問題,但是可以用非線性曲面分割,是非線性可分的問題,這很好理解。其次,一個線性不可分問題,也可能是近似線性可分的。什麼是近似線性可分呢?這就需要先說說硬間隔和軟間隔。

間隔(margin)是指樣本點到超平面的距離。硬間隔(hard margin)是指對給定的樣本資料集中所有的樣本都能正確分類。

對於線性不可分的樣本集,無法滿足線性可分支援向量機的不等式約束,也就是不存在對所有樣本都能正確分類的超平面。這種情況可能是因為問題本身是非線性的,也可能問題是線性可分的,但個別樣本點標記錯誤或存在誤差而導致樣本集線性不可分。

因此,我們可以允許對少量的樣本分類錯誤,容忍特異點的存在,而對於去除特異點的樣本集是線性可分的,這時稱為軟間隔(soft margin)。

Python數模筆記-Sklearn(5)支援向量機

在凸二次規劃問題中引入損失函式和鬆弛變數 \xi,目標函式為:

\[min\;\frac{1}{2}w^Tw+C\sum_{i=1}^m \xi_i,\\ s.t.\; y_i(w^Tx_i + b) \geq 1-\xi \\ \xi_i \geq 0 \]

目標函式包括兩部分,一部分是樣本點到間隔的距離,另一部分是錯誤分類的損失函式,C 是懲罰係數。C 值越大,對錯誤分類的懲罰項越強,說明要求分類的準確性較高;C 值越小,對錯誤分類的懲罰項越弱,說明要求間隔比較大,而對分類錯誤比較寬容。

1.3 非線性可分

有些線性不可分的問題並不是個別樣本的誤差或錯誤,而是由於問題本身是非線性的,這時採用軟間隔方法也不能有效地分割。容易想到,如果不能用平面分割樣本集,能不能用曲面分割樣本集呢?基於核函式的支援向量機,就是使用對映函式將一類非線性可分問題從原始的特徵空間對映到更高維的特徵空間,轉化為高維特徵空間的線性可分問題。

Python數模筆記-Sklearn(5)支援向量機

通過對映函式 ϕi(x) 構造的超曲面可以用非線性方程來描述:

\[w^T*z+b=0,z=\phi_i(x) \]

對映函式 ϕi(x) 對應的核函式 K(x,z) 是一個對稱的半正定矩陣:

\[K(x,z)=\phi(x)\cdot\phi(z) \]

常用的核函式有:線性核函式(Linear),多項式核函式(polynomial)、高斯核函式(RBF)、拉普拉斯核函式(Laplacian)和 Sigmoid核函式(Sigmoid)。


2、線性可分支援向量機(LinearSVC)

SKlearn 中的支援向量機模組是 sklearn.svm,包括分類演算法和迴歸演算法。本文介紹分類演算法,包括 SVC、NuSVC 和 LinearSVC 三個類。

2.1 LinearSVC 類使用說明

LinearSVC 是線性分類支援向量機,不能使用核函式方法處理非線性分類問題。
LinearSVC 演算法與 SVC 演算法在使用 'linear' 核函式時的結果基本一致,但 LinearSVC 是基於 liblinear 庫實現,計算速度更快。
LinearSVC 有多種懲罰引數和損失函式可供選擇,可以應用於大樣本集(大於10000)訓練。
sklearn.svm.LinearSVC 類是線性分類支援向量機的具體實現,官網介紹詳見:https://scikit-learn.org/stable/modules/generated/sklearn.svm.LinearSVC.html#sklearn.svm.LinearSVC

sklearn.svm.LinearSVC()

class sklearn.svm.LinearSVC(penalty='l2', loss='squared_hinge', *, dual=True, tol=0.0001, C=1.0, multi_class='ovr', fit_intercept=True, intercept_scaling=1, class_weight=None, verbose=0, random_state=None, max_iter=1000)

LinearSVC() 類的主要引數:

  • C:float, default=1.0  懲罰係數,必須大於0,預設值 1.0。用於設定對錯誤分類的懲罰強度,對於完全線性可分的硬間隔問題不需要設定。
  • fit_intercept : boolean, optional (default=True)  是否計算截距,預設為 True。如果資料均值為 0,則可以選擇 False 不計算截距。
  • multi_class : string, ‘ovr’ or ‘crammer_singer’ (default=’ovr’)  多類別分類策略開關。對於多元分類問題,選擇 'ovr' 將使用多類別策略(one-vs-rest)直接對多個類別進行分類(預設方法);選擇 'crammer_singer' 將逐次進行二值分類。
  • class_weight:dict or ‘balanced’, default=None  特徵變數的加權係數。用於為某個特徵變數設權重,預設所有特徵變數的權重相同。

LinearSVC() 類的主要屬性:

  • coef_:  決策函式的引數估計值,即線性模型引數 w1,w2,... 的估計值。
  • intercept_:  決策函式中的常數項,即線性模型截距 w0 的估計值。
  • classes_:  樣本資料的分類標籤。指分幾類,每一類如何表示。

LinearSVC() 類的主要方法:

  • fit(X, y[, sample_weight])  用樣本集的資料(X,y)訓練SVM模型。
  • get_params([deep])  獲取模型引數。注意不是指分類模型的係數,而是指 penalty, C, fit_intercept, class_weight 等訓練的設定引數。
  • decision_function(X)  由SVM模型計算 X 的決策函式值,即樣本 X 到分離超平面的距離。注意不是分類判別結果。
  • predict(X)  用訓練好的 SVM 模型預測資料集 X 的分類判別結果,如0/1。
  • score(X,y[,sample_weight])  評價指標,對訓練樣本集 X 的分類準確度。

LinearSVC 定義訓練樣本集的輸入格式為 (X,y),X 是 n行(樣本數)*m列(特徵數)的二維陣列,y 是樣本分類標籤。

2.2 LinearSVC 使用例程

# skl_SVM_v1a.py
# Demo of linear SVM by scikit-learn
# v1.0a: 線性可分支援向量機模型(SciKitLearn)
# Copyright 2021 YouCans, XUPT
# Crated:2021-05-15

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC, LinearSVC
from sklearn.datasets import make_blobs

X, y = make_blobs(n_samples=40, centers=2, random_state=27)  # 產生資料集: 40個樣本, 2類
modelSVM = SVC(kernel='linear', C=100)  # SVC 建模:使用 SVC類,線性核函式
# modelSVM = LinearSVC(C=100)  # SVC 建模:使用 LinearSVC類,執行結果同上
modelSVM.fit(X, y)  # 用樣本集 X,y 訓練 SVM 模型

print("\nSVM model: Y = w0 + w1*x1 + w2*x2") # 分類超平面模型
print('截距: w0={}'.format(modelSVM.intercept_))  # w0: 截距, YouCans
print('係數: w1={}'.format(modelSVM.coef_))  # w1,w2: 係數, XUPT
print('分類準確度:{:.4f}'.format(modelSVM.score(X, y)))  # 對訓練集的分類準確度

# 繪製分割超平面和樣本集分類結果
plt.scatter(X[:,0], X[:,1], c=y, s=30, cmap=plt.cm.Paired)  # 散點圖,根據 y值設定不同顏色
ax = plt.gca()  # 移動座標軸
xlim = ax.get_xlim()  # 獲得Axes的 x座標範圍
ylim = ax.get_ylim()  # 獲得Axes的 y座標範圍
xx = np.linspace(xlim[0], xlim[1], 30)  # 建立等差數列,從 start 到 stop,共 num 個
yy = np.linspace(ylim[0], ylim[1], 30)  #
YY, XX = np.meshgrid(yy, xx)  # 生成網格點座標矩陣 XUPT
xy = np.vstack([XX.ravel(), YY.ravel()]).T  # 將網格矩陣展平後重構為陣列
Z = modelSVM.decision_function(xy).reshape(XX.shape)
ax.contour(XX, YY, Z, colors='k', levels=[-1, 0, 1], alpha=0.5,
           linestyles=['--', '-', '--'])  # 繪製決策邊界和分隔
ax.scatter(modelSVM.support_vectors_[:, 0], modelSVM.support_vectors_[:, 1], s=100,
           linewidth=1, facecolors='none', edgecolors='k')  # 繪製 支援向量
plt.title("Classification by LinearSVM (youcans, XUPT)")
plt.show()
# === 關注 Youcans,分享更多原創系列 https://www.cnblogs.com/youcans/ ===

2.3 LinearSVC 程式執行結果

SVM model: Y = w0 + w1*x1 + w2*x2
截距: w0=[-3.89974328]
係數: w1=[[0.72181271 0.34692337]]
分類準確度:1.0000
Python數模筆記-Sklearn(5)支援向量機

2.4 LinearSVC 程式說明

modelSVM = SVC(kernel='linear', C=100) # SVC 建模:使用 SVC類,線性核函式

modelSVM = LinearSVC(C=100) # SVC 建模:使用 LinearSVC類,執行結果同上

以上程式分別用 SVC()類、LinearSVC()類建模。使用 SVC() 類並選擇 'linear' 線性核函式時,模型訓練結果與 LinearSVC() 是一致的。但 SVC()類、LinearSVC()類的引數、屬性和方法的定義存在差異,例如 LinearSVC()類沒有程式中的 support_vectors_ 屬性。


3、基於核函式非線性可分支援向量機(NuSVC)

SVC 和 NuSVC 都可以使用核函式方法實現非線性分類。

3.1 NuSVC 類使用說明

NuSVC 是非線性分類支援向量機,使用核函式方法來處理非線性分類問題,基於 libsvm 庫實現。
SVC 和 NuSVC 都可以使用核函式方法實現非線性分類,但引數設定有所區別。對於多類別分類問題,通過構造多個“one-versus-one”的二值分類器逐次分類。

sklearn.svm.NuSVC 類是線性分類支援向量機的具體實現,官網介紹詳見:https://scikit-learn.org/stable/modules/generated/sklearn.svm.NuSVC.html#sklearn.svm.NuSVC

sklearn.svm.NuSVC()

class sklearn.svm.NuSVC(*, nu=0.5, kernel='rbf', degree=3, gamma='scale', coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=- 1, decision_function_shape='ovr', break_ties=False, random_state=None)

NuSVC() 類的主要引數:

  • kernel:{‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’}, default=’rbf’  設定核函式,‘linear’:線性核函式,‘poly’:多項式核函式,‘rbf’:高斯核函式,‘sigmoid’:S形核函式,‘precomputed’:自定義核。預設值為 'rbf' 。
  • nu:float, default=0.5  訓練錯誤率的上限,也即支援向量的百分比下限。預設值0.5,取值範圍(0,1]。
  • degree:int, default=3  多項式核函式的次數,預設值為 3。其它核函式時不適用。
  • gamma:{‘scale’, ‘auto’} or float, default=’scale’  ‘rbf’,'poly’ 和 ’sigmoid' 核函式的引數選擇方式。
  • coef0:float, default=0.0  'poly’ 和 ’sigmoid‘ 核函式的引數。
  • class_weight:dict or ‘balanced’, default=None  特徵變數的加權係數。用於為某個特徵變數設權重,預設所有特徵變數的權重相同。\
  • probabilitybool:default=False  是否啟用概率估計。預設值 False:不啟用。

需要注意的是,NuSVC() 類的引數有兩類:一類是針對模型訓練的通用引數,對所有核函式都適用,例如 nu、tol、max_iter;另一類是針對特定的核函式,只對某種核函式有效,並不適用於其它核函式,例如 degree 只適用於 'poly'核函式,coef0 只適用於'poly’ 和 ’sigmoid‘ 核函式,而且在 'poly’ 和 ’sigmoid‘ 核函式中的含義也不相同。

NuSVC() 類的主要屬性:

  • classes_:  樣本資料的分類標籤。指分幾類,每一類如何表示。
  • coef_:  決策函式的引數估計值。僅在核函式 ‘linear' 時有效,其它核函式時不適用。
  • dual_coef_:  對偶係數,即支援向量在決策函式中的係數。
  • fit_status_:  演算法狀態。0 表示演算法成功,1 表示演算法不收斂。
  • intercept_:  決策函式中的常數項。

NuSVC() 類的主要方法:

  • fit(X, y[, sample_weight])  用樣本集的資料(X,y)訓練 SVM 模型。
  • get_params([deep])  獲取模型引數。注意不是指分類模型的係數,而是指kernel, nu,class_weight等訓練的設定引數。
  • decision_function(X)  由SVM模型計算 X 的決策函式值,即樣本 X 到分離超平面的距離。注意不是分類判別結果。
  • predict(X)  用訓練好的 SVM 模型預測資料集 X 的分類判別結果,如0/1。
  • score(X,y[,sample_weight])  評價指標,對訓練樣本集 X 的分類準確度。

3.2 NuSVC 使用例程

# skl_SVM_v1b.py
# Demo of nonlinear SVM by scikit-learn
# v1.0b: 線性可分支援向量機模型(SciKitLearn)
# Copyright 2021 YouCans, XUPT
# Crated:2021-05-15

import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC, NuSVC, LinearSVC
from sklearn.datasets import make_moons

# 資料準備:生成訓練資料集,生成等高線網格資料
X, y = make_moons(n_samples=100, noise=0.1, random_state=27) # 生成資料集
x0s = np.linspace(-1.5, 2.5, 100)  # 建立等差數列,從 start 到 stop,共 num 個
x1s = np.linspace(-1.0, 1.5, 100)  # start, stop 根據 Moon 資料範圍選擇確定
x0, x1 = np.meshgrid(x0s, x1s)  # 生成網格點座標矩陣
Xtest = np.c_[x0.ravel(), x1.ravel()]  # 返回展平的一維陣列
# SVC 建模,訓練和輸出
modelSVM1 = SVC(kernel='poly', degree=3, coef0=0.2)  # 'poly' 多項式核函式
modelSVM1.fit(X, y)  # 用樣本集 X,y 訓練支援向量機 1
yPred1 = modelSVM1.predict(Xtest).reshape(x0.shape)  # 用模型 1 預測分類結果
# NuSVC 建模,訓練和輸出
modelSVM2 = NuSVC(kernel='rbf', gamma='scale', nu=0.1)  #'rbf' 高斯核函式
modelSVM2.fit(X, y)  # 用樣本集 X,y 訓練支援向量機 2
yPred2 = modelSVM2.predict(Xtest).reshape(x0.shape)  # 用模型 2 預測分類結果

# === 關注 Youcans,分享更多原創系列 https://www.cnblogs.com/youcans/ ===
fig, ax = plt.subplots(figsize=(8, 6))  
ax.contourf(x0, x1, yPred1, cmap=plt.cm.brg, alpha=0.1) # 繪製模型1 分類結果
ax.contourf(x0, x1, yPred2, cmap='PuBuGn_r', alpha=0.1) # 繪製模型2 分類結果
ax.plot(X[:,0][y==0], X[:,1][y==0], "bo")  # 按分類繪製資料樣本點
ax.plot(X[:,0][y==1], X[:,1][y==1], "r^")  # XUPT
ax.grid(True, which='both')
ax.set_title("Classification of moon data by LinearSVM")
plt.show()

3.3 NuSVC 程式執行結果

Python數模筆記-Sklearn(5)支援向量機

3.4 NuSVC 程式說明

modelSVM1 = SVC(kernel='poly', degree=3, coef0=0.2) # 'poly' 多項式核函式

modelSVM2 = NuSVC(kernel='rbf', gamma='scale', nu=0.1) #'rbf' 高斯核函式

  • 以上程式分別用 SVC()類、NuSVC()類建模,並且使用了不同的核函式。
  • 如果使用相同的核函式、模型引數, SVC()類、NuSVC()類的模型訓練結果是一致的,但引數、屬性和方法的定義存在差異。
  • 圖中分類結果的差異,不是使用 SVC()類、NuSVC()類所導致的,而是使用不同的核函式和模型引數的結果。
  • SVC()類、NuSVC()類的引數都有兩種,一類是針對模型訓練的通用引數,另一類是針對特定的核函式,只對某種核函式有效,並不適用於其它核函式。例如,degree、coef0 都是針對多項式核函式的專用引數,nu、gamma 則是NuSVC() 學習演算法的通用引數。

4、支援向量機分類的總結

  1. 兩分分類問題,按照從簡單到複雜的程度可以分為:線性可分、近似線性可分、非線性可分、非線性可分也搞不定。
  2. 近似線性可分與非線性可分具有本質區別,千萬不能把近似線性可分理解為輕微的非線性。近似線性可分,針對的還是線性可分的問題,只是由於資料集中個別樣本的誤差或錯誤,造成線性分割時個別點會分類判別錯誤,訓練的結果得到的是一個線性分類器。非線性可分,針對的是非線性分類問題,訓練結果得到的是一個非線性分類器。
  3. 針對具體問題如何選擇線性分類、近似線性分類還是非線性分類?這其實是兩個問題。線性分類與近似線性分類不是非此即彼的對立關係,只是對分類準確性要求的程度差異。懲罰係數 C>0 就反映了對於分類錯誤的懲罰程度,C值越大表示對於分類準確性的要求越高,C取無窮大就意味著要求嚴格線性可分、沒有錯誤分類。選擇線性分類模型,如果對訓練樣本或檢驗樣本進行分類判斷的錯誤率很高(score 低),就要考慮使用非線性模型進行分類了。
  4. 核函式的選擇,這是一個非常複雜而且沒有標準答案的問題。SVC() 和 NuSVC() 都提供了核函式 'linear','poly','rbf','sigmoid','precomputed' 可供選擇。
  • 'linear' 就不用說了,這簡直就是來搗亂的;'precomputed' 也不用說,你如果能搞定就不用看這篇文章裡。
  • 接下來,推薦使用 'poly' 和 'rbf' 核函式,優先選擇 'poly' 多項式核函式。
  • 再接下來,使用 'poly' 核函式時,推薦選擇 degree=2、degree=3 分別試試。

=== 關注 Youcans,分享更多原創系列 https://www.cnblogs.com/youcans/ ===
版權說明:

本文中案例問題和資料來自:Sci-Kit Learn 官網:https://scikit-learn.org/stable/modules/svm.html#svm-classification
本文例程參考了Sci-Kit Learn 官網的例程,但作者重寫了這些例程。
本文內容為作者原創,並非轉載書籍或網路內容。

YouCans 原創作品
Copyright 2021 YouCans, XUPT
Crated:2021-05-09

相關文章