详解支持向量机-ROC曲线中的概率和阈值菜菜的sklearn课堂笔记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解支持向量机-ROC曲线中的概率和阈值菜菜的sklearn课堂笔记相关的知识,希望对你有一定的参考价值。

基于混淆矩阵,我们学习了总共六个指标:准确率Accuracy,精确度Precision,召回率Recall,精确度和召回度的平衡指标F measure,特异度Specificity,以及假正率FPR。 其中,假正率有一个非常重要的应用:我们在追求较高的Recall的时候,Precision会下降,就是说随着更多的少数类被捕捉出来,会有更多的多数类被判断错误,但我们很好奇,随着Recall的逐渐增加,模型将多数类判断错误的能力如何变化呢?我们希望理解,我每判断正确一个少数类,就有多少个多数类会被判断错误。假正率正好可以帮助我们衡量这个能力的变化。相对的,Precision无法判断这些判断错误的多数类在全部多数类中究竟占多大的比例,所以无法在提升Recall的过程中也顾及到模型整体的Accuracy。因此,我们可以使用Recall和FPR之间的平衡,来替代Recall和Precision之间的平衡,让我们衡量模型在尽量捕捉少数类的时候,误伤多数类的情况如何变化,这就是我们的ROC曲线衡量的平衡。 ROC曲线,全称The Receiver Operating Characteristic Curve,译为受试者操作特性曲线。这是一条以不同阈值下的假正率FPR为横坐标,不同阈值下的召回率Recall为纵坐标的曲线

概率(probability)与阈值(threshold)

要理解概率与阈值,最容易的状况是来回忆一下我们用逻辑回归做分类的时候的状况。逻辑回归的predict_proba接口对每个样本生成每个标签类别下的似然(类概率)。对于这些似然,逻辑回归天然规定,当一个样本所对应的这个标签类别下的似然大于0.5的时候,这个样本就被分为这一类。比如说,一个样本在标签1下的似然是0.6,在标签0下的似然是0.4,则这个样本的标签自然就被分为1。逻辑回归的回归值本身,其实也就是标签1下的似然。在这个过程中,0.5就被称为阈值。来看看下面的例子:

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

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)
prob,prob.shape # 返回11个样本属于类别0和类别1的概率(似然)
# 谁的概率更大,就认为样本点是哪一类
---
(array([[0.60466356, 0.39533644],
        [0.45891589, 0.54108411],
        [0.71798647, 0.28201353],
        [0.67910911, 0.32089089],
        [0.66473898, 0.33526102],
        [0.56277457, 0.43722543],
        [0.66205409, 0.33794591],
        [0.35516738, 0.64483262],
        [0.38160618, 0.61839382],
        [0.58528378, 0.41471622],
        [0.50149311, 0.49850689]]), (11, 2))

import pandas as pd

prob = pd.DataFrame(prob,columns=[0,1])
prob # 往后只写前五行,有必要的话展示全部
---
	0	1
0	0.604664	0.395336
1	0.458916	0.541084
2	0.717986	0.282014
3	0.679109	0.320891
4	0.664739	0.335261

for i in range(prob.shape[0]):
    if prob.loc[i,1] > 0.5: # 这里0.5就是我们设定的阈值
        prob.loc[i,pred] = 1
    else:
        prob.loc[i,pred] = 0
prob
---
	0	1	pred
0	0.604664	0.395336	0.0
1	0.458916	0.541084	1.0
2	0.717986	0.282014	0.0
3	0.679109	0.320891	0.0
4	0.664739	0.335261	0.0

prob[y_true] = y
prob = prob.sort_values(by=1,ascending=False)
# 按照1列数字大小排序,使用非升序(也就是降序)
prob
---
	0	1	pred	y_true
7	0.355167	0.644833	1.0	1
8	0.381606	0.618394	1.0	1
1	0.458916	0.541084	1.0	0
10	0.501493	0.498507	0.0	1
5	0.562775	0.437225	0.0	0
# 得到一组pred和y_true就可以生成一个混淆矩阵,从而得到Recall和FPR,进而得到ROC曲线上的一个点
# 但下面我们先计算precision和recall,说明阈值变化的一个问题
confusion_matrix(y_true, y_pred, labels=None, sample_weight=None)
# y_true:样本的真实标签,也就是上面的y_True
# y_pred:样本指定模型和阈值下的预测标签,也就是上面的pred
# labels:说明需要哪些类,注意,少数类在前

precision_score(
    [y_true, y_pred, labels=None, pos_label=1, "average=binary", sample_weight=None],
)
# 同confusion_matrix

recall_score(
    [y_true, y_pred, labels=None, pos_label=1, "average=binary", sample_weight=None],
)
# 同confusion_matrix
from sklearn.metrics import confusion_matrix as CM, precision_score as P, recall_score as R

CM(prob.loc[:,y_true],prob.loc[:,pred],labels=[1,0])
---
array([[2, 2],
       [1, 6]], dtype=int64)

通过这个表格我们可以先自己计算一下 $$ Precision = \\frac23=0.666,Recall = \\frac24=0.5 $$ 用代码验证一下

