Python機器學習筆記:SVM(4)——sklearn實現

  上一節我學習了SVM的推導過程,下面學習如何實現SVM,具體的參考連接都在第一篇文章中,SVM四篇筆記連接爲:html

Python機器學習筆記:SVM(1)——SVM概述

Python機器學習筆記:SVM(2)——SVM核函數

Python機器學習筆記:SVM(3)——證實SVM

Python機器學習筆記:SVM(4)——sklearn實現

  對SVM的概念理清楚後,下面咱們對其使用sklearn進行實現。git

1,Sklearn支持向量機庫概述

  咱們知道SVM相對感知器而言,它能夠解決線性不可分的問題,那麼它是如何解決的呢?其思想很簡單就是對原始數據的維度變換,通常是擴維變換,使得原樣本空間中的樣本點線性不可分,可是在變維以後的空間中樣本點是線性可分的,而後再變換後的高維空間中進行分類。github

  上面將SVM再贅述了一下,下面學習sklearn中的SVM方法,sklearn中SVM的算法庫分爲兩類,一類是分類的算法庫,主要包含LinearSVC,NuSVC和SVC三個類,另外一類是迴歸算法庫,包含SVR,NuSVR和LinearSVR三個類,相關模塊都包裹在sklearn.svm模塊中。面試

  對於SVC,NuSVC和LinearSVC 三個分類的庫,SVC和NuSVC差很少,區別僅僅在於對損失的度量方式不一樣,而LinearSVC從名字就能夠看出,他是線性分類,也就是不支持各類低維到高維的核函數,僅僅支持線性核函數,對線性不可分的數據不能使用。算法

  一樣的對於SVR,NuSVR和LinearSVR 三個迴歸的類,SVR和NuSVR差很少,區別也僅僅在於對損失的度量方式不一樣。LinearSVR是線性迴歸,只能使用線性核函數。數組

  咱們使用這些類的時候,若是有經驗知道數據是線性能夠擬合的,那麼使用LinearSVC去分類或者LinearSVR去迴歸,他們不須要咱們去慢慢的調參選擇各類核函數以及對應的參數,速度也快。若是咱們對數據分佈沒有什麼經驗,通常使用SVC去分類或者SVR去迴歸,這就須要咱們選擇核函數以及對核函數調參了。app

2,回顧SVM分類算法和迴歸算法

  咱們這裏仍然先對SVM算法進行回顧,首先對於SVM分類算法,其原始形式以下:dom

  其中 n 爲樣本個數,咱們的樣本爲(x1,  y1),(x2,y2),....(xn,  yn),w,b是咱們的分離超平面的 wT*xi + b = 0的係數,ξi 爲第 i 個樣本的鬆弛係數,C 爲懲罰係數,xi (也有時候寫爲Φ(xi) 爲低維到高維的映射函數)爲樣本數。機器學習

  經過拉格朗日以及對偶化後的形式爲:函數

  其中和原始形式不一樣的 α 爲拉格朗日系數向量,<xi,  xj> 爲咱們要使用的核函數。

  對於SVM迴歸算法,(我本身沒有總結,借用劉建平老師的博客),其原始形式以下:

  其中 m 爲樣本個數,咱們的樣本爲(x1, y1),(x2,  y2),....,(xm, ym),w,b是咱們迴歸超平面 wT*xi + b = 0 的係數,ξv, ξ^ 爲第 i 個樣本的鬆弛係數, C爲懲罰係數,ε 爲損失邊界,到超平面距離小於 ε 的訓練集的點沒有損失,Φ(xi) 爲低維到高維的映射函數

  經過拉格朗日函數以及對偶後的形式爲:

  其中和原始形式不一樣的 αv, α^ 爲拉格朗日系數向量,K(xi, xj) 爲咱們要使用的核函數。

3,SVM核函數概述

  我在第二篇SVM中學習了核函數,有好幾種,最經常使用的就是線性核函數,多項式核函數,高斯核函數和Sigmoid核函數,在scikit-learn中,內置的核函數也恰好有這四種。

3.1,線性核函數(Linear Kernel)

  線性核函數表達式爲:

  就是普通的內積,LinearSVC和LinearSVR只能使用它。

3.2,多項式核函數(Polynomial Kernel)

  多項式核函數是線性不可分SVM經常使用的核函數之一,表達式爲:

  參數都須要本身調參定義,比較麻煩。

3.3,高斯核函數(Gaussian Kernel)

  高斯核函數,在SVM中也稱爲 徑向基核函數(Radial Basisi Function,RBF),它是libsvm默認的核函數,固然也是sklearn默認的核函數,表達式爲:

  其中 r 大於0,須要本身調參定義,不過通常狀況,咱們都使用高斯核函數。

3.4,Sigmoid核函數(Sigmoid Kernel)

  Sigmoid核函數也是線性不可分SVM經常使用的核函數之一,表示爲:

  其中 beta, t 都須要本身調參定義。

  通常狀況下,對於非線性數據使用默認的高斯核函數會有比較好的效果,若是你不是SVM調參高手的話,建議使用高斯核來作數據分析。

4,SVM分類算法庫參數小結

  下面咱們將具體介紹這三種分類方法都有那些參數值以及不一樣參數值的含義。

4.1, LinearSVC

  其函數原型以下:

