上一節我學習了SVM的推導過程,下面學習如何實現SVM,具體的參考連接都在第一篇文章中,SVM四篇筆記連接爲:html
對SVM的概念理清楚後,下面咱們對其使用sklearn進行實現。git
咱們知道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
咱們這裏仍然先對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) 爲咱們要使用的核函數。
我在第二篇SVM中學習了核函數,有好幾種,最經常使用的就是線性核函數,多項式核函數,高斯核函數和Sigmoid核函數,在scikit-learn中,內置的核函數也恰好有這四種。
線性核函數表達式爲:
就是普通的內積,LinearSVC和LinearSVR只能使用它。
多項式核函數是線性不可分SVM經常使用的核函數之一,表達式爲:
參數都須要本身調參定義,比較麻煩。
高斯核函數,在SVM中也稱爲 徑向基核函數(Radial Basisi Function,RBF),它是libsvm默認的核函數,固然也是sklearn默認的核函數,表達式爲:
其中 r 大於0,須要本身調參定義,不過通常狀況,咱們都使用高斯核函數。
Sigmoid核函數也是線性不可分SVM經常使用的核函數之一,表示爲:
其中 beta, t 都須要本身調參定義。
通常狀況下,對於非線性數據使用默認的高斯核函數會有比較好的效果,若是你不是SVM調參高手的話,建議使用高斯核來作數據分析。
下面咱們將具體介紹這三種分類方法都有那些參數值以及不一樣參數值的含義。
其函數原型以下:
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)
參數說明
懲罰係數:
錯誤項的懲罰係數。C越大,即對分錯樣本的懲罰程度越大,所以在訓練樣本中準確率越高,可是泛化能力下降,也就是對測試數據的分類準確率下降。相反,減小C的話,允許訓練樣本中有一些誤分類錯誤樣本,泛化能力強。對於訓練樣本帶有噪音的狀況,通常採用後者,把訓練樣本集中錯誤分類的樣本做爲噪音。
其函數原型以下:
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)
參數說明:
經常使用的核函數有如下幾種:
其函數原型以下:
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)
參數說明:
SVC和NuSVC方法基本一致,惟一區別就是損失函數的度量方式不一樣(NuSVC中的nu參數和SVC中的C參數)即SVC使用懲罰係數C來控制懲罰力度,而NuSVC使用nu來控制懲罰力度。
下面咱們將具體介紹這三種分類方法都有那些參數值以及不一樣參數值的含義。
其函數原型以下:
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)
參數說明
其函數原型以下:
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)
參數說明:
經常使用的核函數有如下幾種:
其函數原型以下:
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來控制懲罰力度。
三種分類的方法基本一致,因此一塊兒來講:
加粗的三個屬性是咱們經常使用的,後面會舉例說明 support_vectors_。
這裏使用(http://staff.ustc.edu.cn/~ketang/PPT/PRLec5.pdf)的PPT進行整理。
下面再對其餘調參要點作一個小結:
在SVM中,其中最重要的就是核函數的選取和參數選擇了,固然這個須要大量的經驗來支撐,這裏幾個例子只是本身網上找的SVM的小例子。
下面學習支持向量機的使用方法以及一些參數的調整,支持向量機的原理就是將低維不可分問題轉換爲高維可分問題。這裏再也不贅述。
首先作一個簡單的線性可分的例子,這裏直接使用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個數據點,決策邊界都沒有發生變換,全部只要支持向量沒變,其餘的數據怎麼加都無所謂。
這個分類器成功的關鍵在於:爲了擬合,只有支持向量的位置是最重要的;任何遠離邊距的點都不會影響擬合的結果,邊界以外的點不管有多少都不會對其形成影響,也就是說無論有多少點是非支持向量,對最終的決策邊界都不會產生任何影響。
下面引入核函數,來看看核函數的威力,首先咱們導入一個線性不可分的數據集。
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
分類結果如圖所示:
能夠清楚的看到效果很好,咱們將線性不可分的兩對數據分割開來。使用這種核支持向量機,咱們學習一個合適的非線性決策邊界。這種核變換策略在機器學習中常常被使用。
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)。
下面咱們用一個實例學習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)。
非線性的話,咱們一方面能夠利用核函數構造出非線性,一方面咱們能夠本身構造非線性。下面首先學習本身構造非線性。
咱們構造非線性數據的代碼以下:
# _*_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()
結果以下:
從結果來看,咱們使用線性支持向量機將兩類數據區分開是沒有問題的。而最重要的是咱們如何使用核函數呢?下面繼續學習
咱們首先看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值(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)。
傳送門:請點擊我
若是點擊有誤: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