1. 程式人生 > 其它 >python機器學習——SVM支援向量機

python機器學習——SVM支援向量機

背景與原理:

支援向量機是一種用來解決分類問題的演算法,其原理大致可理解為:對於所有$n$維的資料點,我們希望能夠找到一個$n$維的直線(平面,超平面),使得在這個超平面一側的點屬於同一類,另一側的點屬於另一類。而我們在尋找這個超平面的時候,我們只需要找到最接近劃分超平面的點,而一個$n$維空間中的點等同於一個$n$維向量,所以這些點就可以被稱為支援向量。

在一個$n$維空間中,一個超平面可以用$0=w^{T}x+b$表示,比如一條二維空間中的直線可以表示成$ax+by+c=0$,而一個三維空間中的平面可以表示成$ax+by+cz+d=0$,以此類推。而$n$維空間的一個超平面會把$n$維空間中的點分成三部分:對一個$n$維空間中的點$x_{0}$和超平面$w^{T}x+b=0$,若$w^{T}x_{0}+b<0$,說明$x_{0}$在超平面的一側,而若$w^{T}x_{0}+b=0$,說明$x_{0}$在超平面上,若$x^{T}x_{0}+b>0$,說明$x_{0}$在超平面的另一側,也就是說,當我們給定了一個超平面之後,空間中的所有點就自然被這個超平面分成了兩類(如果忽略超平面上的點)

線性可分支援向量機:

首先我們考慮線性可分的點,即客觀存在一個超平面,使得兩類資料點分別分佈在超平面的兩側,那麼不難想見,這樣的超平面有無窮多個,但為了模型的泛化效能,我們希望選取的超平面應該是位於兩組資料集中間的(個人理解:考慮一種極端情況:這個超平面是貼著某一類的資料點經過的,那麼在實際情況中,屬於這類的資料點很可能有些稍微散佈到了這條直線外,這樣這個分類器的效能就不夠好了,而如果位於中間,即使有些資料點相較於訓練資料有些往外散佈,也能較為正確地分類。)

那麼由計算幾何中的已知公式,我們可以看到:$n$維空間中任意一個點$x_{0}$到一個給定超平面$w^{T}x+b=0$的距離為$\dfrac{|w^{T}x_{0}+b|}{|w|}$,其中$|w|=\sqrt{\sum_{i=1}^{n}w_{i}^{2}}$。而假設對於第$i$組資料,其類別為$y_{i}$,由於我們要根據資料點在超平面的哪一側確定類別,因此我們實際是要求資料點的位置與資料點的類別相匹配,那麼我們的類別也非常自然地選取1/-1,而是否匹配則可以由$y_{i}(w^{T}x_{i}+b)$的正負性來度量,如果為正我們認為是相匹配的。

那麼這個問題就變成了:對於一組資料$(X_{1},y_{1}),...,(X_{m},y_{m})$,其中$X_{i}$是$n$維向量,我們要求一個$n$維超平面$w^{T}x+b=0$,而如果令$r=min_{i=1}^{m}\dfrac{|w^{T}X_{i}+b|}{|w|}$,那麼我們要最大化$r$,而約束條件則是$y_{i}\dfrac{w^{T}X_{i}+b}{|w|}\geq r$(這個約束條件有點唬人:$y$的取值只有1/-1,所以$y$不影響左邊這個東西的絕對值,而$r$一定小於等於左邊這個東西的絕對值,因此這個約束條件實際只約束了符號)

那麼令$w=\dfrac{w}{|w|r},b=\dfrac{b}{|w|r}$(這並不影響超平面),這樣就得到了約束條件:$y_{i}(w^{T}X_{i}+b)\geq 1$(對超平面的所有引數除以同一個值並不改變這個超平面,只是讓約束條件更好看了)

而在這個約束條件下,我們可以看到$|w^{T}X_{i}+b|\geq 1$,於是$r \leq \dfrac{1}{|w|}$(由$r$的定義立得),於是我們實際上要最大化的是$\dfrac{1}{|w|}$,那麼也就是最小化$|w|$,而這不好求解,所以我們要求最小化$\dfrac{1}{2}|w|^{2}$(這些變化都是為了計算簡便)

這樣我們的問題就轉化為了:在$y_{i}(w^{T}X_{i}+b)\geq 1$的條件下,最小化$\dfrac{1}{2}|w|^{2}$

那麼這是個條件最值,我們應用拉格朗日乘子法,構造拉格朗日函式:

$L=\dfrac{|w|^{2}}{2}-\sum_{i=1}^{m}\alpha_{i}(y_{i}(w^{T}X_{i}+b)-1)$

這個問題並不好求解,因為引數個數等於資料集大小了,因此我們考慮改進:設$\theta(w)=max_{\alpha_{i}\geq 0}L$

那麼可以看到,在所有的約束條件都成立時,後面要減掉的那一項一定非負,這樣的話對於所有的$y_{i}(w^{T}X_{i}+b)-1>0$,應該取對應的$\alpha_{i}=0$才能取得最大值,而最後能取得的最大值就是前面那項$\dfrac{|w|^{2}}{2}$,於是此時的$\theta(w)=\dfrac{|w|^{2}}{2}$

而如果有某個約束條件不成立,後面要減掉的那一項是個負值,這樣的話對應的$\alpha_{i}$越大,整個函式值越大,這樣$\theta(w)=+\infty$

這樣的話我們如果我們想在所有約束條件成立的情況下最小化$\dfrac{|w|^{2}}{2}$,我們實際上就是在最小化$\theta(w)$,即我們要進行的是這樣的操作:

$min_{w,b}max_{\alpha_{i}\geq 0}L$

而可以證明,這個問題與其對偶問題:

$max_{\alpha_{i}\geq 0}min_{w,b}L$

是等價的,因此我們來解決這個對偶問題:首先我們對$w$和$b$求偏導:

$\dfrac{\partial L}{\partial w}=0 \Rightarrow w=\sum_{i=1}^{m}\alpha_{i}y_{i}X_{i}$

$\dfrac{\partial L}{\partial b}=0 \Rightarrow \sum_{i=1}^{m}\alpha_{i}y_{i}=0$

這樣我們直接代入,可得我們要進行的任務是:

$max_{\alpha_{i}\geq 0}\dfrac{|\sum_{i=1}^{m}\alpha_{i}y_{i}X_{i}|^{2}}{2}-\sum_{i=1}^{m}\alpha_{i}(y_{i}((\sum_{j=1}^{m}\alpha_{j}y_{j}X_{j})X_{i}+b)-1)$

這樣實際就是

$max_{\alpha_{i}\geq 0}-\dfrac{\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}(X_{i}\cdot X_{j})}{2}+\sum_{i=1}^{m}\alpha_{i}$

取個符號變成等價的問題:

$min_{\alpha_{i}\geq 0} \dfrac{\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}(X_{i}\cdot X_{j})}{2}-\sum_{i=1}^{m}\alpha_{i}$

這樣最後的問題就確定了:我們要在$\alpha_{i}$非負的條件下最小化$\dfrac{\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}(X_{i}\cdot X_{j})}{2}-\sum_{i=1}^{m}\alpha_{i}$,求出最小的$\alpha$後我們就可以計算出:

$w=\sum_{i=1}^{m}\alpha_{i}y_{i}X_{i}$

同時一定存在一個$\alpha_{j}>0$(否則$w=0$),那麼對於這個$j$,有:

$y_{j}-w^{T}X_{j}=b$

代入上面求出的$w$,即得到:

$b=y_{j}-\sum_{i=1}^{m}\alpha_{i}y_{i}(X_{i}\cdot X_{j})$

這裡有一個邏輯:我們上文已經提到了,對於所有的$y_{i}(w^{T}X_{i}+b)-1>0$,應該取對應的$\alpha_{i}=0$,也就是說只有$y_{i}(w^{T}X_{i}+b)=1$才會產生非零的$\alpha_{i}$,而由於$y_{i}$只能為1/-1,因此移項即得$y_{j}-w^{T}X_{j}=b$。

而這同樣也啟示我們:訓練完成後,大部分樣本都不需要保留,支援向量機只與處於邊界位置的支援向量有關。

當然了,這並不是線性問題的結束,因為真實生活中,很難存在絕對線性可分的資料,大多數的資料在分割位置上是有交叉的,這樣的話上面的理論模型就不好用了。

因此我們退一步,允許有一定的偏移,同時在損失函式中增加一個懲罰項,也即我們要最小化

$\dfrac{|w|^{2}}{2}+C\sum_{i=1}^{m}\xi_{i}$