class sklearn.svm.LinearSVC(self, penalty='l2', loss='squared_hinge', dual=True, tol=1e-4,
             C=1.0, multi_class='ovr', fit_intercept=True,
             intercept_scaling=1, class_weight=None, verbose=0,
             random_state=None, max_iter=1000)

  參數說明

  • penalty :正則化參數,L1 和L2兩種參數可選,僅LinearSVC有。默認是L2 正則化,若是咱們須要產生稀疏的話,能夠選擇L1正則化,這和線性迴歸裏面的Lasso迴歸相似
  • loss:損失函數,有「hinge」 和「squared_hinge」 兩種可選,前者又稱爲L1損失,後者稱爲L2損失,默認是「squared_hinge」,其中hinge是SVM的標準損失,squared_hinge是hinge的平方。
  • dual:是否轉化爲對偶問題求解,默認是True。這是一個布爾變量,控制是否使用對偶形式來優化算法。
  • tol:殘差收斂條件,默認是0.0001,與LR中的一致。
  • C:懲罰係數,用來控制損失函數的懲罰係數,相似於LR中的正則化係數。默認爲1,通常須要經過交叉驗證來選擇一個合適的C,通常來講,噪點比較多的時候,C須要小一些
  • multi_class:負責多分類問題中分類策略制定,有‘ovr’和‘crammer_singer’ 兩種參數值可選,默認值是’ovr’,'ovr'的分類原則是將待分類中的某一類看成正類,其餘所有歸爲負類,經過這樣求取獲得每一個類別做爲正類時的正確率,取正確率最高的那個類別爲正類;‘crammer_singer’ 是直接針對目標函數設置多個參數值,最後進行優化,獲得不一樣類別的參數值大小。
  •  fit_intercept:是否計算截距,與LR模型中的意思一致。
  • class_weight:與其餘模型中參數含義同樣,也是用來處理不平衡樣本數據的,能夠直接以字典的形式指定不一樣類別的權重,也可使用balanced參數值。若是使用「balanced」,則算法會本身計算權重,樣本量少的類別所對應的樣本權重會高,固然,若是你的樣本類別分佈沒有明顯的偏倚,則能夠無論這個係數,選擇默認的None
  • verbose:是否冗餘,默認爲False
  • random_state:隨機種子的大小
  • max_iter:最大迭代次數,默認爲1000.

懲罰係數:

  錯誤項的懲罰係數。C越大,即對分錯樣本的懲罰程度越大,所以在訓練樣本中準確率越高,可是泛化能力下降,也就是對測試數據的分類準確率下降。相反,減小C的話,允許訓練樣本中有一些誤分類錯誤樣本,泛化能力強。對於訓練樣本帶有噪音的狀況,通常採用後者,把訓練樣本集中錯誤分類的樣本做爲噪音。

4.2,NuSVC

  其函數原型以下:

class sklearn.svm.NuSVC(self, nu=0.5, kernel='rbf', degree=3, gamma='auto_deprecated',
             coef0=0.0, shrinking=True, probability=False, tol=1e-3,
             cache_size=200, class_weight=None, verbose=False, max_iter=-1,
             decision_function_shape='ovr', random_state=None)

   參數說明

  • nu:訓練偏差部分的上限和支持向量部分的下限,取值在(0,1)之間,默認是0.5,它和懲罰係數C相似,均可以控制懲罰的力度。
  • kernel:核函數,核函數是用來將非線性問題轉化爲線性問題的一種方法,默認是「rbf」核函數

      經常使用的核函數有如下幾種:

  • degree:當核函數是多項式核函數(「poly」)的時候,用來控制函數的最高次數。(多項式核函數是將低維的輸入空間映射到高維的特徵空間),這個參數只對多項式核函數有用,是指多項式核函數的階數 n。若是給的核函數參數是其餘核函數,則會自動忽略該參數。
  • gamma:核函數係數,默認是「auto」,即特徵維度的倒數。核函數係數,只對rbf  poly  sigmoid 有效。
  • coef0:核函數常數值( y = kx + b 的b值),只有「poly」和「sigmoid」 函數有,默認值是0.
  • max_iter:最大迭代次數,默認值是 -1 ,即沒有限制。
  • probability:是否使用機率估計,默認是False。
  • decision_function_shape:與「multi_class」參數含義相似,能夠選擇「ovo」 或者「ovr」(0.18版本默認是「ovo」,0.19版本爲「ovr」) OvR(one vs rest)的思想很簡單,不管你是多少元分類,咱們均可以看作二元分類,具體的作法是,對於第K類的分類決策,咱們把全部第K類的樣本做爲正例,除第K類樣本之外的全部樣本做爲負類,而後在上面作二元分類,獲得第K類的分類模型。 OvO(one vs one)則是每次在全部的T類樣本里面選擇兩類樣本出來,不妨記爲T1類和T2類,把全部的輸出爲T1 和 T2的樣本放在一塊兒,把T1做爲正例,T2 做爲負例,進行二元分類,獲得模型參數,咱們一共須要T(T-1)/2 次分類。從上面描述能夠看出,OvR相對簡單,可是分類效果略差(這裏是指大多數樣本分佈狀況,某些樣本分佈下OvR可能更好),而OvO分類相對精確,可是分類速度沒有OvR快,通常建議使用OvO以達到較好的分類效果
  • chache_size:緩衝大小,用來限制計算量大小,默認是200M,若是機器內存大,推薦使用500MB甚至1000MB

4.3,SVC

  其函數原型以下:

class sklearn.svm.SVC(self, C=1.0, kernel='rbf', degree=3, gamma='auto_deprecated',
             coef0=0.0, shrinking=True, probability=False,
             tol=1e-3, cache_size=200, class_weight=None,
             verbose=False, max_iter=-1, decision_function_shape='ovr',
             random_state=None)

  參數說明:

  • C:懲罰係數(前面有詳細學習)

  SVC和NuSVC方法基本一致,惟一區別就是損失函數的度量方式不一樣(NuSVC中的nu參數和SVC中的C參數)即SVC使用懲罰係數C來控制懲罰力度,而NuSVC使用nu來控制懲罰力度。

