Sklearn(v3)——SVM理论(4)

二分类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,可不填,默认NoneSVC将类i的参数C设置为class_weight [i] * C如果没有给出具体的class_weight,则所有类都被假设为占有相同的权重1模型会根据数据原本的状况去训练如果希望改善样本不均衡状况请输入形如{"标签的值1"权重1"标签的值2"权重2}的字典则参数C将会自动被设为标签的值1C:权重1 * C,标签的值2C权重2*C。

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

SVC的接fit的参数sample_weight

数组,结构为 (n_samples, ),必须对应输入t中的特征矩阵的每个样本

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

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

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

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

import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.datasets import make_blobs

class_1 = 500 #类别1有500个样本
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)
#其中红色点是少数类,紫色点是多数类

#不设定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))
wclf.score(X,y)

结果: 

0.9418181818181818
0.9127272727272727

 绘制两个模型下数据的决策边界

#首先要有数据分布
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()

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

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

SVC的模型评估指标

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

但此时新问题又出现了我们对多数类判断错误后会需要人工甄别或者更多的业务上的措施来一一排除我们判断错误的多数类这种行为往往伴随着很高的成本比如银行在判断一个申请信用卡的客户是否会出现违约行的时候,如果一个客户被判断为会违约,这个客户的信用卡申请就会被驳回如果为了捕捉出会违约的人大量地将不会违约的客户判断会违约的客户就会有许多无辜的客户的申请被驳回信用卡对银行来说意味着利息收入而拒绝了许多本来不会违约的客户对银行来说就是巨大的损失同理大众在召回不符合欧盟标准的汽车时,如果为了找到所有不符合标准的汽车而将一堆本来符合标准了的汽车召回这个成本是不可估量的

也就是说单纯地追求捕捉出少数类就会成本太高而不顾及少数类又会无法达成模型的效果以在现实我们往往在寻找捕获少数类的能力和将多数类判错后需要付出的成本的平衡如果一个模型在能够尽量捕获少 数类的情况下还能够尽量对多数类判断正确则这个模型就非常优秀了为了评估这样的能力我们将引入新的模型评估指标:混淆矩阵和ROC曲线来帮助我们

