機器學習系列(三十二)——Support Vector Machine

本篇主要內容:SVM,Hard Margin SVM,Soft Margin SVM

什麼是支撐向量機

支撐向量機(Support Vector Machine)作爲非常經典的機器學習算法,即可以解決分類問題,也可以解決迴歸問題。這裏首先介紹SVM 是如何解決分類問題的。
SVM是一種二分類模型,它的目的是尋找一個超平面(曲面)來對樣本進行分割,分割的原則是間隔最大化,最終轉化爲一個凸二次規劃問題來求解。由簡至繁的模型包括:

  • 當訓練樣本線性可分時,即樣本有嚴格的直線決策邊界時,對於每一個訓練樣本都必須給出正確分類,通過硬間隔最大化,學習一個線性可分SVM(Hard Margin SVM);
  • 當訓練樣本(近似)線性可分時,可能會有一些不太好的噪音點,爲了不影響模型泛化能力,通過軟間隔最大化,允許模型有一定的容錯能力,學習一個有容錯能力的線性SVM(Soft Margin SVM);
  • 當訓練樣本線性不可分時,通過核技巧和軟間隔最大化,學習一個非線性SVM。

本篇主要介紹線性可分問題。
一般的分類問題都可以得到一個決策邊界,這個決策邊界可以一定程度上將兩個類別分開,不過這樣的決策邊界往往不是唯一的,這種決策邊界不唯一的問題叫做不適定問題。

Logistic迴歸爲了解決這種不適定問題,定義了一個以概率爲基礎的損失函數,最小化這個損失函數最終得到唯一的決策邊界,這個決策邊界是由所有的訓練樣本所決定的。
SVM的思路略有不同,我們都知道機器學習的最終目的是尋找泛化能力最好的模型,以讓模型能在未知數據上有很好的預測能力,對於下面這種情況:

在訓練集上,雖然決策邊界很好地將兩個類別完全分開,但是直觀來看,靠近藍色點羣的紅色點在實際中被分爲藍色可能會更加合理些。也就是這個決策邊界泛化效果是不夠好的,因爲決策邊界離其中一個類別太近了。那什麼樣的決策邊界更好呢?顯然我們希望離決策邊界最近的點能離決策邊界的距離儘可能的遠,也就是決策邊界即要離紅色的點遠也要離藍色的點遠,於是我們得到這樣的決策邊界:

這就是SVM的基本思路,爲了讓模型的泛化效果更好,並沒有寄希望在數據預處理階段,也沒有對模型進行正則化,這個考量直接放到了算法內部。SVM是有很強的數學和統計學理論支撐的,我們可以嚴格證明出在一個不適定的問題中使用SVM找到的決策邊界具有很好的泛化能力。

離決策邊界最近的點就被稱之爲支撐向量,最優的決策邊界就是支撐向量包含的區域中心,這就是支撐向量機名稱的由來,它是由支撐向量來確定的決策邊界。設支撐向量到決策邊界的距離爲d,支撐向量之間的距離爲margin,顯然margin=2d,SVM最終就是要最大化margin,此時SVM已經是一個最優化問題,可以用數學的方式求解。


SVM背後的最優化

既然SVM的目的是最大化margin,也就是最大化d,我們就來看一下如何最優化d,設決策邊界的直線方程爲:
W_{T}x+b=0

所有的樣本點必都滿足:
\begin{cases} \frac{W^Tx^{(i)}+b}{||W||} \geq +d& y^{(i)}= 1\\\frac{W^Tx^{(i)}+b}{||W||} \leq -d& y^{(i)}= -1 \end{cases}

即:
\begin{cases} \frac{W^Tx^{(i)}+b}{||W||d} \geq +1& y^{(i)}= 1 \\ \frac{W^Tx^{(i)}+b}{||W||d} \leq -1& y^{(i)}= -1 \end{cases}

由於分母是常數,於是:
\begin{cases} W_{d}^Tx^{(i)}+b_d \geq +1& y_i= +1\\ W_d^Tx^{(i)}+b_d \leq -1& y_i= -1\end{cases}

於是三條直線的方程分別化爲:
W_{d}^Tx^{(i)}+b_d = 1W_{d}^Tx^{(i)}+b_d = 0W_{d}^Tx^{(i)}+b_d = -1

爲了表示方便,現在用W和b代替上面出現的W_db_d,只是要注意,這裏使用的W和b和最開始設的已經不是一個W和b。
再將類別考慮進約束調節,即:
y^{(i)}(W^Tx^{(i)}+b)\geq1

我們的目標最優化式爲:
max\frac{|W^Tx^{(i)}+b|}{||W||}

又由於支撐向量帶入後分子是1,於是即求min||W||,爲了方便求導操作,最終的目標函數即是:
min\frac{1}{2} ||W||^2

約束條件:
s.t.y^{(i)}(W^Tx^{(i)}+b)\geq1

這是一個典型的凸規劃,運籌學有它詳細求解證明過程,對證明過程感興趣的可以翻閱運籌學教材,這裏略去。


Soft Margin SVM與SVM的正則化

前面我們推導了SVM的數學最優化形式,不過那是在嚴格線性可分情況下的,對於一些並非嚴格線性可分情況或者線性可分但可能受outliner影響的情況,我們需要有一定容錯能力的SVM模型:

於是Soft Margin SVM被提出,所謂Soft Margin SVM就是讓SVM有一定的容錯能力,也就是讓約束條件寬鬆一些:
s.t.y^{(i)}(W^Tx^{(i)}+b)\geq1-\eta_i,\eta_i\geq0

