【机器学习】基础入门(3)SVM支持向量机及代码实现

机器学习 专栏收录该内容
3 篇文章 0 订阅

1 支持向量机SVM

一篇非常好的SVM理论讲解(点这里)

1.1 支持向量机介绍

STEP 1:构建数据集

import matplotlib.pyplot as plt
from sklearn.datasets.samples_generator import make_blobs

# 散点图可视化
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=60, cmap=plt.cm.Paired)

在这里插入图片描述

make_blobs模块构造数据集
sklearn.datasets.make_blobs(n_samples=100, n_features=2, centers=3, cluster_std=1.0, center_box=(-10.0, 10.0), shuffle=True, random_state=None)

  • n_samples: int, optional (default=100),待生成的样本的总数
  • n_features: int, optional (default=2),每个样本的特征数
  • centers: int or array of shape [n_centers, n_features], optional (default=3),要生成的样本中心(类别)数,或者是确定的中心点
  • cluster_std: float or sequence of floats, optional (default=1.0),每个类别的方差,例如我们希望生成2类数据,其中一类比另一类具有更大的方差,可以将cluster_std设置为[1.0,3.0]

STEP 2:线性分类器

# 画散点图
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)
x_fit = np.linspace(0, 3) #将(0,3)等距离分为50份

# 画函数(可能会有多种分法)
y_1 = 1 * x_fit + 0.8
plt.plot(x_fit, y_1, '-c')

y_2 = -0.3 * x_fit + 3
plt.plot(x_fit, y_2, '-k')

plt.show()

在这里插入图片描述

np.linspace:主要用来创建等差数列
numpy.linspace(start, stop, num, endpoint=True, retstep=False, dtype=None, axis=0)
在start和stop之间返回均匀间隔的数据

  • start:返回样本数据开始点
  • stop:返回样本数据结束点
  • num:生成的样本数据量,默认为50
  • endpoint:True则包含stop;False则不包含stop
  • retstep:如果为True则结果会给出数据间隔
    dtype:输出数组类型
  • dtype:输出数组类型

STEP 3:测试新数据分类情况

# 画散点图
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)
# 在散点图上标记新加入的红色节点(3,2.8)为<
plt.scatter([3], [2.8], c='#cccc00', marker='<', s=100, cmap=plt.cm.Paired)
x_fit = np.linspace(0, 3) #将(0,3)等距离分为50份

# 画函数
y_1 = 1 * x_fit + 0.8
plt.plot(x_fit, y_1, '-c')

y_2 = -0.3 * x_fit + 3
plt.plot(x_fit, y_2, '-k')

plt.show()

在这里插入图片描述
可以看到,此时黑色的线会把这个新的数据集分错,蓝色直线分类正确。
However上面这个例子的给出带有主观性,如何客观地评判两种分类方式的健壮性呢?

STEP 4:分类器优劣判断——最大间隔

# 画散点图
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)
x_fit = np.linspace(0, 3)

# 画函数
y_1 = 1 * x_fit + 0.8
plt.plot(x_fit, y_1, '-c')
y_2 = -0.3 * x_fit + 3
plt.plot(x_fit, y_2, '-k')

# 画边距
plt.fill_between(x_fit, y_1 - 0.6, y_1 + 0.6, edgecolor='none', color='#AAAAAA', alpha=0.4)
plt.fill_between(x_fit, y_2 - 0.4, y_2 + 0.4, edgecolor='none', color='#AAAAAA', alpha=0.4)

fill_between:函数区间填充
plt.fill_between( x, y1, y2=0, where=None, interpolate=False, step=None, hold=None, data=None,**kwargs)

  • x - array( length N) 定义曲线的 x 坐标
  • y1 - array( length N ) or scalar 定义第一条曲线的 y 坐标
  • y2 - array( length N ) or scalar 定义第二条曲线的 y 坐标
  • where - array of bool (length N), optional, default: None 排除一些(垂直)区域被填充。where = 条件表达式,这里的 where =可省略
  • alpha 透明度
    在这里插入图片描述
    可以看出蓝色的线最大间隔是大于黑色的线,所以我们选择蓝色的线作为分类器。

STEP 5 :选取最优分类器——SVM

# SVM 函数
from sklearn.svm import SVC
clf = SVC(kernel='linear')
clf.fit(X, y)

# 最佳函数 
# 超平面方程格式形如 w1y+w0x+b=0, y= (-w0/w1)*x1-b/w1
w = clf.coef_[0] # 获取w
a = -w[0] / w[1] # 斜率
y_3 = a*x_fit - (clf.intercept_[0]) / w[1]
# intercept_用来获得截距

