从负无穷学习机器学习(五)支持向量机SVM

天大地大妈咪最大 提交于 2020-01-13 14:15:18

一、支持向量机

如果样本数据是线性不可分的,之前的分类器处理这类数据效果不太好,而SVM(Support Vector Machine)是一种专门处理线性不可分数据的算法。

SVM算法中,训练模型的过程实际上是对每个数据点对于数据分类决定边界的重要性进行判断。也就是说在训练数据集中,只有一部分数据对于边界的确定是有作用的,而这些数据点正好在决定边界上,这些数据被称为“支持向量”。

二、支持向量机的核函数

SVM可以将二维数据(2 Dimension)转变成三维数据(3 Dimension),这称为将数据投射至高维空间

这正是SVM算法的核函数(kernel trick)功能,用的最普遍的用于将数据投射到高维空间的方法是多项式内核(Polynomial kernel)和径向基内核(Radial Basis Function kernel,RBF)多项式内核就是将不同的特征乘方处理。而RBF内核也称为高斯内核(Gaussian kernel),接下来介绍一下它。

(一)、线性(linear)内核的SVM的分类器

# 导入科学计算工具
import numpy as np
# 导入画图工具
import matplotlib.pyplot as plt
# 导入支持向量机
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,
           linewidths=1, facecolors='none')
plt.title('Classifier: SVM Linear kernel')
plt.show()

在这里插入图片描述

(二)、RBF内核的SVM的分类器

# 如果SVM支持向量机使用RBF内核
# 创建一个RBF内核的支持向量机模型
clf_rbf = svm.SVC(kernel='rbf', C=1000, gamma=0.5) # 要指定gamma
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, linewidths=1, facecolors='none')
plt.title('Classifier: SVM RBF kernel')
plt.show()

在这里插入图片描述

使用RBF内核时,数据点之间的距离是通过如下公式获得的。

在这里插入图片描述

公式中的x1和x2代表不同的数据点,而||x1-x2||^2代表两个点之间的欧几里得距离。γ(gamma)用于控制RBF内核的宽度,也就是图中实线和两条虚线的距离。

欧几里得距离在二维和三维下的计算公式分别如下所示:

在这里插入图片描述

三、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, max_iter=10000),
          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.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(Support Vector Machine Classifier)和linearSVC的图像很相似,但还是有一些差异,因为linearSVC对L2范数进行最小化,二线性内核的SVC对L1范数进行最小化。

而RBF内核的SVC和多项式内核的SVC分类器的决定边界则完全不是线性的,他们更加灵活且富有弹性。而决定它们形状的就是他们的参数。

在多项式内核的SVC分类器中,起决定性作用的参数是乘方参数degree和正则化参数C;而在RBF内核的SVC分类器中,起决定性作用的参数是gamma和正则化参数C

在这里插入图片描述

(二)、不同参数的对比

下面看下不同gamma值对于RBF内核的SVC分类器有什么影响。

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 = ('RBF kernel gamma = 0.1',
          'RBF kernel gamma = 1',
          'RBF kernel 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_label('Feature 0')
    ax.set_label('Feature 1')
    ax.set_xticks(())
    ax.set_yticks(())
    ax.set_title(title)
    
plt.show()

在这里插入图片描述

四、SVM示例——波士顿房价回归分析

(一)、查看数据集

# 导入波士顿房价数据集
from sklearn.datasets import load_boston
boston = load_boston()
print(boston.keys())
print(boston['DESCR'])
# 总共有506个样本,每个样本有13个特征变量,还有一个名为中位值的target,以千美元为单位。

在这里插入图片描述

(二)、使用SVR建模

使用SVR(support vector machine regression)进行建模

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

# 使用linear和rbf两种核函数建立回归模型
from sklearn.svm import SVR
# 分别测试linear和rbf核函数
for kernel in ['linear', 'rbf']:
    svr = SVR(kernel=kernel)
    svr.fit(X_train, y_train)
    print(kernel,'kernel function train set score:{:.3f}'.format(svr.score(X_train, y_train)))
    print(kernel,'kernel function test set score:{:.3f}'.format(svr.score(X_test, y_test)))

可以看到两个模型得分都一般,特别是rbf内核的模型得分简直是灾难,这是因为我们没有对数据进行预处理,下面分析和处理一下数据。

在这里插入图片描述

将特征数值中的最大值和最小值用散点画出来。

plt.plot(X.min(axis=0), 'v', label='min')
plt.plot(X.max(axis=0), '^', label='max')
# 设定纵坐标为对数形式
plt.yscale('log')
# 设置图注位置为最佳
plt.legend(loc='beast')
# 设定横纵坐标标题
plt.xlabel('features')
plt.ylabel('feature magnitude')
plt.show()
# 可以看到特征数据最大差了4个数量级,下面对数据进行预处理

# %%
# 导入数据及预处理工具
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)

# 将预处理后的数据集表示出来
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.yscale('log')
plt.legend(loc='best')

plt.xlabel('scaled features')
plt.ylabel('scaled feature magnitude')
plt.show()

可以看到特征值的最大值和最小值相差不超10,最小值更是小到趋近于零以至于没显示出来,下面使用经过预处理的数据训练模型。
在这里插入图片描述

for kernel in ['linear', 'rbf']:
    svr = SVR(kernel=kernel)
    svr.fit(X_train_scaled, y_train)
    print(kernel,'kernel function scaled train set score:{:.3f}'.format(svr.score(X_train_scaled, y_train)))
    print(kernel,'kernel function scaled test set score:{:.3f}'.format(svr.score(X_test_scaled, y_test)))

可以看到样本数据经过预处理之后,虽然linear核函数训练的模型分数提升不高,但对于rbf和函数来说提升非常显著。

在这里插入图片描述
进一步调整SVR的参数gamma和C,看分数可以提升多少。

for kernel in ['linear', 'rbf']:
    svr = SVR(kernel=kernel, C=100,  gamma=0.1)
    svr.fit(X_train_scaled, y_train)
    print(kernel,'kernel function scaled train set after adjust params score:{:.3f}'.format(svr.score(X_train_scaled, y_train)))
    print(kernel,'kernel function scaled test set after adjust params score:{:.3f}'.format(svr.score(X_test_scaled, y_test)))


对于RBF核函数来说分数不错,综上所述可以看出SVM算法对于数据的预处理和调参的要求很高。

在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!