机器学习-Sklearn(第三版)Day8 SVM下集

一、 二分类SVC的进阶

1 SVC用于二分类的原理复习
在支持向量SVM(上)中,学习了二分类SVC的所有基本知识,包括SVM的原理,二分类SVC的损失函数,拉格朗日函数,拉格朗日对偶函数,预测函数以及这些函数在非线性,软间隔这些情况上的推广,并且引出了核函数这个关键概念。今天,基于已经学过的理论,继续探索支持向量机的其他性质,并在真实数据集上运用SVM。开始今天的探索之前,先来简单回忆一下支持向量机是如何工作的。

在这里插入图片描述

支持向量机分类器,是在数据空间中找出一个超平面作为决策边界,利用这个决策边界来对数据进行分类,并使分类误差尽量小的模型。决策边界是比所在数据空间小一维的空间,在三维数据空间中就是一个平面,在二维数据空间中就是一条直线。以二维数据为例,图中的数据集有两个特征,标签有两类,一类为紫色,一类为红色。对于这组数据,找出的决策边界被表达为wx+b=0,决策边界把平面分成了上下两部分,决策边界以上的样本都分为一类,决策边界以下的样本被分为另一类。以图像为例,绿色实线上部分为一类(全部都是紫色点),下部分为另一类(全都是红色点)。

平行于决策边界的两条虚线是距离决策边界相对距离为1的超平面,他们分别压过两类样本中距离决策边界最近的样本点,这些样本点就被成为支持向量。两条虚线超平面之间的距离叫做边际,简写为d。支持向量机分类器,就是以找出最大化的边际d为目标来求解损失函数,以求解出参数和,以构建决策边界,然后用决策边界来分类的分类器。

当然,不是所有数据都是线性可分的,不是所有数据都能够一眼看出,有一条直线,或一个平面,甚至一个超平面可以将数据完全分开。比如下面的环形数据。对于这样的数据,需要对它进行一个升维变化,让数据从原始的空间x投射到新空间Φ(x)中。升维之后,明显可以找出一个平面,能够将数据切分开来。Φ是一个映射函数,它代表了某种能够将数据升维的非线性的变换,对数据进行这样的变换,确保数据在自己的空间中一定能够线性可分。

在这里插入图片描述

但这种手段是有问题的,很难去找出一个函数Φ(x)来满足需求,并且并不知道数据究竟被映射到了一个多少维度的空间当中,有可能数据被映射到了无限空间中,陷入“维度诅咒”,让计算和预测都变得无比艰难。为了避免这些问题,使用核函数来帮助。核函数K ( x , x t e s t ) K(x,x_test)K(x,xt​est)能够用原始数据空间中向量计算来表示升维后的空间中的点积Φ ( x ) ∗ Φ ( x t e s t ) Φ(x)*Φ(x_test)Φ(x)∗Φ(xt​est),以帮助避免寻找Φ(x)。选用不同的核函数,就可以解决不同数据分布下的寻找超平面问题。在sklearn的SVC中,这个功能由参数“kernel”(ˈkərnl)和一系列与核函数相关的参数来进行控制,包括gamma,coef0和degree。同时,还讲解了软间隔和硬间隔中涉及到的参数C。今天就从参数C的进阶理解开始继续探索支持向量机。
2 参数C的理解进阶
有一些数据,可能是线性可分,但在线性可分状况下训练准确率不能达到100%,即无法让训练误差为0,这样的数据被称为“存在软间隔的数据”。此时此刻,需要让决策边界能够忍受一小部分训练误差,就不能单纯地寻求最大边际了。

在这里插入图片描述

因为对于软间隔的数据来说,边际越大被分错的样本也就会越多,因此需要找出一个”最大边际“与”被分错的样本数量“之间的平衡。因此,引入松弛系数ζ和松弛系数的系数C作为一个惩罚项,来惩罚对最大边际的追求。

那参数C如何影响决策边界呢?在硬间隔的时候,决策边界完全由两个支持向量和最小化损失函数(最大化边际)来决定,而支持向量是两个标签类别不一致的点,即分别是正样本和负样本。然而在软间隔情况下边际依然由支持向量决定,但此时此刻的支持向量可能就不再是来自两种标签类别的点了,而是分布在决策边界两边的,同类别的点。回忆一下图像:

在这里插入图片描述

此时虚线超平面w ∗ x i + b = 1 − ζ i w*x_i+b=1-ζ_iw∗xi​+b=1−ζi​是由混杂在红色点中间的紫色点来决定的,所以此时此刻,这个紫色点就是支持向量了。所以软间隔让决定两条虚线超平面的支持向量可能是来自于同一个类别的样本点,而硬间隔的时候两条虚线超平面必须是由来自两个不同类别的支持向量决定的。而C值会决定究竟是依赖红色点作为支持向量(只追求最大边界),还是要依赖软间隔中,混杂在红色点中的紫色点来作为支持向量(追求最大边界和判断正确的平衡)。如果C值设定比较大,那SVC可能会选择边际较小的,能够更好地分类所有训练点的决策边界,不过模型的训练时间也会更长。如果C的设定值较小,那SVC会尽量最大化边界,尽量将掉落在决策边界另一方的样本点预测正确,决策功能会更简单,但代价是训练的准确度,因为此时会有更多红色的点被分类错误。换句话说,C在SVM中的影响就像正则化参数对逻辑回归的影响。

此时此刻,所有可能影响超平面的样本可能都会被定义为支持向量,所以支持向量就不再是所有压在虚线超平面上的点,而是所有可能影响超平面的位置的那些混杂在彼此的类别中的点了。观察一下对不同数据集分类时,支持向量都有哪些?软间隔如何影响了超平面和支持向量,就一目了然了。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import svm
from sklearn.datasets import make_circles, make_moons, make_blobs,make_classification

n_samples = 100
datasets = [
    make_moons(n_samples=n_samples, noise=0.2, random_state=0),
    make_circles(n_samples=n_samples, noise=0.2, factor=0.5, random_state=1),
    make_blobs(n_samples=n_samples, centers=2, random_state=5),
    make_classification(n_samples=n_samples,n_features =2,n_informative=2,n_redundant=0, random_state=5)
    ]

Kernel = ["linear"]

#四个数据集分别是什么样子呢?
for X,Y in datasets:
    plt.figure(figsize=(5,4))
    plt.scatter(X[:,0],X[:,1],c=Y,s=50,cmap="rainbow")

nrows=len(datasets)
ncols=len(Kernel) + 1

fig, axes = plt.subplots(nrows, ncols,figsize=(10,16))

#第一层循环:在不同的数据集中循环
for ds_cnt, (X,Y) in enumerate(datasets):
    ax = axes[ds_cnt, 0]
    if ds_cnt == 0:
         ax.set_title("Input data")
    ax.scatter(X[:, 0], X[:, 1], c=Y, zorder=10, cmap=plt.cm.Paired,edgecolors='k')
    ax.set_xticks(())
    ax.set_yticks(())

    for est_idx, kernel in enumerate(Kernel):
        ax = axes[ds_cnt, est_idx + 1]

        clf = svm.SVC(kernel=kernel, gamma=2).fit(X, Y)
        score = clf.score(X, Y)

        ax.scatter(X[:, 0], X[:, 1], c=Y,zorder=10,cmap=plt.cm.Paired,edgecolors='k')
        ax.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1], s=100,facecolors='none', zorder=10, edgecolors='white')
        x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
        y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
        XX, YY = np.mgrid[x_min:x_max:200j, y_min:y_max:200j]
        Z = clf.decision_function(np.c_[XX.ravel(), YY.ravel()]).reshape(XX.shape)
        ax.pcolormesh(XX, YY, Z > 0, cmap=plt.cm.Paired)
        ax.contour(XX, YY, Z, colors=['k', 'k', 'k'], linestyles=['--', '-', '--'],levels=[-1, 0, 1])
        ax.set_xticks(())
        ax.set_yticks(())

        if ds_cnt == 0:
            ax.set_title(kernel)
        ax.text(0.95, 0.06, ('%.2f' % score).lstrip('0')
                , size=15
                , bbox=dict(boxstyle='round', alpha=0.8, facecolor='white')
                #为分数添加一个白色的格子作为底色
                , transform=ax.transAxes #确定文字所对应的坐标轴,就是ax子图的坐标轴本身
                , horizontalalignment='right' #位于坐标轴的什么方向
                )
    plt.tight_layout()
    plt.show()

