题前故事:小 D 最近也交了一个女朋友,但是这个女孩好像非常情绪化, 喜怒无常,让小 D 捉摸不透,小 D 女朋友的情绪完全不是“线性可分”的,于是小D 想到了 SVM 算法, 也就是大名鼎鼎的一一支持向量机。支持向量机理解引入
首先需要知道线性可分和线性不可分的概念
我们提取样本特征是“是否有妹子” 和“是否有好吃的”这两项的时候, 能够很容易用图中的直线把男生的情绪分成“开心”和“不开心”两类,这种情况下我 们说样本是线性可分的。
女生的情绪可能要复杂很多, 有时候从男生的角度来看,她们的情绪分布可能是下图分布
感受到线性模型“深深的绝望”了,无论是用哪一条直线, 都无法将女生的情绪进行正确的分类, 在这种情况下,我们说样本是线性不可分的。 SVM 支持向量机针对线性不可分问题有较好的处理能力
对于女生的情绪,假如“开心”的情绪是轻飘飘的,而“不开心”的情绪是沉重的,假设以上的情绪表现防于水里,”开心”就会漂浮起来,而“不开心”就会沉下去,如下图:
经过处理之后的数据,很容易用一块玻璃板将两种心情进 行分类了。如果从正上方向下看,将三维视图还原成二维,如下图:
通过利用“开心”和“不开心”的重量差实现将二维数据变成三维的过程,称为将数据投射至高维空间。这正是 SVM 算法的核函数功能,在 SVM 中用得最普遍的两种把数据 投射到高维空间的方法分别是多项式内核和径向基内核。
多项式内核比较容易理解,它是通过把样本原始特征进行乘 方来把数据投射到高维空间, 比如特征 l 乘 2 次方、特征 2 乘 3 次方,特征 3 乘 5 次方等支持向量机SVM核函数
训练模型的过程实际上是对每个数据点对于数据分类决定边界的重要性进行判断。也就是说, 在训练数据集中,只有一部分数据对于边界的确定是有帮助的,而这些数据点就是正好位于决定边界上的,这些数据被称为“支持向量”。通过画图帮助理解支持向量的概念:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_blobs
X,y = make_blobs(n_samples=50,centers=2,random_state=6)
clf = svm.SVC(kernel='linear',C=1000)
clf.fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=30,cmap=plt.cm.Paired)
ax=plt.gca()
xlim=ax.get_xlim()
ylim=ax.get_ylim()
xx=np.linspace(xlim[0],xlim[1],30)
yy=np.linspace(ylim[0],ylim[1],30)
YY,XX=np.meshgrid(yy,xx)
xy=np.vstack([XX.ravel(),YY.ravel()]).T
Z=clf.decision_function(xy).reshape(XX.shape)
ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,
linestykes=['--','-','--'])
ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=100,
linewidths=1,facecolors='none')
plt.show()
清晰地看到,在分类器两侧分别有两条虚线,那些正好压在虚线上的数据点,就是我们刚刚提到的支持向量。
把 SVM 的内核换成是RBF,如下代码实现:
clf_rbf=svm.SVC(kernel='rbf',C=1000)
clf_rbf.fit(X,y)
plt.scatter(X[:,0],X[:,1],c=y,s=30,cmap=plt.cm.Paired)
ax = plt.gca()
xlim=ax.get_xlim()
ylim=ax.get_ylim()
xx=np.linspace(xlim[0],xlim[1],30)
yy=np.linspace(ylim[0],ylim[1],30)
YY,XX=np.meshgrid(yy,xx)
xy=np.vstack([XX.ravel(),YY.ravel()]).T
Z=clf_rbf.decision_function(xy).reshape(XX.shape)
ax.contour(XX,YY,Z,colors='k',levels=[-1,0,1],alpha=0.5,
linestykes=['--','-','--'])
ax.scatter(clf_rbf.support_vectors_[:,0],clf_rbf.support_vectors_[:,1],s=100,
linewidths=1,facecolors='none')
plt.show()
们使用rbf内核的时候,数据点之间的距离是用如下公式来计算的:
x1和x2代表两个不同的数据点,而 llx1-x2ll 代表两个点之间的欧几里得距离。 (gamma)是用来控制rfb内核宽度的参数,也就是图中实线距离两条虚线的距离。SVM 的核函数与参数选择
能够直观体验不同内核的 SVM 算法在分类中的不同表现,我们画个图像来进行展示:
from sklearn.datasets import load_wine
def make_meshgrid(x,y,h=.02):
x_min,x_max=x.min()-1,x.max()+1
y_min,y_max=y.min()-1,y.max()+1
xx,yy = np.meshgrid(np.arange(x_min,x_max),
np.arange(y_min,y_max))
return xx,yy
def plot_countours(ax,clf,xx,yy,**params):
Z=clf.predict(np.c_[xx.ravel(),yy.ravel()])
Z = Z.reshape(xx.shape)
out = ax.countourf(xx,yy,Z,**params)
return out
wine = load_wine()
X=wine.data[:,:2]
y=wine.target
C=1.0
models = (svm.SVC(kernel='linear',C=C),
svm.LinearSVC(C=C),
svm.SVC(kernel='rbf',gamma=0.7,C=C),
svm.SVC(kernel='poly',degree=3,C=C))
models = (clf.fit(X,y) for clf in models)
titles = (' SVC with linear kernel ',
' LinearSVC linear kernel',
'SVC with RBF kernel ',
'SVC with polynomial (degree 3) kernel'
)
fig,sub = plt.subplot(2,2)
plt.subplots_adjust(wspace=0.4,hspace=0.4)
X0,X1=X[:,0],X[:,1]
xx,yy = make_meshgrid(X0,X1)
for clf,title,ax in zip(models,titles,sub.flatten()):
plot_contours(ax,clf,xx,yy,cmap=plt.plasma,alpha=0.8)
ax.scatter(X0,X1,c=y,cmap=plt.cm.plasma,alpha=0.8)
ax.set_xlim(xx.min(),xx.max())
ax.set_ylim(yy.min(),yy,max())
ax.set_xlabel('Feature 0')
ax.set_ylabel('Feature 0')
ax.set_xticks(())
ax.set_yticks((title))
plt.show()
线性内核的 SVC 与 linearSVC 得到的结果非常近似的原因是 linearSVC 对 L2 范数进行最小化,而线性内核的 SVC 是对 Ll 范数进行最小化。linearSVC 和线性内核的 SVC 生成的决定边界都是线性的,在更高维数据集中将会是相交的超平面。
SVM 应对高维数据集和低维数据集都还算是得心应手。但是,前提条件是数据集的规模不太大。如果数据集中的样本数量在 l 万以内, SVM 都能驾驭得了,但如果样本数量超过 10 万的话, SVM 就会非常耗费时间和内存。支持向量机应用实例
通过一个实例来介绍 SVM 在回归分析中的应用。使用波士顿房价数据集该数据集为大家讲解 SVM 中用 于回归分析的 SVR的用法。
首先查看数据集的结构:
from sklearn.datasets import load_boston
boston = load_boston()
print(boston.keys())
print(boston ['DESCR'])
数据集中共有 506 个样本, 每个样本有 13 个特征变量。 而后面还有一个叫作中位数的第 14 个变量,这个变量就是该数据集中 的target。
制作训练数据集和测试数据集,输入代码如下:
from sklearn.model_selection import train_test_split
X,y = boston.data,boston.target
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=8)
print('\n\n\n')
print('代码运行结果')
print('===================')
print(X_train.shape)
print(X_test.shape)
print('=======================')
print('\n\n\n')
from sklearn.svm import SVR
fro kernel in ['linear','rbf']:
svr=SVR(kernel=kernel)
svr.fit(X_train,y_train)
print(kernel,'核函数的模型训练得分:{:,.3f}'.format(svr.score((X_train,y_train))))
print(kernel,'核函数的模型测试得分:{:,.3f}'.format(svr.score((X_test,y_test))))
"linear" 核函数的模型在训练集的得分只有 0.709,而在测试集只有 0.696。不过使用rbf核函 数的模型更糟糕,在训练数据集的分只有 0.145,而在测试集的得分只有 0.001 分。对于此模型不理想的结果,需要优化。