混淆矩阵(Confusion Matrix

混淆矩阵是二分类问题的多维衡量指标体系在样本不平衡时极其有用在混淆矩阵中我们将少数类认为是正多数类认为是负例在决策树随机森林这些普通的分类算法里是说少数类是1多数类是0SVM就是说少数类是1,多数类是-1。普通的混淆矩阵,一般使用{0,1}来表示混淆矩阵阵如其名十分容易让人混在许多教材中混淆矩阵中各种各样的名称和定义让大家难以理解难以记忆我为大家找出了一种简化的方式来显示标准二分类的混淆矩阵如图所示 

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

准确率

#对于没有class_weight,没有做样本平衡的灰色决策边界来说:
print((y[y == clf.predict(X)] == 1).sum()/(clf.predict(X) == 1).sum())
#对于有class_weight,做了样本平衡的红色决策边界来说:
(y[y == wclf.predict(X)] == 1).sum()/(wclf.predict(X) == 1).sum()

结果:

0.7142857142857143
0.5102040816326531

精确度也叫查准率,也就是决策边界上方的所有点中红色点所占的比例

可以看出,做了样本平衡之后,精确度是下降的。因为很明显,样本平衡之后,有更多的多数类紫色点被我们误伤了。精确度可以帮助我们判断,是否每一次对少数类的预测都精确,所以又被称为”查准率。在现实的样本不平衡

例子中, 当每一次将 多数类 判断错误的成本非常高昂的时候(比如大众召回车辆的例子),我们会追求高精确度 。 精确度越低,我们对多数类的判断就会越错误。当然了,如果我们的目标是不计一切代价捕获少数类,那我们并不在意精确度。(在意多数类是否判断正确

召回率

召回率Recall又被称为敏感度(sensitivity)真正率查全率表示所有真实为1的样本中被我们预测正确的样本所占的比例在支持向量机中召回率可以被表示为决策边界上方的所有红色点占全部样本中的红色点的比召回率越高代表我们尽量捕捉出了越多的少数类召回率越低代表我们没有捕捉出足够的少数类
#所有predict为1的点  / 全部为1的点的比例
#对于没有class_weight,没有做样本平衡的灰色决策边界来说:
print((y[y == clf.predict(X)] == 1).sum()/(y == 1).sum())

#对于有class_weight,做了样本平衡的红色决策边界来说:
(y[y == wclf.predict(X)] == 1).sum()/(y == 1).sum()
0.6
1.0

如果我们希望不计一切代价找出少数类(比如找出潜在犯罪者的例子),那我们就会追求高召回相反如果我们的目标不是尽量捕获少数类那我们就不需要在意召回率

注意召回率和精确度的分子是相同的(都是11),只是分母不同而召回率和精确度是此消彼长的两者之间的平 衡代表了捕捉少数类的需求和尽量不要误伤多数类的需求的平衡究竟要偏向于哪一方取决于我们的业务需求究竟是误伤多数类的成本更高,还是无法捕捉少数类的代价更高

F1分数

为了同时兼顾精确度和召回率我们创造了两者的调和平均数作为考量两者平衡的综合性指称之为F1 measure。两个数之间的调和平均倾向于靠近两个数中比较小的那一个数因此我们追求尽量高的F1 measure 能够保证我们的精确度和召回率都比较高F1 measure[0,1]之间分布越接近1越好

假负率(False Negative Rate,它等于 1 - Recall。

特异度和假正率

#所有被正确预测为0的样本  / 所有的0样本
#对于没有class_weight,没有做样本平衡的灰色决策边界来说:
print((y[y == clf.predict(X)] == 0).sum()/(y == 0).sum())

#对于有class_weight,做了样本平衡的红色决策边界来说:
(y[y == wclf.predict(X)] == 0).sum()/(y == 0).sum()

结果:

0.976
0.904

混淆矩阵

sklearn当中提供了大量的类来帮助我们了解和使用混淆矩阵

基于混淆矩阵我们学习了总共六个指标准确率Accuracy精确度Precision召回Recall精确度和召回度的平衡指标F measure特异度Specificity以及假正率FPR其中,假正率有一个非常重要的应用我们在追求较高的Recall的时候Precision会下降就是说随着更多的少数类被捕捉出来会有更多的多数类被判断错误但我们很好奇随着Recall的逐渐增加模型将多数类判断错误的能力如何变化呢?

我们希望理解我每判断正确一个少数类就有多少个多数类会被判断错误假正率正好可以助我们衡量这个能力的变化相对的Precision无法判断这些判断错误的多数类在全部多数类中究竟占多大的比例,所以无法在提升Recall的过程中也顾及到模型整体的Accuracy因此我们可以使用RecallFPR之间的平衡,来替代RecallPrecision之间的平衡让我们衡量模型在尽量捕捉少数类的时候误伤多数类的情况如何变化,这就是我们的ROC线衡量的平衡

ROC曲线,全称The Receiver Operating Characteristic Curve译为受试者操作特性曲线这是一条以不同阈值下的假正率FPR为横坐标,不同阈值下的召回率Recall为纵坐标的曲线让我们先从概率和阈值开始讲起

class_1_ = 7
class_2_ = 4
centers_ = [[0.0, 0.0], [1,1]]
clusters_std = [0.5, 1]
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=30)

 

from sklearn.linear_model import LogisticRegression as LogiR
clf_lo = LogiR().fit(X_,y_)

prob = clf_lo.predict_proba(X_)

#将样本和概率放到一个DataFrame中
import pandas as pd
prob = pd.DataFrame(prob)
prob.columns = ["0","1"]

prob

#手动调节阈值,来改变我们的模型效果
for i in range(prob.shape[0]):
    if prob.loc[i,"1"] > 0.5:
        prob.loc[i,"pred"] = 1
    else:
        prob.loc[i,"pred"] = 0
prob["y_true"] = y_
prob = prob.sort_values(by="1",ascending=False)
prob

from sklearn.metrics import confusion_matrix as CM, precision_score as P, recall_score as R

print(CM(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])) #1是少数类(写前面)0是多数类
#试试看手动计算Precision和Recall?
print(P(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0]))
R(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0])
[[2 2]
 [0 7]]
1.0
0.5
for i in range(prob.shape[0]):
    if prob.loc[i,"1"] > 0.4:
        prob.loc[i,"pred"] = 1 
    else:
        prob.loc[i,"pred"] = 0

print(CM(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0]))
print(P(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0]))
print(R(prob.loc[:,"y_true"],prob.loc[:,"pred"],labels=[1,0]))
[[2 2]
 [1 6]]
0.6666666666666666
0.5

