python机器学习5:支持向量机SVM算法

1.支持向量机的原理和基本构造

1.定义:

在SVM算法中,训练模型的过程实际上是对每个数据点对于数据分类决定边界的重要性进行判断。也就是说在训练数据集中,只有一部分数据对于边界的确定是有帮助的,我们把那一部分有帮助的点的向量,称为“支持向量”,这也是“支持向量机”名字的由来。
SVM的原理就是使用分隔超平面来划分数据集,并使得支持向量(数据集中离分隔超平面最近的点)到该分隔超平面的距离最大。其最大特点是能构造出最大间距的决策边界,从而提高分类算法的鲁棒性。

2.SVM算法的核函数:

将二维数据变成三维的过程,称为将数据投射至高维空间,也就是SVM算法的核函数功能。

3.SVM算法最普遍的核函数有两种:

1.多项式内核(Polynomial kernel):通过把样本原始特征进行乘方来把数据投射到高维空间,比如特征1乘2次方、特征2乘3次方,特征3乘5次方等。
2.径向基内核(Radial basis function kernel,RBF):RBF内核也被称为高斯内核(Gaussian kernel),接下来详细介绍RBF内核。

2.支持向量机的核函数

1.构造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,
           linewidth=1, facecolors='none')
plt.show()

运行代码,如图所示:


image.png

结果分析:
可以看到,在分类器两侧分别有两条虚线,那些正好压在虚线上的数据点,就是我们所说的支持向量。本例使用的方法称为“最大边界间隔超平面”。指的是说中间这条实线(在高维数据中是一个超平面),和所有支持向量之间的距离,都是最大的。

2.构造kernel=RBF的线性内核的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,
           linestyles=['--', '-', '--'])

ax.scatter(clf_rbf.support_vectors_[:, 0], clf_rbf.support_vectors_[:, 1], s=100,
           linewidth=1, facecolors='none')
plt.show()

运行代码,如下图所示:

image.png

结果分析:
可以看到分类器的样子变得完全不一样了,这是因为当我们使用RBF内核的时候,数据点之间的距离是用如下公式来计算的:

公式中的x1和x2代表两个不同的数据点,而||x1-x2||代表两点之间的欧几里得距离,是用来空值rbf内核宽度的参数,也就是图中实线和两条虚线的距离。

3.SVM的核函数和参数选择

1.核函数对比:

为了直观体验不同内核的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 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()

运行代码,如图所示:


image.png

结果分析:从图中可以看到,线性内核的SVC与linearSVC得到的结果非常近似,但仍然有一点点差别。
差别原因:linearSVC对L2范数进行最小化,而线性内核的SVC是对L1范数进行最小化。
不论如何,linearSVC和线性内核的SVC生成的决定边界都是线性的,在更高维数据集中将会是相交的超平面。而RBF内核的SVC和polynomial内核的SVC分类器的决定边界则完全不是线性的。
非线性原因:决定它们边界形状的,就是它们的参数,在polynomial内核的SVC中,起决定性作用的参数就是degree和正则化参数C,在本例中我们使用的degree为3,也就是对原始数据集的特征进行乘3次方操作。
RBF内核的SVC中,起决定作用的是正则化参数C和参数gamma,接下来讲gamma参数。

2.svm的gamma参数调节:

1.首先看一下不同的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 = ('gamma = 0.1',
          'gamma = 1',
          'gamma = 10',
          )


fig, sub = plt.subplots(1, 3,figsize = (10,3))
#plt.subplots_adjust(wspace=0.8, hspace=0.2)

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()

运行代码,如下图所示:


image.png

结果分析:
可以看出
1.从左至右gamma值从0.1增加到10,gamma值越小,则RBF内核的直径越大,这样就会有更多的点被模型圈进决定边界中,所以决定边界也就越平滑,这时的模型也就越简单;
2.随着参数的增加,模型则更倾向于把每一个点都放到相应的决定边界中,这时模型的复杂度也相应提高了。所以gamma值越小,模型越倾斜于欠拟合,而gamma值越大,则模型越倾向于出现过拟合的问题。

