上一节我学习了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