可见,在不同阈值下,我们的模型评估指标会发生变化,我们正利用这一点来观察RecallFPR之间如何互相影响。  但是注意,并不是升高阈值,就一定能够增加或者减少Recall一切要根据数据的实际分布来进行判断而要体现阈值的影响首先必须的得到分类器在少数类下的预测概率对于逻辑回归这样天生生成似然的算法和朴素贝叶斯这样就是在计算概率的算法,自然非常容易得到概率但对于一些其他的分类算法比如决策树比如SVM 们的分类方式和概率并不相关那在他们身上我们就无法画ROC曲线了吗?并非如此

决策树有叶子节点一个叶子节点上可能包含着不同类的样本假设一个样本被包含在叶子节点a节点a包含10 个样本其中6个为14个为01这个正类在这个叶子节点中的出现概率就是60%类别0这个叶子节点中的出现概率就是40%对于所有在这个叶子节点中的样本而言节点上的10出现的概率就是这个样本对应的取到10的概率大家可以去自己验证一下但是思考一个问题由于决策树可以被画得很深在足够深的情况下策树的每个叶子节点上可能都不包含多个类别的标签了可能一片叶子中只有唯一的一个标签即叶子节点的不纯度为0,此时此刻,对于每个样本而言,他们所对应的概率就是0或者1这个时候我们就无法调节阈值来调   节我们的RecallFPR对于随机森林也是如此

所以,如果我们有概率需求,我们还是会优先追求逻辑回归或者朴素贝叶斯不过其实SVM也可以生成概率们一起来看看它是怎么做的

SVM概率预测重要参数probability接口predict_proba以及decision_function



我们在画等高线,也就是决策边界的时候曾经使用SVC接口decision_function它返回我们输入的特征矩阵中每个样本到划分数据集的超平面的距离。我们在SVM中利用超平面来判断我们的样本本质上来说当两个点的距离是相同的符号的时候越远离超平面的样本点归属于某个标签类的概率就很大比如说一个距离超平面0.1点,和一个距离超平面100的点,明显是距离为0.1的点更有可能是负类别的点混入了边界同理一个距离超平面距离为-0.1的点,和一个离超平面距离为-100的点明显是-100的点的标签更有可能是负类所以到超平面的距离一定程度上反应了样本归属于某个标签类的可能性  接口decision_function返回的值也因此被我们认为是SVM 中的置信度(confidence)

#使用最初的X和y,样本不均衡的这个模型
class_1 = 500 #类别1有500个样本
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)

#其中红色点是少数类,紫色点是多数类
clf_proba = svm.SVC(kernel="linear",C=1.0,probability=True).fit(X,y)

print(clf_proba.predict_proba(X)) #生成在各类标签下的概率

print(clf_proba.decision_function(X))

print(clf_proba.decision_function(X).shape)


[[0.69850902 0.30149098]
 [0.28868513 0.71131487]
 [0.96116579 0.03883421]
 ...
 [0.17705259 0.82294741]
 [0.38049218 0.61950782]
 [0.34181702 0.65818298]]