白色圈圈出的就是支持向量,可以看到,所有在两条虚线超平面之间的点,和虚线超平面外,但属于另一个类别的点,都被认为是支持向量。并不是因为这些点都在超平面上,而是因为超平面由所有的这些点来决定,可以通过调节C来移动超平面,让超平面过任何一个白色圈圈出的点。参数C就是这样影响了决策,可以说是彻底改变了支持向量机的决策过程。
3 二分类SVC中的样本不均衡问题:重要参数class_weight
对于分类问题,永远都逃不过的一个痛点就是样本不均衡问题。样本不均衡是指在一组数据集中,标签的一类天生占有很大的比例,但有着捕捉出某种特定的分类的需求的状况。比如,现在要对潜在犯罪者和普通人进行分类,潜在犯罪者占总人口的比例是相当低的,也许只有2%左右,98%的人都是普通人,而目标是要捕获出潜在犯罪者。这样的标签分布会带来许多问题。

首先,分类模型天生会倾向于多数的类,让多数类更容易被判断正确,少数类被牺牲掉。因为对于模型而言,样本量越大的标签可以学习的信息越多,算法就会更加依赖于从多数类中学到的信息来进行判断。如果希望捕获少数类,模型就会失败。其次,模型评估指标会失去意义。这种分类状况下,即便模型什么也不做,全把所有人都当成不会犯罪的人,准确率也能非常高,这使得模型评估指标accuracy变得毫无意义,根本无法达到“要识别出会犯罪的人”的建模目的。

所以现在,首先要让算法意识到数据的标签是不均衡的,通过施加一些惩罚或者改变样本本身,来让模型向着捕获少数类的方向建模。然后,要改进模型评估指标,使用更加针对于少数类的指标来优化模型。

要解决第一个问题,在逻辑回归中已经介绍了一些基本方法,比如上采样下采样。但这些采样方法会增加样本的总数,对于支持向量机这个样本总是对计算速度影响巨大的算法来说,完全不想轻易地增加样本数量。况且,支持向量机中的决策仅仅决策边界的影响,而决策边界又仅仅受到参数C和支持向量的影响,单纯地增加样本数量不仅会增加计算时间,可能还会增加无数对决策边界无影响的样本点。因此在支持向量机中,要大力依赖调节样本均衡的参数:SVC类中的class_weight和接口fit中可以设定的sample_weight。

在逻辑回归中,参数class_weight默认None,此模式表示假设数据集中的所有标签是均衡的,即自动认为标签的比例是1:1。所以当样本不均衡的时候,可以使用形如{“标签的值1”:权重1,“标签的值2”:权重2}的字典来输入真实的样本标签比例,来让算法意识到样本是不平衡的。或者使用”balanced“模式,直接使用n_samples/(n_classes × np.bincount(y))作为权重,可以比较好地修正样本不均衡情况。

但在SVM中,分类判断是基于决策边界的,而最终决定究竟使用怎样的支持向量和决策边界的参数是参数C,所以所有的样本均衡都是通过参数C来调整的。

SVC的参数:class_weight
可输入字典或者"balanced”,可不填,默认None 对SVC,将类i的参数C设置为class_weight [i] ×C。如果没有给出具体的class_weight,则所有类都被假设为占有相同的权重1,模型会根据数据原本的状况去训练。如果希望改善样本不均衡状况,请输入形如{“标签的值1”:权重1,“标签的值2”:权重2}的字典,则参数C将会自动被设为:

标签的值1的C:权重1×C,标签的值2的C:权重2×C

或者,可以使用“balanced”模式,这个模式使用y的值自动调整与输入数据中的类频率成反比的权重为n_samples/(n_classes×np.bincount(y))

SVC的接口fit的参数:sample_weight
数组,结构为 (n_samples, ),必须对应输入fit中的特征矩阵的每个样本

