说明:本文依据《Sklearn 与 TensorFlow 机器学习实用指南》完成,所有版权和解释权均归作者和翻译成员所有,我只是搬运和做注解。
第五章是对支持向量机SVM的系统介绍,阐述支持向量机的核心概念,怎么使用这个强大的模型,以及它是如何工作的。
这应该是第三次做这方面的内容了,贴出另一个比较全面的SVM进阶博客。
https://blog.csdn.net/v_JULY_v/article/details/7624837
支持向量机(SVM)是个非常强大并且有多种功能的机器学习模型,能够做线性或者非线性的分类,回归,甚至异常值检测。机器学习领域中最为流行的模型之一,是任何学习机器学习的人必备的工具。SVM特别适合应用于复杂但中小规模数据集的分类问题。
本章使用数据为Sklearn中make_moons数据
源代码已经同步在github中 https://github.com/jwc19890114/-02-learning-file-100days
2. 非线性支持向量机分类
尽管线性SVM分类器在许多案例上表现得出乎意料的好,但是很多数据集并不是线性可分的。一种处理非线性数据集方法是增加更多的特征,例如多项式特征(正如你在第4章所做的那样);在某些情况下可以变成线性可分的数据。
下左图是一个只有一个特征x1的简单的数据集,该数据集不是线性可分的。但是如果你增加了第二个特征 x2=(x1)^2,产生的 2D 数据集就能很好的线性可分。
X1D=np.linspace(-4,4,9).reshape(-1,1) 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()
为了实施这个想法,通过Sklearn你可以创建一个流水线(Pipeline)去包含多项式特征(PolynomialFeatures)变换,然后一个StandardScaler和LinearSVC。
使用卫星数据集(moons datasets)测试一下效果。
可以看到,在pipeline里面已经完成了特征缩放和svm模型设置,在后面引入数据训练即可
from sklearn.datasets import make_moons from sklearn.pipeline import Pipeline from sklearn.preprocessing import PolynomialFeatures 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, loss="hinge", random_state=42)) ]) 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) y_decision = clf.decision_function(X).reshape(x0.shape) plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2) # plt.contourf(x0, x1, y_decision, cmap=plt.cm.brg, alpha=0.1) 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中有核技巧,应用后得到的结果与高次数多项式类似。
在这里同样使用卫星数据测试效果
可以使用网格搜索找到最优参数,先进行粗略的网格搜索,然后再最佳值的基础上再进行更细的。
#degree控制多项式核的阶数,分别使用3阶和10阶处理 #coef控制了高阶多项式与低阶多项式对模型的影响 from sklearn.svm import SVC 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) poly100_kernel_svm_clf = Pipeline([ ("scaler", StandardScaler()), ("svm_clf", SVC(kernel="poly", degree=10, coef0=100, C=5)) ]) poly100_kernel_svm_clf.fit(X, y) poly1000_kernel_svm_clf = Pipeline([ ("scaler", StandardScaler()), ("svm_clf", SVC(kernel="poly", degree=50, coef0=1000, C=5)) ]) poly1000_kernel_svm_clf.fit(X, y) plt.figure(figsize=(20, 4)) plt.subplot(131) 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(132) plot_predictions(poly100_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.subplot(133) plot_predictions(poly1000_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=50, r=1000, C=5$", fontsize=18) plt.show()
添加相似特征
另一种解决非线性问题的方法是使用相似函数(similarity funtion)计算每个样本与特定地标(landmark)的相似度。例如,前面使用的一维数据集,在x1=-2和x1=1之间增加两个地标(图 5-8 左图)。接下来,我们定义一个相似函数,即高斯径向基函数(Gaussian Radial Basis Function,RBF),设置γ = 0.3。
何选择地标。最简单的方法是在数据集中的每一个样本的位置创建地标。这将产生更多的维度从而增加了转换后数据集是线性可分的可能性。但缺点是,m个样本,n个特征的训练集被转换成了m个实例,m个特征的训练集。这样一来,如果你的训练集非常大,你最终会得到同样大的特征。与前面多项式特征一样,过高的相似特证数会导致过大计算开销,这里使用高斯RBF核来处理。
def gaussian_rbf(x, landmark, gamma): return np.exp(-gamma * np.linalg.norm(x - landmark, axis=1)**2) gamma = 0.3 x1s = np.linspace(-4.5, 4.5, 200).reshape(-1, 1) x2s = gaussian_rbf(x1s, -2, gamma) x3s = gaussian_rbf(x1s, 1, gamma) XK = np.c_[gaussian_rbf(X1D, -2, gamma), gaussian_rbf(X1D, 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(x1s, x2s, "g--") plt.plot(x1s, 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}