[ -0.39182241   0.95617053  -2.24996184  -2.63659269  -3.65243197
  -1.67311996  -2.56396417  -2.80650393  -1.76184723  -4.7948575
  -7.59061196  -3.66174848  -2.2508023   -4.27626526   0.78571364
  -3.24751892  -8.57016271  -4.45823747  -0.14034183  -5.20657114
  -8.02181046  -4.18420871  -5.6222409   -5.12602771  -7.22592707
  -5.07749638  -6.72386021  -3.4945225   -3.51475144  -5.72941551
  -5.79160724  -8.06232013  -4.36303857  -6.25419679  -5.59426696
  -2.60919281  -3.90887478  -4.38754704  -6.46432224  -4.54279979
  -4.78961735  -5.53727469   1.33920817  -2.27766451  -4.39650854
  -2.97649872  -2.26771979  -2.40781748  -1.41638181  -3.26142275
  -2.7712218   -4.87288439  -3.2594128   -5.91189118   1.48676267
   0.5389064   -2.76188843  -3.36126945  -2.64697843  -1.63635284
  -5.04695135  -1.59196902  -5.5195418   -2.10439349  -2.29646147
  -4.63162339  -5.21532213  -4.19325629  -3.37620335  -5.0032094
  -6.04506666  -2.84656859   1.5004014   -4.02677739  -7.07160609
  -1.66193239  -6.60981996  -5.23458676  -3.70189918  -6.74089425
  -2.09584948  -2.28398296  -4.97899921  -8.12174085  -1.52566274
  -1.99176286  -3.54013094  -4.8845886   -6.51002015  -4.8526957
  -6.73649174  -8.50103589  -5.35477446  -5.93972132  -3.09197136
  -5.95218482  -5.87802088  -3.41531761  -1.50581423   1.69513218
  -5.08155767  -1.17971205  -5.3506946   -5.21493342  -3.73358514
  -2.01273566  -3.39045625  -6.34357458  -3.54776648  -0.17804673
  -6.26887557  -4.17973771  -6.68896346  -3.46095619  -5.47965411
  -7.30835247  -4.41569899  -4.95103272  -4.52261342  -2.32912228
  -5.78601433  -4.75347157  -7.10337939  -0.4589064   -7.67789856
  -4.01780827  -4.3031773   -1.83727693  -7.40091653  -5.95271547
  -6.91568411  -5.20341905  -7.19695832  -3.02927263  -4.48056922
  -7.48496425  -0.07011269  -5.80292499  -3.38503533  -4.58498843
  -2.76260661  -3.01843998  -2.67539002  -4.1197355   -0.94129257
  -5.89363772  -1.6069038   -2.6343464   -3.04465464  -4.23219535
  -3.91622593  -5.29389964  -3.59245628  -8.41452726  -3.09845691
  -2.71798914  -7.1383473   -4.61490324  -4.57817871  -4.34638288
  -6.5457838   -4.91701759  -6.57235561  -1.01417607  -3.91893483
  -4.52905816  -4.47582917  -7.84694737  -6.49226452  -2.82193743
  -2.87607739  -7.0839848   -5.2681034   -4.4871544   -2.54658631
  -7.54914279  -2.70628288  -5.99557957  -8.02076603  -4.00226228
  -2.84835501  -1.9410333   -3.86856886  -4.99855904  -6.21947623
  -5.05797444  -2.97214824  -3.26123902  -5.27649982  -3.13897861
  -6.48514315  -9.55083209  -6.46488612  -7.98793665  -0.94456569
  -3.41380968  -7.093158    -5.71901588  -0.88438995  -0.24381463
  -6.78212695  -2.20666714  -6.65580329  -2.56305221  -5.60001636
  -5.43216357  -4.96741585  -0.02572912  -3.21839147   1.13383091
  -1.58640099  -7.57106914  -4.16850181  -6.48179088  -4.67852158
  -6.99661419  -2.1447926   -5.31694653  -2.63007619  -2.55890478
  -6.4896746   -3.94241071  -2.71319258  -4.70525843  -5.61592746
  -4.7150336   -2.85352156  -0.49195707  -8.16191324  -3.80462978
  -6.43680611  -4.58137592  -1.38912206  -6.93900334  -7.7222725
  -8.41592264  -5.613998     0.44396046  -3.07168078  -1.36478732
  -1.20153628  -6.30209808  -6.49846303  -0.60518198  -3.83301464
  -6.40455571  -0.22680504   0.54161373  -5.99626181  -5.98077412
  -3.45857531  -2.50268554  -5.54970836  -9.26535525  -4.22097425
  -0.47257602  -9.33187038  -4.97705346  -1.65256318  -1.0000177
  -5.82202444  -8.34541689  -4.97060946  -0.34446784  -6.95722208
  -7.41413036  -1.8343221   -7.19145712  -4.8082824   -4.59805445
  -5.49449995  -2.25570223  -5.41145249  -5.97739476  -2.94240518
  -3.64911853  -2.82208944  -3.34705766  -8.19712182  -7.57201089
  -0.61670956  -6.3752957   -5.06738146  -2.54344987  -3.28382401
  -5.9927353   -2.87730848  -3.58324503  -7.1488302   -2.63140119
  -8.48092542  -4.91672751  -5.7488116   -3.80044426  -9.27859326
  -2.475992    -6.06980518  -2.90059294  -5.22496057  -5.97575155
  -6.18156775  -5.38363878  -7.41985155  -6.73241325  -4.43878791
  -9.06614408  -1.69153658  -3.71141045  -3.19852116  -4.05473804
  -3.45821856  -4.92039492  -6.55332449  -1.28332784  -4.17989583
  -5.45916562  -3.80974949  -4.27838346  -5.31607024  -0.62628865
  -2.21276478  -3.7397342   -6.66779473  -2.38116892  -2.83460004
  -7.01238422  -2.75282445  -3.01759368  -6.14970454  -6.1300394
  -7.58620719  -3.14051577  -5.82720807  -2.52236034  -7.03761018
  -7.82753368  -8.8447092   -3.11218173  -4.22074847  -0.99624534
  -3.45189404  -1.46956557  -9.42857926  -2.75093993  -0.61665367
  -2.09370852  -9.34768018  -3.39876535  -5.8635608   -2.12987936
  -8.40706474  -3.84209244  -0.5100329   -2.48836494  -1.54663048
  -4.30920238  -5.73107193  -1.89978615  -6.17605033  -3.10487492
  -5.51376743  -4.32751131  -8.20349197  -3.87477609  -1.78392197
  -6.17403966  -6.52743333  -3.02302099  -4.99201913  -5.72548424
  -7.83390422  -1.19722286  -4.59974076  -2.99496132  -6.83038116
  -5.1842235   -0.78127198  -2.88907207  -3.95055581  -6.33003274
  -4.47772201  -2.77425683  -4.44937971  -4.2292366   -1.15145162
  -4.92325347  -5.40648383  -7.37247783  -4.65237446  -7.04281259
  -0.69437244  -4.99227188  -3.02282976  -2.52532913  -6.52636286
  -5.48318846  -3.71028837  -6.91757625  -5.54349414  -6.05345046
  -0.43986605  -4.75951272  -1.82851406  -3.24432919  -7.20785221
  -4.0583863   -3.27842271  -0.68706448  -2.76021537  -5.54119808
  -4.08188794  -6.4244794   -4.76668274  -0.2040958   -2.42898945
  -2.03283232  -4.12879797  -2.70459163  -6.04997273  -2.79280244
  -4.20663028   0.786804    -3.65237777  -3.55179726  -5.3460864
 -10.31959605  -6.69397854  -6.53784926  -7.56321471  -4.98085596
  -1.79893146  -3.89513404  -5.18601688  -3.82352518  -5.20243998
  -3.11707515  -5.80322513  -4.42380099  -5.74159836  -6.6468986
  -3.18053496  -4.28898663  -6.73111304  -3.21485845  -4.79047586
  -4.51550728  -2.70659984  -3.61545839  -7.86496861  -0.1258212
  -7.6559803   -3.15269699  -2.87456418  -6.74876767  -0.42574712
  -7.58877495  -5.30321115  -4.79881591  -4.5673199   -3.6865868
  -4.46822682  -1.45060265  -0.53560561  -4.94874171  -1.26112294
  -1.66779284  -5.57910033  -5.87103484  -3.35570045  -6.25661833
  -1.51564145   0.85085628  -3.82725071  -1.47077448  -3.36154118
  -5.37972404  -2.22844631  -2.78684422  -3.75603932  -1.85645
  -3.33156093  -2.32968944  -5.06053069  -1.73410541  -1.68829408
  -3.79892942  -1.62650712  -1.00001873  -6.07170511  -4.89697898
  -3.66269926  -3.13731451  -5.08348781  -3.71891247  -2.09779606
  -3.04082162  -5.12536015  -2.96071945  -4.28796395  -6.6231135
   1.00003406   0.03907036   0.46718521  -0.3467975    0.32350521
   0.47563771   1.10055427  -0.67580418  -0.46310299   0.40806733
   1.17438632  -0.55152081   0.84476439  -0.91257798   0.63165546
  -0.13845693  -0.22137683   1.20116183   1.18915628  -0.40676459
   1.35964325   1.14038015   1.27914468   0.19329823  -0.16790648
  -0.62775078   0.66095617   2.18236076   0.07018415  -0.26762451
  -0.25529448   0.32084111   0.48016592   0.28189794   0.60568093
  -1.07472716  -0.5088941    0.74892526   0.07203056  -0.10668727
  -0.15662946   0.09611498  -0.39521586  -0.79874442   0.65613691
  -0.39386485  -1.08601917   1.44693858   0.62992794   0.76536897]