# 最大下界
b_down = clf.support_vectors_[0] # 求出过切线的点(支持向量)
y_down = a* x_fit + (b_down[1] - a * b_down[0]) #下边界
# 最大上界
b_up = clf.support_vectors_[-1]
y_up = a* x_fit + (b_up[1] - a * b_up[0])

# 可视化上述上下边界

# 画散点图
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)
# 画函数
plt.plot(x_fit, y_3, '-c')
# 画边距
plt.fill_between(x_fit, y_down, y_up, edgecolor='none', color='#AAAAAA', alpha=0.4)
# 画支持向量
plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], edgecolor='b',s=80, facecolors='none')
plt.show()

在这里插入图片描述
带黑边的点是距离当前分类器最近的点,我们称之为支持向量。

支持向量机为我们提供了在众多可能的分类器之间进行选择的原则,从而确保对未知数据集具有更高的泛化性。

1.2 软间隔

In fact,我们真实拿到的数据很多是下图这种样子的,这种情况并不容易找到这样的最大间隔,于是我们就有了软间隔 ,即允许个别数据出现在间隔带中。
在这里插入图片描述
然而,如果没有一个原则进行约束,满足软间隔的分类器也会出现很多个,所以需要对分错的数据进行惩罚。
SVC 函数中参数 C 即为惩罚参数 ,惩罚参数越小,容忍性就越大,可兼容更多的错分样本。
举个栗子:

# 软间隔 C=1时

# 画散点图
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.9)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)
x_fit = np.linspace(-2, 4)

# 惩罚参数:C=1
from sklearn.svm import SVC
clf = SVC(C=1, kernel='linear')
clf.fit(X, y)

# 最佳函数
w = clf.coef_[0]
a = -w[0] / w[1]
y_3 = a*x_fit - (clf.intercept_[0]) / w[1]

# 最大下界
b_down = clf.support_vectors_[0]
y_down = a* x_fit + b_down[1] - a * b_down[0]
# 最大上界
b_up = clf.support_vectors_[-1]
y_up = a* x_fit + b_up[1] - a * b_up[0]

# 画散点图
X, y = make_blobs(n_samples=60, centers=2, random_state=0, cluster_std=0.4)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)

# 画函数
plt.plot(x_fit, y_3, '-c')

# 画边距
plt.fill_between(x_fit, y_down, y_up, edgecolor='none', color='#AAAAAA', alpha=0.4)

# 画支持向量
plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], edgecolor='b', s=80, facecolors='none')
plt.title('C=1')
plt.show()

在这里插入图片描述
将参数改为0.2可得下图:
在这里插入图片描述
从图中可明显看出,惩罚参数越小,容忍性就越大,可兼容更多的错分样本。

1.3 超平面

假设存在如下图所示的这样一个数据集,其构造方式如下

from sklearn.datasets.samples_generator import make_circles
from sklearn.svm import SVC

# 画散点图
X, y = make_circles(100, factor=.1, noise=.1, random_state=2019)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)
clf = SVC(kernel='linear').fit(X, y)

在这里插入图片描述
make_circles模块构造数据集:
sklearn.datasets.make_circles(n_samples=100, shuffle=True, noise=None, random_state=None, factor=0.8)
factors:

  • n_samples:int,设置样本数量,default100
  • shuffle:bool,optional,default True,是否打乱样本
  • noise:double or None ,default None,将高斯噪声的标准差加入到数据中
  • random_state:int RandomState instance or None,确定数据集变换和噪声的随机数生成
    是否打乱样本
  • factor:0 < double < 1 默认值0.8,内外圆之间的比例因子

return:

  • X:[n_samples, 2]形状的数组,生成的样本
  • y:[n_samples]形状的数组,每个样本的标签(0或1)

如果我们用之前的线性分类器方式进行分类,得到的结果如下图,此种方法是无效的。
在这里插入图片描述
对于类似于上面的这种问题,我们可以将低维空间的数据映射到高维空间中。 此时我们便可以通过一个超平面对数据进行划分。
我们映射的目的在于使用 SVM 在高维空间找到超平面的能力

# 数据映射
r = np.exp(-(X[:, 0] ** 2 + X[:, 1] ** 2))
ax = plt.subplot(projection='3d') #subplot绘制多个子图
ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap=plt.cm.Paired)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
x_1, y_1 = np.meshgrid(np.linspace(-1, 1), np.linspace(-1, 1))
z = 0.01*x_1 + 0.01*y_1 + 0.5
ax.plot_surface(x_1, y_1, z, alpha=0.3)