5,SVM迴歸算法庫參數小結

  下面咱們將具體介紹這三種分類方法都有那些參數值以及不一樣參數值的含義。

5.1, LinearSVR

  其函數原型以下:

class sklearn.svm.LinearSVR(self, epsilon=0.0, tol=1e-4, C=1.0,
             loss='epsilon_insensitive', fit_intercept=True,
             intercept_scaling=1., dual=True, verbose=0,
             random_state=None, max_iter=1000)

  參數說明

  • epsilon:距離偏差epsilon,即迴歸模型中的 epsilon,訓練集中的樣本須要知足:

  • loss:損失函數,有「hinge」 和「squared_hinge」 兩種可選,前者又稱爲L1損失,後者稱爲L2損失,默認是「squared_hinge」,其中hinge是SVM的標準損失,squared_hinge是hinge的平方。
  • dual:是否轉化爲對偶問題求解,默認是True。這是一個布爾變量,控制是否使用對偶形式來優化算法。
  • tol:殘差收斂條件,默認是0.0001,與LR中的一致。
  • C:懲罰係數,用來控制損失函數的懲罰係數,相似於LR中的正則化係數。默認爲1,通常須要經過交叉驗證來選擇一個合適的C,通常來講,噪點比較多的時候,C須要小一些
  •  fit_intercept:是否計算截距,與LR模型中的意思一致。
  • verbose:是否冗餘,默認爲False
  • random_state:隨機種子的大小
  • max_iter:最大迭代次數,默認爲1000.

5.2,NuSVR

  其函數原型以下:

class sklearn.svm.NuSVR(self, nu=0.5, C=1.0, kernel='rbf', degree=3,
             gamma='auto_deprecated', coef0=0.0, shrinking=True,
             tol=1e-3, cache_size=200, verbose=False, max_iter=-1)

   參數說明

  • nu:訓練偏差部分的上限和支持向量部分的下限,取值在(0,1)之間,默認是0.5,它和懲罰係數C相似,均可以控制懲罰的力度。
  • kernel:核函數,核函數是用來將非線性問題轉化爲線性問題的一種方法,默認是「rbf」核函數

      經常使用的核函數有如下幾種:

  • degree:當核函數是多項式核函數(「poly」)的時候,用來控制函數的最高次數。(多項式核函數是將低維的輸入空間映射到高維的特徵空間),這個參數只對多項式核函數有用,是指多項式核函數的階數 n。若是給的核函數參數是其餘核函數,則會自動忽略該參數。
  • gamma:核函數係數,默認是「auto」,即特徵維度的倒數。核函數係數,只對rbf  poly  sigmoid 有效。
  • coef0:核函數常數值( y = kx + b 的b值),只有「poly」和「sigmoid」 函數有,默認值是0.
  • chache_size:緩衝大小,用來限制計算量大小,默認是200M,若是機器內存大,推薦使用500MB甚至1000MB

5.3,SVR

  其函數原型以下:

class sklearn.svm.SVC(self, kernel='rbf', degree=3, gamma='auto_deprecated',
             coef0=0.0, tol=1e-3, C=1.0, epsilon=0.1, shrinking=True,
             cache_size=200, verbose=False, max_iter=-1)

  參數說明:

  SVR和NuSVR方法基本一致,惟一區別就是損失函數的度量方式不一樣(NuSVR中的nu參數和SVR中的C參數)即SVR使用懲罰係數C來控制懲罰力度,而NuSVR使用nu來控制懲罰力度。

6,SVM的方法與對象

6.1 方法

  三種分類的方法基本一致,因此一塊兒來講:

  • decision_function(x):獲取數據集X到分離超平面的距離
  • fit(x , y):在數據集(X,y)上使用SVM模型
  • get_params([deep]):獲取模型的參數
  • predict(X):預測數值型X的標籤
  • score(X,y):返回給定測試集合對應標籤的平均準確率

6.2  對象

  • support_:以數組的形式返回支持向量的索引
  • support_vectors_:返回支持向量
  • n_support_:每一個類別支持向量的個數
  • dual_coef:支持向量係數
  • coef_:每一個特徵係數(重要性),只有核函數是LinearSVC的是可用,叫權重參數,即w
  • intercept_:截距值(常數值),稱爲偏置參數,即b

  加粗的三個屬性是咱們經常使用的,後面會舉例說明 support_vectors_。

 

7,SVM類型算法的模型選擇

7.1 PPT總結

  這裏使用(http://staff.ustc.edu.cn/~ketang/PPT/PRLec5.pdf)的PPT進行整理。

7.2 SVM算法庫其餘調參要點

  下面再對其餘調參要點作一個小結:

  • 1,通常推薦在作訓練以前對數據進行歸一化,固然測試集的數據也要作歸一化
  • 2,在特徵數很是多的狀況下,或者樣本數遠小於特徵數的時候,使用線性核,效果就很好了,而且只須要選擇懲罰係數C便可
  • 3,在選擇核函數的時候,若是線性擬合效果很差,通常推薦使用默認的高斯核(rbf),這時候咱們主要對懲罰係數C和核函數參數 gamma 進行調參,通過多輪的交叉驗證選擇合適的懲罰係數C和核函數參數gamma。
  • 4,理論上高斯核不會比線性覈查,可是這個理論就創建在要花費更多的時間上調參上,因此實際上能用線性核解決的問題咱們儘可能使用線性核函數

   在SVM中,其中最重要的就是核函數的選取和參數選擇了,固然這個須要大量的經驗來支撐,這裏幾個例子只是本身網上找的SVM的小例子。

8,SVM調參實例1

  下面學習支持向量機的使用方法以及一些參數的調整,支持向量機的原理就是將低維不可分問題轉換爲高維可分問題。這裏再也不贅述。

8.1  線性可分支持向量機

  首先作一個簡單的線性可分的例子,這裏直接使用sklearn.datasets.make_blobs 生成數據。生成數據代碼以下:

# 生成數據集
from sklearn.datasets.samples_generator import make_blobs
from matplotlib import pyplot as plt

# n_samples=50 表示取50個點,centers=2表示將數據分爲兩類
X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)
# 畫圖形
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
plt.show()

   咱們畫圖展現以下:

  咱們嘗試繪製分離兩組數據的直線,從而建立分類模型,對於這裏所示的數據,這是咱們能夠手動完成的任務。可是立馬能夠看出有不少分界線能夠完美的區分兩個類。

   下面畫出決策邊界。

