机器学习【六】支持向量机SVM——专治线性不可分

SVM原理

线性可分与线性不可分

线性可分

线性不可分-------【无论用哪条直线都无法将女生情绪正确分类】

SVM的核函数可以帮助我们:

假设‘开心’是轻飘飘的,“不开心”是沉重的

将三维视图还原成二维:

刚利用“开心”“不开心”的重量差实现将二维数据变成三维的过程,称为将数据投射至高维空间,这正是核函数的功能

在SVM中,用的最普遍的两种把数据投射到高维空间的方法分别是多项式内核、径向基内核(RFB)

多项式内核:

通过把样本原始特征进行乘方来把数据投射到高维空间【如特征1^2,特征2^3,特征3^5......】

RBF:

又称高斯内核

 

 支持向量机的SVM核函数

用图形直观了解:

#导入numpy
import numpy as np
#导入画图工具
import matplotlib.pyplot as plt
#导入支持向量机SVM
from sklearn import svm
#导入数据集生成工具
from sklearn.datasets import make_blobs
#先创建50个数据点,让它们分成两类
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,linestyles=['--','-','--'])
ax.scatter(clf.support_vectors_[:,0],clf.support_vectors_[:,1],s=100,linewidth=1,facecolors='none')
plt.show()

——线性内核的SVM分类器

【结果分析】:

在分类器两侧有两条虚线,正好压在虚线上的数据点,即支持向量

本例所使用的方法称为“最大边界间隔超平面”

指,实线【在高维数据中是一个超平面】和所有支持向量的距离都是最大的

 

 

把SVM的内核换成RBF:

#创建一个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,linestyles=['--','-','--'])
ax.scatter(clf_rbf.support_vectors_[:,0],clf_rbf.support_vectors_[:,1],s=100,linewidth=1,facecolors='none')
plt.show()

 ——RBF内核的SVM分类器

【结果分析】:

使用RBF内核时,数据点距离计算是用如下公式计算的:

 

 

SVM的核函数和参数选择

1.不同核函数的SVM对比

线性模型中提到过,linearSVM算法,就是一种使用了线性内核的SVM算法,不过linearSVM不支持对核函数进行修改【默认只能使用线性内核】

 

直观体验不同内核的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,h),np.arange(y_min,y_max,h))
    return xx,yy
#定义一个绘制等高线的函数
def plot_contours(ax,clf,xx,yy, **params):
    Z = clf.predict(np.c_[xx.ravel(),yy.ravel()])
    Z = Z.reshape(xx.shape)
    out = ax.contourf(xx,yy,Z, **params)
    return out
#使用酒的数据集
wine = load_wine()
#选取数据集的前两个特征
X = wine.data[:,:2]
y = wine.target
C = 1.0  #SVM的正则化参数
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 kernal','LinearSVC (linear kernal)','SVC with RBF kernal','SVC with polynomial (degree 3) kernal')
#设定一个子图形的个数和排列方式
fig,sub = plt.subplots(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.cm.plasma,alpha=0.8)
    ax.scatter(X0,X1,c=y,cmap=plt.cm.plasma,s=20,edgecolors='k')
    ax.set_xlim(xx.min(),xx.max())
    ax.set_ylim(yy.min(),yy.max())
    ax.set_xlabel("Feature 0")
    ax.set_ylabel("Feature 1")
    ax.set_xticks(())
    ax.set_yticks(())
    ax.set_title(title)
plt.show()

【结果分析】:

线性内核的SVC与linearSVC得到的结果近似,但仍然有一点差别——原因是linearSVC对L2范数进行最小化,而线性内核的SVC是对L1进行最小化

无论如何,线性内核的SVC和linearSVC生成的决定边界都是线性的【在更高维数据集中将会是相交的超平面】

 

 RBF内核的SVC和polynomial内核的SVC分类器的决定边界则完全不是线性的,更加弹性

决定他们边界形状的是参数:

polynomial内核的SVC分类器

degree和正则化参数C

【本例中degree=3,即对原始数据集的特征乘3次方操作】

RBF内核的SVC

gamma和正则化参数C

 

RBF内核SVC的gamma参数调节

C = 1.0  #SVM的正则化参数
models = (svm.SVC(kernel='rbf',gamma=0.1,C=C),svm.SVC(kernel='rbf',gamma=1,C=C),svm.SVC(kernel='rbf',gamma=10,C=C))
models = (clf.fit(X,y) for clf in models)
#设定图题
titles = ('gamma=0.1','gamma=1','gamma=10')
#设置子图形个数和排列
flg,sub = plt.subplots(1,3,figsize=(10,3))
#使用前面定义的函数进行画图
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.cm.plasma,alpha=0.8)
 ax.scatter(X0,X1,c=y,cmap=plt.cm.plasma,s=20,edgecolors='k')
 ax.set_xlim(xx.min(),xx.max())
 ax.set_ylim(yy.min(),yy.max())
 ax.set_xlabel("Feature 0")
 ax.set_ylabel("Feature 1")
 ax.set_xticks(())
 ax.set_yticks(())
 ax.set_title(title)