此段代码运行报warning,打印出结果图为:
在这里插入图片描述
UserWarning: The left and right margins cannot be made large enough to accommodate all axes decorations. warnings.warn('The left and right margins cannot be made large ’
解决方法是把interpreter从py3.5换成了py3.7…居然就好了…然后得到了下面的正确结果图:
在这里插入图片描述
plt.subplot(): 绘制多个子图官方文档
plt.subplot(nrows, ncols, index, **kwargs)

  • 可使三个整数来描述子图的位置。三个整数是行数、列数和索引值,索引从1开始。plt.subplot(2, 3, 5) 和 plt.subplot(235) 是一样的,数字不能超过10
  • projection : {None, ‘aitoff’, ‘hammer’, ‘lambert’, ‘mollweide’, ‘polar’, ‘rectilinear’, str}, optional,可选择子图类型,比如polar是极点图,默认none是线形图。

在 SVC 中,我们可以用高斯核函数来实现这以功能:kernel='rbf’

# SVC中实现数据映射,用高斯核函数kernel='rbf'

# 画图
X, y = make_circles(100, factor=.1, noise=.1, random_state=2019)
plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap=plt.cm.Paired)
clf = SVC(kernel='rbf')
clf.fit(X, y)

ax = plt.gca() #plt.gca()进行坐标轴的移动
x = np.linspace(-1, 1)
y = np.linspace(-1, 1)
x_1, y_1 = np.meshgrid(x, y)

P = np.zeros_like(x_1)
# zeros_like(a),生成和数组a同shape的全0数组
for i, xi in enumerate(x): #i为索引,xi为数据
    for j, yj in enumerate(y): #j为索引,yj为数据
        P[i, j] = clf.decision_function(np.array([[xi, yj]]))
        # decision_function,计算样本点到分割超平面的函数距离

ax.contour(x_1, y_1, P, colors='k', levels=[-1, 0, 0.9], alpha=0.5, linestyles=['--', '-', '--']) #等高线
plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], edgecolor='b', s=80, facecolors='none')
plt.show()

在这里插入图片描述

此时便完成了非线性分类。

enumerate()函数: 枚举
用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
enumerate(sequence, [start=0])

参数:

  • sequence – 一个序列、迭代器或其他支持迭代对象
  • start – 下标起始位置

返回值:

  • enumerate(枚举) 对象。

decision_function()
计算样本点到分割超平面的函数距离(将几何距离进行归一化)

  • 将x1=(3,3),x2=(4,3),x3=(1,1),labels=(1,1,−1)带入决策函数decision_function( ),也就是分割超平面1/2x1 + 1/2x2 -2即可得到函数距离:1, 1.5, -1,其中1 和 -1 刚好在margin边缘上,x1,x3也就是支持向量

2 Demo实践

2.1 库函数导入

利用sklearn直接调用 SVM函数进行实践尝试

from sklearn import svm

2.2 构建数据集

# 构造数据集
x_fearures = np.array([[-1, -2], [-2, -1], [-3, -2], [1, 3], [2, 1], [3, 2]])
y_label = np.array([0, 0, 0, 1, 1, 1])

2.3 建模

# 调用SVC模型 (支持向量机分类)
svc = svm.SVC(kernel='linear')
svc = svc.fit(x_fearures, y_label)

# 查看模型参数 y= -w0/w1*x1-b1/w1
print('the weight w :', svc.coef_)
print('the intercept b :', svc.intercept_)
the weight w : [[0.33364706 0.33270588]]
the intercept b : [-0.00031373]

2.4 模型预测

# 模型预测
y_train_pred = svc.predict(x_fearures)
print('The predction result:', y_train_pred)
The predction result: [0 0 0 1 1 1]

2.5 模型可视化

# 最佳函数
x_range = np.linspace(-3, 3)
w = svc.coef_[0]
a = -w[0] / w[1]
y_3 = a*x_range - (svc.intercept_[0]) / w[1]

# 可视化决策边界
plt.figure()
plt.scatter(x_fearures[:,0],x_fearures[:,1], c=y_label, s=50, cmap='viridis')
plt.plot(x_range, y_3, '-c')
plt.show()

在这里插入图片描述
对照之前的逻辑回归模型的决策边界,发现两个决策边界是有一定差异的,原因其实是由于两者选择的 最优目标是不一致的。

2020.8.25
TBC…

  • 0
    点赞
  • 1
    评论
  • 3
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 1 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页

打赏作者

baekii

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值