但是爲了不讓\eta_i離譜的大,我們的目標函數加上一項\eta_i的和:
min\frac{1}{2} ||W||^2+C\sum_{i=1}^{m}\eta_i

一定程度上這可以看作SVM的L1正則化,C是一個超參,C越大容錯空間越小,相應的也有L2正則。這就是Soft Margin SVM。


sklearn中的SVM

接下來在鳶尾花數據集上使用SVM算法來直觀感受一下SVM,由於SVM的邏輯稍微複雜,這裏不再進行低層編寫,直接使用sklearn中封裝好的SVM。導入數據集:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()

x=iris.data
y=iris.target
'''暫時只處理二分類問題,而且爲方便可視化,只取前兩個特徵'''
x=x[y<2,:2]
y=y[y<2]

可視化我們的數據集:

plt.scatter(x[y==0,0],x[y==0,1],color='red')
plt.scatter(x[y==1,0],x[y==1,1],color='blue')
plt.show()

使用SVM和使用knn算法一樣,要首先對數據特徵進行標準化處理:

from sklearn.preprocessing import StandardScaler
standardScaler = StandardScaler()
standardScaler.fit(x)
x_standard = standardScaler.transform(x)

這裏只是展示SVM算法的分類效果,所以不進行train_test_split,直接對X整體進行fit:

'''調用線性SVM'''
from sklearn.svm import LinearSVC#C是classifier
svc = LinearSVC(C=1e9)#C越大越偏向hard_SVM
svc.fit(x_standard,y)

繪圖查看決策邊界:

'''繪圖查看決策邊界'''
def plot_decision_boundary(model,axis):
    x0,x1=np.meshgrid(
        np.linspace(axis[0],axis[1],int((axis[1]-axis[0])*100)).reshape(-1,1),
        np.linspace(axis[2],axis[3],int((axis[3]-axis[2])*100)).reshape(-1,1)
    )
    x_new=np.c_[x0.ravel(),x1.ravel()]
    y_predict=model.predict(x_new)
    zz=y_predict.reshape(x0.shape)
    from matplotlib.colors import ListedColormap
    custom_cmap=ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    plt.contourf(x0,x1,zz,linewidth=5,cmap=custom_cmap)

plot_decision_boundary(svc,axis=[-3,3,-3,3])
plt.scatter(x_standard[y==0,0],x_standard[y==0,1])
plt.scatter(x_standard[y==1,0],x_standard[y==1,1])
plt.show()

由於C比較大,這裏每個樣本都被正確分類,這幾乎就是一個Hard Margin SVM,接下來調節參數C,在C=0.01情況下訓練一個SVM,並繪製決策邊界:

'''修改C'''
svc2 = LinearSVC(C=0.01)
svc2.fit(x_standard,y)
plot_decision_boundary(svc2,axis=[-3,3,-3,3])
plt.scatter(x_standard[y==0,0],x_standard[y==0,1])
plt.scatter(x_standard[y==1,0],x_standard[y==1,1])
plt.show()

可以看到C變小後,該模型有一個藍色的點被錯誤分類爲橙色,此時SVM有了一定的容錯能力。
接下來繪製由支撐向量決定的平行於決策邊界的直線,直觀看一下margin,首先給出SVM求解的係數和截距:

修改繪圖函數,繪製margin:

'''繪圖查看決策區域'''
def plot_svc_decision_boundary(model,axis):
    x0,x1=np.meshgrid(
        np.linspace(axis[0],axis[1],int((axis[1]-axis[0])*100)).reshape(-1,1),
        np.linspace(axis[2],axis[3],int((axis[3]-axis[2])*100)).reshape(-1,1)
    )
    x_new=np.c_[x0.ravel(),x1.ravel()]
    y_predict=model.predict(x_new)
    zz=y_predict.reshape(x0.shape)
    from matplotlib.colors import ListedColormap
    custom_cmap=ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    plt.contourf(x0,x1,zz,linewidth=5,cmap=custom_cmap)
    w = model.coef_[0]
    b = model.intercept_[0]
    #w0*x0+w1*x1+b=0
    #x1 = -w0/w1*x0-b/w1
    plot_x = np.linspace(axis[0],axis[1],200)
    up_y = -w[0]/w[1] * plot_x - b/w[1] + 1/w[1]
    down_y = -w[0]/w[1] * plot_x - b/w[1] - 1/w[1]
    
    up_index = (up_y>=axis[2]) & (up_y<=axis[3])
    down_index = (down_y>=axis[2]) & (down_y<=axis[3])
    
    plt.plot(plot_x[up_index],up_y[up_index],color='black')
    plt.plot(plot_x[down_index],down_y[down_index],color='black')

svc1(Hard)對應的margin區域:

plot_svc_decision_boundary(svc,axis=[-3,3,-3,3])
plt.scatter(x_standard[y==0,0],x_standard[y==0,1])
plt.scatter(x_standard[y==1,0],x_standard[y==1,1])
plt.show()

svc2(Soft)對應的margin區域:

plot_svc_decision_boundary(svc2,axis=[-3,3,-3,3])
plt.scatter(x_standard[y==0,0],x_standard[y==0,1])
plt.scatter(x_standard[y==1,0],x_standard[y==1,1])
plt.show()

並非所有分類問題都是線性可分的,對於非線性可分的問題,我們將在下篇介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章