文章目录
1 线性SVM1.1 优化的目标1.2 直观展示1.3 公式表达1.3.1 约束条件1.3.2 硬间隔形式1.3.3 软间隔形式 1.4 sklearn中的线性SVM1.4.1 原型1.4.2 常用参数1.4.3 常用属性1.4.4 常用方法 1.5 实例1:利用sklearn中的线性SVM实现分类可视化1.5.1 效果展示示例1.5.2 可视化函数的设计1.5.2.1 设计思路1.5.2.2 代码实现 1.5.4 观察标准化的作用1.5.5 数据集的加载1.5.6 定义不同的线性SVM分类器1.5.7 实现分类效果可视化的准备工作1.5.7.1 对各分类器的各个参数做标准化处理1.5.7.2 求解偏置 b \boldsymbol b b1.5.7.3 decision_function函数1.5.7.3 代码实现 1.5.8 分类效果可视化图片的绘制1 线性SVM
1.1 优化的目标
线性SVM的优化目标,用一句话来概括就是:最大化间隔,同时尽可能减少分类错误。
下面首先从图片展示的角度来对这句话进行解释。
1.2 直观展示
假设有红色和蓝色两类样本(如下图所示),其中蓝色表示正样本(标签为1),红色表示负样本(标签为-1)。SVM要做的,就是找到一个合适的边界,使得该边界能够较好地将两类样本分开。寻找边界的直观过程如下:
如果想要画出一条边界将红蓝两类样本分开,实际上有非常多种画法:
这就出现了一个问题:到底要选哪一条?哪条是最优的分法?对此,我们可以尝试将边界的宽度扩大,使得边界变成很粗的直线,再来分开这两类样本。效果如下:
同样有很多种分隔的方法,无法确定具体要使用哪一种。若找准角度,使用一条最粗的线分隔,如下图所示:
这样就找到了大间隔和决策边界,如下:
上图中一些字母和线条的含义如下:
margin:最优的分类间隔,所产生的分类结果最鲁棒,对未见样本的泛化能力最强中间线:决策边界
上面找的大间隔和决策边界是肉眼可以直观分出来的,那计算机要如何自己找到呢?
1.3 公式表达
1.3.1 约束条件
假设有如下数据集:
D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x n , y n ) } D= \{ (x_1,y_1),(x_2,y_2),...,(x_n,y_n)\} D={(x1,y1),(x2,y2),...,(xn,yn)}
其中, x i ∈ R d x_i\in\mathbb{R}^d xi∈Rd , y i ∈ { − 1 , + 1 } y_i\in\{-1,+1\} yi∈{−1,+1} 。
决策边界为:
w T x + b = 0 \boldsymbol w^T\boldsymbol x+b=0 wTx+b=0
w \boldsymbol w w 为法向量,决定了决策边界的方向 b \boldsymbol b b 为偏置项,决定了决策边界与原点之间的距离
则样本被正确分类的条件为:
y i ( w T x i + b ) ⩾ 1 y_i(\boldsymbol w^T\boldsymbol x_i+b)\geqslant 1 yi(wTxi+b)⩾1
即:
{ w T x i + b ⩾ 1 ( y i = 1 ) w T x i + b ⩽ − 1 ( y i = − 1 ) \left\{\begin{aligned}\boldsymbol w^T\boldsymbol x_i+b \geqslant 1\quad (y_i=1 ) \\ \boldsymbol w^T\boldsymbol x_i+b \leqslant -1 \quad(y_i=-1 ) \end{aligned} \right. {wTxi+b⩾1(yi=1)wTxi+b⩽−1(yi=−1)
满足该约束的 w \boldsymbol w w 和 b b b 便可以将训练集的样本正确分开。
接下来看看如何找到满足该条件的 w w w 和 b b b,使得间隔最大化。
1.3.2 硬间隔形式
分类间隔 m a r g i n margin margin 的计算公式为:
m a r g i n = 4 w T w w T w × w T w = 2 w T w margin = \sqrt{\frac{4\boldsymbol w^T\boldsymbol w}{\boldsymbol w^T\boldsymbol w×\boldsymbol w^T\boldsymbol w}}=\frac{2}{\sqrt{\boldsymbol w^T \boldsymbol w}} margin=wTw×wTw4wTw =wTw 2
则 SVM 线性分类器所要求解的问题转化为:
max w , b 2 w T w s . t . y i ( w T x i + b ) ⩾ 1 \max_{w,b}\frac{2}{\sqrt{\boldsymbol w^T\boldsymbol w}} \\ s.t. \quad y_i(\boldsymbol w^T \boldsymbol {x_i}+b) \geqslant 1 w,bmaxwTw 2s.t.yi(wTxi+b)⩾1
这种形式称为SVM的基本型,它要求所有的样本均要被正确分类。这称为SVM问题的硬间隔形式。
1.3.3 软间隔形式
硬间隔的约束条件如下:
y i ( w T x i + b ) − 1 ⩾ 0 y_i(\boldsymbol w^T \boldsymbol {x_i}+b)-1 \geqslant 0 yi(wTxi+b)−1⩾0
它要求所有的样本均被正确分类,这在某些有噪声的情况下是不现实的。如下图所示:
为了处理带噪声的情况,可以采用软间隔,该形式允许一部分样本样本不满足上述约束条件,但同时也要尽可能最大化间隔,并使得不满足约束条件的样本尽可能少。在这种情况下,目标函数可写为:
min w , b 1 2 w T w + C ∑ i = 1 m l ( y i ( w T x i + b ) − 1 ) \min_{\boldsymbol w,b}\frac{1}{2}\boldsymbol w^T\boldsymbol w+C\sum_{i=1}^m l(y_i(\boldsymbol w^T \boldsymbol {x_i}+b)-1 ) w,bmin21wTw+Ci=1∑ml(yi(wTxi+b)−1)
其中 l l l 表示损失函数。损失函数 l l l 一般有如下两种。为了方便表达,下面令 z = y i ( w T x i + b ) z = y_i(\boldsymbol w^T \boldsymbol {x_i}+b) z=yi(wTxi+b):
0/1损失函数:
l 0 / 1 = { 1 , z < 0 0 , o t h e r l_{0/1}= \left\{\begin{aligned} 1, \quad z < 0 \\ 0,\quad other \end{aligned} \right. l0/1={1,z<00,other
hinge损失函数:
l h i n g e ( z ) = m a x ( 0 , 1 − z ) ) l_{hinge}(z)=max(0,1- z)) lhinge(z)=max(0,1−z))
上述两种损失函数的对比图如下:
可以看到, l 0 / 1 l_{0/1} l0/1的 z < 0 z<0 z<0和 z ≥ 0 z \ge 0 z≥0 时均为常数,数学性质不好,用来做损失函数可能会导致梯度消失的情况产生;而 l h i n g e ( z ) l_{hinge}(z) lhinge(z) 在 z < 1 z<1 z<1时为连续直线,可导,所以数学性质好于 l 0 / 1 l_{0/1} l0/1。
l h i n g e ( z ) l_{hinge}(z) lhinge(z) 函数也是线性SVM最常用的损失函数。sklearn将其设置为SVC的默认损失函数。
在上面分析结果的基础上,令:
ξ i = m a x ( 0 , 1 − z ) ⩾ 0 \xi_i = max(0,1- z) \geqslant 0 ξi=max(0,1−z)⩾0
则软间隔的目标函数改写为:
min w , b , ξ i 1 2 w T w + C ∑ i = 1 m ξ i s . t . y i ( w T x i + b ) ⩾ 1 − ξ i \min_{\boldsymbol w,b,\xi_i}\frac{1}{2}\boldsymbol w^T\boldsymbol w+C\sum_{i=1}^m \xi_i\\ s.t. \quad y_i(\boldsymbol w^T \boldsymbol {x_i}+b) \geqslant 1-\xi_i w,b,ξimin21wTw+Ci=1∑mξis.t.yi(wTxi+b)⩾1−ξi
其中, ξ \xi ξ叫做松弛变量,它能够给分错的样本加上惩罚。当 $\xi $ 等于0时,约束条件与基本型(硬间隔)相同,即要求每个样本均被正确分类。
在松弛变量 ξ \xi ξ前面带有一个参数 C C C ,它是线性SVM中一个非常重要的超参数,起到了平衡松弛变量的作用。该参数发挥的具体作用如下:
当 C C C 为有限值时,允许一些样本不满足约束,但 C C C 值越大, ξ \xi ξ应越小,表示惩罚强度越大、允许分错的样本越少;当 C C C→+∞时, ξ \xi ξ必须为0,目标函数才不会 → \to →+∞,这样就迫使所有样本均满足上面的约束,此时目标函数等价于基本型(硬间隔)的目标函数形式。
因为 C C C 对分类效果的影响非常大,所以它也是线性SVM中一个主要的调参对象。
sklearn中的LinearSVC类对这些原理均进行了实现,在理解了上述内容的基础上,就可以为sklearn调参过程提供一定的导向。
1.4 sklearn中的线性SVM
sklearn中的LinearSVC类对线性SVM进行了实现。下面就让我们来看看这个类的使用方法,以及我们如何将该类中的参数、方法、属性等与上面所介绍到的点进行一一对应。
1.4.1 原型
sklearn.svm.LinearSVC(penalty=’l2’, loss=’squared_hinge’, dual=True, tol=0.0001, C=1.0, multi_class=’ovr’, fit_intercept=True, intercept_scaling=1, class_weight=None, verbose=0, random_state=None, max_iter=1000)
1.4.2 常用参数
loss
:字符串类型,表示损失函数
hinge
: 使用 hinge loss 损失函数(标准SVM的损失函数)
squared_hinge
:hinge loss 损失函数的平方
tol
:浮点数类型,默认为 1 0 − 4 10^{-4} 10−4,表示对损失的容忍度,损失降低到 tol 时,停止训练
C
:浮点数类型,表示惩罚系数,起到平衡松弛变量的作用。注意:C 必须为正数!
multi_class
:字符串类型,指定多类分类问题的策略
ovr
:默认参数,表示采用 one-vs-rest(一对多余)的分类策略crammer_singer
:多类联合分类
max_iter
:整型,默认为1000,表示训练的最大迭代次数
1.4.3 常用属性
coef_
: 数组,给出各个特征的权重(对应 w \boldsymbol w w)intercept_
: 数组,给出截距(对应 b \boldsymbol b b)classes_
:样本中所有类别的标签(对应 y i \boldsymbol y_i yi)support_vectors_
:获取支持向量(即落在两条边界线之上或之间的样本点)这4个属性常用于线性SVM分类效果的可视化。
1.4.4 常用方法
fit(X, y)
:训练模型predict(X)
:用模型进行预测,返回预测值score(X, y[, sample_weight])
:返回预测的准确率decision_function(X)
:预测每个样本的置信度分数(在线性SVM中为各个样本到决策边界的有符号距离)1.5 实例1:利用sklearn中的线性SVM实现分类可视化
在对sklearn中的线性SVM的API有个大概了解之后,接下来将围绕着该API的使用做一步步展开,对一个二分类的简单数据集做可视化。该实例的目的为:
涵盖该API中的多个点,尽量让对整个LinearSVC的使用有一个整体的认识;将对C调参的结果与可视化结合起来,使得读者可以从视觉上观察出调参的规律。
1.5.1 效果展示示例
我们要画出类似如下图的效果,实现二元分类效果的可视化,绘制出线性SVM分类中的决策边界、大间隔以及支持向量。效果图如下:
1.5.2 可视化函数的设计
1.5.2.1 设计思路
想要将线性SVM的分类效果可视化,需要定义可视化分类效果的函数。该函数的设计思路如下:
形参
svm_classifier:表示需要可视化分类效果的分类器xmin, xmax:横轴的范围
设计流程
利用分类器svm_classifier中的 coef_ 属性获取权值 w \boldsymbol w w。
其中w[0]表示第一个特征对应的权值,w[1]表示第二个特征对应的权值;
定义横轴,用来表示第一个特征 x 0 x_0 x0;
定义纵轴,用第一个特征 x 0 x_0 x0来表示第二个特征 x 1 x_1 x1。 由公式:
w 0 x 0 + w 1 x 1 + b = 0 ( 1.5.2.1 ) w_0 x_0 + w_1 x_1+b=0\quad\quad (1.5.2.1) w0x0+w1x1+b=0(1.5.2.1)
得:
x 1 = − w o w 1 x 0 − b w 1 ( 1.5.2.2 ) x_1=-\frac{w_o}{w_1}x_0-\frac{b}{w_1} \quad\quad (1.5.2.2) x1=−w1wox0−w1b(1.5.2.2)
该式子实际上即为决策边界(上图中间黑实线)的计算公式;
定义求大间隔的公式:
m a r g i n = 2 w 0 2 + w 1 2 ( 1.5.2.3 ) margin=\frac{2}{\sqrt{w_0^2+w_1^2}}\quad\quad (1.5.2.3) margin=w02+w12 2(1.5.2.3)
计算上下边界(上图两虚线),计算公式为:
u p _ l i n e = x 1 + m a r g i n 2 ( 1.5.2.4 ) d o w n _ l i n e = x 1 − m a r g i n 2 ( 1.5.2.5 ) up\_line=x_1+\frac{margin}{2} \quad\quad (1.5.2.4)\\ down\_line=x_1-\frac{margin}{2}\quad\quad (1.5.2.5) up_line=x1+2margin(1.5.2.4)down_line=x1−2margin(1.5.2.5)
利用 support_vectors_ 属性获取支持向量;
将上述求解结果用代码进行实现。
1.5.2.2 代码实现
按照上述思路,该函数的完整代码如下:
def plot_linear_svm_decision_margin_and_boundary(svm_classifier, xmin, xmax):# 获得分类器最终学到的的权值w = svm_classifier.coef_[0]# 获得分类器最终学到的偏置b = svm_classifier.intercept_[0]# 定义横轴为特征x0,范围为xmin到xmax,200等分x0 = np.linspace(xmin, xmax, 200)# 定义纵轴为特征x1,求取x1的公式为[2.1.2],该公式实际上也是决策边界的表达式x1 = -w[0]/w[1] * x0 - b/w[1]# 实现公式[2.1.3]求得大间隔margin,再除以二求得大间隔的一半margin_half = 1 / np.sqrt(w[0]**2 + w[1]**2)# 右上分界线的表达式up_line = x1 + margin_half# 左下分界线的表达式down_line = x1 - margin_half# 获得支持向量(即落在两条边界线上或之间的样本点)support_vecs = svm_classifier.support_vectors_# 画出支持向量,并在周围打上阴影做标注plt.scatter(support_vecs[:, 0], support_vecs[:, 1], s = 120, facecolors='red' )# 画出决策边界plt.plot(x0, x1, "k-", linewidth=2)# 画出两条分类边界plt.plot(x0, up_line, "k--", linewidth=2)plt.plot(x0, down_line, "k--", linewidth=2)
1.5.4 观察标准化的作用
在使用线性SVM完成分类任务之前,进行标准化是非常重要的步骤。如果不进行这一步,会对分类效果产生很大的负面影响。下面就以二维空间中几个分布稀疏的点为例,分别绘制未标准化前和未标准化后的分类情况,从而直观展示出标准化的重要性。代码如下:
# 画出几个分布稀疏的点Xs = np.array([[1, 50], [3, 30], [5, 20], [2, 85],[4, 70] ,[5, 80]]).astype(np.float64)ys = np.array([0, 0, 0,1, 1, 1])# 定义线性SVM分类器svm_clf = SVC(kernel="linear", C=100)# 使用分类器svm_clf对上述点进行拟合svm_clf.fit(Xs, ys)plt.figure(figsize=(10,4))# 第一个图展示未标准化的分类情况plt.subplot(121)plt.plot(Xs[:, 0][ys==1], Xs[:, 1][ys==1], "bo", label='Class 0')plt.plot(Xs[:, 0][ys==0], Xs[:, 1][ys==0], "gs", label='Class 1')plot_linear_svm_decision_margin_and_boundary(svm_clf, 0, 8)plt.xlabel("$x_0$", fontsize=14)plt.ylabel("$x_1$ ", fontsize=14, rotation=0)plt.title("Before Being Standardized", fontsize=14)plt.axis([0, 6, 0, 90])# 定义标准化类scaler = StandardScaler()# 使用标准化类对Xs中的点进行标准化,得到标准化后的数据X_scaled = scaler.fit_transform(Xs)# 使用分类器svm_clf对标准化后的点进行拟合svm_clf.fit(X_scaled, ys)# 第二个图展示标准化后的分类情况plt.subplot(122)plt.plot(X_scaled[:, 0][ys==1], X_scaled[:, 1][ys==1], "bo", label='Class 0')plt.plot(X_scaled[:, 0][ys==0], X_scaled[:, 1][ys==0], "gs",label='Class 1')plot_linear_svm_decision_margin_and_boundary(svm_clf, -2, 2)plt.xlabel("$x_0$", fontsize=14)plt.title("After Being Standardized", fontsize=14)plt.axis([-2, 2, -2, 2])
运行结果如下:
观察上面两张图,可以发现:
左图表示未标准化前的情况,横坐标的范围为0到6,纵坐标的范围为0到90,各个点的分布非常稀疏,导致整体的分类效果较差;右图表示标准化后的情况,横坐标和纵坐标的范围均缩小到个位数,导致数据点整体分布非常紧凑,分类的效果也比左图好得多。
由此可以看到:SVM线性分类中标准化的分类效果比未标准化好很多,所以在使用SVM做线性分类之前请务必先标准化。
1.5.5 数据集的加载
线性SVM中一个很重要的参数是 C C C,该参数的选择会对分类效果造成很大影响。下面选取了鸢尾花数据集作为训练数据集,通过调整参数 C C C,并调用上面定义的可视化函数,在二维空间中实现了不同C值下分类效果的可视化,使得读者可以直观感受到 C C C 值调参对线性SVM分类效果的影响。数据集加载的代码如下:
# 加载鸢尾花数据集iris = datasets.load_iris()# 选取每个样本的第2、3个特征,组成二维数据集,便于可视化X = iris["data"][:, (2, 3)] # 属于类别2的样本标签置为1,否则置为0y = (iris["target"] == 2).astype(np.float64) # 由于SVM中要求类别的标签为-1或1,故需要对y做一下处理y = y *2 -1svm_clf = Pipeline([("scaler", StandardScaler()),("linear_svc", LinearSVC(C=1, loss="hinge", random_state=42)),])svm_clf.fit(X, y)
经过处理后的标签 y 就全都是1或-1,这样就符合二分类线性SVM对标签格式的要求。
1.5.6 定义不同的线性SVM分类器
下面定义了4个不同的线性分类器,目的是为了观察不同 C C C 值对分类效果的影响,所以必须保证除了 C C C 之外的其他参数均保持一致。
同时,对于每个分类器,在分类之前均需要对数据进行标准化操作。代码如下:
# 定义数据集的标准化方法scaler = StandardScaler()# 定义参数C不相同、其他参数相同的4个线性分类器svm_clf1 = LinearSVC(C=1, loss="hinge", random_state=42)svm_clf2 = LinearSVC(C=10, loss="hinge", random_state=42)svm_clf3 = LinearSVC(C=100, loss="hinge", random_state=42)svm_clf4 = LinearSVC(C=float("inf"), loss="hinge", random_state=42)# 对这4个分类器分别添加标准化方法,在对数据集的进行标准化后才拟合数据scaled_svm_clf1 = Pipeline([("scaler", scaler),("linear_svc", svm_clf1),])scaled_svm_clf2 = Pipeline([("scaler", scaler),("linear_svc", svm_clf2),])scaled_svm_clf3 = Pipeline([("scaler", scaler),("linear_svc", svm_clf3),])scaled_svm_clf4 = Pipeline([("scaler", scaler),("linear_svc", svm_clf4),])scaled_svm_clf1.fit(X, y)scaled_svm_clf2.fit(X, y)scaled_svm_clf3.fit(X, y)scaled_svm_clf4.fit(X, y)
1.5.7 实现分类效果可视化的准备工作
1.5.7.1 对各分类器的各个参数做标准化处理
标准化操作实际上分为如下两点:
数据集的标准化;分类器的参数 w \boldsymbol w w 和 b b b 的标准化。
步骤1上面已经实现,下面实现步骤2 。
在实现步骤2之前,需要先对sklearn中的标准化类 **StandardScaler() **的用法进行了解。
返回值
使用StandardScaler()类对样本 x x x 进行标准化的计算公式为:
z = x − u s z = \frac{x - u} s z=sx−u
设数据集为 X X X。其中:
x x x 为当前样本 u u u 为 X X X中所有样本的各个特征所对应的均值 s s s 为各个特征的最大值与最小值之差 z z z (返回值)为样本经过标准化后的结果
原型
class sklearn.preprocessing.StandardScaler(X, copy=True, with_mean=True, with_std=True)
属性
scale_
:ndarray or None, shape (n_features,)
数据集中每个特征的最大值与最小值之差
mean_
:ndarray or None, shape (n_features,)
数据集中每个特征的平均值
var_
:ndarray or None, shape (n_features,)
数据集中每个特征的方差
方法
fit(X[, y])
:计算数据集的均值和标准差,用于后续数据集的标准化
fit_transform(X[, y])
:在fit(X[, y])
求得的标准差的基础上对数据X进行标准化
transform(X[, copy])
:在fit(X[, y])
已求得的均值的基础上,对数据X进行标准化
上面用到的计算公式:
z = x − u s z = \frac{x - u} s z=sx−u
实际上用的就是这种方法。
1.5.7.2 求解偏置 b \boldsymbol b b
偏置 b b b 的求解步骤如下:
求出 u ′ = − u / s \boldsymbol u^{'}=-{\boldsymbol u}/{s} u′=−u/s ,即每个特征经过缩放后的平均值的相反数,其shape为 ( 2 , ) (2, ) (2,);将 u ′ \boldsymbol u^{'} u′ 送入各分类器所对应的decision_function函数中。
1.5.7.3 decision_function函数
该函数是线性SVM分类器里的一种方法。
若形参X为样本,则求的是该样本到决策边界的有符号距离;若形参X为 u ′ \boldsymbol u^{'} u′,则求的是分类器对应的偏置 b b b,对应上面的求解过程。
1.5.7.3 代码实现
将上面三个小节整合成代码如下:
# 获取各分类器经过标准化后的偏置bb1 = svm_clf1.decision_function([-scaler.mean_ / scaler.scale_])b2 = svm_clf2.decision_function([-scaler.mean_ / scaler.scale_])b3 = svm_clf3.decision_function([-scaler.mean_ / scaler.scale_])b4 = svm_clf4.decision_function([-scaler.mean_ / scaler.scale_])# 获取各分类器经过标准化后的权重w1 = svm_clf1.coef_[0] / scaler.scale_w2 = svm_clf2.coef_[0] / scaler.scale_w3 = svm_clf3.coef_[0] / scaler.scale_w4 = svm_clf4.coef_[0] / scaler.scale_# 将标准化后的偏置和权值赋值给各个分类器,使得各个分类器的参数被正确标准化svm_clf1.intercept_ = np.array([b1])svm_clf2.intercept_ = np.array([b2])svm_clf3.intercept_ = np.array([b3])svm_clf4.intercept_ = np.array([b4])svm_clf1.coef_ = np.array([w1])svm_clf2.coef_ = np.array([w2])svm_clf3.coef_ = np.array([w3])svm_clf4.coef_ = np.array([w4])
由于后续需要标注出支持向量,所以还需要找出支持向量。
支持向量满足的公式为:
y i ( w T x i + b ) < 1 y_i(\boldsymbol w^T \boldsymbol {x_i}+b) < 1 yi(wTxi+b)<1
利用该公式,就可以获取不同分类器作用下的支持向量:
# 获取标准化后各分类器支持向量的索引,用于后续标注支持向量support_vectors_idx1 = (y * (X.dot(w1) + b1) < 1).ravel()support_vectors_idx2 = (y * (X.dot(w2) + b2) < 1).ravel()support_vectors_idx3 = (y * (X.dot(w3) + b3) < 1).ravel()support_vectors_idx4 = (y * (X.dot(w4) + b4) < 1).ravel()# 将标准化后各分类器的支持向量赋值给分类器的原支持向量svm_clf1.support_vectors_ = X[support_vectors_idx1]svm_clf2.support_vectors_ = X[support_vectors_idx2]svm_clf3.support_vectors_ = X[support_vectors_idx3]svm_clf4.support_vectors_ = X[support_vectors_idx4]
将上述代码封装成如下函数,可以使得代码更加简洁。
def standardize_svm_clf_parameters(svm_clf):b = svm_clf.decision_function([-scaler.mean_ / scaler.scale_])w = svm_clf.coef_[0] / scaler.scale_svm_clf.intercept_ = np.array([b])svm_clf.coef_ = np.array([w])support_vectors_idx = (y * (X.dot(w) + b) < 1).ravel()svm_clf.support_vectors_ = X[support_vectors_idx]svm_clfs = [svm_clf1, svm_clf2, svm_clf3,svm_clf4]for i in range(4):standardize_svm_clf_parameters(svm_clfs[i])
到此为止,所有的准备工作已经就绪。接下来就是将上面的过程整合起来,画图表示。
1.5.8 分类效果可视化图片的绘制
线性SVM中一个很重要的参数是 C C C,该参数的选择会对分类效果造成很大影响。下面将通过调整该参数 ,并调用上面定义的可视化函数,在二维空间中实现不同 C 值下分类效果的可视化,使得读者可以直观感受到 C C C 值调参对线性SVM分类效果的影响。
代码如下:
fig, axes = plt.subplots(nrows=2,ncols=2, figsize=(12,8), sharey=True)# 左上,C=1plt.sca(axes[0][0])plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^", label="Class 0")plt.plot(X[:, 0][y==-1], X[:, 1][y==-1], "bo", label="Class 1")plot_linear_svm_decision_margin_and_boundary(svm_clf1, 2.0, 8.0)plt.ylabel("$x_2$", fontsize=10, rotation=0)plt.legend(loc="upper left", fontsize=10)plt.title("$C = {}$".format(svm_clf1.C), fontsize=14)plt.axis([2.0, 8.0, 0.8, 3.0])# 右上,C=10plt.sca(axes[0][1])plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")plt.plot(X[:, 0][y==-1], X[:, 1][y==-1], "bo")plot_linear_svm_decision_margin_and_boundary(svm_clf2, 2.0, 8.0)plt.title("$C = {}$".format(svm_clf2.C), fontsize=14)plt.axis([2.0, 8.0, 0.8, 3.0])# 左下,C=100plt.sca(axes[1][0])plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")plt.plot(X[:, 0][y==-1], X[:, 1][y==-1], "bo")plot_linear_svm_decision_margin_and_boundary(svm_clf3, 2.0, 8.0)plt.xlabel("$x_1$", fontsize=10)plt.ylabel("$x_2$", fontsize=10, rotation=0)plt.title("$C = {}$".format(svm_clf3.C), fontsize=14)plt.axis([2.0, 8.0, 0.8, 3.0])# 右下,C → +∞plt.sca(axes[1][1])plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")plt.plot(X[:, 0][y==-1], X[:, 1][y==-1], "bo")plot_linear_svm_decision_margin_and_boundary(svm_clf4, 2.0, 8.0)plt.xlabel("$x_1$", fontsize=10)plt.title("$C = {}$".format(svm_clf4.C), fontsize=14)plt.axis([2.0, 8.0, 0.8, 3.0])
运行结果如下:
这样就画出了不同 C 的取值下线性SVM的分类情况。用红色圈起来的部分表示支持向量。
可以看到,随着 C C C 的增加,分类间隔越窄,分类的要求越来越严格,允许分错的样本数越少。
从理论上讲,当 C → C\to C→ +∞ 时,若数据集中有噪声,数据集将不可分,进而产生错误。
对此,sklearn中对于 C → C\to C→ +∞ 的情况进行了默认优化,将硬间隔优化成了软间隔,从而规避了使用硬间隔导致不可分的情况。