(550,)
#生成的数据550行,1列

值得注意的是,在二分类过程中,  decision_function只会生成一列距离样本的类别由距离的符号来判断但是 predict_proba会生成两个类别分别对应的概率。SVM也可以生成概率所以我们可以使用和逻辑回归同样的方式来在SVM上设定和调节我们的阈

毋庸置疑,Platt缩放中涉及的交叉验证对于大型数据集来说非常昂贵计算会非常缓慢另外由于Platt缩放的理论原因,在二分类过程中,有可能出现predict_proba回的概率小于0.5但样本依旧被标记为正类的情况出现 毕竟支持向量机本身并不依赖于概率来完成自己的分类。如果我们的确需要置信度分数,但不一定非要是概率形式的话那建议可以将probability设置为False使用decision_function这个接口而不是predict_proba

绘制SVM的ROC曲线 

#开始绘图
recall = []
FPR = []

probrange = np.linspace(clf_proba.predict_proba(X)[:,1].min(),clf_proba.predict_proba(X)[:,1].max(),num=50,endpoint=False)

from sklearn.metrics import confusion_matrix as CM, recall_score as R 
import matplotlib.pyplot as plot
for i in probrange:
    y_predict = []
    for j in range(X.shape[0]):
        if clf_proba.predict_proba(X)[j,1] > i:
            y_predict.append(1)
        else:
            y_predict.append(0)
    cm = CM(y,y_predict,labels=[1,0])
    recall.append(cm[0,0]/cm[0,:].sum())
    FPR.append(cm[1,0]/cm[1,:].sum())