# 生成數據集
from sklearn.datasets.samples_generator import make_blobs
from matplotlib import pyplot as plt
import numpy as np

# n_samples=50 表示取50個點,centers=2表示將數據分爲兩類
X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)

# 畫圖形
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
# 線性等分詳細
xfit = np.linspace(-1, 3.5)
plt.plot([0.6], [2.1], 'x', color='red', markeredgewidth=2, markersize=10)

for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]:
    plt.plot(xfit, m * xfit + b, '-k')

plt.show()

   圖以下:

  (注意:這三條直線是我隨便畫的,其實你可使用Logistic迴歸,線性迴歸等分類,畫出線,我這裏是爲了方便)

   這裏是三條不一樣的分割直線,而且這些分割直線可以徹底區分這些樣例。可是根據支持向量機的思想,哪一條直線是最優的分割線呢?支持向量機並非簡單的繪製一條直線,而是畫出邊距爲必定寬度的直線,直到最近的點。

  下面咱們對直線進行加粗,代碼以下:

# 生成數據集
from sklearn.datasets.samples_generator import make_blobs
from matplotlib import pyplot as plt
import numpy as np

# n_samples=50 表示取50個點,centers=2表示將數據分爲兩類
X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)

# 畫圖形
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
# 線性等分詳細
xfit = np.linspace(-1, 3.5)
plt.plot([0.6], [2.1], 'x', color='red', markeredgewidth=2, markersize=10)

for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]:
    yfit = m * xfit + b
    plt.plot(xfit, yfit, '-k')
    plt.fill_between(xfit, yfit - d, yfit + d, edgecolor='none',
                     color='#AAAAAA', alpha=0.4)  # alpha爲透明度
plt.show()

   如圖所示:

   在支持向量機中,邊距最大化的直線是咱們將選擇的最優模型。支持向量機是這種最大邊距估計器的一個例子。

  接下來,咱們訓練一個基本的SVM,咱們使用sklearn的支持向量機,對這些數據訓練SVM模型。目前咱們將使用一個線性核並將C參數設置爲一個默認的數值。以下:

from sklearn.svm import SVC  # Support Vector Classifier

model = SVC(kernel='linear') # 線性核函數
model.fit(X, y)

   咱們順便看看SVC的全部參數狀況:

SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
    kernel='linear', max_iter=-1, probability=False, random_state=None,
    shrinking=True, tol=0.001, verbose=False)

   爲了更好展示這裏發生的事情,下面咱們建立一個輔助函數,爲咱們繪製SVM的決策邊界。

def plot_SVC_decision_function(model, ax=None, plot_support=True):
    '''Plot the decision function for a 2D SVC'''
    if ax is None:
        ax = plt.gca()  #get子圖
    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    # create grid to evaluate model
    x = np.linspace(xlim[0], xlim[1], 30)
    y = np.linspace(ylim[0], ylim[1], 30)
    # 生成網格點和座標矩陣
    Y, X = np.meshgrid(y, x)
    # 堆疊數組
    xy = np.vstack([X.ravel(), Y.ravel()]).T
    P = model.decision_function(xy).reshape(X.shape)

    # plot decision boundary and margins
    ax.contour(X, Y, P, colors='k', levels=[-1, 0, 1],
               alpha=0.5, linestyles=['--', '-', '--'])  # 生成等高線 --

    # plot support vectors
    if plot_support:
        ax.scatter(model.support_vectors_[:, 0],
                   model.support_vectors_[:, 1],
                   s=300, linewidth=1, facecolors='none')

    ax.set_xlim(xlim)
    ax.set_ylim(ylim)

   下面繪製決策邊界:

def train_SVM():
    # n_samples=50 表示取50個點,centers=2表示將數據分爲兩類
    X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)

    # 線性核函數
    model = SVC(kernel='linear')
    model.fit(X, y)
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(model)
    plt.show()
    return X, y

   結果如圖所示:

   這是最大化兩組點之間的間距的分界線,那中間這條線就是咱們最終的決策邊界了。請注意:一些訓練點碰到了邊緣,如圖所示,在兩個邊界上包含兩個紅點和一個黃點,因此這三個點又稱爲支持向量,是 alpha 值不爲零的,這些點是這種擬合的關鍵要素,被稱爲支持向量。在sklearn中,這些點存儲在分類器的 support_vectors_ 屬性中。

  咱們經過下面代碼能夠得出支持向量的結果。

    print(model.support_vectors_)
    '''
    [[0.44359863 3.11530945]
     [2.33812285 3.43116792]
     [2.06156753 1.96918596]]
    '''

  在支持向量機只有位於支持向量上面的點纔會對決策邊界有影響,也就是說無論有多少的點是非支持向量,那對最終的決策邊界都不會產生任何影響。咱們能夠看到這一點,例如,若是咱們繪製該數據集的前 60個點和前120個點得到的模型:

def plot_svm(N=10, ax=None):
    X, y = make_blobs(n_samples=200, centers=2, random_state=0, cluster_std=0.6)
    X, y = X[:N], y[:N]
    model = SVC(kernel='linear')
    model.fit(X, y)

    ax = ax or plt.gca()
    ax.scatter(X[:, 0], X[:, 1], c=y, cmap='autumn')
    ax.set_xlim(-1, 4)
    ax.set_ylim(-1, 6)
    plot_SVC_decision_function(model, ax)

if __name__ == '__main__':
    # train_SVM()
    fig, ax = plt.subplots(1, 2, figsize=(16, 6))
    fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    for axi, N in zip(ax, [60, 120]):
        plot_svm(N, axi)
        axi.set_title('N = {0}'.format(N))

   結果如圖所示:

  上面就是咱們繪製的該數據集前60個點和前120個點得到的模型,能夠發現不管使用60,仍是使用120個數據點,決策邊界都沒有發生變換,全部只要支持向量沒變,其餘的數據怎麼加都無所謂。

   這個分類器成功的關鍵在於:爲了擬合,只有支持向量的位置是最重要的;任何遠離邊距的點都不會影響擬合的結果,邊界以外的點不管有多少都不會對其形成影響,也就是說無論有多少點是非支持向量,對最終的決策邊界都不會產生任何影響。

8.2 線性不可分支持向量機

  下面引入核函數,來看看核函數的威力,首先咱們導入一個線性不可分的數據集。

def train_svm_plus():
    # 二維圓形數據 factor 內外圓比例(0, 1)
    X, y = make_circles(100, factor=0.1, noise=0.1)
    
    clf = SVC(kernel='linear')
    clf.fit(X, y)
    
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(clf, plot_support=False)

   數據集如圖所示:

   很明顯,用線性分類器不管怎麼畫線也不能分好,那咋辦呢?下面試試高斯核變換吧。在進行核變換以前,先看看數據在高維空間下的映射:

def plot_3D(X, y, elev=30, azim=30):
    # 咱們加入了新的維度 r
    r = np.exp(-(X ** 2).sum(1))
    ax = plt.subplot(projection='3d')
    ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
    ax.view_init(elev=elev, azim=azim)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')


if __name__ == '__main__':
    X, y = train_svm_plus()
    plot_3D(elev=30, azim=30, X=X, y=y)

   畫出三維圖形,如圖所示:

   見證核變換威力的時候到了,引入徑向基函數(也叫高斯核函數),進行核變換:

def train_svm_plus():
    # 二維圓形數據 factor 內外圓比例(0, 1)
    X, y = make_circles(100, factor=0.1, noise=0.1)
    # 加入徑向基函數
    clf = SVC(kernel='rbf')
    clf.fit(X, y)

    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(clf, plot_support=False)
    return X, y

   獲得的SVM模型爲:

SVC(C=1000000.0, cache_size=200, class_weight=None, coef0=0.0,
    decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
    kernel='rbf', max_iter=-1, probability=False, random_state=None,
    shrinking=True, tol=0.001, verbose=False)

   再次進行分類任務,代碼以下:

def train_svm_plus():
    # 二維圓形數據 factor 內外圓比例(0, 1)
    X, y = make_circles(100, factor=0.1, noise=0.1)
    # 加入徑向基函數
    clf = SVC(kernel='rbf')
    clf.fit(X, y)

    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(clf, plot_support=False)
    plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
                s=300, lw=1, facecolors='none')
    return X, y

   分類結果如圖所示:

   能夠清楚的看到效果很好,咱們將線性不可分的兩對數據分割開來。使用這種核支持向量機,咱們學習一個合適的非線性決策邊界。這種核變換策略在機器學習中常常被使用。

8.3 線性近似可分支持向量機——軟間隔問題

  SVM模型有兩個很是重要的參數C與gamma,其中C是懲罰係數,即對偏差的寬容忍,C越高,說明越不能容忍出現偏差,容易過擬合。C越小,容易欠擬合。C過大或太小,泛化能力變差。

  gamma 是選擇 RBF 函數做爲kernel後,該函數自帶的一個參數。隱含的決定了數據映射到新的特徵空間後的分佈,gamma越大,支持向量越小,gamma值越小,支持向量越多。

  下面咱們分別調劑一下C和gamma來看一下對結果的影響。

  首先咱們調節C,先作一個有噪音的數據分佈

# n_samples=50 表示取50個點,centers=2表示將數據分爲兩類
X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)

plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')

   結果如圖所示:

   上面的分佈看起來要劃分彷佛有點困難,因此咱們能夠進行軟件各調整看看。

# n_samples=50 表示取50個點,centers=2表示將數據分爲兩類
X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)

fig, ax = plt.subplots(1, 2, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)

for axi, C in zip(ax, [10.0, 0.1]):
    model = SVC(kernel='linear', C=C)
    model.fit(X, y)
    axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(model, axi)
    axi.scatter(model.support_vectors_[:, 0],
                model.support_vectors_[:, 1],
                s=300, lw=1, facecolors='none')
    axi.set_title('C={0:.1f}'.format(C), size=14)

   結果如圖所示:

   能夠看到左邊這幅圖C值比較大,要求比較嚴格,不能分錯東西,隔離帶中沒有進入任何一個點,可是隔離帶的距離比較小,泛化能力比較差。右邊這幅圖C值比較小,要求相對來講比較鬆一點,隔離帶較大,可是隔離帶中進入了不少的黃點和紅點。那麼C大一些好仍是小一些好呢?這須要考慮實際問題,能夠進行K折交叉驗證來獲得最合適的C值。

   下面再看看另外一個參數gamma值,這個參數值只是在高斯核函數裏面纔有,這個參數控制着模型的複雜程度,這個值越大,模型越複雜,值越小,模型就越精簡。

  代碼以下:

# n_samples=50 表示取50個點,centers=2表示將數據分爲兩類
X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)

fig, ax = plt.subplots(1, 3, figsize=(16, 6))
fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)

for axi, gamma in zip(ax, [10.0, 1.0, 0.1]):
    model = SVC(kernel='rbf', gamma=gamma)
    model.fit(X, y)
    axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plot_SVC_decision_function(model, axi)
    axi.scatter(model.support_vectors_[:, 0],
                model.support_vectors_[:, 1],
                s=300, lw=1, facecolors='none')
    axi.set_title('gamma={0:.1f}'.format(gamma), size=14)

   結果以下:

   能夠看出,當這個參數較大時,能夠看出模型分類效果很好,可是泛化能力不太好。當這個參數較小時,能夠看出模型裏面有些分類是錯誤的,可是這個泛化能力更好,通常也應有的更多。

  經過這個簡單的例子,咱們對支持向量機在SVM中的基本使用,以及軟間隔參數的調整,還有核函數變換和gamma值等一些參數的比較。

  完整代碼請參考個人GitHub(地址:https://github.com/LeBron-Jian/MachineLearningNote)。

9,SVM調參實例2

  下面咱們用一個實例學習SVM RBF分類調參(此例子是劉建平老師的博客內容,連接在文後)。

  首先,咱們生成一些隨機數據,爲了讓數據難一點,咱們加入了一些噪音,代碼以下:

# _*_coding:utf-8_*_
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_moons, make_circles
from sklearn.preprocessing import StandardScaler
from matplotlib.colors import ListedColormap

X, y = make_circles(noise=0.2, factor=0.5, random_state=1)
# 對數據進行標準化
X = StandardScaler().fit_transform(X)

# 下面看看數據長什麼樣子
cm = plt.cm.RdBu
cm_birght = ListedColormap(['#FF0000', '#0000FF'])
ax = plt.subplot()

ax.set_title('Input data')
# plot the training points
ax.scatter(X[:, 0], X[:, 1], c=y, cmap=cm_birght)
ax.set_xticks([])
ax.set_yticks([])
plt.tight_layout()
plt.show()

  上面代碼對數據作了標準化,注意作標準化和不作標準化的差別(不必定全部的數據標準化後的效果更好,可是絕大多數確實更好)。好比下圖:

  咱們看,當不作數據標準化,咱們x1的取值範圍由0~90不等,當作了數據標準化以後,其取值範圍就在-2~2之間了。說明標準化的做用仍是很明顯的,很少贅述,下面繼續。

   生成的數據以下(可能下一次運行,就變了哈):

   知道數據長什麼樣了,下面咱們要對這個數據集進行SVM RBF分類了,分類時咱們採用了網格搜索,在C=(0.1, 1, 10)和 gamma=(1, 0.1, 0.01)造成的9種狀況中選擇最好的超參數,咱們用了4折交叉驗證。這裏只是一個例子,實際運用中,可能須要更多的參數組合來進行調參。

  代碼及其結果以下:

# 網格搜索尋找最佳參數
grid = GridSearchCV(SVC(), param_grid={'C': [0.1, 1, 10], 'gamma': [1, 0.1, 0.01]}, cv=4)
grid.fit(X, y)
print("The best parameters are %s with a score of %0.2f"
      % (grid.best_params_, grid.best_score_))
# The best parameters are {'C': 10, 'gamma': 0.1} with a score of 0.91

   就是說,咱們經過網格搜索,在咱們給定的9組超參數組合中,C=10, gamma=0.1 分數最高,這就是咱們最終的參數候選。

  下面咱們看看SVM分類後的可視化,這裏咱們把上面九種組合各個訓練後,經過對網格里的點預測來標色,觀察分類的效果圖,代碼以下:

# SVM 分類後進行可視化
x_min, x_max = X[:, 0].min(), X[:, 0].max() + 1
y_min, y_max = X[:, 1].min(), X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
                     np.arange(y_min, y_max, 0.02))

