本篇主要內容:SVM解決非線性可分,Kernel Function
添加多項式特徵解決非線性可分問題
上篇我們介紹了SVM是如何生成線性決策邊界解決線性可分或近似線性可分問題的,本篇將介紹SVM如何解決線性不可分問題。來看這樣的一個數據集:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X, y = datasets.make_moons(noise=0.15,random_state=666)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
這是sklearn的datasets中的moon數據,這裏給它加入了一定的噪音,顯然這是一個線性不可分數據。在Logostic迴歸中,爲了得到非線性決策邊界,我們爲數據加入了多項式特徵,同樣的思想,我們也可以在使用SVM時加入多項式特徵,這樣就可以解決一些非線性可分問題。針對上面的例子:
from sklearn.preprocessing import PolynomialFeatures,StandardScaler
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
def PolynomialSVC(degree,C=1.0):
return Pipeline([
('poly',PolynomialFeatures(degree=degree)),
('std_scaler',StandardScaler()),
('linearSVC',LinearSVC(C=C))
])
poly_svc = PolynomialSVC(degree=3)
poly_svc.fit(X,y)
這裏將所有過程封裝了一個Pipeline。使用它創建一個SVM分類器,給數據添加3次冪多項式特徵,訓練該分類器,並繪製決策邊界:
plot_decision_boundary(poly_svc,axis=[-1.5,2.5,-1.0,1.5])
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
可以看到決策邊界已經是非線性的了,並且對於圖中的兩類樣本也很好地進行了分類。
核函數(Kernel Function)
SVM本質上就是求解一個二次規劃問題,不過一般我們並不直接求解:
而是求解它的對偶問題(具體求解翻閱機器學習或運籌學教材),在它的對偶問題中,會遇到不同特徵向量的內積:
如果要解決線性不可分問題,還需要SVM通過一個非線性變換 φ( x) ,將輸入的低維特徵映射到高維特徵空間,特徵空間的維數可能非常高。考慮到真正求解SVM的最優化問題的時候,往往只會用到兩個高維特徵空間特徵的內積,如果SVM的求解只用到內積運算,而在低維輸入空間又存在某個函數 K(x, x′) ,它恰好等於在高維空間中這個內積,即K( x, x′) =<φ( x)⋅φ( x′) > 。那麼SVM就不用計算複雜的非線性變換,而由這個函數 K(x, x′) 直接得到非線性變換的內積,大大簡化了計算。這樣的函數 K(x, x′) 就被稱爲核函數。
以一個例子來展示一下核函數的效果,假設現在有兩個二維空間中的數據點x=(x1,x2)和y=(y1,y2),考慮下面這個二元函數:
於是,
發現最後結果恰好是兩個向量的內積,而且兩個向量分別是二維空間數據點x和y在5維空間中的映射!想到剛纔核函數的定義,我們很容易知道,K(x,y)就是一個核函數。它給出了一個二維的表達式,使得x,y代入即可求值,而不再需要先把x,y映射成5維空間中的向量p,q再求內積,這樣大大簡化了運算。
sklearn中常用的核函數:
通過在SVM中使用這些核函數可以將原本線性不可分的數據映射到高維特徵空間後變的線性可分。
sklearn中的RBF核
RBF(Radial Basis Function)中文叫徑向基函數,其實就是Gauss核函數:
它可以將特徵映射到無窮維空間,其中的是一個超參數,越大表示模型複雜度越高,相應的也就越容易出現過擬合,實際中要通過調參得到最好的。下面在moon數據集上使用Gauss核函數:
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
def RBFKernelSVC(gamma=1.0):
return Pipeline([
('std_scaler',StandardScaler()),
('svc',SVC(kernel='rbf',gamma=gamma))
])
接下來訓練一個使用RBF核的SVM,此時默認是1,並繪製決策邊界:
svc = RBFKernelSVC(gamma=1.0)
svc.fit(x,y)
plot_decision_boundary(svc,axis=[-1.5,2.5,-1.0,1.5])
plt.scatter(x[y==0,0],x[y==0,1])
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()
決策邊界:
可以看到時模型的效果還是不錯的。
再來看一下的情況:
svc100 = RBFKernelSVC(gamma=100)#過擬合
svc100.fit(x,y)
plot_decision_boundary(svc100,axis=[-1.5,2.5,-1.0,1.5])
plt.scatter(x[y==0,0],x[y==0,1])
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()
決策邊界:
此時明顯出現了過擬合。
可以繼續測試,當時會出現欠擬合,這裏不再展示。
在sklearn中可以很方便地調用它提供的核函數,當然也可以自己編寫核函數,在符合sklearn接口的情況下以下能夠無縫銜接。