recall.sort()
FPR.sort()

plt.plot(FPR,recall,c="red")
plt.plot(probrange+0.05,probrange+0.05,c="black",linestyle="--") 
plt.show()

现在我们就画出了ROC曲线了,那我们如何理解这条曲线呢?先来回忆一下我们建立ROC曲线的根本目的是找寻RecallFPR之间的平衡,让我们能够衡量模型在尽量捕捉少数类的时候误伤多数类的情况会如何变化横坐标FPR代表着模型将多数类判断错误的能力纵坐标Recall代表着模型捕捉少数类的能力所以ROC曲线代表随着Recall的不断增加FPR如何增加。我们希望随着Recall的不断提升,FPR增加得越慢越好,这说明我们可 以尽量高效地捕捉出少数类而不会将很多地多数类判断错误。所以,我们希望看到的图像是纵坐标急速上升 横坐标缓慢增长也就是在整个图像左上方的一条弧线这代表模型的效果很不错拥有较好的捕获少数类的

中间的虚线代表着,当recall增加1%,我们的FPR也增加1%也就是说我们每捕捉出一个少数类就会有一个多数类被判错这种情况下模型的效果就不好这种模型捕获少数类的结果会让许多多数类被误伤从而增加我们的成本ROC曲线通常都是凸型的对于一条凸型ROC曲线来说,曲线越靠近左上角越好,越往下越糟糕,曲线如果在虚线的下方则证明模型完全无法使用但是它也有可能是一条凹形的ROC曲线。对于一条凹型ROC曲线来应该越靠近右下角越好凹形曲线代表模型的预测结果与真实情况完全相反那也不算非常糟只要我们手动将模型的结果逆转,就可以得到一条左上方的弧线了。最糟糕的就是,无论曲线是凹形还是凸型曲线位于图像中间,和虚线非常靠近,那我们拿它无能为力

好了现在我们有了这条曲线我们的确知道模型的效果还算是不错了但依然非常摸棱两可有没有具体的数字来帮助我们理解ROC曲线和模型的效果呢?的确存在这个数字就叫做AUC面积它代表了ROC曲线下方的面积这个面积越代表ROC曲线越接近左上角模型就越好AUC面积的计算比较繁琐因此我们使用sklearn帮助我们。接下来我们来看看,在sklearn当中,如何绘制我们的ROC曲线找出我们的的AUC面积

ROC曲线找出最佳阈值

sklearn中,我们有帮助我们计算ROC曲线的横坐标假正率FPR纵坐标Recall和对应的阈值的类sklearn.metrics.roc_curve。同时有帮助我们计算AUC面积的类sklearn.metrics.roc_auc_score在一些比较老旧的sklearn版本中我们使用sklearn.metrics.auc这个类来计算AUC面积这个类即将在0.22版本中被放弃,因此建议大家都使用roc_auc_score,来看看我们的这两个类

置信度,概率作为y_score都可 

from sklearn.metrics import roc_curve

FPR, recall, thresholds = roc_curve(y,clf_proba.decision_function(X), pos_label=1)

print(FPR)
print(recall)
print(thresholds)

结果:

