機器學習【六】支持向量機SVM——專治線性不可分


SVM原理

線性可分與線性不可分

線性可分

線性不可分-------【無論用哪條直線都無法將女生情緒正確分類】

SVM的核函數可以幫助我們:

假設‘開心’是輕飄飄的,“不開心”是沉重的

將三維視圖還原成二維:

剛利用“開心”“不開心”的重量差實現將二維數據變成三維的過程,稱為將數據投射至高維空間,這正是核函數的功能

在SVM中,用的最普遍的兩種把數據投射到高維空間的方法分別是多項式內核、徑向基內核(RFB)

多項式內核:

通過把樣本原始特征進行乘方來把數據投射到高維空間【如特征1^2,特征2^3,特征3^5......】

RBF:

又稱高斯內核

 

 支持向量機的SVM核函數

用圖形直觀了解:

#導入numpy
import numpy as np
#導入畫圖工具
import matplotlib.pyplot as plt
#導入支持向量機SVM
from sklearn import svm
#導入數據集生成工具
from sklearn.datasets import make_blobs
#先創建50個數據點,讓它們分成兩類
X,y = make_blobs(n_samples=50,centers=2,random_state = 6)
#創建一個線性內核的支持向量機模型
clf = svm.SVC(kernel = 'linear',C=1000)
clf.fit(X,y)
#把數據點畫出來
plt.scatter(X[:,0],X[:,1],c=y,s=30,cmap=plt.cm.Paired)
#建立圖像坐標
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
#生成兩個等差數列
xx = np.linspace(xlim[0],xlim[1],30)
yy = np.linspace(ylim[0],ylim[1],30)
YY,XX = np.meshgrid(yy,xx)
xy = np.vstack([XX.ravel(),YY.ravel()]).T
Z = clf.decision_function(xy).reshape(XX.shape)
#把分類的邊界畫出來
ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestyles=['--','-','--'])
ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=100,linewidth=1,facecolors='none')
plt.show()

——線性內核的SVM分類器

【結果分析】:

在分類器兩側有兩條虛線,正好壓在虛線上的數據點,即支持向量

本例所使用的方法稱為“最大邊界間隔超平面”

指,實線【在高維數據中是一個超平面】和所有支持向量的距離都是最大的

 

 

把SVM的內核換成RBF:

#創建一個RBF內核的支持向量機模型
clf_rbf = svm.SVC(kernel='rbf',C=1000)
clf_rbf.fit(X,y)
#把數據點畫出來
plt.scatter(X[:,0],X[:,1],c=y,s=30,cmap=plt.cm.Paired)
#建立圖像坐標
ax = plt.gca()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
#生成兩個等差數列
xx = np.linspace(xlim[0],xlim[1],30)
yy = np.linspace(ylim[0],ylim[1],30)
YY,XX = np.meshgrid(yy,xx)
xy = np.vstack([XX.ravel(),YY.ravel()]).T
Z = clf_rbf.decision_function(xy).reshape(XX.shape)
#把分類的邊界畫出來
ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,linestyles=['--','-','--'])
ax.scatter(clf_rbf.support_vectors_[:,0],clf_rbf.support_vectors_[:,1],s=100,linewidth=1,facecolors='none')
plt.show()

 ——RBF內核的SVM分類器

【結果分析】:

使用RBF內核時,數據點距離計算是用如下公式計算的:

 

 

SVM的核函數和參數選擇

1.不同核函數的SVM對比

線性模型中提到過,linearSVM算法,就是一種使用了線性內核的SVM算法,不過linearSVM不支持對核函數進行修改【默認只能使用線性內核】

 

直觀體驗不同內核的SVM算法在分類中的不同表現

#導入紅酒數據集
from sklearn.datasets import load_wine
#定義一個函數來畫圖
def make_meshgrid(x,y,h=.02):
    x_min,x_max = x.min() -1,x.max() +1
    y_min,y_max = y.min() -1,y.max() +1
    xx,yy = np.meshgrid(np.arange(x_min,x_max,h),np.arange(y_min,y_max,h))
    return xx,yy