plt.show()

 

【结果分析】:

gamma值越小,RBF内核的直径越大——> 有更多的点被模型圈进决定边界中——> 边界越圆滑

  • gamma越小,模型倾向于 欠拟合
  • gamma越大,模型倾向于 过拟合

正则化参数C,可以见线性模型一章

  • C越小,模型越受限【单个数据对模型的影响越小】,模型越简单
  • C越大,每个数据点对模型的影响越大,模型越复杂

 

SVM优点

  • 可应对高维数据集和低维数据集
  • 即使数据集中样本特征的测度都比较接近,如图像识别领域,以及样本特征数和样本数比较接近的时候,都游刃有余

SVM缺点 

  • 当数据集中特征数量在1万以内,SVM可以驾驭,但数量大于10万,就非常占内存和耗费时间
  • 对数据预处理和参数调节要求很高

 

 

 【注意】

SVM中3个参数非常重要:

  1. 核函数的选择
  2. 核函数的参数【如RBF的gamma】
  3. 正则化参数C 

 RBF内核的gamma值是用来调节内核宽度的,gamma值和C值一起控制模型的复杂度,数值越大,模型越复杂【实际中,一起调节,才能达到最好的效果】

 

 

 

实战

 SVM在回归分析中的应用——波士顿房价数据集

1.了解数据集

#导入波士顿房价
from sklearn.datasets import load_boston
boston = load_boston()
#打印键
print(boston.keys())

 

#打印数据集中的短描述
print(boston['DESCR'])

 

 

【一部分】

【结果分析】

数据集共有506个样本,每个样本有13个特征变量,后面还有一个叫做中位数的第14个变量【这变量就是该数据集中的target】

 

2.通过SVR算法建立房价预测模型


#导入数据集拆分工具
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(X_train.shape)
print(X_test.shape)

 

用SVR建模

#导入支持向量回归模型
from sklearn.svm import SVR
#分别测试linear核函数和rbf核函数
for kernel in ['linear','rbf']:
   svr = SVR(kernel=kernel)
   svr.fit(X_train,y_train)
   print(kernel,'核函数模型训练集得分:',svr.score(X_train,y_train))
   print(kernel,'核函数模型测试集得分:',svr.score(X_test,y_test))

 

 

 SVM算法对数据预处理的要求较大,如果  数据特征量级差异较大  ,就需要预处理数据

先用图形可视化:

#将特征数值中的min和max用散点图画出来
plt.plot(X.min(axis=0),'v',label='min')
plt.plot(X.max(axis=0),'^',label='max')
#设定纵坐标为对数形式
plt.yscale('log')
#设定图注位置最佳
plt.legend(loc='best')
plt.xlabel('features')
plt.ylabel('feature magnitude')
plt.show()

显然,量级差异较大

预处理:

#导入数据预处理工具
from sklearn.preprocessing import StandardScaler
#预处理
scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
#将预处理后的数据特征的max和min用散点图表示出来
plt.plot(X_train_scaled.min(axis=0),'v',label='train set min')
plt.plot(X_train_scaled.max(axis=0),'^',label='train set max')
plt.plot(X_test_scaled.min(axis=0),'v',label='test set min')
plt.plot(X_test_scaled.max(axis=0),'^',label='test set max')
#设置图注位置
plt.legend(loc='best')
plt.xlabel('scaled features')
plt.ylabel('scaled feature magnitude')
plt.show()

 

【结果分析】

 经过预处理,无论训练集还是测试集,所有特征的最大值不会超过10,最小值趋近0,以至图中看不到

 

用预处理的数据训练模型:

#使用预处理后的数据重新训练模型
for kernel in ['linear','rbf']:
 svr = SVR(kernel=kernel)
 svr.fit(X_train_scaled,y_train)
 print(kernel,'训练集得分:',svr.score(X_train_scaled,y_train))
 print(kernel,'测试集得分:',svr.score(X_test_scaled,y_test))

 

进一步调整参数


#设置模型的C参数和gamma参数
svr = SVR(C=100,gamma=0.1)
svr.fit(X_train_scaled,y_train)
print('训练集得分:',svr.score(X_train_scaled,y_train))
print('测试集得分:',svr.score(X_test_scaled,y_test))

 

posted @ 2019-04-28 13:36  远征i  阅读(2633)  评论(2编辑  收藏  举报