P(prob.loc[:,y_true],prob.loc[:,pred],labels=[1,0])
---
0.6666666666666666

R(prob.loc[:,y_true],prob.loc[:,pred],labels=[1,0])
---
0.5

改变阈值,观察precision和recall的变化

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

prob
---
	0	1	pred	y_true
7	0.355167	0.644833	1.0	1
8	0.381606	0.618394	1.0	1
1	0.458916	0.541084	1.0	0
10	0.501493	0.498507	1.0	1
5	0.562775	0.437225	1.0	0

CM(prob.loc[:,y_true],prob.loc[:,pred],labels=[1,0])
---
array([[4, 0],
       [2, 5]], dtype=int64)
# 显然我们的混淆矩阵发生了变化

P(prob.loc[:,y_true],prob.loc[:,pred],labels=[1,0])
---
0.6666666666666666
# 但精确度没有发生变化,因此阈值变化不一定让参数单调变化
# 因此这个参数绘制曲线可能会导致不单调,不好研究最优值
# 因此之后我们换成假正率FPR

R(prob.loc[:,y_true],prob.loc[:,pred],labels=[1,0]) # 完全捕获少数类
---
1.0

注意,降低或者升高阈值并不一定能够让模型的效果一定变好,一切基于我们要追求怎样的模型效果。通常来说,降低阈值能够升高Recall 而要体现阈值的影响,首先必须的得到分类器在少数类下的预测概率。对于逻辑回归这样天生生成似然的算法和朴素贝叶斯这样就是在计算概率的算法,自然非常容易得到概率,但对于一些其他的分类算法,比如决策树,比如SVM,他们的分类方式和概率并不相关。那在他们身上,我们就无法画ROC曲线了吗?并非如此。

不过其实,SVM也可以生成概率

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

我们在画等高线,也就是决策边界的时候曾经使用SVC的接口decision_function,它返回我们输入的特征矩阵中每个样本到决策边界的距离。我们在SVM中利用决策边界来判断我们的样本,本质上来说,当两个点的距离是相同的符号的时候,越远离超平面的样本点归属于某个标签类的概率就很大。比如说,一个距离超平面0.1的点,和一个距离超平面100的点,明显是距离为0.1的点更有可能是负类别的点混入了边界。同理,一个距离超平面距离为-0.1的点,和一个离超平面距离为-100的点,明显是-100的点的标签更有可能是负类。所以,到超平面的距离一定程度上反应了样本归属于某个标签类的可能性。接口decision_function返回的值也因此被我们认为是SVM中的置信度(confidence)。 不过,置信度始终不是概率,它没有边界,可以无限大,大部分时候也不是以百分比或者小数的形式呈现,而SVC 的判断过程又不像决策树一样可以求解出一个比例。为了解决这个矛盾,SVC有重要参数probability。

SVC(
    [C=1.0, "kernel=rbf", degree=3, "gamma=auto_deprecated", coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, "decision_function_shape=ovr", random_state=None],
)
# 如果我们想要使用概率接口predict_proba和predict_log_proba,就要在实例化模型的时候设定probability=True
# 启用此功能会减慢SVM的运算速度
# 在二分类情况下,SVC将使用Platt缩放来生成概率,即在decision_function生成的距离上进行Sigmoid压缩,并附加训练数据的交叉验证拟合,来生成类逻辑回归的SVM分数。
from sklearn.svm import SVC

class_1 = 500
class_2 = 50
centers = [[0.0,0.0],[2.0,2.0]]
cluster_std = [1.5,0.5]
X,y = make_blobs(n_samples=[class_1,class_2]
                ,centers=centers
                ,cluster_std=cluster_std
                ,random_state=0
                ,shuffle=False)

plt.scatter(X[:,0],X[:,1],c=y,cmap=rainbow,s=10)

clf = SVC(kernel="linear",C=1,probability=True).fit(X,y)
clf.predict_proba(X).shape
# 生成的各类标签下的概率
# 因为我们之前设置了probability=True
---
(550, 2) # 我们有550个样本,标签的取值有0,1两个,因此shape为(550,2)
# 我们可以使用和逻辑回归同样的方式来在SVM上设定和调节我们的阈值。

以上是关于详解支持向量机-ROC曲线中的概率和阈值菜菜的sklearn课堂笔记的主要内容,如果未能解决你的问题,请参考以下文章

详解支持向量机-支持向量机分类器原理菜菜的sklearn课堂笔记

详解支持向量机-探索核函数的优势和缺陷菜菜的sklearn课堂笔记

详解支持向量机-SVC真实数据案例:预测明天是否会下雨-填补缺失值和编码菜菜的sklearn课堂笔记

详解支持向量机-SVC真实数据案例:预测明天是否会下雨-探索标签和处理异常值菜菜的sklearn课堂笔记

详解支持向量机-选取与核函数相关的参数:degree & gamma & coef0菜菜的sklearn课堂笔记

菜菜的sklearn课堂笔记支持向量机-线性SVM用于分类的原理