一、支持向量機理論簡介
支持向量機是一類按監督學習方式對數據進行二元分類的廣義線性分類器,其決策邊界是對學習樣本求解的最大邊距超平面
1.線性分類
線性可分性
在分類問題中給定輸入數據和學習目標:X={X1,X2,…,XN},y={y1,…,yN},其中輸入數據的每個樣本都包含多個特徵並由此構成特徵空間:X=[x1,x2,…,xn],而學習目標爲二元變量
表示負類和正類
若輸入數據所在的特徵空間存在作爲決策邊界的超平面將學習目標按正類和負類分開,並使任意樣本的點到平面距離大於等於1
則稱該分類問題具有線性可分性,參數w,b分別爲超平面的法向量和截距。
滿足該條件的決策邊界實際上構造了2個平行的超平面作爲間隔邊界以判別樣本的分類:
所有在上間隔邊界上方的樣本屬於正類,在下間隔邊界下方的樣本屬於負類。兩個間隔邊界的距離d=2/||w||被定義爲邊距(margin),位於間隔邊界上的正類和負類樣本爲支持向量。
2.核方法
一些線性不可分的問題可能是非線性可分的,即特徵空間存在超曲面將正類和負類分開。使用非線性函數可以將非線性可分問題從原始的特徵空間映射至更高維的希爾伯特空間,從而轉化爲線性可分問題,此時作爲決策邊界的超平面表示如下:
由於映射函數具有複雜的形式,難以計算其內積,因此可使用核方法,即定義映射函數的內積爲核函數:
以迴避內積的顯式計算
Mercer定理
核函數的選擇需要一定條件,函數
是核函數的充要條件是,對輸入空間的任意向量:
其核矩陣,即如下形式的格拉姆矩陣
是半正定矩陣。上述結論被稱爲Mercer定理
作爲充分條件:特徵空間內兩個函數的內積是一個二元函數,在其核矩陣爲半正定矩陣時,該二元函數具有可再生性:
因此其內積空間是一個賦範向量空間,可以完備化得到希爾伯特空間 ,即再生核希爾伯特空間。作爲必要條件,對核函數構造核矩陣後易知:
常見的核函數
3.算法
線性SVM
(1)硬邊距
給定輸入數據和學習目標:X={X1,X2,…,XN},y={y1,…,yN},硬邊界SVM是在線性可分問題中求解最大邊距超平面的算法,約束條件是樣本點到決策邊界的距離大於等於1。硬邊界SVM可以轉化爲一個等價的二次凸優化問題進行求解:
由上式得到的決策邊界可以對任意樣本進行分類:
我們注意到雖然超平面法向量w是唯一優化目標,但學習數據和超平面的截距通過約束條件影響了該優化問題的求解。硬邊距SVM是正則化係數取0時的軟邊距SVM,下面我們來看一看軟邊距SVM。
(2)軟邊距
在線性不可分問題中使用硬邊距SVM將產生分類誤差,因此可在最大化邊距的基礎上引入損失函數構造新的優化問題。SVM使用鉸鏈損失函數,沿用硬邊界SVM的優化問題形式,軟邊距SVM的優化問題有如下表示:
上式表明可知,軟邊距SVM是一個L2正則化分類器,式中Li表示鉸鏈損失函數。使用鬆弛變量:ξ≥0處理鉸鏈損失函數的分段取值後,上式可化爲:
求解上述軟邊距SVM通常利用其優化問題的對偶性
推導:
定義軟邊距SVM的優化問題爲原問題,通過拉格朗日乘子:
可得到其拉格朗日函數:
令拉格朗日函數對優化目標w,b,ξ的偏導數爲0,可得到一系列包含拉格朗日乘子的表達式:
將其帶入拉格朗日函數後可得原問題的對偶問題:
對偶問題的約束條件中包含不等關係,因此其存在局部最優的條件是拉格朗日乘子滿足條件:
由上述KKT條件可知,對任意樣本(Xi,yi),總有αi=0或yi(w^TXi+b)=1-ξi ,對前者,該樣本不會對決策邊界wTXi+b=0產生影響,對後者,該樣本滿足yi(wTXi+b)=1-ξi意味其處於間隔邊界上(αi< C)、間隔內部(αi=C)或被錯誤分類(αi>C),即該樣本是支持向量。由此可見,軟邊距SVM決策邊界的確定僅與支持向量有關,使用鉸鏈損失函數使得SVM具有稀疏性
非線性SVM
使用非線性函數將輸入數據映射至高維空間後應用線性SVM可得到非線性SVM。非線性SVM有如下優化問題:
類比軟邊距SVM,非線性SVM有如下對偶問題:
注意到式中存在映射函數內積,因此可以使用核方法,即直接選取核函數:
非線性SVM的對偶問題的KKT條件可同樣類比軟邊距線性SVM。
二、代碼實現
未經標準化的原始數據點分佈
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
iris = datasets.load_iris()
X = iris.data
y = iris.target
# X = X[y<2,:2] #只取y<2的類別,也就是0 1 並且只取前兩個特徵 y = y[y<</span>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()
# 標準化
standardScaler = StandardScaler()
standardScaler.fit(X) #計算訓練數據的均值和方差
X_standard = standardScaler.transform(X) #再用scaler中的均值和方差來轉換X,使X標準化
svc = LinearSVC(C=1e9) #線性SVM分類器
svc.fit(X_standard,y) # 訓練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))#最後生成svm
])
from matplotlib.colors import ListedColormap
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)
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])
# # x,y軸都在-3到3之間
# # 繪製原始數據
# plt.scatter(X_standard[y==0,0],X_standard[y==0,1],color='red')
# plt.scatter(X_standard[y==1,0],X_standard[y==1,1],color='blue')
# plt.show()
使用多項式特徵和核函數
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X, y = datasets.make_moons()
#使用生成的數據
print(X.shape)
# (100,2)
print(y.shape)
# (100,)
(100, 2)
(100,)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
增加噪聲點
X, y = datasets.make_moons(noise=0.15,random_state=777)
#隨機生成噪聲點,random_state是隨機種子,noise是方差
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
利用多項式特徵的SVM來進行分類
from sklearn.linear_model import LogisticRegressionCV
poly_svc = PolynomialSVC(degree=3)
poly_svc.fit(X,y)
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()
利用覈對數據進行處理,使其維度提升,使原本線性不可分的數據,在高維空間變成線性可分的。再用線性SVM來進行處理。
from sklearn.svm import SVC
def PolynomialKernelSVC(degree,C=1.0):
return Pipeline([ ("std_scaler",StandardScaler()), ("kernelSVC",SVC(kernel="poly")) # poly代表多項式特徵
])
poly_kernel_svc = PolynomialKernelSVC(degree=3)
poly_kernel_svc.fit(X,y)
plot_decision_boundary(poly_kernel_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()
模擬升維
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-4,5,1)
#生成測試數據
y = np.array((x >= -2 ) & (x <= 2),dtype='int')
plt.scatter(x[y==0],[0]*len(x[y==0]))
# x取y=0的點, y取0,有多少個x,就有多少個y
plt.scatter(x[y==1],[0]*len(x[y==1]))
plt.show()
利用高斯核函數,進行數據映射
# 高斯核函數
def gaussian(x,l):
gamma = 1.0
return np.exp(-gamma * (x -l)**2)
l1,l2 = -1,1
X_new = np.empty((len(x),2))
#len(x) ,2
for i,data in enumerate(x):
X_new[i,0] = gaussian(data,l1)
X_new[i,1] = gaussian(data,l2)
plt.scatter(X_new[y==0,0],X_new[y==0,1])
plt.scatter(X_new[y==1,0],X_new[y==1,1])
plt.show()
超參數
生成數據
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X,y = datasets.make_moons(noise=0.15,random_state=777)
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
定義一個RBF核的SVM
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)) ])
svc = RBFKernelSVC()
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()
改變svc = RBFKernelSVC() 中的參數爲100
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)) ])
svc = RBFKernelSVC(100)
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()
改成10
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)) ])
svc = RBFKernelSVC(10)
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()
改成0.1
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)) ])
svc = RBFKernelSVC(0.1)
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()
SVM解決迴歸問題
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
boston = datasets.load_boston()
X = boston.data
y = boston.target
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=777)
# 把數據集拆分成訓練數據和測試數據
from sklearn.svm import LinearSVR
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler
def StandardLinearSVR(epsilon=0.1):
return Pipeline([ ('std_scaler',StandardScaler()), ('linearSVR',LinearSVR(epsilon=epsilon)) ])
svr = StandardLinearSVR()
svr.fit(X_train,y_train)
svr.score(X_test,y_test)
0.6991720760289629
三、參考文章
http://blog.sina.com.cn/s/blog_6c3438600102yn9x.html
百度百科