[0.    0.    0.006 0.006 0.008 0.008 0.01  0.01  0.014 0.014 0.018 0.018
 0.022 0.022 0.024 0.024 0.028 0.028 0.03  0.03  0.032 0.032 0.036 0.036
 0.04  0.04  0.042 0.042 0.044 0.044 0.05  0.05  0.054 0.054 0.058 0.058
 0.066 0.066 0.072 0.072 0.074 0.074 0.086 0.086 1.   ]

[0.   0.02 0.02 0.06 0.06 0.16 0.16 0.2  0.2  0.22 0.22 0.36 0.36 0.42
 0.42 0.6  0.6  0.62 0.62 0.64 0.64 0.68 0.68 0.7  0.7  0.74 0.74 0.76
 0.76 0.82 0.82 0.84 0.84 0.86 0.86 0.88 0.88 0.92 0.92 0.94 0.94 0.96
 0.96 1.   1.  ]

[  3.18236076   2.18236076   1.48676267   1.35964325   1.33920817
   1.14038015   1.13383091   1.00003406   0.85085628   0.84476439
   0.78571364   0.60568093   0.5389064    0.46718521   0.44396046
   0.03907036  -0.07011269  -0.10668727  -0.1258212   -0.13845693
  -0.14034183  -0.16790648  -0.2040958   -0.22137683  -0.24381463
  -0.26762451  -0.34446784  -0.3467975   -0.39182241  -0.40676459
  -0.4589064   -0.46310299  -0.49195707  -0.5088941   -0.53560561
  -0.55152081  -0.62628865  -0.67580418  -0.78127198  -0.79874442
  -0.88438995  -0.91257798  -1.01417607  -1.08601917 -10.31959605]
from sklearn.metrics import roc_auc_score as AUC
area = AUC(y,clf_proba.decision_function(X))
area

结果:

0.9696400000000001
plt.figure()
plt.plot(FPR, recall, color='red',label='ROC curve (area = %0.2f)' % area)
plt.plot([0, 1], [0, 1], color='black', linestyle='--')
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('Recall')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()

 结果:

现在,有了ROC曲线,了解了模型的分类效力以及面对样本不均衡问题时的效力那我们如何求解我们最佳的阈值呢?我们想要了解,什么样的状况下我们的模型的效果才是最好的回到我们对ROC曲线的理解来ROC曲线反应的是recall增加的时候FPR何变化也就是当模型捕获少数类的能力变强的时候会误伤多数类的情况是否严重。我们的希望是模型在捕获少数类的能力变强的时候尽量不误伤多数类也就是说随着recall的变大FPR 的大小越小越好。所以我们希望找到的最有点,其实是RecallFPR差距最大的点这个点又叫做约登指数。

maxindex = (recall - FPR).tolist().index(max(recall - FPR))
print(thresholds[maxindex])

#我们可以在图像上来看看这个点在哪里
plt.scatter(FPR[maxindex],recall[maxindex],c="black",s=30)

#把上述代码放入这段代码中:
plt.figure()
plt.plot(FPR, recall, color='red',label='ROC curve (area = %0.2f)' % area)
plt.plot([0, 1], [0, 1], color='black', linestyle='--')   
plt.scatter(FPR[maxindex],recall[maxindex],c="black",s=30) 
plt.xlim([-0.05, 1.05])
plt.ylim([-0.05, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('Recall')
plt.title('Receiver operating characteristic example') 
plt.legend(loc="lower right")

结果:

最佳阈值就这样选取出来了由于现在我们是使用decision_function来画ROC曲线所以我们选择出来的最佳阈值其实是最佳距离。如果我们使用的是概率,我们选取的最佳阈值就会使一个概率值了只要我们让这个距离/概率以上的点,都为正类,让这个距离/概率以下的点都为负类,模型就是最好的即能够捕捉出少数类又能够尽量不误伤多数类,整体的精确性和对少数类的捕捉都得到了保证

而从找出的最优阈值点来看这个点其实是图像上离左上角最近的点离中间的虚线最远的点也是ROC曲线的转折点。如果没有时间进行计算或者横坐标比较清晰的时候我们就可以观察转折点来找到我们的最佳阈值

到这里为止,SVC的模型评估指标就介绍完毕了。但是,SVC的样本不均衡问题还可以有很多的探索。另外,我们还可以使用KS曲线,或者收益曲线(profit chart)来选择我们的阈值都是和ROC曲线类似的用法大家若有余力可以自己深入研究一下。模型评估指标还有很多深奥的地方

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值