每个样本在fit时的权重,让权重×每个样本对应的C值来迫使分类器强调设定的权重更大的样本。通常,较大的权重加在少数类的样本上,以迫使模型向着少数类的方向建模

通常来说,这两个参数只选取一个来设置。如果同时设置了两个参数,则C会同时受到两个参数的影响,即 class_weight中设定的权重×sample_weight中设定的权重×C。

接下来就来看看如何使用这个参数。

首先,自建一组样本不平衡的数据集。在这组数据集上建两个SVC模型,一个设置有class_weight参数,一个不设置class_weight参数。对两个模型分别进行评估并画出他们的决策边界,以此来观察class_weight带来的效果。

#1. 导入需要的库和模块
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_blobs

#2. 创建样本不均衡的数据集
class_1 = 500 #类别1有500个样本  10:1
class_2 = 50 #类别2只有50个
centers = [[0.0, 0.0], [2.0, 2.0]] #设定两个类别的中心
clusters_std = [1.5, 0.5] #设定两个类别的方差,通常来说,样本量比较大的类别会更加松散
X, y = make_blobs(n_samples=[class_1, class_2],
                  centers=centers,
                  cluster_std=clusters_std,
                  random_state=0, shuffle=False)
#看看数据集长什么样
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10)
plt.show()
#其中红色点是少数类,紫色点是多数类

#3. 在数据集上分别建模
#不设定class_weight
clf = svm.SVC(kernel='linear', C=1.0)
clf.fit(X, y)
#设定class_weight
wclf = svm.SVC(kernel='linear', class_weight={1: 10})
wclf.fit(X, y)
#给两个模型分别打分看看,这个分数是accuracy准确度
print(clf.score(X,y))
print(wclf.score(X,y))
#样本均衡之后,准确率下降了,没有样本均衡的准确率更高

#4.绘制两个模型下数据的决策边界(利用Contour)
#Contour是专门用来绘制等高线的函数。等高线,本质上是在二维图像上表现三维图像的一种形式,其中两维X和Y是两条坐标轴上的取值,而Z表示高度。
#Contour就是将由X和Y构成平面上的所有点中,高度一致的点连接成线段的函数,在同一条等高线上的点一定具有相同的Z值。回忆一下,决策边界是wx+b=0,并在决策边界的两边找出两个超平面,使得超平面到决策边界的相对距离为1。那其实,只需要在样本构成的平面上,把所有到决策边界的距离为0的点相连,就是决策边界。而到决策边界的距离可以使用接口decision_function来调用。
#首先要有数据分布
plt.figure(figsize=(6,5))
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="rainbow",s=10)
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 = clf.decision_function(xy).reshape(XX.shape)
a = ax.contour(XX, YY, Z_clf, colors='black', levels=[0], alpha=0.5, linestyles=['-'])
Z_wclf = wclf.decision_function(xy).reshape(XX.shape)
b = ax.contour(XX, YY, Z_wclf, colors='red', levels=[0], alpha=0.5, linestyles=['-'])

#第三步:画图例
plt.legend([a.collections[0], b.collections[0]], ["non weighted", "weighted"],loc="upper right")
plt.show()
#图例这一步是怎么做到的?
print(a.collections) #调用这个等高线对象中画的所有线,返回一个惰性对象
#用[*]把它打开试试看
[*a.collections] #返回了一个linecollection对象,其实就是等高线里所有的线的列表,现在只有一条线,所以可以使用索引0来锁定这个对象
print(a.collections[0])
#plt.legend([对象列表],[图例列表],loc)
#只要对象列表和图例列表相对应,就可以显示出图例

从图像上可以看出,灰色是做样本均衡之前的决策边界,灰色线上方的点被分为一类,下方的点被分为另一类。可以看到,大约有一半少数类(红色)被分错,多数类(紫色点)几乎都被分类正确了。红色是做样本平衡之后的决策边界,同样是红色线上方一类,红色线下方一类。可以看到,做了样本平衡后,少数类几乎全部都被分类正确了,但是多数类有许多被分错了。两种情况下模型的准确率如何表现:

#给两个模型分别打分看看,这个分数是accuracy准确度
#做样本均衡之后,准确率下降了,没有样本均衡的准确率更高
print(clf.score(X,y))
print(wclf.score(X,y))

可以看出,从准确率角度来看,不做样本平衡的时候准确率反而更高,做了样本平衡准确率反而变低了,这是因为做了样本平衡后,为了要更有效地捕捉出少数类,模型误伤了许多多数类样本,而多数类被分错的样本数量 > 少数类被分类正确的样本数量,使得模型整体的精确性下降。现在,如果目的是模型整体的准确率,那就要拒绝样本平衡,使用class_weight被设置之前的模型。

然而在现实中,往往都在追求捕捉少数类,因为在很多情况下,将少数类判断错的代价是巨大的。比如之前提到的,判断潜在犯罪者和普通人的例子,如果没有能够识别出潜在犯罪者,那么这些人就可能去危害社会,造成恶劣影响,但如果把普通人错认为是潜在犯罪者,也许只是需要增加一些监控和人为甄别的成本。所以宁愿把普通人判错,也不想放过任何一个潜在犯罪者。希望不惜一切代价来捕获少数类,或者希望捕捉出尽量多的少数类,那就必须使用class_weight设置后的模型。

二、 SVC的模型评估指标

从上一节的例子中可以看出,如果目标是希望尽量捕获少数类,那准确率这个模型评估逐渐失效,所以需要新的模型评估指标。如果简单来看,其实只需要查看模型在少数类上的准确率就好了,只要能够将少数类尽量捕捉出来,就能够达到目的。

但此时,新问题又出现了,对多数类判断错误后,会需要人工甄别或者更多的业务上的措施来一一排除判断错误的多数类,这种行为往往伴随着很高的成本。比如银行在判断”一个申请信用卡的客户是否会出现违约行为“的时候,如果一个客户被判断为”会违约“,这个客户的信用卡申请就会被驳回,如果为了捕捉出”会违约“的人,大量地将”不会违约“的客户判断为”会违约“的客户,就会有许多无辜的客户的申请被驳回。信用卡对银行来说意味着利息收入,而拒绝了许多本来不会违约的客户,对银行来说就是巨大的损失。同理,大众在召回不符合欧盟标准的汽车时,如果为了找到所有不符合标准的汽车,而将一堆本来符合标准了的汽车召回,这个成本是不可估量的。也就是说,单纯地追求捕捉出少数类,就会成本太高,而不顾及少数类,又会无法达成模型的效果。所以在现实中,往往在寻找捕获少数类的能力和将多数类判错后需要付出的成本的平衡。如果一个模型在能够尽量捕获少数类的情况下,还能够尽量对多数类判断正确,则这个模型就非常优秀了。为了评估这样的能力,将引入新的模型评估指标:混淆矩阵和ROC曲线。
1 混淆矩阵(Confusion Matrix)
混淆矩阵是二分类问题的多维衡量指标体系,在样本不平衡时极其有用。在混淆矩阵中,将少数类认为是正例,多数类认为是负例。在决策树,随机森林这些普通的分类算法里,即是说少数类是1,多数类是0。在SVM里,就是说少数类是1,多数类是-1。普通的混淆矩阵,一般使用{0,1}来表示。混淆矩阵阵如其名,十分容易让人混淆,在许多教材中,混淆矩阵中各种各样的名称和定义让大家难以理解难以记忆。一种简化的方式来显示标准二分类的混淆矩阵,如图所示:

在这里插入图片描述

混淆矩阵中,永远是真实值在前,预测值在后。其实可以很容易看出,11和00的对角线就是全部预测正确的,01和10的对角线就是全部预测错误的。基于混淆矩阵,有六个不同的模型评估指标,这些评估指标的范围都在[0,1]之间,所有以11和00为分子的指标都是越接近1越好,所有以01和10为分子的指标都是越接近0越好。对于所有的指标,用橙色表示分母,用绿色表示分子,则有:

1.1 模型整体效果:准确率

在这里插入图片描述Accuracy=(11+00)/(11+10+01+00)
准确率Accuracy就是所有预测正确的所有样本除以总样本,通常来说越接近1越好。
1.2 捕捉少数类的艺术

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值