其中$\xi_{i}=max(0,1-y_{i}(w^{T}X_{i}+b)$表示一個分類錯誤的程度,而$C$是懲罰係數,$C$越大代表我們對分類錯誤的懲罰越大。

因此整個演算法流程為:

(1)給定訓練集$(X_{1},y_{1},...,(X_{m},y_{m})$,其中$y_{i}=1/-1$,選取懲罰引數$C$

(2)最小化$\dfrac{\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}(X_{i}\cdot X_{j})}{2}-\sum_{i=1}^{m}\alpha_{i}$,約束條件是$0\leq \alpha_{i} \leq C$和$\sum_{i=1}^{m}\alpha_{i}y_{i}=0$(這個求解過程與無懲罰項的時候類似,這裡不展開了)

(3)$w=\sum_{i=1}^{m}\alpha_{i}y_{i}X_{i}$,找到$\alpha_{j}>0$,對於這個$j$,$b=y_{j}-\sum_{i=1}^{m}\alpha_{i}y_{i}(X_{i}\cdot X_{j})$,得到超平面$w^{T}x+b=0$

(4)分類函式即為$f(x)=sgn(w^{T}x+b)$

非線性可分支援向量機:

在現實生活中,很多資料並不是線性可分的,比如二維空間按一個圓內和圓外對點進行分類,這顯然不是一個線性可分的問題,那麼對於這種問題我們的處理方法是將其對映到更高維度的空間中去,以期在更高維度的空間中其是線性可分的。

但是在上述討論過程中我們可以看到,我們需要的其實並不是真正的高維空間中的點是什麼,我們只需要計算出兩個點之間的點積!

那麼如果我們設函式$\phi(x)$把$x$投影到高維空間,那我們只需要一個函式$K(x,z)=\phi(x) \cdot \phi(z)$,這樣的話我們就只需最小化

$\dfrac{\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}K(X_{i}, X_{j})}{2}-\sum_{i=1}^{m}\alpha_{i}$

而不需要得知某個$X$投影到高維空間中具體是什麼了。

所以我們實際上要選取的是$K$,這個$K$又被稱為核函式,常用的核函式有多項式核,高斯核$K(x_{1},x_{2})=e^{-\frac{|x_{1}-x_{2}|^{2}}{2\sigma^{2}}}$和線性核$K(x_{1},x_{2})=x_{1}\cdot x_{2}$(線性核存在的意義是統一了所有的支援向量機的形式,這樣無論是什麼問題我們都要選取一個核然後操作,而不用根據是否是線性可分選取不同的形式了)

因此所有的支援向量機的演算法步驟為:

(1)給定訓練集$(X_{1},y_{1},...,(X_{m},y_{m})$,其中$y_{i}=1/-1$,選取懲罰引數$C$和核函式$K$

(2)最小化$\dfrac{\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha_{i}\alpha_{j}y_{i}y_{j}K(X_{i}, X_{j})}{2}-\sum_{i=1}^{m}\alpha_{i}$,約束條件是$0\leq \alpha_{i} \leq C$和$\sum_{i=1}^{m}\alpha_{i}y_{i}=0$

(3)找到$\alpha_{j}>0$,對於這個$j$,$b=y_{j}-\sum_{i=1}^{m}\alpha_{i}y_{i}K(X_{i}, X_{j})$,得到超平面$w^{T}x+b=0$

(4)分類函式即為$f(x)=sgn(\sum_{i=1}^{m}\alpha_{i}y_{i}K(X,X_{i})+b)$

程式碼實現:

 

import numpy as np
import math
import matplotlib.pyplot as plt
from sklearn import svm


x=np.arange(0.,10.,0.02)
y=5-2*x/3+np.random.randn(500)
now=0
dataset=[]
for i in range(0,500):
    typ = -1
    if 2*x[i]+3*y[i] <= 15:
        if abs(np.random.randn(1)[0])<2:
            typ = 1
        else:
            typ = -1
    else:
        if abs(np.random.randn(1)[0]) < 2:
            typ = -1
        else:
            typ = 1

    dataset.append([x[i],y[i],typ])

X=(np.array(dataset)[:,0:2])
Y=(np.array(dataset)[:,2])
model=svm.SVC(C=1.0,kernel='linear').fit(X,Y)



for i in range(0,500):
    if Y[i]==1:
        plt.scatter(X[i,0],X[i,1],c='r')
    else:
        plt.scatter(X[i,0],X[i,1],c='b')

w=model.coef_
b=model.intercept_
plt.plot(X[:,0],-(w[0][0]/w[0][1])*X[:,0]-b[0]/w[0][1],c='g',linewidth=3)
plt.show()

支援向量機的程式碼不好實現,這裡直接調了庫,要分類的資料和之前邏輯迴歸是的是一樣的,分類效果如下:

可以看到分類效果還是很不錯的