【用Sklearn進行機器學習】第三篇 - 深入監督學習:支持向量機
來自專欄 用Sklearn進行機器學習
之前我們已經介紹了監督學習。有許多監督學習演算法,這裡我們介紹一種最強大且有意思的一種方法:支持向量機(SVMs)。
%matplotlib inlineimport numpy as npimport matplotlib.pyplot as pltfrom scipy import stats# use seaborn plotting defaultsimport seaborn as sns; sns.set()# eliminate warningsdef warn(*args, **kwargs): passimport warningswarnings.warn = warn
支持向量機的動機
支持向量機(SVMs)是一種強大的監督學習演算法,它可以用來進行分類和回歸。SVMs是一種判別式分類器:用它可以在數據集之間劃分邊界。
from sklearn.datasets.samples_generator import make_blobsX, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.60)plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=spring);
一個判別式分類器試圖在兩個不同的數據集之間畫一條直線。但我們馬上發現,這類直線是有問題的,我們可以畫出好幾條直線來完美區分這兩類數據:
xfit = np.linspace(-1, 3.5)plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=spring)for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]: plt.plot(xfit, m * xfit + b, -k)plt.xlim(-1, 3.5);
在這些樣本中,我們有三種分割方式。到底採用哪種分割方式,才能適應新的數據,這個真的很糾結!
我們如何才能改進它呢?
支持向量機:最大化Margin
支持向量機可以解決這個問題。
支持向量機不僅僅畫一條直線,還考慮了和這些直線相關的region,下面我們來看個例子。xfit = np.linspace(-1, 3.5)plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=spring)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)plt.xlim(-1, 3.5);
注意如果我們想最大化這個region,中間的直線貌似是最好的選擇。
這是支持向量機直觀表現,它通過數據集之間的margin來優化線性判別模型。擬合一個支持向量機
現在我們將對這些點擬合一個支持向量機分類器。儘管SVM模型的數學細節很有意思,但是這裡不會涉及。我們把sklearn中SVM演算法當做一個黑盒來完成上面的任務。
from sklearn.svm import SVC # "Support Vector Classifier"clf = SVC(kernel=linear)clf.fit(X, y)SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0, decision_function_shape=None, degree=3, gamma=auto, kernel=linear, max_iter=-1, probability=False, random_state=None, shrinking=True, tol=0.001, verbose=False)
為了更好的可視化,我們寫了一個函數來來繪製SVM決策邊界:
def plot_svc_decision_function(clf, ax=None): """Plot the decision function for a 2D SVC""" if ax is None: ax = plt.gca() x = np.linspace(plt.xlim()[0], plt.xlim()[1], 30) y = np.linspace(plt.ylim()[0], plt.ylim()[1], 30) Y, X = np.meshgrid(y, x) P = np.zeros_like(X) for i, xi in enumerate(x): for j, yj in enumerate(y): P[i, j] = clf.decision_function([xi, yj]) # plot the margins ax.contour(X, Y, P, colors=k, levels=[-1, 0, 1], alpha=0.5, linestyles=[--, -, --])plt.scatter(X[:, 0].reshape(-1, 1), X[:, 1].reshape(-1, 1), c=y, s=50, cmap=spring)plot_svc_decision_function(clf);
注意虛線上面有一對點落在上面,這些點是這次擬合的關鍵,被稱為支持向量。在sklearn中,它們被存儲在分類器的support_vectors_
屬性中。
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=spring)plot_svc_decision_function(clf)plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=200, facecolors=none);
讓我們用IPython的interact
功能探索一下,點的分布是如何影響支持向量和判別擬合的。
from IPython.html.widgets import interactdef plot_svm(N=10): X, y = make_blobs(n_samples=200, centers=2, random_state=0, cluster_std=0.60) X = X[:N] y = y[:N] clf = SVC(kernel=linear) clf.fit(X, y) plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=spring) plt.xlim(-1, 4) plt.ylim(-1, 6) plot_svc_decision_function(clf, plt.gca()) plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=200, facecolors=none)interact(plot_svm, N=[10, 200], kernel=linear);
注意如果你移動其他的任何點,如果它們沒有越過決策邊界,就不會對分類結果有影響。
進一步看下Kernel方法
SVM還有一項很有用的特性叫做kernels。為了利用這個特性,我們看一些不能線性分割的數據:
from sklearn.datasets.samples_generator import make_circlesX, y = make_circles(100, factor=.1, noise=.1)clf = SVC(kernel=linear).fit(X, y)plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=spring)plot_svc_decision_function(clf);
很明顯,只有非線性的才能劃分這些數據。一種方式是我們採用kernel,它能對輸入數據做些變換。例如,我們能用的一個簡單模型叫做徑向基函數核
r = np.exp(-(X[:, 0] ** 2 + X[:, 1] ** 2))
如果我們繪製出我們的數據,我們能看到它的效果。
from mpl_toolkits import mplot3ddef plot_3D(elev=30, azim=30): ax = plt.subplot(projection=3d) ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap=spring) ax.view_init(elev=elev, azim=azim) ax.set_xlabel(x) ax.set_ylabel(y) ax.set_zlabel(r)interact(plot_3D, elev=[-90, 90], azip=(-180, 180));
我們能看到加上了額外的維度,數據變得在很小範圍可以線性可分。這是相對簡單的核,SVM內置有一個更複雜的核。我們能使用它通過設置kernel=rbf
,全稱徑向基函數核:
clf = SVC(kernel=rbf)clf.fit(X, y)plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=spring)plot_svc_decision_function(clf)plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=200, facecolors=none);
Jupyter實現
推薦閱讀:
※機器學習筆記(1)—— 回歸和分類
※CS231N 課程筆記合集
※強化學習——簡介
※你需要的Scikit-learn中文文檔:步入機器學習的完美實踐教程
※平移不變的正則線性回歸