支持向量机(Support Vector Machine,SVM)是一个非常强大的算法,具有非常完善的数学理论,常用于数据分类,也可以用于数据的回归预测中,由于其其优美的理论保证和利用核函数对于线性不可分问题的处理技巧,在上世纪90年代左右,SVM曾红极一时。
说到SVM的算法强大,可以从下面这张图感受一下(图片来源于网络)
image.png
从实际应用来看,SVM在各种实际问题中都表现非常优秀。它在手写识别数字和人脸识别中应用广泛,在文本和超文本的分类中举足轻重,因为SVM可以大量减少标准归纳(standard inductive)和转换设置(transductive settings)中对标记训练实例的需求。同时,SVM也被用来执行图像的分类,并用于图像分割系统。实验结果表明,在仅仅三到四轮相关反馈之后,SVM就能实现比传统的查询细化方案(query refinement schemes)高出一大截的搜索精度。除此之外,生物学和许多其他科学都是SVM的青睐者,SVM现在已经广泛被用于蛋白质分类,现在化合物分类的业界平均水平可以达到90%以上的准确率。在生物科学的尖端研究中,人们还使用支持向量机来识别用于模型预测的各种特征,以找出各种基因表现结果的影响因素。
从学术的角度来看,SVM是最接近深度学习的机器学习算法。线性SVM可以看成是神经网络的单个神经元(虽然损失函数与神经网络不同),非线性的SVM则与两层的神经网络相当,非线性的SVM中如果添加多个核函数,则可以模仿多层的神经网络。
当然了,没有算法是完美的,比SVM强大的算法在集成学习和深度学习中还有很多很多。但不可否认,在传统的机器学习领域,SVM是一个非常耀眼的存在。
本期我们就从最简单的几个概念:最大间隔,软间隔,超平面初步入门支持向量机算法。
我们先从一个最简单的二分类问题入手。
如下图,我们需要引入一个分类器将下面两个类别的点分开。
对此,我们简单的画出两个直线作为分类器进行分割。
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 = 65, cmap=plt.cm.Paired)
x_fit = np.linspace(0,3)
y_1 = 1*x_fit + 0.8
y_2 = -0.3 * x_fit +3
plt.plot(x_fit, y_1, '-b')
plt.plot(x_fit, y_2, '-k')
image.png
可以看到黑线和蓝色的线都可以将两类点分开,那么那条线更好呢?为此我们引入一个判断标准:好的分类器不仅仅是能够很好的分开已有的数据集,还能对未知数据集 进行两个的划分。我们在加入一个新的点看看两个分类器分类情况:
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.scatter([3], [2.5], c='#cccc00', marker='*', s=150, cmap=plt.cm.Paired)
x_fit = np.linspace(0, 3)
# 画函数
y_1 = 1 * x_fit + 0.8
plt.plot(x_fit, y_1, '-b')
y_2 = -0.3 * x_fit + 3
plt.plot(x_fit, y_2, '-k')
image.png
可以看到,此时黑色的线会把这个新的数据集分错,因为新加入的数据五角星明显和红色的点位置更近。而蓝色的线则可以正确的区分。
上面的例子带有一定的主观性, 那么如何客观的评判两条线的健壮性呢? 此时,我们需要引入一个非常重要的概念:最大间隔。 最大间隔刻画着当前分类器与数据集的边界,以这两个分类器为例:
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')
# 画边距
plt.fill_between(x_fit, y_1 - 0.6, y_1 + 0.6, edgecolor='none', color='#AAAAAA', alpha=0.4)
y_2 = -0.3 * x_fit + 3
plt.plot(x_fit, y_2, '-k')
plt.fill_between(x_fit, y_2 - 0.4, y_2 + 0.4, edgecolor='none', color='#AAAAAA', alpha=0.4)
image.png
可以看到, 蓝色的线最大间隔是大于黑色的线的。 所以我们会选择蓝色的线作为我们的分类器。 那么有没有更好的呢?这就是下面的主角-SVM算法。为此我们要引入SVM算法:
from sklearn.svm import SVC
clf = SVC(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=60, cmap=plt.cm.Paired)
# 画函数
plt.plot(x_fit, y_3, '-b')
# 画边距
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')
效果如下:
image.png
带黑边的点是距离当前分类器最近的点,我们称之为支持向量。 支持向量机为我们提供了在众多可能的分类器之间进行选择的原则,从而确保对未知数据集具有更高的泛化性。同时也可以直观的感受到SVM算法给出的边界要明显由于上面的两条线。
在实际应用中,我们要进行分类的数据要比上面这个情况复杂的多,可能和下面的类似:
image.png
这种情况并不容易找到这样的最大间隔。 于是我们就有了软间隔,相比于硬间隔而言,我们允许个别数据出现在间隔带中。
我们知道,如果没有一个原则进行约束,满足软间隔的分类器也会出现很多条。 所以需要对分错的数据进行惩罚,SVC 函数中,有一个参数 C 就是惩罚参数。 惩罚参数越小,容忍性就越大,效果如下:
# 画散点图
plt.figure(figsize=(15,8))
plt.subplot(121)
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)
# 惩罚参数:C=1
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', fontsize = 15)
plt.subplot(122)
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)
# 惩罚参数:C=0.2
clf = SVC(C=0.2, kernel='linear')
clf.fit(X, y)
x_fit = np.linspace(-1.5, 4)
# 最佳函数
w = clf.coef_[0]
a = -w[0] / w[1]
y_3 = a*x_fit - (clf.intercept_[0]) / w[1]
# 最大边距 下届
b_down = clf.support_vectors_[10]
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=0.2', fontsize = 15)
image.png
可以看到C=0.2时,算法的会更具包容性,从而兼容更多的错分样本。
更复杂一点,如果遇到下面这种情况,就无法使用线性分类器来分了:
image.png
这时就需要用到超平面的概念 我们可以将二维(低维)空间的数据映射到三维(高维)空间中。 此时,我们便可以通过一个超平面对数据进行划分 所以,我们映射的目的在于使用 SVM 在高维空间找到超平面的能力。
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()
x = np.linspace(-1, 1)
y = np.linspace(-1, 1)
x_1, y_1 = np.meshgrid(x, y)
P = np.zeros_like(x_1)
for i, xi in enumerate(x):
for j, yj in enumerate(y):
P[i, j] = clf.decision_function(np.array([[xi, yj]]))
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')
画出的边界如下:
image.png
网友评论