学习目标:
本篇博客包括以下三个基本内容:
1、支持向量机的核函数
2、不同核函数的SVM对比
3、支持向量机的gamma参数调节
(本次实验在Jupyter Notebook中运行)
基本原理:
- 支持向量机(support vector machines, SVM)是一种二分类模型,它的基本模型是定义在特征空间上的间隔最大的线性分类器,间隔最大使它有别于感知机。
- SVM还包括核技巧,这使它成为实质上的非线性分类器。
- SVM的的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失函数的最小化问题。
- SVM的的学习算法就是求解凸二次规划的最优化算法。
一、支持向量机SVM的核函数:
【结尾会附赠全部代码】
1、导入numpy以及画图工具
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
2、导入支持向量机分类库
from sklearn.svm import SVC
3、导入数据集生成工具
from sklearn.datasets import make_blobs
4、生成100个具有两个特征的两分类点并展示出来
X,y = make_blobs(n_samples = 100,
n_features = 2,
centers = 2,
random_state = 6)
plt.scatter(X[:,0],X[:,1],c = y,s = 30,cmap = plt.cm.Paired)
显示为:
5、拆分训练集与测试集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.3,random_state = 6)
6、创建一个线性内核的支持向量机模型
clf = SVC(kernel = 'linear',C = 1000)
7、模型训练
clf.fit(X_train,y_train)
结果为:
SVC(C=1000, kernel='linear')
8、查看模型准确率
clf.score(X_test,y_test)
结果为:
1.0
9、查看支持向量个数(每类分别有多少个)
clf.n_support_
结果为:
array([2, 1])
10、查看支持向量的坐标
clf.support_vectors_
结果为:
array([[ 5.73005848, -4.19481136],
[ 7.80996597, -4.80944368],
[ 6.49868019, -7.13530714]])
11、绘制一个线性内核的SVM分类器(下面代码必须同时运行)
#结果可视化
plt.scatter(X_train[:,0],X_train[:,1],s = 120,c = y_train,cmap = plt.cm.spring,edgecolors = "k")
#建立图像坐标
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()
结果为:
结果分析:
从上面图,可以清晰地看到,在分类器两侧分别有两条虚线,那些正好压在虚线上的数据点,就是支持向量。而该实验使用的这种方法称为“最大边界间隔超平面"(Maximum Margin Separating Hyerplane)。指的是说中间这条实线(在高维数据中是一个超平面),和所有支持向量之间的距离,都是最大的。
如果将SVM的内核换成RBF,会发生什么变化呢?
【创建一个RBF内核的支持向量机模型】
clf = SVC(kernel = 'rbf',C = 1)
clf.fit(X_train,y_train)
#结果可视化
plt.scatter(X_train[:,0],X_train[:,1],s = 120,c = y_train,cmap = plt.cm.spring,edgecolors = "k")
#建立图像坐标
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()
结果为:
通过调节参数C的大小来观察变化
-
C=0.1
-
C = 10
- C=1000
**当以RBF作为内核时,数据点之间的距离用下面的公式计算:
公式中的x1和x2代表两个不同的数据点,而||x1-x2||代表两个点之间的欧几里得距离。y(gamma)是用来控制RBF内核宽度的参数,也就是图中实线距离两条虚线的距离。
全部代码为:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
#导入支持向量机分类库
from sklearn.svm import SVC
#生成100个具有两个特征的两分类点
from sklearn.datasets import make_blobs
X,y = make_blobs(n_samples = 100,
n_features = 2,
centers = 2,
random_state = 6)
plt.scatter(X[:,0],X[:,1],c = y,s = 30,cmap = plt.cm.Paired)
#拆分训练集和测试集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size = 0.3,random_state = 6)
#导入支持向量机分类库
from sklearn.svm import SVC
#实例化#创建一个线性内核的支持向量机模型
clf = SVC(kernel = 'linear',C = 1000)
#模型训练
clf.fit(X_train,y_train)
#查看模型准确率
clf.score(X_test,y_test)
#查看支持向量个数(每类分别有多少个)
clf.n_support_
#查看支持向量的坐标
clf.support_vectors_
#(必须放在一块运行)
#结果可视化
plt.scatter(X_train[:,0],X_train[:,1],s = 120,c = y_train,cmap = plt.cm.spring,edgecolors = "k")
#建立图像坐标
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对比:
1、导入红酒数据集
from sklearn.datasets import load_wine
2、定义一个函数用来画图
def make_meshgrid(x,y,h = 0.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
3、定义一个绘制等高线的函数
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
4、实例化红酒数据集
wine = load_wine()
5、选取数据集的前两个特征
X = wine.data[:, :2]
y = wine.target
6、导入支持向量机SVM模型
from sklearn import svm
7、正则化
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)
8、绘制不同核函数的支持向量机模型
(下面代码同时运行)
#设定图题
titles = ('SVC with linear kernel',
'LinearSVC (linear kernel)',
'SVC with RBF kernel',
'SVC with polynomial (degree 3) kernel')
#设定一个子图形的个数和排列方式
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范数进行最小化。不论如何,linearSVC 和线性内核的SVC生成的决定边界都是线性的,在更高维数据集中将会是相交的超平面(请读者朋友自行想象)。
而RBF内核的SVC和polynomial内核的SVC分类器的决定边界则完全不是线性的,它们更加弹性。而决定了它们决定边界形状的,就是它们的参数。在polynomial内核的SVC中,起决定性作用的参数就是degree和正则化参数C,在本次实验中使用的degree为3,也就是对原始数据集的特征进行乘3次方操作。而在RBF内核的SVC中,起决定作用的是正则化参数C和参数gamma。
三、支持向量机的gamma参数调节:
#导入红酒数据集
from sklearn.datasets import load_wine
#定义一个函数用来画图
def make_meshgrid(x,y,h = 0.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
#导入SVM库
from sklearn import svm
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 = 'poly',gamma = 10,C = C))
models = (clf.fit(X,y) for clf in models)
#设定图题
titles = ('gamma = 0.1',"gamma = 1","gamma =10")
#设定子图形个数和排列
fig,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值从0.1增加到10, gamma值越小,则RBF内核的直径越大,这样就会有更多的点被模型圈进决定边界中,所以决定边界也就越平滑,这时的模型也就越简单;而随着参数的增加,模型则更倾向于把每一个点都放到相应的决定边界中,这时模型的复杂度也相应提高了。所以gamma值越小,模型越倾向于欠拟合,而gamma值越大,则模型越倾向于出现过拟合的问题。
而至于正则化参数C,C值越小,模型就越受限,也就是说单个数据点对模型的影响越小,模型就越简单;而C值越大,每个数据点对模型的影响就越大,模型也会更加复杂。