for i, C in enumerate((0.1, 1, 10)):
    for j, gamma in enumerate((1, 0.1, 0.01)):
        # plt.subplot()
        clf = SVC(C=C, gamma=gamma)
        clf.fit(X, y)
        Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

        # put the result into a color plot
        Z = Z.reshape(xx.shape)
        plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm)

        # Plot also the training points
        plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.coolwarm)

        plt.xlim(xx.min(), xx.max())
        plt.ylim(yy.min(), yy.max())
        plt.xticks(())
        plt.yticks(())
        plt.xlabel(" gamma=" + str(gamma) + " C=" + str(C))
        plt.show()

   結果以下:

   從我測試的結果來看,劉老師的代碼仍是有一點點問題,顯示不出九個,因此這裏我打算從新學習一個例子。

   完整代碼請參考個人GitHub(地址:https://github.com/LeBron-Jian/MachineLearningNote)。

10,SVM調參實例3(非線性支持向量機)

  非線性的話,咱們一方面能夠利用核函數構造出非線性,一方面咱們能夠本身構造非線性。下面首先學習本身構造非線性。

10.1 本身構造非線性數據

  咱們構造非線性數據的代碼以下:

# _*_coding:utf-8_*_
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.datasets import make_moons, make_circles
from sklearn.preprocessing import StandardScaler

X1D = np.linspace(-4, 4, 9).reshape(-1, 1)
# np.c_是按行鏈接兩個矩陣,就是把兩矩陣左右相加,要求行數相等。
X2D = np.c_[X1D, X1D ** 2]
y = np.array([0, 0, 1, 1, 1, 1, 1, 0, 0])

plt.figure(figsize=(11, 4))

plt.subplot(121)
plt.grid(True, which='both')
plt.axhline(y=0, color='k')
plt.plot(X1D[:, 0][y == 0], np.zeros(4), 'bs')
plt.plot(X1D[:, 0][y == 1], np.zeros(5), 'g*')
plt.gca().get_yaxis().set_ticks([])
plt.xlabel(r'$x_1$', fontsize=20)
plt.axis([-4.5, 4.5, -0.2, 0.2])

plt.subplot(122)
plt.grid(True, which='both')
plt.axhline(y=0, color='k')
plt.axvline(x=0, color='k')
plt.plot(X2D[:, 0][y == 0], X2D[:, 1][y == 0], 'bs')
plt.plot(X2D[:, 0][y == 1], X2D[:, 1][y == 1], 'g*')
plt.xlabel(r'$x_1$', fontsize=20)
plt.ylabel(r'$x_2$', fontsize=20, rotation=0)
plt.gca().get_yaxis().set_ticks([0, 4, 8, 12, 16])
plt.plot([-4.5, 4.5], [6.5, 6.5], 'r--', linewidth=3)
plt.axis([-4.5, 4.5, -1, 17])

plt.subplots_adjust(right=1)
plt.show()

   圖以下:

  從這個圖能夠看到,咱們利用對數據的變換,能夠對數據的維度增長起來,變成非線性。

   假設咱們不使用核函數的思想,先對數據作變換,看能不能達到一個比較好的結果,首先咱們作一個測試的數據,代碼以下:

# _*_coding:utf-8_*_
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons

X, y = make_moons(n_samples=100, noise=0.15, random_state=42)


def plot_dataset(X, y, axes):
    plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs')
    plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g*')
    plt.axis(axes)
    plt.grid(True, which='both')
    plt.xlabel(r'$x_1$', fontsize=20)
    plt.ylabel(r'$x_2$', fontsize=20, rotation=0)

plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()

   生成的圖以下:

   下面代碼將兩類數據分出來了:

Polynomial_svm_clf = Pipeline((('poly_features', PolynomialFeatures(degree=3)),
                               ('scaler', StandardScaler()),
                               ('svm_clf', LinearSVC(C=10))
                               ))
Polynomial_svm_clf.fit(X, y)

def plot_predictions(clf, axes):
    x0s = np.linspace(axes[0], axes[1], 100)
    x1s = np.linspace(axes[2], axes[3], 100)
    x0, x1 = np.meshgrid(x0s, x1s)
    X = np.c_[x0.ravel(), x1.ravel()]
    y_pred = clf.predict(X).reshape(x0.shape)
    # 下面填充一個等高線, alpha表示透明度
    plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)

plot_predictions(Polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()

   結果以下:

   從結果來看,咱們使用線性支持向量機將兩類數據區分開是沒有問題的。而最重要的是咱們如何使用核函數呢?下面繼續學習

10.2 如何對非線性數據進行核函數的變換

   咱們首先看svm的官方文檔:

   核函數默認是 rbf,也就是徑向基核函數。下面分別演示核函數。

  咱們依舊拿上面的數據,首先取核函數爲 多項式核 看看效果(這裏對比的是多項式核的degree,也就是多項式核的維度):

# _*_coding:utf-8_*_
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC, SVC

X, y = make_moons(n_samples=100, noise=0.15, random_state=42)

def plot_dataset(X, y, axes):
    plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs')
    plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g*')
    plt.axis(axes)
    plt.grid(True, which='both')
    plt.xlabel(r'$x_1$', fontsize=20)
    plt.ylabel(r'$x_2$', fontsize=20, rotation=0)

# 展現圖像
def plot_predictions(clf, axes):
    x0s = np.linspace(axes[0], axes[1], 100)
    x1s = np.linspace(axes[2], axes[3], 100)
    x0, x1 = np.meshgrid(x0s, x1s)
    X = np.c_[x0.ravel(), x1.ravel()]
    y_pred = clf.predict(X).reshape(x0.shape)
    # 下面填充一個等高線, alpha表示透明度
    plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)


Poly_kernel_svm_clf = Pipeline((('scaler', StandardScaler()),
                                ('svm_clf', SVC(kernel='poly', degree=3, coef0=1, C=5))
                                ))
Poly_kernel_svm_clf.fit(X, y)
# 下面作一個對比試驗,看看degree的值的變換
Poly_kernel_svm_clf_plus = Pipeline((('scaler', StandardScaler()),
                                     ('svm_clf', SVC(kernel='poly', degree=10, coef0=1, C=5))
                                     ))
Poly_kernel_svm_clf_plus.fit(X, y)