#定義一個繪制等高線的函數
def plot_contours(ax,clf,xx,yy, **params):
    Z = clf.predict(np.c_[xx.ravel(),yy.ravel()])
    Z = Z.reshape(xx.shape)
    out = ax.contourf(xx,yy,Z, **params)
    return out
#使用酒的數據集
wine = load_wine()
#選取數據集的前兩個特征
X = wine.data[:,:2]
y = wine.target
C = 1.0  #SVM的正則化參數
models = (svm.SVC(kernel='linear',C=C),svm.LinearSVC(C=C),svm.SVC(kernel='rbf',gamma=0.7,C=C),svm.SVC(kernel='poly',degree=3,C=C))
models = (clf.fit(X,y) for clf in models)
#設定圖題
titles = ('SVC with linear kernal','LinearSVC (linear kernal)','SVC with RBF kernal','SVC with polynomial (degree 3) kernal')
#設定一個子圖形的個數和排列方式
fig,sub = plt.subplots(2,2)
plt.subplots_adjust(wspace=0.4,hspace=0.4)
#使用前面定義的函數進行畫圖
X0,X1 = X[:,0],X[:,1]
xx,yy = make_meshgrid(X0,X1)
for clf,title,ax in zip(models,titles,sub.flatten()):
    plot_contours(ax,clf,xx,yy,cmap=plt.cm.plasma,alpha=0.8)
    ax.scatter(X0,X1,c=y,cmap=plt.cm.plasma,s=20,edgecolors='k')
    ax.set_xlim(xx.min(),xx.max())
    ax.set_ylim(yy.min(),yy.max())
    ax.set_xlabel("Feature 0")
    ax.set_ylabel("Feature 1")
    ax.set_xticks(())
    ax.set_yticks(())
    ax.set_title(title)
plt.show()

【結果分析】:

線性內核的SVC與linearSVC得到的結果近似,但仍然有一點差別——原因是linearSVC對L2范數進行最小化,而線性內核的SVC是對L1進行最小化

無論如何,線性內核的SVC和linearSVC生成的決定邊界都是線性的【在更高維數據集中將會是相交的超平面】

 

 RBF內核的SVC和polynomial內核的SVC分類器的決定邊界則完全不是線性的,更加彈性

決定他們邊界形狀的是參數:

polynomial內核的SVC分類器

degree和正則化參數C

【本例中degree=3,即對原始數據集的特征乘3次方操作】

RBF內核的SVC

gamma和正則化參數C

 

RBF內核SVC的gamma參數調節

C = 1.0  #SVM的正則化參數
models = (svm.SVC(kernel='rbf',gamma=0.1,C=C),svm.SVC(kernel='rbf',gamma=1,C=C),svm.SVC(kernel='rbf',gamma=10,C=C))
models = (clf.fit(X,y) for clf in models)
#設定圖題
titles = ('gamma=0.1','gamma=1','gamma=10')
#設置子圖形個數和排列
flg,sub = plt.subplots(1,3,figsize=(10,3))
#使用前面定義的函數進行畫圖
X0,X1 = X[:,0],X[:,1]
xx,yy = make_meshgrid(X0,X1)
for clf,title,ax in zip(models,titles,sub.flatten()):
 plot_contours(ax,clf,xx,yy,cmap=plt.cm.plasma,alpha=0.8)
 ax.scatter(X0,X1,c=y,cmap=plt.cm.plasma,s=20,edgecolors='k')
 ax.set_xlim(xx.min(),xx.max())
 ax.set_ylim(yy.min(),yy.max())
 ax.set_xlabel("Feature 0")
 ax.set_ylabel("Feature 1")
 ax.set_xticks(())
 ax.set_yticks(())
 ax.set_title(title)
plt.show()

 

【結果分析】:

gamma值越小,RBF內核的直徑越大——> 有更多的點被模型圈進決定邊界中——> 邊界越圓滑

  • gamma越小,模型傾向於 欠擬合
  • gamma越大,模型傾向於 過擬合

正則化參數C,可以見線性模型一章

  • C越小,模型越受限【單個數據對模型的影響越小】,模型越簡單
  • C越大,每個數據點對模型的影響越大,模型越復雜

 