3.SVM算法的优势与不足:

优势:
1.SVM在机器学习领域是非常强大的算法,对各种不同类型的数据集都有不错的表现。它可以在数据特征很少的情况下生成非常复杂的决定边界,当然特征数量很多的情况下表现也不错,换句话说,SVM应对高维数据集和低维数据集都还算是得心应手。但是,前提是数据集的规模不太大。如果数据集中的样本数量在1万以内,SVM都能驾驭得了,但是超过10万,SVM就会非常耗费时间和内存。
缺点:
SVM还有一个短板,就是对于数据预处理和参数调节要求非常噶,所以现在很多场景下大家都会更乐意用上一章介绍的随机森林算法或者梯度上升决策树算法,因为他们不需要对数据预处理,也不用费尽心思调参。

4.支持向量机实例-----对波士顿房价进行回归分析

1.数据集准备:

from sklearn.datasets import load_boston
boston = load_boston()
print(boston.keys())

运行代码,如所示:
dict_keys(['data', 'target', 'feature_names', 'DESCR'])

波士顿房价数据集:数据集中有506个样本,每个样本有13个特征变量。还有一个叫做中位数的第14个变量,这个变量就是数据集中的target。
准备训练数据集和册数数据集:

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)

2.使用SVR进行建模:由于不知道使用哪种核函数,分别尝试一下两种核函数:“linear”和“rbf”

from sklearn.svm import SVR
for 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
linear 核函数的模型测试集得分:0.696
rbf 核函数的模型训练集得分:0.145
rbf 核函数的模型测试集得分:0.001
结果分析:
从结果看到,两种核函数的模型得分都不能令人满意。特别是rbf核函数更糟糕。
原因:
思考:会不会是数据集各个特征之间的量级差比较远?
因为SVM对数据预处理的要求比较高,如果数据特征量级差较大,我们就需要进行预处理。所以先用图形可视化一下数据集中各个特征的数量级是什么情况:

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()

image.png

结果分析:
果然,各个特征的量级差异还是比较大的,第一个特征值最小值在,而最大值达到了
因此,进行数据预处理:

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()

运行代码,如下图:


image.png

结果分析:可以看出,经过了预处理,特征量级差异减小很多。
再次用经过预处理的数据来训练模型,看看结果会有什么不同:

for kernel in ['linear','rbf']:
    svr = SVR(kernel=kernel)
    svr.fit(X_train_scaled, y_train)
    print('数据预处理后',kernel,'核函数的模型训练集得分:{:.3f}'.format(
        svr.score(X_train_scaled, y_train)))
    print('数据预处理后',kernel,'核函数的模型测试集得分:{:.3f}'.format(
        svr.score(X_test_scaled, y_test)))

运行结果,如下:
数据预处理后 linear 核函数的模型训练集得分:0.705
数据预处理后 linear 核函数的模型测试集得分:0.698
数据预处理后 rbf 核函数的模型训练集得分:0.665
数据预处理后 rbf 核函数的模型测试集得分:0.694
结果分析:
从结果可以看到,“linear”内核的SVR得分变化不大,而“rbf“内核的SVR得分有了巨大提升。尤其是测试集的得分,从0.001分直接提升到0.694.
思考一下,如果我们进一步调整“rbf”内核的svr模型参数,会不会让它的表现进一步提升呢?
和SVC一样,SVR也有gamma和C两个参数,进行调参:

svr = SVR(C=100, gamma=0.1)
svr.fit(X_train_scaled, y_train)
print('调节参数后的模型在训练集得分:{:.3f}'.format(
        svr.score(X_train_scaled, y_train)))
print('调节参数后的模型在测试集得分:{:.3f}'.format(
        svr.score(X_test_scaled, y_test)))

运行代码,结果如下:
调节参数后的模型在训练集得分:0.966
调节参数后的模型在测试集得分:0.894
结果分析:
效果还不错,可以看到通过调节参数,“rbf”内核的SVR模型在训练集的得分已经高达0.966,测试集得分也不低。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358