描述
核支持向量機(通常簡稱為SVM)可以推廣到更復雜模型的擴展,這些模型無法被輸入空間的超平面定義。
SVM 的核心思想是找到一個最優的超平面,將不同類別的數據分開。這個超平面不僅要能夠正確分類數據,還要使得兩個類別之間的間隔(margin)最大化。
- 超平面
- 在二維空間中,超平面是一個直線
- 在三維空間中,超平面是一個平面
- 在更高維空間中,超平面是一個分割空間的超平面
- 支持向量
- 支持向量是離超平面最近的樣本點,這些支持向量對于定義超平面至關重要
- 支持向量機通過最大化支持向量到超平面的距離(及最大化間隔)來選擇最佳超平面
- 最大間隔
- SVM的目標是最大化分類間隔,使得分類邊界盡可能遠離兩類數據點,這可以有效地減少模型的泛化誤差
- 核技巧
- 對于非線性可分的數據,SVM使用核函數將數據映射到更高維的空間,在這個空間中,數據可能是線性可分的。
- 常用的核函數有:線性核、多項式核、徑向基函數(RBF)核等。
線性模型在低維空間中可能非常受限,因為線和平面的靈活性有限。有一種方法可以讓線性模型更加靈活,就是添加更多的特。
下面舉個例子,添加輸入特征的交互項或多項式:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import mglearn
import warnings
warnings.filterwarnings('ignore')X,Y=mglearn.datasets.make_blobs(centers=4,random_state=8) # 隨機創造一些數據
Y=Y%2mglearn.discrete_scatter(X[:, 0], X[:, 1], Y)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
執行上例,可以看到數據有明顯的分類
from sklearn.svm import LinearSVC
linear_svm = LinearSVC().fit(X, Y)mglearn.plots.plot_2d_separator(linear_svm, X)
mglearn.discrete_scatter(X[:, 0], X[:, 1], Y)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
用于分類的線性模型只能用一條直線來劃分數據點,對這個數據集無法給出較好的結果
X_new = np.hstack([X,X[:,1:]**2])from mpl_toolkits.mplot3d import Axes3D,art3dfigure = plt.figure()
ax = Axes3D(figure, elev=-152, azim=-26)
mask = Y == 0
ax.scatter(X_new[mask, 0], X_new[mask, 1], X_new[mask, 2], c='b',cmap=mglearn.cm2, s=60)
ax.scatter(X_new[~mask, 0], X_new[~mask, 1], X_new[~mask, 2], c='r', marker='^', cmap=mglearn.cm2, s=60)
ax.set_xlabel("feature0")
ax.set_ylabel("feature1")
ax.set_zlabel("feature1 ** 2")
figure.add_axes(ax)
plt.show()
上例中添加第二個特征的平方(feature1 ** 2)作為一個新特征。現在我們將每個數據點表示為三維點 (feature0, feature1, feature1 ** 2),而不是二維點 (feature0, feature1)。
linear_svm_3d = LinearSVC().fit(X_new,Y )
coef, intercept = linear_svm_3d.coef_.ravel(), linear_svm_3d.intercept_
figure = plt.figure()
ax = Axes3D(figure, elev=-152, azim=-26)
xx = np.linspace(X_new[:, 0].min() - 2, X_new[:, 0].max() + 2, 50)
yy = np.linspace(X_new[:, 1].min() - 2, X_new[:, 1].max() + 2, 50)
XX, YY = np.meshgrid(xx, yy)
ZZ = (coef[0] * XX + coef[1] * YY + intercept) / -coef[2]
ax.plot_surface(XX, YY, ZZ, rstride=8, cstride=8, alpha=0.3)
ax.scatter(X_new[mask, 0], X_new[mask, 1], X_new[mask, 2], c='b',cmap=mglearn.cm2, s=60)
ax.scatter(X_new[~mask, 0], X_new[~mask, 1], X_new[~mask, 2], c='r', marker='^',cmap=mglearn.cm2, s=60)
ax.set_xlabel("feature0")
ax.set_ylabel("feature1")
ax.set_zlabel("feature1 ** 2")
figure.add_axes(ax)
plt.show()
可以用線性模型(三維空間中的平面)將這兩個類別分開。
核技巧
向數據表示中添加非線性特征,可以讓線性模型變得更強大。但是,通常來說并不知道要添加哪些特征。有一種巧妙的數學技巧,可以在更高維空間中學習分類器,而不用實際計算可能非常大的新的數據表示。這種技巧叫作核技巧(kernel trick)。它的原理是直接計算擴展特征表示中數據點之間的距離(準確地說是內積),而不用實際對擴展進行計算。
對于支持向量機,將數據映射到更高維空間中有兩種常用的方法:
- 多項式核:在一定階數內計算原始特征所有可能的多項式(比如 feature1 ** 2 * feature2 ** 5)
- 徑向基函數核(高斯核):(對應無限維的特征空間)考慮所有階數的所有可能的多項式,但階數越高,特征的重要性越小。
不過在實踐中,核 SVM 背后的數學細節并不是很重要,主要是掌握用法、參數調優。
理解SVM
在訓練過程中,SVM 學習每個訓練數據點對于表示兩個類別之間的決策邊界的重要性。通常只有一部分訓練數據點對于定義決策邊界來說很重要:位于類別之間邊界上的那些點。這些點叫作支持向量(support vector)。
想要對新樣本點進行預測,需要測量它與每個支持向量之間的距離。分類決策是基于它與支持向量之間的距離以及在訓練過程中學到的支持向量重要性(保存在 SVC 的 dual_coef_屬性中)來做出的。
from sklearn.svm import SVCX,Y = mglearn.tools.make_handcrafted_dataset()
svm = SVC(kernel='rbf',C=10,gamma=0.1).fit(X,Y)
mglearn.plots.plot_2d_separator(svm, X, eps=.5)
mglearn.discrete_scatter(X[:, 0], X[:, 1], Y)
sv = svm.support_vectors_
sv_labels = svm.dual_coef_.ravel() > 0
mglearn.discrete_scatter(sv[:, 0], sv[:, 1], sv_labels, s=15, markeredgewidth=3)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
執行上例子,SVM 給出了非常平滑且非線性(不是直線)的邊界。
gamma 參數:用于控制高斯核的寬度,它決定了點與點之間“靠近”是指多大的距離。
C 參數:正則化參數,與線性模型中用到的類似,它限制每個點的重要性(或者更確切地說,每個點的 dual_coef_)。
fig,axes=plt.subplots(3,3,figsize=(15,10))for ax,C in zip(axes,[-1,0,3]):for a,gamma in zip(ax,range(-1,2)):mglearn.plots.plot_svm(log_C=C,log_gamma=gamma,ax=a)axes[0,0].legend(['class 0','class 1','sv class 0','sv class 1'],ncol=4,loc=(.9,1.2))
執行上例:
從左到右,將參數 gamma 的值從 0.1 增加到 10。gamma 較小,說明高斯核的半徑較大,許多點都被看作比較靠近。這一點可以在圖中看出:左側的圖決策邊界非常平滑,越向右的圖決策邊界更關注單個點。小的 gamma 值表示決策邊界變化很慢,生成的是復雜度較低的模型,而大的 gamma 值則會生成更為復雜的模型。
從上到下,將參數 C 的值從 0.1 增加到 1000。與線性模型相同,C 值很小,說明模型非常受限,每個數據點的影響范圍都有限。可以看到,左上角的圖中,決策邊界看起來幾乎是線性的,誤分類的點對邊界幾乎沒有任何影響。再看左下角的圖,增大 C 之后這些點對模型的影響變大,使得決策邊界發生彎曲來將這些點正確分類。
將 RBF 核 SVM 應用到乳腺癌數據集上。默認情況下,C=1,gamma=1/n_features:
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_splitcancer = load_breast_cancer()X_train,X_test,Y_train,Y_test=train_test_split(cancer.data,cancer.target,random_state=42)svc = SVC(C=1,gamma=1.0/len(X_train[0]))
svc.fit(X_train,Y_train)print(svc.score(X_train,Y_train),svc.score(X_test,Y_test))
執行上例,這個模型在訓練集上的分數十分完美,但在測試集上的精度打了6折,存在相當嚴重的過擬合。雖然 SVM 的表現通常都很好,但它對參數的設定和數據的縮放非常敏感。特別地,它要求所有特征有相似的變化范圍。
為SVM預處理數據
plt.plot(X_train.min(axis=0), 'o', label="min")
plt.plot(X_train.max(axis=0), '^', label="max")
plt.legend(loc=4)
plt.xlabel("Feature index")
plt.ylabel("Feature magnitude")
plt.yscale("log")
執行上例可以明顯看出,乳腺癌數據集的特征具有完全不同的數量級。這對其他模型來說(比如線性模型)可能是小問題,但對核 SVM 卻有極大影響。
解決這個問題的一種方法就是對每個特征進行縮放,使其大致都位于同一范圍。核 SVM常用的縮放方法就是將所有特征縮放到 0 和 1 之間。
min_on_training = X_train.min(axis=0) # 計算訓練集中每個特征的最小值
range_on_training = (X_train-min_on_training).max(axis=0) # 計算訓練集中每個特性的范圍(最大值-最小值)
# 減去最小值,然后除以范圍
# 這樣每個特征都是min=0和max=1
X_train_scaled = (X_train-min_on_training)/range_on_training
print("Minimum for each feature\n{}".format(X_train_scaled.min(axis=0)))
print("Maximum for each feature\n {}".format(X_train_scaled.max(axis=0)))# 利用訓練集的最小值和范圍對測試集做相同的變換
X_test_scaled = (X_test - min_on_training) / range_on_trainingsvc = SVC()
svc.fit(X_train_scaled,Y_train)
print(svc.score(X_train_scaled,Y_train),svc.score(X_test_scaled,Y_test))
可以嘗試增大 C 或 gamma 來擬合更為復雜的模型
svc = SVC(C=1000,gamma=2.0/len(X_train[0]))
svc.fit(X_train_scaled,Y_train)
print(svc.score(X_train_scaled,Y_train),svc.score(X_test_scaled,Y_test))
執行上面的兩個例子,可以明顯看到,測試得分有大幅度提高。
優缺點、參數
核支持向量機是非常強大的模型,在各種數據集上的表現都很好。SVM 允許決策邊界很復雜,即使數據只有幾個特征。它在低維數據和高維數據(即很少特征和很多特征)上的表現都很好,但對樣本個數的縮放表現不好。
SVM 的另一個缺點是,預處理數據和調參都需要非常小心。這也是為什么很多應用中用的都是基于樹的模型,比如隨機森林或梯度提升(需要很少的預處理,甚至不需要預處理)。此外,SVM 模型很難檢查,可能很難理解為什么會這么預測,而且也難以將模型向非專家進行解釋。
不過 SVM 仍然是值得嘗試的,特別是所有特征的測量單位相似(比如都是像素密度)而且范圍也差不多時。
參數:gamma 和 C 控制的都是模型復雜度,較大的值都對應更為復雜的模型。
- C:正則化參數,較小的 C 值可以讓算法盡量適應“大多數”數據點,而較大的 C 值更強調每個數據點都分類正確的重要性。
- gamma :用于控制高斯核的寬度,高斯核的半徑較大,許多點都被看作比較靠近。