plt.subplot(121)
plot_predictions(Poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.title(r'$d=3, r=1, C=5$', fontsize=18)

plt.subplot(122)
plot_predictions(Poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.title(r'$d=10, r=100, C=5$', fontsize=18)
plt.show()

   結果以下:

   咱們是把數據映射到高維空間,而後再拿回來看效果,實際上並無去高維空間作運算。。這就是咱們想要展現的多項式核函數,下面學習高斯核函數。

  高斯核函數:利用類似度來變換特徵

  咱們選擇一份一維數據,並在 x1=-2,  x1=1 處爲其添加兩個高斯函數,接下來讓我門將類似度函數定義爲 gamma=0.3 的徑向基核函數(RBF):

  例如: x1 = -1:它位於距第一個地標距離爲1的地方,距離第二個地標距離爲2。所以其新特徵爲 x2 = exp(-0.3*1^2)=0.74 ,而且  x3 = exp(-0.3 * 2^2)=0.3。

  圖以下:

   這裏說一下,就是假設 X2和 X3爲兩個高斯函數,咱們看 x這個點距離兩個地標的距離。離高斯分佈的中心越近,就愈加生什麼。。通過計算出來距離兩個地標的距離,咱們就能夠依此類推,來計算全部一維座標相對應的二維座標。(二維座標就是距離兩個高斯函數的距離)。

  咱們這裏用類似度特徵來替換本來的特徵。

  下面咱們作一個實驗,咱們只看 gamma的變換,高斯函數的開口變化:

X1D = np.linspace(-4, 4, 9).reshape(-1, 1)
X2D = np.c_[X1D, X1D ** 2]

X, y = make_moons(n_samples=100, noise=0.15, random_state=42)


def gaussian_rbf(x, landmark, gamma):
    return np.exp(-gamma * np.linalg.norm(x - landmark, axis=1) ** 2)


gamma = 0.3

# 下面進行訓練,獲得一個支持向量機的模型(這裏咱們沒有訓練,直接畫出來了)
# 由於測試的數據是咱們本身寫的,爲了方便,咱們本身畫出來,固然你也能夠本身作
xls = np.linspace(-4.5, 4.5, 200).reshape(-1, 1)
x2s = gaussian_rbf(xls, -2, gamma)
x3s = gaussian_rbf(xls, 1, gamma)

XK = np.c_[gaussian_rbf(X1D, -2, gamma), gaussian_rbf(X2D, 1, gamma)]
yk = np.array([0, 0, 1, 1, 1, 1, 1, 0, 0])

plt.figure(figsize=(11, 4))

# plt.subplot(121)
plt.grid(True, which='both')
plt.axhline(y=0, color='k')
plt.scatter(x=[-2, 1], y=[0, 0], s=150, alpha=0.5, c='red')
plt.plot(X1D[:, 0][yk == 0], np.zeros(4), 'bs')
plt.plot(X1D[:, 0][yk == 1], np.zeros(5), 'g*')
plt.plot(xls, x2s, 'g--')
plt.plot(xls, x3s, 'b:')
plt.gca().get_yaxis().set_ticks([0, 0.25, 0.5, 0.75, 1])
plt.xlabel(r'$x_1$', fontsize=20)
plt.ylabel(r'Similarity', fontsize=14)

plt.annotate(r'$\mathbf{x}$',
             xy=(X1D[3, 0], 0),
             xytest=(-0.5, 0.20),
             ha='center',
             arrowprops=dict(facecolor='black', shrink=0.1),
             fontsize=18,
             )
plt.text(-2, 0.9, "$x_2$", ha='center', fontsize=20)
plt.text(1, 0.9, "$x_3$", ha='center', fontsize=20)
plt.axis([-4.5, 4.5, -0.1, 1.1])

   結果以下(下面咱們分別調試gamma):

   理論狀況下,咱們會獲得怎麼維特徵呢?能夠對每個實例(樣本數據點)建立一個地標,此時會將mn 的訓練集轉換成 mm 的訓練集(m表示樣本個數,n表示特徵維度個數)。

  SVM中利用核函數的計算技巧,大大下降了計算複雜度

  • 增長gamma 使高斯曲線變窄,所以每一個實例的影響範圍都較小,決策邊界最終變得不規則,在個別實例周圍擺動
  • 減小gamma 使高斯曲線變寬,所以實例具備更大的影響範圍,而且決策邊界更加平滑

   下面作一個對比試驗(gamma值(0.1  0.5), C值(0.001, 1000)):

rbf_kernel_svm_clf = Pipeline((('scaler', StandardScaler()),
                               ('svm_clf', SVC(kernel='rbf', gamma=5, C=0.001))
                               ))

gamma1, gamma2 = 0.1, 5
C1, C2 = 0.001, 1000
hyperparams = (gamma1, C1), (gamma1, C2), (gamma2, C1), (gamma2, C2)

svm_clfs = []
for gamma, C in hyperparams:
    rbf_kernel_svm_clf.fit(X, y)
    svm_clfs.append(rbf_kernel_svm_clf)

plt.figure(figsize=(11, 7))

for i, svm_clfs in enumerate(svm_clfs):
    plt.subplot(221 + i)
    plot_predictions(svm_clfs, [-1.5, 2.5, -1, 1.5])
    plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
    gamma, C = hyperparams[i]
    plt.title(r'$\gamma={}, C={}$'.format(gamma, C), fontsize=16)
plt.show()

   結果以下:

   咱們看第一幅圖,邊界比較平穩,沒有過擬合的風險,咱們看當 gamma比較大的時候,過擬合的風險卻比較大了。因此說最終咱們看的仍是高斯函數的開口大仍是小,大一點,也就是gamma小,過擬合風險大,反之同理。

  完整代碼請參考個人GitHub(地址:https://github.com/LeBron-Jian/MachineLearningNote)。

完整代碼及其數據,請移步小編的GitHub

  傳送門:請點擊我

  若是點擊有誤:https://github.com/LeBron-Jian/MachineLearningNote

 

 

參考文獻:https://blog.csdn.net/BIT_666/article/details/79979580

https://www.cnblogs.com/tonglin0325/p/6107114.html

https://cloud.tencent.com/developer/article/1146077

https://www.cnblogs.com/xiaoyh/p/11604168.html

https://www.cnblogs.com/pinard/p/6126077.html

https://www.cnblogs.com/pinard/p/6117515.html

相關文章
相關標籤/搜索