SVM優點

  • 可應對高維數據集和低維數據集
  • 即使數據集中樣本特征的測度都比較接近,如圖像識別領域,以及樣本特征數和樣本數比較接近的時候,都游刃有余

SVM缺點 

  • 當數據集中特征數量在1萬以內,SVM可以駕馭,但數量大於10萬,就非常占內存和耗費時間
  • 對數據預處理和參數調節要求很高

 

 

 【注意】

SVM中3個參數非常重要:

  1. 核函數的選擇
  2. 核函數的參數【如RBF的gamma】
  3. 正則化參數C 

 RBF內核的gamma值是用來調節內核寬度的,gamma值和C值一起控制模型的復雜度,數值越大,模型越復雜【實際中,一起調節,才能達到最好的效果】

 

 

 

實戰

 SVM在回歸分析中的應用——波士頓房價數據集

1.了解數據集

#導入波士頓房價
from sklearn.datasets import load_boston
boston = load_boston()
#打印鍵
print(boston.keys())

 

#打印數據集中的短描述
print(boston['DESCR'])

 

 

【一部分】

【結果分析】

數據集共有506個樣本,每個樣本有13個特征變量,后面還有一個叫做中位數的第14個變量【這變量就是該數據集中的target】

 

2.通過SVR算法建立房價預測模型


#導入數據集拆分工具
from sklearn.model_selection import train_test_split
#建立數據集和測試集
X,y = boston.data,boston.target
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=8)
#打印訓練集和測試集的形態
print(X_train.shape)
print(X_test.shape)

 

用SVR建模

#導入支持向量回歸模型
from sklearn.svm import SVR
#分別測試linear核函數和rbf核函數
for kernel in ['linear','rbf']:
   svr = SVR(kernel=kernel)
   svr.fit(X_train,y_train)
   print(kernel,'核函數模型訓練集得分:',svr.score(X_train,y_train))
   print(kernel,'核函數模型測試集得分:',svr.score(X_test,y_test))

 

 

 SVM算法對數據預處理的要求較大,如果  數據特征量級差異較大  ,就需要預處理數據

先用圖形可視化:

#將特征數值中的min和max用散點圖畫出來
plt.plot(X.min(axis=0),'v',label='min')
plt.plot(X.max(axis=0),'^',label='max')
#設定縱坐標為對數形式
plt.yscale('log')
#設定圖注位置最佳
plt.legend(loc='best')
plt.xlabel('features')
plt.ylabel('feature magnitude')
plt.show()

顯然,量級差異較大

預處理:

#導入數據預處理工具
from sklearn.preprocessing import StandardScaler
#預處理
scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
#將預處理后的數據特征的max和min用散點圖表示出來
plt.plot(X_train_scaled.min(axis=0),'v',label='train set min')
plt.plot(X_train_scaled.max(axis=0),'^',label='train set max')
plt.plot(X_test_scaled.min(axis=0),'v',label='test set min')
plt.plot(X_test_scaled.max(axis=0),'^',label='test set max')
#設置圖注位置
plt.legend(loc='best')
plt.xlabel('scaled features')
plt.ylabel('scaled feature magnitude')
plt.show()

 

【結果分析】

 經過預處理,無論訓練集還是測試集,所有特征的最大值不會超過10,最小值趨近0,以至圖中看不到

 

用預處理的數據訓練模型:

#使用預處理后的數據重新訓練模型
for kernel in ['linear','rbf']:
 svr = SVR(kernel=kernel)
 svr.fit(X_train_scaled,y_train)
 print(kernel,'訓練集得分:',svr.score(X_train_scaled,y_train))
 print(kernel,'測試集得分:',svr.score(X_test_scaled,y_test))

 

進一步調整參數


#設置模型的C參數和gamma參數
svr = SVR(C=100,gamma=0.1)
svr.fit(X_train_scaled,y_train)
print('訓練集得分:',svr.score(X_train_scaled,y_train))
print('測試集得分:',svr.score(X_test_scaled,y_test))

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM