Bootstrap

[学习笔记] [机器学习] 5. 逻辑回归(逻辑回归、混淆矩阵、分类评估指标、ROC曲线、AUC指标、类别不均衡问题、PR曲线、AP、mAP)

  1. 视频链接
  2. 数据集下载地址:无需下载

本文学习目标

  1. 知道逻辑回归的损失函数、优化方法
  2. 知道逻辑回归的应用场景
  3. 应用 LogisticRegression 实现逻辑回归预测
  4. 知道精确率、召回率等指标的区别
  5. 知道如何解决样本不均衡情况下的评估
  6. 会绘制 ROC 曲线图形

1. 逻辑回归介绍

学习目标:

  • 了解逻辑回归的应用场景
  • 知道逻辑回归的原理
  • 掌握逻辑回归的损失函数和优化方案

逻辑回归(Logistic Regression)是机器学习中的一种分类模型,逻辑回归是一种分类算法,虽然名字中带有回归,但是它与回归之间有一定的联系。由于算法的简单和高效,在实际中应用非常广泛。

逻辑回归(Logistic Regression)是机器学习领域最为常见的模型方法之一,常常用于作为处理各种任务的基准模型(baseline)。简单来说,逻辑回归是一种用于解决二分类(0 或 1)问题的机器学习方法,用于估计某种事物的可能性


Q1:在机器学习中,逻辑回归和线性回归有什么联系吗?
A1:逻辑回归和线性回归都属于广义线性回归模型的特例。逻辑回归假设因变量 y y y 服从伯努利分布,而线性回归假设因变量 y y y 服从高斯分布。因此与线性回归有很多相同之处,去除 Sigmoid 映射函数的话,逻辑回归算法就是一个线性回归。


Q2:什么是伯努利分布,什么是高斯分布?
A2:伯努利分布(英语:Bernoulli distribution),又名两点分布或者 0-1 分布,是一种离散型概率分布。若伯努利试验成功,则伯努利随机变量取值为 1。若伯努利试验失败,则伯努利随机变量取值为 0。记其成功概率为 p p p,失败概率为 1 − p 1-p 1p

高斯分布(Gaussian distribution),也称正态分布(Normal distribution),是一种连续型概率分布。它的概率密度函数呈钟形,两头低,中间高,左右对称。正态分布由两个参数 μ \mu μ σ 2 \sigma^2 σ2 定义,它们是数轴上的最小值和最大值,通常缩写为 N ( μ , σ 2 ) N(\mu, \sigma^2) N(μ,σ2)


Q3:逻辑回归和线性回归的区别是什么?
A3:线性回归和逻辑回归都属于广义线性回归模型的特例。线性回归用于解决回归问题,而逻辑回归用于解决分类问题(二分类、多分类)。线性回归无 link-function 或不起作用,逻辑回归的 link-function 是对数几率函数。线性回归使用最小二乘法作为参数估计方法,逻辑回归使用极大似然法作为参数估计方法。


Q4:是不是线性回归是拟合一根连续的线,而逻辑回归拟合的是一些离散的点?
A4:线性回归和逻辑回归都是拟合一个函数,但它们的目的不同。

  • 线性回归用于预测连续值变量,它拟合的是一条直线或者一个平面。
  • 逻辑回归用于解决分类问题,它拟合的是一个 S 形曲线(Sigmoid 函数),这条曲线将数据分成两类或多类。逻辑回归的输出是概率值,表示数据属于某一类的概率

1.1 逻辑回归的应用场景

  1. 广告点击率
  2. 是否为垃圾邮件
  3. 是否患病
  4. 是否为金融诈骗
  5. 是否为虚假账号

看到上面的例子,我们可以发现其中的特点,那就是都属于两个类别之间的判断。逻辑回归就是解决二分类问题的利器。

逻辑解决的是:是或否。而逻辑回归是给出具体的值。

1.2 逻辑回归的原理

要想掌握逻辑回归,必须掌握两点:

  1. 逻辑回归中,其输入值是什么?
  2. 如何判断逻辑回归的输出?

1.2.1 逻辑回归的输入

h ( w ) = w 1 x 1 + w 2 x 2 + . . . + b = w T x \begin{aligned} h(w) &= w_1 x_1 + w_2 x_2 + ... + b \\ &= w^Tx \end{aligned} h(w)=w1x1+w2x2+...+b=wTx

其中, w 1 , w 2 , . . . , b w_1, w_2, ..., b w1,w2,...,b 是模型的参数, x 1 , x 2 , . . . x_1, x_2, ... x1,x2,... 是输入数据的特征, h ( w ) h(w) h(w) 表示假设函数(Hypothesis Function),它是一个关于模型参数 w w w 的函数,也是逻辑回归的输入。这个公式计算了输入数据的特征和模型参数的线性组合,它的输出可以看作是线性回归的输出。因此我们需要记住:逻辑回归的输入就是一个线性回归的输出

1.2.2 激活函数

该怎样理解上面那句话:“逻辑回归的输入就是一个线性回归的输出”?

这是因为逻辑回归会通过一个 Sigmoid 函数将线性回归的输出映射到 0 和 1 之间。Sigmoid 函数的输出可以看作是数据属于某一类的概率。Sigmoid 函数如下所示:

g ( w T , x ) = 1 1 + e − h ( w ) = 1 1 + e − w T x \begin{aligned} g(w^T, x) &= \frac{1}{1 + e^{-h(w)}} \\ &= \frac{1}{1 + e^{-w^Tx}} \end{aligned} g(wT,x)=1+eh(w)1=1+ewTx1

Sigmoid 函数将线性回归的输出映射到 0 和 1 之间,它的输出可以看作是数据属于某一类的概率。其中 w T x w^Tx wTx 表示输入数据的特征和模型参数的线性组合,也就是线性回归的输出

逻辑回归的判断标准

  1. 回归的结果输入到 Sigmoid 函数当中
  2. 输出结果为 [ 0 , 1 ] [0,1] [01] 区间中的一个概率值,默认 0.5 0.5 0.5 为阈值

在这里插入图片描述

逻辑回归最终的分类是通过属于某个类别的概率值来判断是否属于某个类别,并且这个类别默认标记为 1(正例),另外的一个类别会标记为 0(反例)。这样做的目的是方便计算损失。

输出结果解释:假设有两个类别 A 和 B,现在有一个样本输入到逻辑回归,其输出结果 0.55。因为这个概率值超过阈 值0.5,这意味着我们训练或者预测的结果就是 A(用 1 表示) 类别。反之,如果得出结果为 0.3 那么,训练或者预测结果就为 B(用 0 表示) 类别。

关于逻辑回归的阈值是可以进行改变的。比如上面举例中,如果把阈值设置为 0.6,那么输出的结果 0.55,就属于 B 类。


Q1:逻辑回归的输入是线性回归的输出,再通过一个 Sigmoid 函数得到所属不同类别的概率。那 Sigmoid 函数是什么函数?
A1:Sigmoid 函数在机器学习中常被用作神经网络的阈值函数,将变量映射到 [ 0 , 1 ] [0,1] [0,1],可以用来做二分类。在深度学习中,由于其单增以及反函数单增等性质,Sigmoid 函数常被用作神经网络的激活函数,将变量映射到 [ 0 , 1 ] [0,1] [0,1] 之间。


Q2:在机器学习和深度学习中,阈值函数有哪些?
A2:在机器学习和深度学习中,阈值函数是一种将输入信号的总和转换为输出信号的函数,也称为激活函数(Activation Function)。常见的激活函数有 Sigmoid 函数和 ReLU 函数。


Q3:ReLU 激活函数也可以用作逻辑回归吗?将线性回归的输出作为输入,得到一个 [ 0 , 1 ] [0, 1] [0,1] 的概率?
A3:ReLU 激活函数通常不用于逻辑回归。逻辑回归通常使用 Sigmoid 函数作为激活函数,将线性回归的输出映射到 [ 0 , 1 ] [0,1] [0,1] 区间,得到一个概率,用来做二分类。而 ReLU 函数的输出范围是 [ 0 , + ∞ ) [0, +\infty) [0,+),并不适合用来表示概率。


Q4:将线性回归的输出作为逻辑回归的输入,得到一个 [ 0 , 1 ] [0, 1] [0,1] 的概率,是不是只有 Sigmoid 这一种函数?
A4:不是的。虽然 Sigmoid 函数是逻辑回归中最常用的激活函数,但也可以使用其他能将实数映射到 [ 0 , 1 ] [0,1] [0,1] 区间的函数。例如,Softmax 函数通常用于多分类问题,它可以将一组实数映射到 [ 0 , 1 ] [0,1] [0,1] 区间,并且这组数的和为 1。当 Softmax 函数用于二分类问题时,它与 Sigmoid 函数等效。


在之前,我们用最小二乘法衡量线性回归的损失。在逻辑回归中,当预测结果不对的时候,我们该怎么衡量其损失呢?

我们来看下图(下图中,设置阈值为 0.6):

在这里插入图片描述

可以看到,一共有 5 个样本,其中三个样本预测错了,两个预测对了。那么如何去衡量逻辑回归的预测结果与真实结果的差异呢?

1.3 损失以及优化

1.3.1 损失函数(对数似然损失,Log-likelihood Loss Function,LLLF)

逻辑回归的损失,称之为对数似然损失(Log-likelihood Loss Function,LLLF),公式如下:

一、分开类别

L ( h θ ( x ) , y ) = { − log ⁡ ( h θ ( x ) ) i f   y = 1 − log ⁡ ( 1 − h θ ( x ) ) i f   y = 0 \mathcal{L}(h_\theta(x), y) = \begin{cases} -\log{(h_\theta(x))} && \mathrm{if} \ y = 1 \\ -\log{(1 - h_\theta(x))} && \mathrm{if} \ y = 0 \end{cases} L(hθ(x),y)={log(hθ(x))log(1hθ(x))if y=1if y=0

其中:

  • L \mathcal{L} L 表示损失函数
  • h θ ( x ) h_\theta(x) hθ(x) 表示模型对输入 x x x 的预测概率
  • y y y 表示真实标签,取值为0或1
  • log ⁡ \log log 默认以自认常数 e e e 为底,所以 log ⁡ ↔ ln ⁡ \log \leftrightarrow \ln logln

该函数图像如下:

在这里插入图片描述

  • y = 1 y=1 y=1 时,损失函数为 − log ⁡ ( h θ ( x ) ) -\log{(h_\theta(x))} log(hθ(x))。当模型预测正确,即 h θ ( x ) h_\theta(x) hθ(x) 接近 1 时,损失接近 0;当模型预测错误,即 h θ ( x ) h_\theta(x) hθ(x) 接近 0 时,损失变得很大。

  • y = 0 y=0 y=0 时,损失函数为 − log ⁡ ( 1 − h θ ( x ) ) -\log{(1 - h_\theta(x))} log(1hθ(x))。当模型预测正确,即 h θ ( x ) h_\theta(x) hθ(x) 接近 0 时,损失接近 0;当模型预测错误,即 h θ ( x ) h_\theta(x) hθ(x) 接近 1 时,损失变得很大。

总之,对数损失函数能够衡量模型预测概率与真实标签之间的差异。当模型预测正确时,损失接近 0;当模型预测错误时,损失变得很大(不超过1)

无论何时,我们都希望损失函数值,越小越好。分情况讨论,对应的损失函数值:

  • y = 1 y=1 y=1 时,我们希望 h θ ( x ) h_\theta(x) hθ(x) 值越大越好
  • y = 0 y=0 y=0 时,我们希望 h θ ( x ) h_\theta(x) hθ(x) 值越小越好

二、综合完整损失函数

L ( h θ ( x ) , y ) = ∑ i = 1 m − y i log ⁡ ( h θ ( x ) ) − − − − − − − − − − − 当 y i = 0 时此项为 0    − ( 1 − y i ) log ⁡ ( 1 − h θ ( x ) ) − − − − − − − − − − − − − − − − − − − 当 y i = 1 时此项为 0 \mathcal{L}(h_\theta(x), y) = \sum_{i = 1}^m \underset{\underset{当y_i=0时此项为0}{-----------}}{- y_i \log{(h_\theta(x))}} \ \ \underset{\underset{当y_i=1时此项为0}{-------------------}}{-(1 - y_i) \log{(1 - h_\theta(x))}} L(hθ(x),y)=i=1myi=0时此项为0−−−−−−−−−−−yilog(hθ(x))  yi=1时此项为0−−−−−−−−−−−−−−−−−−−(1yi)log(1hθ(x))

其中:

  • L \mathcal{L} L 表示损失函数
  • h θ ( x ) h_\theta(x) hθ(x) 表示模型对输入 x x x 的预测概率
  • y y y 表示真实标签,取值为0或1
  • m m m 表示样本数量
  • log ⁡ \log log 默认以自认常数 e e e 为底,所以 log ⁡ ↔ ln ⁡ \log \leftrightarrow \ln logln

这个公式是对数损失函数的向量化形式,它计算了所有样本的损失之和。对于每个样本 i i i,损失为 − y i log ⁡ ( h θ ( x ) ) − ( 1 − y i ) log ⁡ ( 1 − h θ ( x ) ) - y_i \log{(h_\theta(x))} - (1 - y_i) \log{(1 - h_\theta(x))} yilog(hθ(x))(1yi)log(1hθ(x))

  • y i = 1 y_i=1 yi=1 时,损失为 − log ⁡ ( h θ ( x ) ) -\log{(h_\theta(x))} log(hθ(x))
  • y i = 0 y_i=0 yi=0 时,损失为 − log ⁡ ( 1 − h θ ( x ) ) -\log{(1 - h_\theta(x))} log(1hθ(x))

总之,对数损失函数能够衡量模型预测概率与真实标签之间的差异。当模型预测正确时,损失接近 0;当模型预测错误时,损失变得很大。


接下来我们就带入上面那个例子来计算一遍,就能理解意义了。

在这里插入图片描述

计算损失:

L = − [ 1 log ⁡ ( 0.4 ) + ( 1 − 0 ) log ⁡ ( 1 − 0.68 ) + 1 log ⁡ ( 0.41 ) + ( 1 − 0 ) log ⁡ ( 1 − 0.55 ) + 1 log ⁡ ( 0.71 ) ] = − [ log ⁡ ( 0.4 ) + log ⁡ ( 1 − 0.68 ) + log ⁡ ( 0.41 ) + log ⁡ ( 1 − 0.55 ) + log ⁡ ( 0.71 ) ] = − [ log ⁡ ( 0.4 ) + log ⁡ ( 0.32 ) + log ⁡ ( 0.41 ) + log ⁡ ( 0.45 ) + log ⁡ ( 0.71 ) ] = − [ − 0.916290731874155 + ( − 1.139434283188365 ) + ( − 0.8915981192837835 ) + ( − 0.7985076962177716 ) + ( − 0.3424903089467762 ) ] = 4.088321139511852 \begin{aligned} \mathcal{L} &= -[1\log(0.4) + (1-0)\log(1-0.68)+1\log(0.41)+(1-0)\log(1-0.55)+1\log(0.71)] \\ &= -[\log(0.4) + \log(1-0.68)+\log(0.41)+\log(1-0.55)+\log(0.71)] \\ &= -[\log(0.4) + \log(0.32)+\log(0.41)+\log(0.45)+\log(0.71)] \\ &= -[-0.916290731874155 + (-1.139434283188365) + (-0.8915981192837835) + (-0.7985076962177716) + (-0.3424903089467762)] \\ &= 4.088321139511852 \end{aligned} L=[1log(0.4)+(10)log(10.68)+1log(0.41)+(10)log(10.55)+1log(0.71)]=[log(0.4)+log(10.68)+log(0.41)+log(10.55)+log(0.71)]=[log(0.4)+log(0.32)+log(0.41)+log(0.45)+log(0.71)]=[0.916290731874155+(1.139434283188365)+(0.8915981192837835)+(0.7985076962177716)+(0.3424903089467762)]=4.088321139511852

我们已经知道了,对于 log ⁡ ( P ) \log(P) log(P) P P P 值越大,结果越小。所以我们可以对这个损失的式子去分析。

1.3.2 优化函数(优化过程、如何利用损失优化模型参数)

同样使用梯度下降优化算法,去减少损失函数的值。这样去更新逻辑回归前面对应算法的权重参数,提升原本属于 1 类别的概率,降低原本是 0 类别的概率


Q:为什么要降低原本属于 0 类别的样本的预测概率?
A:看公式说话。逻辑回归损失函数如下:

L ( h θ ( x ) , y ) = ∑ i = 1 m − y i log ⁡ ( h θ ( x ) ) − − − − − − − − − − − ①当 y i = 0 时此项为 0    − ( 1 − y i ) log ⁡ ( 1 − h θ ( x ) ) − − − − − − − − − − − − − − − − − − − ②当 y i = 1 时此项为 0 \mathcal{L}(h_\theta(x), y) = \sum_{i = 1}^m \underset{\underset{①当y_i=0时此项为0}{-----------}}{- y_i \log{(h_\theta(x))}} \ \ \underset{\underset{②当y_i=1时此项为0}{-------------------}}{-(1 - y_i) \log{(1 - h_\theta(x))}} L(hθ(x),y)=i=1myi=0时此项为0−−−−−−−−−−−yilog(hθ(x))  yi=1时此项为0−−−−−−−−−−−−−−−−−−−(1yi)log(1hθ(x))

  • 对于某一个数据而言,它的真实值是 1,那么 ② 直接为 0,公式变为: L ( h θ ( x ) , y ) = ∑ i = 1 m − log ⁡ ( h θ ( x ) ) \mathcal{L}(h_\theta(x), y) = \sum_{i = 1}^m - \log{(h_\theta(x))} L(hθ(x),y)=i=1mlog(hθ(x))。我们期望损失 Loss 越小越好,所以对于真实值为 1 的数据,且 − log ⁡ P -\log{P} logP 是一个单调递减的函数,因此 P P P 越大,Loss 越小,即提升原本属于 1 类别的概率
  • 对于某一个数据而言,它的真实值是 0,那么 ① 直接为 0,公式变为: L ( h θ ( x ) , y ) = ∑ i = 1 m − log ⁡ ( 1 − h θ ( x ) ) \mathcal{L}(h_\theta(x), y) = \sum_{i = 1}^m - \log{(1 - h_\theta(x))} L(hθ(x),y)=i=1mlog(1hθ(x))。我们期望损失 Loss 越小越好,所以对于真实值为 1 的数据,且 − log ⁡ ( 1 − P ) -\log{(1-P)} log(1P) 是一个单调递增的函数,因此 P P P 越大,Loss 越大,而我们希望 P P P 越小越好,即降低原本是 0 类别的概率

总之:逻辑回归损失函数的目标是最小化损失,因此对于真实值为 1 的数据,我们希望提高其预测概率,而对于真实值为 0 的数据,我们希望降低其预测概率。


  • 逻辑回归概念【知道】
    • 解决的是一个二分类问题
    • 逻辑回归的输入是线性回归的输出
  • 逻辑回归的原理【掌握】
    • 输入:线性回归的输出
    • 激活函数:Sigmoid函数
      • 把整体的值映射到 [ 0 , 1 ] [0,1] [0,1]
      • 再设置一个阈值,进行分类判断
  • 逻辑回归的损失和优化【掌握】
    • 损失函数:对数似然损失
      • 借助了 log ⁡ \log log(对数) 思想,进行完成
      • 真实值等于 0,等于 1 两种情况进行划分
    • 优化过程:提升原本属于 1 类别的概率,降低原本是 0 类别的概率

对数似然损失函数的公式如下:

L i = L i ( p i , y i ) = { − l o g ( p i ) y i = 1 − l o g ( 1 − p i ) y i = 0 = − y i l o g ( p i ) − ( 1 − y i ) l o g ( 1 − p i ) \begin{aligned} \mathcal{L_i} & = \mathcal{L_i}(p_i,y_i) = \begin{cases} -log(p_i) & y_i = 1 \\ -log(1-p_i) & y_i = 0 \end{cases} \\ &= -y_ilog(p_i) - (1-y_i)log(1-p_i) \end{aligned} Li=Li(pi,yi)={log(pi)log(1pi)yi=1yi=0=yilog(pi)(1yi)log(1pi)

其中, p i p_i pi 表示样本 i i i 预测为正类的概率, y i y_i yi 表示样本 i i i 的真实标签,正类为 1 1 1,负类为 0 0 0。当预测为正类的概率越大,损失越小。

2. 逻辑回归 API 介绍

学习目标

  • 知道逻辑回归 API 的用法

sklearn.linear_model.LogisticRegression(solver='liblinear', penalty='l2', C=1.0)
  • 作用:用于实现逻辑回归(Logistic Regression)分类器
  • 参数:
    • penalty:指定正则化项中使用的范式。可选值为 'l1''l2''elasticnet'None。默认值为 'l2'
    • C:正则化强度的倒数,必须是正浮点数。值越小表示正则化强度越大。默认值为 1.0。
    • solver:指定优化算法。可选值有 'newton-cg''lbfgs''liblinear''sag''saga'。默认值为 'lbfgs'
      • 'newton-cg':牛顿法家族的一种,利用损失函数的二阶导数矩阵(即海森矩阵)来迭代优化损失函数。
      • 'lbfgs':拟牛顿法的一种,利用损失函数的二阶导数矩阵(即海森矩阵)来迭代优化损失函数。
      • 'liblinear':使用开源的 liblinear 库实现,内部使用了坐标轴下降法来迭代优化损失函数。
      • 'sag':随机平均梯度下降,是梯度下降法的变种,每次迭代仅仅用一部分的样本来计算梯度,适合于样本数据多的时候。
      • 'saga':线性收敛的随机优化算法的变种。
      • 说明:
        • 对于小数据集,'liblinear' 是一个不错的选择
        • 对于大数据集,'sag''saga' 由于速度更快而更加适合
        • 对于多分类问题,只有 'newton-cg''sag''saga''lbfgs' 能够处理多项损失,而 'liblinear' 受限于一对剩余 (OvR)。
    • fit_intercept:是否在决策函数中添加常数项(即截距)。默认值为 True。
    • random_state:随机数种子。仅在优化算法为 sag 或 liblinear 时有用。默认值为 None。
  • 返回值:本身是一个类,因此它的构造函数返回的是一个逻辑回归分类器实例化对象。

C 代表着“容忍度”,即模型对于分类错误的容忍程度。当 C 值较大时,模型对于分类错误的容忍度较高,因此正则化强度较小;而当 C 值较小时,模型对于分类错误的容忍度较低,因此正则化强度较大。
在逻辑回归中,C 参数被用作正则化强度的倒数,与 SVM 中的 C 参数有类似的作用。因此可以将 C 看作是一个惯用符号,而不必过于纠结它的字面含义

LogisticRegression方法相当于SGDClassifier(loss="log", penalty="")SGDClassifier实现了一个普通的随机梯度下降学习;而LogisticRegression实现了随机平均梯度下降(Stochastic Average Gradient Descent,SAG)

SGDClassifier(loss="log", penalty="")
  • 作用:SGDClassifier 是 scikit-learn 库中的一个类,它实现了带有随机梯度下降(SGD)学习的正则化线性模型。SGD 每次估计损失的梯度时都会对样本进行一次采样,并且模型会在采样过程中进行更新。
  • 参数:
    • loss 参数指定了要使用的损失函数
      • "log" 表示逻辑回归,是一种概率分类器
    • penalty 参数指定了要使用的惩罚(正则化项),默认值为 "l2",表示标准的线性 SVM 模型的正则化器
      • penalty 设置为空字符串时,不会添加任何惩罚

注意逻辑回归默认将类别数量少的当做正例


Q1:在逻辑回归中,什么是正例,什么是反例?
A1:在逻辑回归中,正例通常指的是标记为 1 的类别,而反例则指的是标记为 0 的类别。逻辑回归最终的分类是通过属于某个类别的概率值来判断是否属于某个类别。例如,当概率值大于 0.5 时,我们可以将其判断为正例,否则判断为反例。


Q2:正例和反例是正样本和负样本吗?
A2:是的,正例和反例通常指的是正样本和负样本。在二分类问题中,正样本通常指的是标记为 1 的类别,而负样本则指的是标记为 0 的类别。


Q3:正样本和负样本的定义是什么?
A3:在二分类问题中,正样本和负样本是指数据集中属于两个不同类别的样本。通常情况下,正样本指的是标记为 1 的类别,而负样本则指的是标记为 0 的类别。这些标记可以是人为定义的,也可以是根据实际情况确定的。例如,在垃圾邮件分类问题中,垃圾邮件可以被定义为正样本,而非垃圾邮件则被定义为负样本。


Q4:那在机器学习中,正样本和负样本的定义是什么?
A4:在机器学习中,正样本和负样本的定义通常与特定任务相关。例如,在二分类问题中,正样本通常指与预测目标相关的样本,而负样本则指与预测目标无关的样本。例如,在判断一张图片是否为猫的二分类任务中,正样本是猫的图片,而负样本是非猫的图片。


Q5:在深度学习中,正样本和负样本的定义是什么?
A5:在深度学习中,正样本和负样本的定义通常与训练目标有关。例如,在二分类问题中,正样本通常指与预测目标一致的样本,而负样本则指与预测目标不一致的样本。例如,在判断一张图片是否为猫的二分类任务中,正样本指图片中有猫的样本,而负样本则指图片中没有猫的样本。


Q6:那在目标检测任务中,正样本和负样本的定义是什么?
A6:在目标检测任务中,正样本是待检测的目标。例如,在检测人脸时,人脸是正样本,非人脸则是负样本。正负样本都是针对于算法经过处理生成的框(如:计算宽高比、交并比、样本扩充等)而言,而非原始的 GT 数据。


Q7:那对于分类问题,正负样本是原始的 GT 数据吗?
A7:对于分类问题,正样本和负样本通常是指原始的 GT 数据。例如,在二分类问题中,正样本是我们想要正确分类出的类别所对应的样本,而负样本则是另一个类别所对应的样本。


Q8:例如,在判断一张图片是否为猫的二分类任务中,正样本是猫的图片,而负样本是非猫的图片。这里的负样本可以多个类别吗,比如负样本是狗或老鼠或老虎或狮子?
A8:是的。在判断一张图片是否为猫的二分类任务中,正样本是猫的图片,而负样本是非猫的图片。负样本可以是多个类别,比如狗、老鼠、老虎或狮子等等。只要不是猫的图片都可以作为负样本


Q9:逻辑回归只有两个类别吗?
A9逻辑回归不仅可以用于二分类问题,也可以用于多分类问题。在处理多分类问题时,通常采用一种称为 One-vs-all(也叫做 One-vs-rest)的方法。这种方法会以参考分类作为参考,构建多个分类模型,计算每个数据属于各个分类的概率,取概率最大的分类作为最终分类。

3. 案例:癌症分类预测-良/恶性乳腺癌肿瘤预测

学习目标

  • 通过肿瘤预测案例,学会如何使用逻辑回归对模型进行训练

3.1 背景介绍

一、数据介绍

在这里插入图片描述

文件介绍

  • breast-cancer-wisconsin.data:数据样本
  • breast-cancer-wisconsin.names:数据描述

数据集介绍

  • 该数据集有 699 条样本,共 11 列数据。第一列为用于检索的 id,后 9 列分别是与肿瘤相关的医学特征(特征值),最后一列表示肿瘤类型的数值(目标值)
  • 该数据集共包含 16 个缺失值,用 ? 标出

二、原始数据集的下载地址

https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/

3.2 案例分析

步骤分析:

# 1. 获取数据
# 2. 基本数据处理
## 2.1 缺失值处理
## 2.2 确定特征值和目标值
## 2.3 分割数据
# 3. 特征工程:标准化
# 4. 机器学习:逻辑回归
# 5. 模型评估

3.3 代码实现

请使用 Jupyter Notebook 以方便可视化。

1. 获取数据

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
import ssl
ssl._create_default_https_context = ssl._create_unverified_context


# 1. 获取数据
names = ["Sample code number", "Clump Thickness", "Uniformity of Cell Size",
         "Uniformity of Cell Shape", "Marginal Adhesion", "Single Epithelial Cell Size",
         "Bare Nuclei", "Bland Chromatin", "Normal Nucleoli", "Mitoses", "Class"]

data = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/breast-cancer-wisconsin.data", 
                   names=names)

data.head()

在这里插入图片描述

最后的 Class 表示肿瘤的类别,其中 2 表示良性;4 表示恶性。

2. 基本数据处理

# 2. 基本数据处理
## 2.1 缺失值处理
data = data.replace(to_replace='?', value=np.NAN)  # 替换缺失值
data = data.dropna()  # 丢弃缺失值的数据(这一行就删除了,因为缺失值不多)

## 2.2 确定特征值和目标值
x = data.iloc[:, 1:10]  # 不要索引列和最后一列
x.head()

y = data["Class"]  # GT
y.head()

## 2.3 分割数据
x_train, x_test, y_train, y_test = train_test_split(data, y, random_state=22)
print(x_train.shape)  # (512, 11)
print(y_train.shape)  # (512,)
print(x_test.shape)  # (171, 11)
print(y_test.shape)  # (171,)

在这里插入图片描述


Q:为什么要先替换缺失值再删除缺失值所在的行,不能直接删除行吗?
A:在这段代码中,首先使用 replace 函数将数据中的 '?' 替换为 np.NAN,然后使用 dropna 函数删除包含缺失值的行。这样做的原因可能是因为原始数据中使用 '?' 来表示缺失值,dropna 函数默认只删除包含 NaNNA 值的行。因此,需要先将 '?' 替换为 np.NAN,然后再使用 dropna 函数删除包含缺失值的行。

当然,也可以直接删除包含 '?' 的行,但这需要编写额外的代码来实现。使用 replacedropna 函数可以更简单地实现删除包含缺失值的行的目的。

3. 特征工程:标准化

# 3. 特征工程:标准化
transfer = StandardScaler()

x_train = transfer.fit_transform(x_train)
x_test = transfer.transform(x_test)

print(x_train.shape)  # (512, 11)
print(x_test.shape)  # (171, 11)

4. 机器学习:逻辑回归

# 4. 机器学习:逻辑回归
estimator = LogisticRegression(penalty="l2")  # 默认就是l2惩罚
estimator.fit(x_train, y_train)

print(estimator)  # LogisticRegression()

5. 模型评估

# 5. 模型评估
## 5.1 准确率
scores = estimator.score(x_test, y_test)
print(f"准确率为:{scores * 100}%")

## 5.2 预测值
y_predict = estimator.predict(x_test)
print(f"预测值为:\r\n{y_predict}")
"""

模型预测效果

准确率为:100.0%
预测值为:
[2 4 4 2 2 2 2 2 2 2 2 2 2 4 2 2 4 4 4 2 4 2 4 4 4 2 4 2 2 2 2 2 4 2 2 2 4
 4 2 2 2 4 2 4 4 4 4 2 4 4 2 2 2 2 2 4 2 2 2 2 4 4 4 4 2 4 2 2 4 2 2 2 2 4
 2 2 2 2 2 2 4 4 4 2 4 4 4 4 2 2 2 4 2 4 2 2 2 2 2 2 4 2 2 4 2 2 4 2 4 4 2
 2 2 2 4 2 2 2 2 2 2 4 2 2 2 2 2 4 2 4 2 2 2 4 2 2 2]

在很多分类场景当中我们不一定只关注预测的准确率!比如以这个癌症举例子。我们并不关注预测的准确率,而是关注在所有的样本当中,癌症患者有没有被全部预测(检测)出来

因此我需要引入额外的模型评估指标。


小结

  • 肿瘤预测案例实现【知道】
    • 如果数据中有缺失值,一定要对其进行处理。通常需要对其进行处理。处理缺失值的方法取决于缺失值的数量和数据的特点:
      • 缺失值较少:如果缺失值较少,可以直接删除包含缺失值的行。这样做可以快速简单地处理缺失值,但会导致数据量减少。(一般是先替换缺失值,再丢弃缺失值所在的行)
      • 缺失值较多:直接删除包含缺失值的行可能会导致数据量减少过多,影响模型的性能。此时,可以使用一些方法来填充缺失值,例如使用均值、中位数或众数填充数值型特征的缺失值,或使用众数填充类别型特征的缺失值。也可以使用更复杂的方法,例如使用其他特征预测缺失值,或使用基于邻居的方法填充缺失值。
    • 准确率并不是衡量分类正确的唯一标准

4. 分类评估方法

学习目标

  • 了解什么是混淆矩阵
  • 知道分类评估中的精确率和召回率
  • 知道 ROC 曲线和 AUC 指标

4.1 分类评估方法

4.1.1 混淆矩阵

混淆矩阵是机器学习中总结分类模型预测结果的情形分析表,以矩阵形式将数据集中的记录按照真实的类别与分类模型预测的类别判断两个标准进行汇总。其中矩阵的行表示真实值,矩阵的列表示预测值。

混淆矩阵是一种评判模型结果指标的可视化工具,属于模型评估的一部分,多用于判断分类器(Classifier)的优劣。特别用于监督学习(有一一对应的 GT),在无监督学习一般叫做匹配矩阵。

在分类任务下,预测结果(Predicted Condition)与正确标记(True Condition)之间存在四种不同的组合,可以构成混淆矩阵(适用于多分类)。混淆矩阵如下图所示。

在这里插入图片描述

在混淆矩阵中,正例和负例(假例)是指分类模型中的两个类别。

  • 真正例(True Positive,TP)是指模型正确预测为正例的数量
  • 伪正例(False Positive,FP)是指模型错误预测为正例的数量
  • 真反例(True Negative,TN)是指模型正确预测为负例的数量
  • 伪反例(False Negative,FN)是指模型错误预测为负例的数量

举个例子:假设我们有一个分类模型用来预测一批顾客是否会购买某种产品。顾客购买产品我们标记为正例,没购买标记为负例。则:

  • TP 就是模型正确地预测顾客购买产品的数量。
  • FP 就是模型错误地预测顾客 购买产品的数量。
  • TN 就是模型正确地预测顾客不会购买产品的数量。
  • FN 就是模型错误地预测顾客不会购买产品的数量。

只有前面是 T(True)的才是预测正确的(预测值与 GT 一致);前面是 F(False)的表示预测错误(预测值与GT不一致)


Q1:如何正确“看”一个混淆矩阵?
A1:要正确“看”一个混淆矩阵,首先需要了解混淆矩阵中各个元素的含义。此外,可以通过计算准确率、精确率、召回率和 F1 分数等指标来评估模型的性能。通过观察混淆矩阵中各个元素的值以及计算相关指标,可以对模型的性能进行评估。

Q2:混淆矩阵有什么鲜明的特点?
A2:混淆矩阵的一个鲜明特点是,对角线上的元素表示模型预测正确的数量,非对角线上的元素表示模型预测错误的数量。例如,在一个二分类问题中,混淆矩阵的左上角元素表示真正例(TP),右下角元素表示真反例(TN),右上角元素表示伪正例(FP),左下角元素表示伪反例(FN)。

此外,混淆矩阵可以用来计算准确率、精确率、召回率和 F1 分数等指标,以评估模型的性能。它可以帮助我们快速了解模型在不同类别上的预测能力,并为我们提供改进模型性能的依据。

4.1.2 精确率(Precision)

在混淆矩阵中,精确率(Precision)是指模型预测为正例且预测正确的样本数量(TP)占模型预测为正样本(正例)的样本数量(TP+FP)的比例。它衡量了模型在预测正样本时的准确性

精确率的计算公式为

P r e c i s i o n = T P T P + F P \rm{Precision} = \frac{TP}{TP + FP} Precision=TP+FPTP

其中 TP 表示真正样本(True Positive)的数量,FP 表示伪正样本(False Positive)的数量。

举个例子:假设我们有一个分类模型用来预测一批顾客是否会购买某种产品。顾客购买产品我们标记为正样本,没购买标记为负例。如果模型预测有 100 个顾客会购买产品(TP + FP),其中有 80 个顾客真正购买了产品(TP),那么精确率就是 80 80 + 20 = 80 100 = 0.8 \frac{80}{80 + 20} = \frac{80}{100} = 0.8 80+2080=10080=0.8

“如果模型预测有 100 个顾客会购买产品(TP + FP),其中有 80 个顾客实际购买了产品(TP)”,这里的“其中”是 TP+FP 的其中,所以“其中有 80 个顾客实际购买了产品”是 TP 而不是 TP + FN。

TP + FN 是所有顾客中实际购买产品的人数

4.1.3 召回率(Recall)

在混淆矩阵中,召回率(Recall)是指模型预测为正样本且预测正确的样本数量(TP)占真实值为正样本的样本数量(TP + FN)的比例。它衡量了模型对正样本的识别能力

召回率的计算公式为

R e c a l l = T P T P + F N \rm Recall = \frac{TP}{TP + FN} Recall=TP+FNTP

其中 TP 表示真正样本(True Positive)的数量,FN 表示伪负样本(反例)(False Negative)的数量。

举个例子:假设我们有一个分类模型用来预测一批顾客是否会购买某种产品。顾客购买产品我们标记为正样本,没购买标记为负例。如果有 100 个顾客实际购买了产品(TP + FN),模型预测其中有 90 个顾客会购买产品(TP + FP),并且其中有 80 个顾客被正确预测为会购买产品(TP),那么召回率就是 80 80 + 20 = 80 100 = 0.8 \frac{80}{80 + 20} = \frac{80}{100} = 0.8 80+2080=10080=0.8

4.1.4 准确率(Accuracy)

在混淆矩阵中,准确率(Accuracy)是指模型预测正确的样本数量(TP + TN)占总样本数量(TP + FP + TN + FN)的比例。它衡量了模型的整体预测能力

准确率的计算公式为

A c c u r a c y = T P + T N T P + F P + T N + F N \rm Accuracy = \frac{TP + TN}{TP + FP + TN + FN} Accuracy=TP+FP+TN+FNTP+TN

其中 TP 表示真正样本(True Positive)的数量,TN 表示真负样本(True Negative)的数量,FP表 示伪正样本(False Positive)的数量,FN 表示伪负样本(False Negative)的数量。

举个例子:假设我们有一个分类模型用来预测一批顾客是否会购买某种产品。顾客购买产品我们标记为正样本,没购买标记为负例。如果总共有 1000 个顾客(TP + FP + TN + FN),如果模型预测有 700 个顾客购买产品且预测正确(TP),有 200 个顾客没有购买产品且预测正确(TN),那么准确率就是 700 + 200 1000 = 0.9 \frac{700 + 200}{1000} = 0.9 1000700+200=0.9

正例和负例通常指分类模型中的两个类别,也可以称为正样本和负样本。在一个二分类问题中,我们通常将其中一个类别标记为正例或正样本,另一个类别标记为负例或负样本。

4.1.5 F1-Score

在混淆矩阵中,F1-Score 是精确率(Precision)和召回率(Recall)的调和平均数。它衡量了模型在预测正例时的准确性和对正例的识别能力。

调和平均数:调和平均数(Harmonic Mean)是一种平均数的计算方法。它是一组数的倒数的算术平均数的倒数。对于一组正实数 x 1 , x 2 , . . . , x n x_1, x_2, ..., x_n x1,x2,...,xn,它们的调和平均数 H H H 定义为:

H = n 1 x 1 + 1 x 2 + . . . + 1 x n H = \frac{n}{\frac{1}{x_1} + \frac{1}{x_2} + ... + \frac{1}{x_n}} H=x11+x21+...+xn1n

调和平均数通常用于计算平均速率或平均比率。它对较小的数值有较大的影响,因此当数据集中有较小的数值时,调和平均数会比算术平均数小

例如:在混淆矩阵中,F1-Score 就是精确率(Precision)和召回率(Recall)的调和平均数。如果精确率和召回率中有一个值较小,那么F1-Score 也会较小

F1-Score 的计算公式为

F 1 = 2 ∗ P r e c i s i o n ∗ R e c a l l P r e c i s i o n + R e c a l l = 2 T P 2 T P + F N + F P \rm \begin{aligned} \mathrm{F1} & = 2 * \frac{\mathrm{Precision} * \mathrm{Recall}}{\mathrm{Precision} + \mathrm{Recall}}\\ & = \frac{2 \rm TP}{\rm 2TP + FN + FP} \end{aligned} F1=2Precision+RecallPrecisionRecall=2TP+FN+FP2TP

其中 Precision 表示精确率,Recall 表示召回率。

注意:

  • F1-Score 的取值范围为: [ 0 , 1 ] [0, 1] [0,1]
  • F1-Score 是精确率和召回率的调和平均数,它综合考虑了模型的精确率和召回率
    • F1-Score 越大,说明模型在精确率和召回率方面都表现得较好,因此可以认为模型更稳健

如果精确率和召回率中有一个值较小,那么F1-Score 也会较小

举个例子:假设我们有一个分类模型用来预测一批顾客是否会购买某种产品。顾客购买产品我们标记为正例(P),没购买标记为负例(N)。如果模型预测有 100 个顾客会购买产品(TP + FP),其中有 80 个顾客实际购买了产品(TP),那么精确率就是 80 100 = 0.8 \frac{80}{100} = 0.8 10080=0.8。如果有 100 个顾客实际购买了产品(TP + FN),模型预测其中有 90 个顾客真正购买产品(TP),那么召回率就是 90 100 = 0.9 \frac{90}{100} = 0.9 10090=0.9。根据公式,F1-Score 就是 2 ∗ 0.8 ∗ 0.9 0.8 + 0.9 ≈ 0.847 2 * \frac{0.8 * 0.9}{0.8 + 0.9} \approx 0.847 20.8+0.90.80.90.847

4.2 分类评估报告 API

sklearn.metrics.classification_report(y_true, y_pred, labels=[], target_names=None)
  • 作用:用于构建一个文本报告,显示主要的分类指标。

  • 参数

    • y_true:1 维数组或类似的数据结构,表示真实的目标值。
    • y_pred:1 维数组或类似的数据结构,表示分类器返回的估计目标值(预测值)。
    • labels:形状为 (n_labels,) 的数组,可选参数,表示要包含在报告中的标签索引列表。
    • target_names:形状为 (n_labels,) 的字符串列表,可选参数,表示与标签匹配的显示名称(顺序相同)。
    • sample_weight:形状为 (n_samples,) 的数组,可选参数,表示样本权重。
    • digits:整数,默认值为 2,表示格式化输出浮点值时保留的位数。
      • output_dict 为 True 时,此参数将被忽略,并且返回的值不会四舍五入。
    • output_dict:布尔值,默认值为 False。如果为 True,则以字典形式返回输出。
    • zero_division:可以是 “warn”01,默认值为 “warn”。设置存在零除法时返回的值。如果设置为 “warn”,则相当于 0,但也会引发警告。
  • 返回值:函数返回一个字符串或字典,表示每个类别的精度、召回率、F1 分数的文本摘要。如果 output_dict 为 True,则返回字典。


还拿之前的案例举例

from sklearn.metrics import classification_report

## 5.3 精确率、召回率、F1-Score评估指标
res = classification_report(y_true=y_test, y_pred=y_predict)
print(f"精确率、召回率、F1-Score评估指标为:\r\n{res}")

结果

精确率、召回率、F1-Score评估指标为:
              precision    recall  f1-score   support

           2       1.00      1.00      1.00        89
           4       1.00      1.00      1.00        48

    accuracy                           1.00       137
   macro avg       1.00      1.00      1.00       137
weighted avg       1.00      1.00      1.00       137

但是我们发现,索引列的 2 和 4 的可读性很差,不够直观,所以我们可以优化classification_report函数:

from sklearn.metrics import classification_report

## 5.3 精确率、召回率、F1-Score评估指标
res = classification_report(y_true=y_test, 
                            y_pred=y_predict, 
                            labels=(2, 4), 
                            target_names=["良性", "恶性"])
print(f"精确率、召回率、F1-Score评估指标为:\r\n{res}")

结果:

精确率、召回率、F1-Score评估指标为:
              precision    recall  f1-score   support

          良性       1.00      1.00      1.00        89
          恶性       1.00      1.00      1.00        48

    accuracy                           1.00       137
   macro avg       1.00      1.00      1.00       137
weighted avg       1.00      1.00      1.00       137

其中:

  • support 表示每个类别的样本数量。例如,在结果中,support 为 89 表示“良性”类别有 89 个样本,而 support 为 48 表示“恶性”类别有 48 个样本。
  • accuracy:准确率,表示分类器正确分类的样本数量占总样本数量的比例。它是一个整体性能指标,不区分不同的类别。
  • macro avg:宏平均,表示对每个类别的精度、召回率和 F1 分数分别求平均值。它没有考虑每个类别的样本数量,因此对于样本不均衡的数据集可能不太合适。
  • weighted avg:加权平均,表示对每个类别的精度、召回率和 F1 分数分别按照该类别的样本数量进行加权求平均。它考虑了每个类别的样本数量,因此对于样本不均衡的数据集更合适。

在这个例子中,结果显示:

  • 对于“良性”类别,精度、召回率和 F1 分数均为 1.00。
  • 对于“恶性”类别,精度、召回率和 F1 分数均为 1.00。
  • 整体准确率为 1.00。

这意味着分类器在测试集上的表现非常好,能够完美地区分“良性”和“恶性”两个类别。


假设这样一个情况:如果 99 个样本为恶性,1 个样本良性,不管怎样我(模型)全都预测为正样本(默认恶性为正样本),那么准确率就为 99%。但是这样的效果并不好,因为模型将所有样本都预测为“恶性”,即正样本。

在这里插入图片描述

因此:

对于“恶性”类别

  • 精度(精确率)(Precision)= 真正例 / (真正例 + 假正例)= 99 / (99 + 1) = 0.99
  • 召回率(Recall)= 真正例 / (真正例 + 假反例)= 99 / (99 + 0) = 1.00
  • F1 分数(F1-Score)= 2 * 精度 * 召回率 / (精度 + 召回率) = 2 * 0.99 * 1.00 / (0.99 + 1.00) = 0.995

对于“良性”类别

  • 由于模型没有预测出任何一个“良性”样本,因此精度、召回率和 F1 分数均为 0。

出现这样的原因是因为样本数量不均衡


样本不均衡问题是指:在分类问题中,不同类别的样本数量相差悬殊。这种情况下,分类器可能会倾向于预测数量较多的类别,从而导致对数量较少的类别的预测不准确

关于何时出现样本不均衡问题,没有一个明确的比例界限在实际应用中,如果不同类别的样本数量相差较大,就可能出现样本不均衡问题

注意当正样本和负样本的比例为 4:1 时就认为出现了样本数量不均衡问题

正负样本比例为 4:1 的情况可能会出现样本不均衡问题,但这并不是一个确定的界限(具体问题应该具体分析)。

问题:如何衡量样本不均衡下的评估?

5. ROC曲线

5.1 TPR与FPR

TPR(True Positive Rate)和 FPR(False Positive Rate)是二分类问题中常用的评估指标。

一、TPR(True Positive Rate,真·正例率)

TPR,也称为召回率(Recall),表示真正样本(TP)占所有正样本(TP + FN)的比例。它的定义为:

T P R = 真正样本 真正样本 + 假负样本 = T P T P + F N \begin{aligned} \rm TPR & = \frac{真正样本}{真正样本 + 假负样本} \\ & = \frac{\rm TP}{\rm TP + FN} \end{aligned} TPR=真正样本+假负样本真正样本=TP+FNTP

其中真正样本(TP)表示分类器正确预测为正样本的样本数量,假负样本(FN)表示分类器错误预测为负例的样本数量(但 GT 为正样本)。

所有真实类别为 1 的样本中,预测类别为 1 的比例

二、FPR(False Positive Rate,假·正例率)

FPR 表示假正样本(FP)占所有负例(FP + TN)的比例。它的定义为:

F P R = 假正样本 假正样本 + 真负样本 = F P F P + T N \begin{aligned} \rm FPR & = \frac{假正样本}{假正样本 + 真负样本} \\ & = \frac{\rm FP}{\rm FP + TN} \end{aligned} FPR=假正样本+真负样本假正样本=FP+TNFP

其中假正样本(FP)表示分类器错误预测为正样本的样本数量,真负样本(FP + TN)表示分类器正确预测为负例的样本数量。

所有真实类别为 0 的样本中,预测类别为 1 的比例

5.2 ROC曲线

ROC(Receiver Operating Characteristic,受试者工作特征曲线)曲线是二分类问题中常用的评估指标。它通过绘制 不同阈值下 TPR(True Positive Rate)和 FPR(False Positive Rate)的关系来评估分类器的性能。

在绘制 ROC 曲线时,横轴表示 FPR,纵轴表示 TPR。对于每个阈值,计算出对应的 TPR 和 FPR 值,并在图上绘制一个点。连接所有点,就得到了 ROC 曲线。

在这里插入图片描述

ROC 曲线越靠近左上角,表示分类器的性能越好。通常使用 AUC(Area Under Curve,曲线下面积)值来衡量 ROC 曲线下方的面积,AUC 值越大,表示分类器的性能越好。

  • 左上角:所有样本均预测正确(TPR = 1 且 FPR = 0)
  • 右下角:所有样本均预测错误(TPR = 0 且 FPR = 1)

当横纵坐标相等时,即 FPR = TPR(图中的红色虚线),表示分类器的预测结果与随机猜测相当(对于不论真实类别是 1 还是 0 的样本,分类器预测为 1 或 0 的概率是相等的)。在这种情况下,分类器无法区分正负样本,其性能不佳

像上面那个假设“不管怎样我(模型)全都预测为正样本”,这就是在随机猜测(特殊的随机猜测),此时模型的性能并不好。

Q:AUC 是什么?
A:AUC(Area Under Curve,曲线下面积)是 ROC 曲线下方的面积。它是二分类问题中常用的评估指标,用于衡量分类器的性能。AUC 值的范围为 0 到 1:AUC 值越大,表示分类器的性能越好。

  • 当 AUC 值为 0.5 时,表示分类器的性能与随机猜测相当😂;
  • 当 AUC 值为 1 时,表示分类器能够完美地区分正负样本👍。

5.3 AUC 指标

AUC 的概率意义是随机取一对正负样本,正样本得分大于负样本得分的概率

  • A U C \rm AUC AUC 的范围在 [ 0 , 1 ] [0,1] [0,1] 之间,并且越接近 1 1 1 越好,越接近 0.5 0.5 0.5 属于乱猜
    • A U C = 1 \rm AUC = 1 AUC=1,完美分类器。采用这个预测模型时,不管设定什么阈值都能得出完美预测。
      • 绝大多数预测的场合,不存在完美分类器。
    • 0.5 < A U C < 1 \rm 0.5 < AUC < 1 0.5<AUC<1,优于随机猜测。这个分类器(模型)妥善设定阈值的话,有一定的预测价值。

注意

  • ROC 是一条曲线
  • AUC 是一个范围在 [ 0 , 1 ] [0,1] [0,1] 之间浮点数,越接近 1 越好

Q:AUC 如果小于 0.5 说明什么?
A:AUC(Area Under Curve)值的范围为 0 到 1。当 AUC 值小于 0.5 时,表示分类器的性能不如随机猜测🤣。这意味着分类器无法区分正负样本,其预测结果与随机猜测相当。在这种情况下,可能需要重新训练模型或更换模型来提高分类器的性能

5.4 AUC 计算 API

from sklearn.metrics import roc_auc_score

sklearn.metrics.roc_auc_score(y_true, y_score)
  • 作用:用于计算 ROC(Receiver Operating Characteristic)曲线下方的面积,即 AUC(Area Under Curve)值。
  • 参数
    • y_true:每个样本的真实类别,因为 AUC 只能用来评价二分类,因此必须使用 0(负样本)或 1(正样本)进行标记(如果不是 0 或 1,需要进行转换)。
      • 形状为 (n_samples,)(n_samples, n_classes)
      • 对于二分类问题,形状为 (n_samples,)
    • y_score:预测得分,可以是正类的估计概率、置信值或者分类器方法的返回值。
      • 形状为 (n_samples,)(n_samples, n_classes)
      • 对于二分类问题,它对应于一个形状为 (n_samples,) 的数组。可以提供概率估计和非阈值决策值。
  • 返回值:函数返回一个浮点数,表示 AUC 值。

from sklearn.metrics import roc_auc_score

# 先将GT的(2, 4)转换为(0, 1)
y_test = np.where(y_test > 2, 1, 0)  # 如果满足条件则设置为1否则设置为0
print(f"转换后的测试集GT为:\r\n{y_test}")
"""
array([0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0,
       1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1,
       1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1,
       0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1,
       0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1,
       0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
       0, 1, 0, 0, 0])
"""

# 5.4 AUC指标计算
# auc应该在(0.5, 1)之间,越接近1越好(如果auc结果<0.5,模型需要重新训练或换一个模型)
auc = roc_auc_score(y_true=y_test, y_score=y_predict)
print(f"AUC值为:{auc}")  # AUC值为:1.0

注意

  • AUC 一般用于评价二分类问题
  • AUC 非常适合评价样本不平衡中的分类器性能

再注意:然而,AUC 并不总是适用于评估正负样本不平衡的分类器性能。在正负样本不平衡的情况下,ROC 曲线可能会给出一个乐观的结果,因为假阳性率(FPR)的计算不受正样本数量的影响。因此,即使模型将大部分负样本都错误地分类为正样本,ROC 曲线仍然可能显示较好的性能。

在正负样本不平衡的情况下,我们通常更关注模型对正样本的预测性能。此时,使用 PR 曲线来评估模型的性能可能更合适。PR 曲线下方的面积(AP)可以用来衡量模型在整个阈值范围内的平均性能


小结

  • 混淆矩阵【了解】
    • 真·正样本(TP)
    • 伪·负样本(FN)
    • 伪·正样本(FP)
    • 真·负样本(TN)
  • 精确率(Precision)与召回率(Recall)【知道】
    • 召回率(查的全不全): R e c a l l = T P T P + F N \rm Recall = \frac{TP}{TP + FN} Recall=TP+FNTP
    • 精确率(查的准不准): P r e c i s i o n = T P T P + F P \rm{Precision} = \frac{TP}{TP + FP} Precision=TP+FPTP
    • 准确率(对不对): A c c u r a c y = T P + T N T P + F P + T N + F N \rm Accuracy = \frac{TP + TN}{TP + FP + TN + FN} Accuracy=TP+FP+TN+FNTP+TN
    • F1-Score(反映模型的稳健性): F 1 = 2 ∗ P r e c i s i o n ∗ R e c a l l P r e c i s i o n + R e c a l l \rm F1 = 2 * \frac{Precision * Recall}{Precision + Recall} F1=2Precision+RecallPrecisionRecall
  • ROC曲线和AUC指标【知道】
    • ROC 曲线:通过 TPR 和 FPR 来进行图形绘制,绘制 ROC 曲线后会形成一个指标 AUC
    • AUC:正常的范围为: ( 0.5 , 1 ) (0.5, 1) (0.5,1)
      • 越接近 1,效果越好
      • 越接近 0,效果越差
      • 越接近 0.5,效果就是胡说(随机猜测)
      • 注意:
        • 这个指标主要用于评价不平衡的二分类问题
        • A U C < 0.5 \rm AUC < 0.5 AUC<0.5 时模型是不可用的,需要重新训练或换模型后再训练

6. ROC曲线的绘制

学习目标

  • 知道如何绘制 ROC 曲线

关于 ROC 曲线的绘制过程,通过如下例子进行说明。

假设有 6 次展示记录,有两次被点击了,得到一个展示序列 (1:1, 2:0, 3:1, 4:0, 5:0, 6:0)。前面的表示序号,后面的表示点击情况(1表示点击;0表示没有点击)。然后在这 6 次展示的时候都通过 model 算出了点击的概率序列。

下面看三种情况。

6.1 曲线绘制

6.1.1 情况1

如果模型推理的结果是(1:0.9, 2:0.7, 3:0.8, 4:0.6, 5:0.5, 6:0.4)

与原来的序列一起,得到序列(根据概率降序排序):

真实标签110000
原来对应的点132456
点击概率0.90.80.70.60.50.4

1 表示点击了,0 表示没有点击

绘制的步骤如下:

Step.1:把概率序列从高到低排序,得到顺序序列(1:0.9, 3:0.8, 2:0.7, 4:0.6, 5:0.5, 6:0.4)


Step.2:我们将概率最大的点1 作为正类。这意味着我们将点1 预测为点击,而其他点预测为未点击(尽管点 3 的真实标签是点击,但在这一步中我们仍然将其预测为未点击)。我们可以用混淆矩阵来表示预测结果和真实标签之间的关系:

点击未点击
预测为点击TP=1FP=0
预测为未点击FN=1TN=4

其中,TP(True Positive)表示真正例,即被正确预测为点击的点;FP(False Positive)表示假正例,即实际上未被点击但被预测为点击的点;FN(False Negative)表示假反例,即实际上被点击但被预测为未点击的点(这里说的是点3);TN(True Negative)表示真反例,即实际上未被点击且被正确预测为未点击的点。

根据混淆矩阵中的值,我们可以计算得到

T P R = T P T P + F P = 1 1 + 1 = 0.5 \rm TPR=\frac{TP}{TP + FP} = \frac{1}{1 + 1}=0.5 TPR=TP+FPTP=1+11=0.5

F P R = F P F P + T N = 0 0 + 4 = 0.0 \rm FPR=\frac{FP}{FP + TN}= \frac{0}{0+4} = 0.0 FPR=FP+TNFP=0+40=0.0


Step.3:我们将概率最大的两个点1点3 都作为正类(积累的过程)。这意味着我们将点1点3 预测为点击,而其他点预测为未点击。我们可以用混淆矩阵来表示预测结果和真实标签之间的关系:

点击未点击
预测为点击TP=2FP=0
预测为未点击FN=0TN=4

此时 点3倍归为TP了,所以FN=0

根据混淆矩阵中的值,我们可以计算得到

T P R = T P T P + F P = 2 2 + 0 = 1.0 \rm TPR=\frac{TP}{TP + FP} = \frac{2}{2 + 0}=1.0 TPR=TP+FPTP=2+02=1.0

F P R = F P F P + T N = 0 0 + 4 = 0.0 \rm FPR=\frac{FP}{FP + TN}= \frac{0}{0+4} = 0.0 FPR=FP+TNFP=0+40=0.0


Step.4:我们将概率最大的三个点1、3和2 都作为正类。这意味着我们将点 1、3 和 2 预测为点击,而其他点预测为未点击。我们可以用混淆矩阵来表示预测结果和真实标签之间的关系:

点击未点击
预测为点击TP=2FP=1
预测为未点击FN=0TN=3

根据混淆矩阵中的值,我们可以计算得到

T P R = T P T P + F P = 2 2 + 0 = 1.0 \rm TPR=\frac{TP}{TP + FP} = \frac{2}{2 + 0}=1.0 TPR=TP+FPTP=2+02=1.0

F P R = F P F P + T N = 1 1 + 3 = 0.25 \rm FPR=\frac{FP}{FP + TN}= \frac{1}{1+3} = 0.25 FPR=FP+TNFP=1+31=0.25

Q:点2 的 GT 是未点击,也可以当作正样本吗?
A:是的,在绘制 ROC 曲线时,我们会逐个将点作为正类,并根据模型预测的概率来计算 TPR 和 FPR。在第三步中,我们将概率最大的三个点 1、3 和 2 都作为正类,尽管点 2 的真实标签是未点击

这样做的目的是为了评估模型在不同阈值下的分类性能。当我们将点 2 作为正类时,相当于我们降低了分类阈值,使得更多的点被预测为点击。通过这种方式,我们可以得到一系列 TPR 和 FPR 值,并据此绘制 ROC 曲线。


Step.5:我们将概率最大的三个点1、3、2、4 都作为正类。这意味着我们将点1、3、2、4 预测为点击,而其他点预测为未点击。我们可以用混淆矩阵来表示预测结果和真实标签之间的关系:

点击未点击
预测为点击TP=2FP=2
预测为未点击FN=0TN=2

根据混淆矩阵中的值,我们可以计算得到

T P R = T P T P + F P = 2 2 + 0 = 1.0 \rm TPR=\frac{TP}{TP + FP} = \frac{2}{2 + 0}=1.0 TPR=TP+FPTP=2+02=1.0

F P R = F P F P + T N = 2 2 + 2 = 0.5 \rm FPR=\frac{FP}{FP + TN}= \frac{2}{2+2} = 0.5 FPR=FP+TNFP=2+22=0.5


Step.6:我们将概率最大的三个点1、3、2、4、5 都作为正类。这意味着我们将点1、3、2、4、5 预测为点击(P),而其他点预测为未点击(N)。我们可以用混淆矩阵来表示预测结果和真实标签之间的关系:

点击未点击
预测为点击TP=2FP=3
预测为未点击FN=0TN=1

根据混淆矩阵中的值,我们可以计算得到

T P R = T P T P + F P = 2 2 + 0 = 1.0 \rm TPR=\frac{TP}{TP + FP} = \frac{2}{2 + 0}=1.0 TPR=TP+FPTP=2+02=1.0

F P R = F P F P + T N = 3 3 + 1 = 0.75 \rm FPR=\frac{FP}{FP + TN}= \frac{3}{3+1} = 0.75 FPR=FP+TNFP=3+13=0.75


Step.7:我们将概率最大的三个点1、3、2、4、5、6(所有点) 都作为正类。这意味着我们将 所有点 预测为点击(P),而 没有 未点击(N)的点。我们可以用混淆矩阵来表示预测结果和真实标签之间的关系:

点击未点击
预测为点击TP=2FP=4
预测为未点击FN=0TN=0

根据混淆矩阵中的值,我们可以计算得到

T P R = T P T P + F P = 2 2 + 0 = 1.0 \rm TPR=\frac{TP}{TP + FP} = \frac{2}{2 + 0}=1.0 TPR=TP+FPTP=2+02=1.0

F P R = F P F P + T N = 4 4 + 0 = 1.0 \rm FPR=\frac{FP}{FP + TN}= \frac{4}{4+0} = 1.0 FPR=FP+TNFP=4+04=1.0


Step.8

此时,我们可以得到 6 对 TPR 和 FPR。然后把这 6 对数据组成 6 个点 (0, 0.5), (0, 1.0), (0.25, 1), (0.5, 1), (0.75, 1), (1.0, 1.0)。并在二维坐标系中能画出来。

import matplotlib.pyplot as plt
from pylab import mpl
# 设置中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False

points = [(0, 0.5), (0, 1.0), (0.25, 1), (0.5, 1), (0.75, 1), (1.0, 1.0)]
point_names = [f"点{i}" for i in [1, 3, 2, 4, 5, 6]]

plt.figure(dpi=300)

count = 0
for x, y in points:
    plt.scatter(x, y, label=point_names[count], zorder=2)
    count += 1

x = [point[0] for point in points]
y = [point[1] for point in points]
plt.plot(x, y, zorder=1)
    
plt.grid(alpha=0.3)
plt.title("不同点的TPR和FPR值")
plt.xlabel("FPR ∈ [0, 1]")
plt.ylabel("TPR ∈ [0, 1]")

for i in range(len(points)):
    if i == 0:
        plt.annotate(point_names[i], xy=points[i], xytext=(5, 0), textcoords='offset points')
    elif i == 1:
        plt.annotate(point_names[i], xy=points[i], xytext=(5, -12), textcoords='offset points')
    else:
        plt.annotate(point_names[i], xy=points[i], xytext=(-5, -12), textcoords='offset points')
plt.show()

在这里插入图片描述

上面画出来的图就是 ROC 曲线。因为ROC 曲线是一种用来评估分类模型性能的图形工具,它通过绘制真正例率(True Positive Rate,TPR)和假正例率(False Positive Rate,FPR)来展示模型的分类能力,所以虽然它看起来不是曲线,但确实是ROC曲线(数据特殊而已)。

6.1.2 情况2

如果模型推理的结果是(1:0.9, 2:0.8, 3:0.7, 4:0.6, 5:0.5, 6:0.4)

与原来的序列一起,得到序列(根据概率降序排序):

真实标签101000
原来对应的点123456
点击概率0.90.80.70.60.50.4

1表示点击了,0表示没有点击

绘制的步骤如下:

  1. 把概率序列从高到低排序,得到顺序(1:0.9, 2:0.8, 3:0.7, 4:0.6, 5:0.5, 6:0.4)
  2. 从概率最大开始取一个点作为正样本,取到点1,其他点都作为负样本。计算得到TPR=0.5,FPR=0.0
  3. 从概率大开始,再取点1和点2作为正样本,其他点都作为负样本。计算得到TPR = 0.5,FPR = 0.25
  4. 从概率大开始,再取点1、点2和点3作为正样本,其他点都作为负样本。计算得到TPR = 1.0,FPR = 0.25
  5. 以此类推,得到 6 对 TPR 和 FPR:(0.0, 0.5), (0.25, 0.5), (0.25, 1.0), (0.5, 1.0), (0.75, 1.0), (1.0, 1.0)
  6. 然后把这 6 对数据组成 6 个点在二维坐标系中画出来

在这里插入图片描述

上面画出来的图就是 ROC 曲线。因为ROC 曲线是一种用来评估分类模型性能的图形工具,它通过绘制真正例率(True Positive Rate,TPR)和假正例率(False Positive Rate,FPR)来展示模型的分类能力,所以虽然它看起来不是曲线,但确实是ROC曲线(数据特殊而已)。

6.1.3 情况3

如果模型推理的结果是(1:0.4, 2:0.6, 3:0.5, 4:0.7, 5:0.8, 6:0.9)

与原来的序列一起,得到序列(根据概率降序排序):

真实标签000011
原来对应的点654231
点击概率0.90.80.70.60.50.4

1表示点击了,0表示没有点击

绘制的步骤如下:

  1. 把概率序列从高到低排序,得到顺序(6:0.9, 5:0.8, 4:0.7, 2:0.6, 3:0.5, 1:0.4)
  2. 从概率最大开始取一个点作为正样本,取到点6,其他点都作为负样本。计算得到TPR = 0.0,FPR = 0.25
  3. 从概率大开始,再取点6和点5作为正样本,其他点都作为负样本。计算得到TPR = 0.0,FPR = 0.5
  4. 从概率大开始,再取点6、点5和点4作为正样本,其他点都作为负样本。计算得到TPR = 0.0,FPR = 0.75
  5. 以此类推,得到 6 对 TPR 和 FPR:(0.25, 0.0), (0.5, 0.0), (0.75, 0.0), (1.0, 0.0), (1.0, 0.5), (1.0, 1.0)
  6. 然后把这 6 对数据组成 6 个点在二维坐标系中画出来

在这里插入图片描述

从上图可以发现一个极端点(1, 0)。当模型取到(1, 0)时说明模型整体都分错了(此时FPR=1,TPR=0)。

6.2 意义解释

如上图的例子,总共 6 个点,2 个正样本,4 个负样本,取一个正样本和一个负样本的情况总共有8种。

上面的第一种情况,从上往下取,无论怎么取,正样本的概率总在负样本之上,所以分对(正确)的概率为1,AUC = 1。再看那个 ROC 曲线,它的积分是什么?也是 1,ROC 曲线的积分与 AUC 相等,此时分类器是一个非常理想、非常完美的分类器(这种效果的分类器一般在解决真实问题时是不存在的)。

上面第二种情况,如果取到了样本 2 和 3,那就分错了,其他情况都分对了。所以分对的概率是 0.875,AUC = 0.875。再看那个 ROC 曲线,它的积分也是 0.875,ROC 曲线的积分与 AUC 相等。

上面的第三种情况,无论怎么取,都是分错的,所以分对的概率是0,AUC = 0.0。再看ROC 曲线,它的积分也是 0.0,ROC 曲线的积分与 AUC 相等。

其实 AUC 的意思是 Area Under ROC Curve,就是 ROC 曲线的积分,也是 ROC 曲线的面积。绘制 ROC 曲线的意义很明显,不断地把可能分错的情况扣除掉,从概率最高往下取点,每有一个是负样本,就会导致分错排在它下面的所有正样本,所以要把它下面的正样本数扣除掉(1-TPR,剩下的正样本的比例)。总的 ROC 曲线就绘制出来了,AUC 就定了,分对的概率也能求出来了。

ROC 曲线是通过不断改变分类阈值来绘制的,每个阈值都会产生一组 TPR 和 FPR 值。AUC 越大,说明模型的分类性能越好。


小结

  • ROC 曲线的绘制【知道】
    1. 构建模型,把模型的预测概率值从大到小进行排序
    2. 从概率最大的点开始取值,一直进行 TPR 和 FPR 的计算
    3. 然后构建整体模型,得到结果
    4. 其实就是在求解ROC的积分(也就是 AUC,即 ROC 曲线的面积)

7. PR 曲线(Precision-Recall Curve)

7.1 定义

PR 曲线(Precision-Recall Curve,精确率-召回率曲线)是指精度(Precision)和召回率(Recall)之间的关系曲线,它是用于评估信息检索系统、文本分类系统等的性能的一种常用方法。在PR曲线中,横轴表示召回率,纵轴表示精度,曲线上每一点的坐标表示在该召回率下的精度值。

7.2 应用场景

PR 曲线适用于正负样本不平衡的情况,尤其是当正样本数量远小于负样本数量时,PR曲线能够更好地反映模型对正样本的预测性能。它可以用来评估信息检索系统、文本分类系统等的性能,帮助我们了解分类器实际的效果和作用,也能够以此进行模型的改进和优化。

7.3 PR 曲线计算方法

PR 曲线的绘制方法通俗来说就是:先设定一个较高的阈值 θ \theta θ,然后计算数据集的精确率和召回率,然后递减地降低阈值 θ \theta θ,并分别记录各自阈值对应下的精确率和召回率。每个阈值 θ \theta θ 对应于一个 ( P r e c i s i o n , R e c a l l ) 点 (\rm Precision, Recall)点 (Precision,Recall),把这些 连起来就是 PR 曲线。


Q:这里的阈值 θ \theta θ 是什么?怎么设定?
A:阈值 θ \theta θ 是用来将模型的预测概率转换为二元分类结果的一个参数。

  • 当模型预测一个样本属于正类的概率大于等于阈值 θ \theta θ 时,我们将该样本分类为正类(正样本);
  • 否则,我们将该样本分类为负类(负样本)。

在绘制 PR 曲线时,我们通常会从 1 开始递减地降低阈值 θ \theta θ,并分别记录各自阈值对应下的精确率和召回率。这意味着我们会使用一系列不同的阈值来绘制 PR 曲线。这些阈值通常取决于模型预测概率的分布情况。

7.4 PR 曲线 API

sklearn 库中,我们可以使用 precision_recall_curve 函数来计算精确率和召回率,然后使用 matplotlib 库来绘制 PR 曲线。

sklearn.metrics.precision_recall_curve(y_true, 
									   probas_pred,
									   pos_label=None, 
									   sample_weight=None)
  • 作用precision_recall_curvesklearn.metrics 模块中的一个函数,它用于计算二分类模型的精确率和召回率。
  • 参数说明
    • y_true: 真实标签,形状为 (n_samples,) 的一维数组。
    • probas_pred: 预测概率,形状为 (n_samples,) 的一维数组。
    • pos_label: 可选参数,指定正类标签。默认值为 1。
    • sample_weight: 可选参数,样本权重。默认值为 None。
  • 返回值
    • precision: 精确率,形状为 (n_thresholds + 1,) 的一维数组。
    • recall: 召回率,形状为 (n_thresholds + 1,) 的一维数组。
    • thresholds: 阈值,形状为 (n_thresholds,) 的一维数组。

下面是一个简单的示例

from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

# 生成数据集
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.1, 0.9], random_state=1)

# 训练模型
model = LogisticRegression()
model.fit(X, y)

# 预测概率
yhat = model.predict_proba(X)
probs = yhat[:, 1]  # 提取模型预测每个样本属于正类的概率

# 计算精确率和召回率
precision, recall, _ = precision_recall_curve(y, probs)

# 绘制 PR 曲线
plt.plot(recall, precision, marker='.')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.show()

在这里插入图片描述


Q:这段代码中,为什么要 probs = yhat[:, 1]
A:在这段代码中,probs = yhat[:, 1] 这一行的作用是提取模型预测每个样本属于正类的概率。

LogisticRegression 模型的 predict_proba 方法返回一个形状为 (n_samples, n_classes) 的数组,其中第 i 行第 j 列的元素表示第 i 个样本属于第 j 类的概率。在二分类问题中,n_classes=2,因此 predict_proba 方法返回一个形状为 (n_samples, 2) 的数组。

在这段代码中,我们假设正类的标签为 1,负类的标签为 0。因此,yhat[:, 0] 表示模型预测每个样本属于负类的概率,而 yhat[:, 1] 表示模型预测每个样本属于正类的概率。所以,我们使用 probs = yhat[:, 1] 来提取模型预测每个样本属于正类的概率。


我们也可以同时将阈值 θ \theta θ 画出来:

from sklearn.metrics import precision_recall_curve
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

# 生成数据集
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.1, 0.9], random_state=1)

# 训练模型
model = LogisticRegression()
model.fit(X, y)

# 预测概率
yhat = model.predict_proba(X)
probs = yhat[:, 1]

# 计算精确率和召回率
precision, recall, thresholds = precision_recall_curve(y, probs)

# 绘制 PR 曲线和阈值曲线
fig, ax1 = plt.subplots(dpi=300)
ax1.plot(recall, precision, marker='.')
ax1.set_xlabel('Recall')
ax1.set_ylabel('Precision', color='b')
ax2 = ax1.twinx()
ax2.plot(recall[:-1], thresholds, 'r')
ax2.set_ylabel('Threshold', color='r')
plt.show()

在这里插入图片描述

需要注意的是:在上面的代码中,我们使用 recall[:-1] 是因为 precision_recall_curve 函数返回的阈值数组的长度比精确率和召回率数组的长度少 1。

precision_recall_curve 函数返回的阈值数组表示在不同阈值下,模型将样本分类为正类的概率。这些阈值是根据模型预测概率的分布情况确定的。由于阈值数组中不包括最大阈值(即 1),所以阈值数组的长度比精确率和召回率数组的长度少 1

为了在同一个图中绘制 PR 曲线和阈值曲线,我们需要保证两条曲线的横坐标具有相同的长度。因此,我们使用 recall[:-1] 来去掉召回率数组中的最后一个元素,使得召回率数组的长度与阈值数组相同。

7.5 如何观察 PR 曲线

PR 曲线是精确率(Precision)和召回率(Recall)之间关系的图形表示。在 PR 曲线中,横轴表示召回率,纵轴表示精确率。PR 曲线越靠近右上角,模型的性能就越好(AUC 曲线是越靠近左上角模型性能越好)。

通常,我们可以通过以下几种方法来根据 PR 曲线判断几个模型的效果是好还是坏:

  • 如果一条曲线完全“包住”另一条曲线,则前者性能优于另一条曲线。
  • 如果两条曲线发生了交叉,则可以使用平衡点(Break-Even Point)作为衡量指标。平衡点是查准率等于查全率时的取值,值越大代表效果越优。
  • 另一种常用的方法是使用 F-1 度量,F-1 分数越高越好。

7.6 PR 曲线与 ROC 曲线比较

7.6.1 优缺点

PR 曲线和 ROC 曲线都是用于评估二分类模型性能的图形工具。它们之间的主要区别在于横纵坐标的选择

  • PR 曲线以召回率为横轴,精确率为纵轴;
  • 而 ROC 曲线以假阳性率(FPR)为横轴,真阳性率(即召回率 TPR)为纵轴。

7.6.2 使用场景

PR 曲线和 ROC 曲线各有优缺点,适用于不同的使用场景。

  • PR 曲线适用于正负样本不平衡的情况,尤其是当正样本数量远小于负样本数量时,PR曲线能够更好地反映模型对正样本的预测性能。这是因为在正负样本不平衡的情况下,精确率(Precision)能够更好地反映模型对正样本的预测性能。

  • 而 ROC 曲线正样本和负样本一视同仁,在类别不平衡时 ROC 曲线往往会给出一个乐观的结果。但是,ROC 曲线在测试集中的正负样本分布变化时能够保持不变。这也是 ROC 曲线一个很好的特性。

ROC曲线在类别不平衡时往往会给出一个乐观的结果,因为假阳性率(FPR)的计算不受正样本数量的影响。因此,即使模型将大部分负样本都错误地分类为正样本,ROC曲线仍然可能显示较好的性能。

另外,ROC曲线在测试集中的正负样本分布变化时能够保持不变。这是因为真阳性率(TPR)和假阳性率(FPR)都是基于比例计算的,而不是基于绝对数量。因此,当测试集中的正负样本分布发生变化时,ROC曲线仍然能够保持不变

总之,在正负样本不平衡的情况下,我们通常更关注模型对正样本的预测性能此时,使用 PR 曲线来评估模型的性能可能更合适

  • 正负样本数量均衡:ROC 曲线
  • 正负样本数量不均衡:PR 曲线

Q:类别不平衡是正负样本数量差异大吗?
A:是的,类别不平衡指的是在分类问题中,不同类别的样本数量差异较大。例如,在二分类问题中,如果正样本数量远小于负样本数量,或者负样本数量远小于正样本数量,那么就称这个问题存在类别不平衡。

类别不平衡问题在现实世界中非常常见,例如信用卡欺诈检测、癌症诊断等。在这些问题中,正样本(欺诈交易、癌症患者)的数量通常远小于负样本(正常交易、健康人)的数量。

类别不平衡问题会给模型的训练和评估带来一些挑战。例如,在类别不平衡问题中,简单地使用准确率作为评估指标可能会产生误导。因此,在处理类别不平衡问题时,我们需要采取一些特殊的方法来训练和评估模型。

7.7 PR 曲线与 ROC 曲线的图像比较

from sklearn.metrics import precision_recall_curve, roc_curve
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

# 生成数据集
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.1, 0.9], random_state=1)

# 训练模型
model = LogisticRegression()
model.fit(X, y)

# 预测概率
yhat = model.predict_proba(X)
probs = yhat[:, 1]

# 计算精确率和召回率
precision, recall, _ = precision_recall_curve(y, probs)
fpr, tpr, _ = roc_curve(y, probs)

# 绘制 PR 曲线和 ROC 曲线
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 8), dpi=300)
ax1.plot(recall, precision, marker='.')
ax1.set_xlabel('Recall')
ax1.set_ylabel('Precision')
ax1.set_title('PR Curve')
ax2.plot(fpr, tpr, marker='.')
ax2.set_xlabel('False Positive Rate')
ax2.set_ylabel('True Positive Rate')
ax2.set_title('ROC Curve')
plt.show()

在这里插入图片描述

在上面的示例中,我们绘制了 PR 曲线和 ROC 曲线。PR 曲线显示了模型在不同阈值下的精确率 Precision 和召回率 Recall,ROC 曲线显示了模型在不同阈值下的真阳性率 TPR 和假阳性率 FPR。

  • 从 PR 曲线中,我们可以看到,当召回率 Recall 增加时,精确率会下降。这是因为当我们降低阈值 θ \theta θ 以检测更多的正样本时,我们也会检测到更多的负样本,从而降低精确率。

  • 从 ROC 曲线中,我们可以看到,当假阳性率 FPR 增加时,真阳性率 TPR 也会增加。这是因为当我们降低阈值 θ \theta θ 以检测更多的正样本时,我们也会检测到更多的负样本,从而增加假阳性率。

总之,PR 曲线和 ROC 曲线都能够帮助我们了解模型在不同阈值下的性能。通过观察这些曲线,我们可以选择一个合适的阈值来平衡精确率 Precision 和召回率 Recall 或真阳性率 TPR 和假阳性率 FPR


同样我们也可以同时将阈值 θ \theta θ 画出来:

from sklearn.metrics import precision_recall_curve, roc_curve
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

# 生成数据集
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.1, 0.9], random_state=1)

# 训练模型
model = LogisticRegression()
model.fit(X, y)

# 预测概率
yhat = model.predict_proba(X)
probs = yhat[:, 1]

# 计算精确率和召回率
precision, recall, thresholds = precision_recall_curve(y, probs)
fpr, tpr, thresholds2 = roc_curve(y, probs)

# 绘制 PR 曲线和 ROC 曲线
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 8), dpi=300)
ax1.plot(recall[:-1], thresholds, 'r')
ax1.set_xlabel('Recall')
ax1.set_ylabel('Threshold', color='r')
ax11 = ax1.twinx()
ax11.plot(recall[:-1], precision[:-1], marker='.')
ax11.set_ylabel('Precision', color='b')
ax1.set_title('PR Curve')
ax2.plot(fpr, thresholds2, 'r')
ax2.set_xlabel('False Positive Rate')
ax2.set_ylabel('Threshold', color='r')
ax22 = ax2.twinx()
ax22.plot(fpr, tpr, marker='.')
ax22.set_ylabel('True Positive Rate', color='b')
ax2.set_title('ROC Curve')
plt.show()

在这里插入图片描述

7.8 AP(Average Precision,平均精度)

7.8.1 AP 的定义

AP(Average Precision)是 PR 曲线下方的面积,它可以用来衡量模型在整个阈值范围内的平均性能。AP 的取值范围为 [ 0 , 1 ] [0, 1] [0,1]

  • AP 越接近 1,表示模型的性能越好;
  • AP 越接近 0,表示模型的性能越差。

7.8.2 AP 的计算方式

AP 的计算方法有很多种,其中一种常用的方法是使用 11 点插值法。

  1. 收点将召回率 Recall 划分为 11 个等级(0.0, 0.1, …, 1.0)
  2. 然后计算每个等级下精确率 Precision 的最大值
  3. 最后取这 11 个最大值的平均数作为 AP。

Q:这里的 “等级” 是 阈值 θ \theta θ 吗?
A:不是的。在计算 AP 时使用的 11 个等级指的是召回率的 11 个等级,而不是阈值 θ \theta θ 的 11 个等级。

例如:假设我们已经计算出了 PR 曲线,现在我们想要计算召回率为 0.5 等级下的精确率。我们可以找到所有召回率大于等于 0.5 的点,然后从这些点中选择精确率最大的一个点,该点的精确率就是召回率为 0.5 等级下的精确率。

7.8.3 AP 的 API

sklearn.metrics.average_precision_score(y_true, 
										y_score,
										average='macro', 
										pos_label=1, 
										sample_weight=None)
  • 作用average_precision_scoresklearn.metrics 模块中的一个函数,它用于计算 PR 曲线下方的面积(AP)。该函数会根据您提供的真实标签和预测概率自动计算 PR 曲线,并返回 PR 曲线下方的面积。

  • 主要参数

    • y_true:形状为 (n_samples,) 的数组,表示每个样本的真实标签。在二分类问题中,标签通常为 0 或 1。
    • y_score:形状为 (n_samples,) 的数组,表示模型预测每个样本属于正类的概率。
    • average:字符串,指定如何计算 AP。可选值为 'micro''macro''samples'。默认值为 'macro'
    • pos_label:标量,指定正类的标签。默认值为 1。
  • 返回值:一个浮点数,表示 PR 曲线下方的面积(AP)。AP 的取值范围为 [ 0 , 1 ] [0, 1] [0,1]。AP 越接近 1,表示模型的性能越好;AP 越接近 0,表示模型的性能越差。

下面是一个简单的示例,演示如何使用 average_precision_score 函数来计算 AP:

from sklearn.metrics import average_precision_score
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

# 生成数据集
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.1, 0.9], random_state=1)

# 训练模型
model = LogisticRegression()
model.fit(X, y)

# 预测概率
y_score = model.predict_proba(X)[:, 1]

# 计算 AP
ap = average_precision_score(y, y_score)
print(f'Average Precision(AP): {ap:.3f}')
Average Precision(AP): 0.986

7.9 mAP(mean Average Precision,平均精度均值)

首先我们需要明白一点:AP(Average Precision)和 mAP(mean Average Precision)不是一样的。

  • AP 表示 PR 曲线下方的面积,它可以用来衡量模型在整个阈值范围内的平均性能。
  • mAP 是多个 AP 值的平均值,它通常用于多类分类问题或目标检测任务中。

在多类分类问题中,我们可以为每个类别单独计算一个 AP 值,然后取这些 AP 值的平均数作为 mAP

  • mAP 越接近 1,表示模型的性能越好;
  • mAP 越接近 0,表示模型的性能越差。

在目标检测任务中,mAP 通常用于衡量模型在检测多个目标类别时的平均性能。我们可以为每个目标类别单独计算一个 AP 值,然后取这些 AP 值的平均数作为 mAP。

  • mAP 越接近 1,表示模型的性能越好;
  • mAP 越接近 0,表示模型的性能越差。

下面是一个简单的示例,演示如何在多类分类问题中计算 mAP:

from sklearn.multiclass import OneVsRestClassifier
from sklearn.metrics import average_precision_score
from sklearn.preprocessing import label_binarize
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression

# 生成数据集
n_classes = 3
X, y = make_classification(n_samples=1000, n_classes=n_classes, n_informative=3, random_state=1)

# print(X.shape)  # (1000, 20)
# print(y)  # [0 1 1 2 ... 0 2 0 2]
# print(y.shape)  # (1000,)

# 将标签二值化
y_bin = label_binarize(y, classes=range(n_classes))
# print(y_bin)
"""
[[1 0 0]
 [0 1 0]
 [0 1 0]
 ...
 [0 0 1]
 [1 0 0]
 [0 0 1]]
"""
# print(y_bin.shape)  # (1000, 3)

# 训练模型
model = OneVsRestClassifier(LogisticRegression())
model.fit(X, y)  # 这里传入的是 y 而非 y_bin

# 预测概率
y_score = model.predict_proba(X)
# print(y_score.shape)  # (1000, 3)

# 计算每个类别的 AP
ap_lst = []
for i in range(n_classes):
    ap_current = average_precision_score(y_true=y_bin[:, i], y_score=y_score[:, i])
    print(f"第[{i}]个类别的AP为:{ap_current}")
    ap_lst.append(ap_current)

print(f"AP列表为:{ap_lst}")
# 计算 mAP
map = sum(ap_lst) / n_classes
print(f'mean Average Precision(mAP): {map:.4f}')
第[0]个类别的AP为:0.8225878493045536
第[1]个类别的AP为:0.760219087683751
第[2]个类别的AP为:0.8268135268525405

AP列表为:[0.8225878493045536, 0.760219087683751, 0.8268135268525405]

mean Average Precision(mAP): 0.8032

在这个示例中,我们首先生成了一个具有 3 个类别的数据集,然后使用 LogisticRegression 模型进行训练。在预测阶段,我们使用 predict_proba 方法计算模型预测每个样本属于每个类别的概率。

接下来,我们使用 average_precision_score 函数为每个类别单独计算一个 AP 值。最后,我们取这些 AP 值的平均数作为 mAP。

OneVsRestClassifier 是 scikit-learn 库中的一个类,它实现了一种称为“一对多”(One-vs-the-rest,OvR)的多分类策略。这种策略的基本思想是为每个类别都训练一个分类器,每个分类器都将该类别与所有其他类别区分开来。除了计算效率(只需要训练 n_classes 个分类器)之外,这种方法的一个优点是它具有可解释性。由于每个类别都只由一个分类器表示,因此可以通过检查相应的分类器来了解该类别的信息。这是最常用的多分类策略,也是一个很好的默认选择。

此外,OneVsRestClassifier 还可以用于多标签分类。要使用此功能,需要在调用 .fit 时为目标 y 提供一个指示矩阵。换句话说,目标标签应格式化为一个二维二进制(0/1)矩阵,其中 [i, j] == 1 表示样本 i 中存在标签 j

Q:mAP 是否可以绘图显示呢?
A:是的,mAP 可以绘制成图形来显示。通常,我们可以使用绘图库(如 matplotlib)来绘制 mAP 随着模型训练轮数的变化情况。这样可以直观地观察模型的训练过程,并帮助我们调整模型参数。

8. 【补充内容】分类中解决类别不平衡问题

前面我们已经初步认识了,什么是类别不平衡问题。其实,在现实环境中,采集的数据(建模样本)往往是比例失衡的。比如网贷数据,逾期人数的比例是极低的(千分之几的比例)。

面对类别不均衡问题,有很多方法,这些方法大致可以分为两类:

  1. 基于数据层面的方法
  2. 基于算法层面的方法

一、基于数据层面的方法

基于数据层面的方法主要包括过采样和欠采样。过采样方法通过增加少数类样本的数量来平衡数据集,例如随机过采样和 SMOTE 算法。欠采样方法通过减少多数类样本的数量来平衡数据集,例如随机欠采样和 Tomek Link 等。

二、基于算法层面的方法
基于算法层面的方法主要包括代价敏感学习和集成学习。代价敏感学习通过调整分类器的损失函数,使其对少数类样本的错误分类具有更高的惩罚。集成学习通过结合多个分类器的预测结果来提高分类性能,例如 Bagging 和 Boosting 等。

这些方法各有优缺点,可以根据具体问题选择合适的方法来解决类别不均衡问题。

接下来我们主要面向基于数据层面的方法。

8.1 类别不平衡数据集基本介绍

在这一节中,当遇到数据类别不平衡的时候,我们该如何处理。在Python中,有 Imblearn 包,它就是为处理数据比例失衡而开发的。

安装Imblearn包

pip install imbalanced-learn

第三方包链接:https://pypi.org/project/imbalanced-learn/


Imblearn 的使用如下

一、创造数据集

from sklearn.datasets import make_classification
import matplotlib.pyplot as plt


# 使用make_classification生成样本数据
X, y = make_classification(n_samples=5000,  # 样本数量
                           n_features=2,  # 特征个数 = n_informative() + n_redundant + n_repeated
                           n_informative=2,  # 多信息的特征个数
                           n_redundant=0,  # 冗余信息,informative特征的随机线性组合
                           n_repeated=0,  # 重复信息,随机提取n_informative和n_redundant特征
                           n_classes=3,  # 分类类别
                           n_clusters_per_class=1,  # 某一个类别是由几个cluster构成的
                           weights=[0.01, 0.05, 0.94],  # 列表类型,权重比
                           random_state=0)
print(X.shape)  # (5000, 2)
print(y.shape)  # (5000,)

注意

  • 特征个数 = 多信息特征个数 + 冗余信息 + 重复信息
  • weights 参数用来控制不同 class(类别)的数量权重,如上设置为weights=[0.01, 0.05, 0.94],说明 3 个类别的样本个数是不均衡的!

二、查看各个标签的样本

# 查看各个标签的样本量
from collections import Counter
Counter(y)  # Counter({2: 4674, 1: 262, 0: 64})

Countercollections 库中的一个函数,它可以用来统计一个 python 列表、字符串、元组等可迭代对象中每个元素出现的次数,并返回一个字典。

例如,你可以使用 Counter 来统计列表中的词频。

三、数据集可视化

# 数据集可视化
plt.figure(dpi=300)
plt.scatter(X[:, 0], X[:, 1], c=y)
plt.show()

print(X.shape) # (5000, 2)
print(y.shape) # (5000,)

在这里插入图片描述

可以看出样本的三个标签中,1,2的样本量极少,样本失衡。

下面使用imblearn进行过采样。

为了解决样本数量不均衡的问题,我们需要基于以上数据,进行相应的处理。关于类别不平衡的问题,主要有两种处理方式:

  1. 过采样方法(Oversampling,OS)增加数量较少那一类样本的数量,使得正负样本比例均衡
  2. 欠采样方法(Undersampling,US)减少数量较多那一类样本的数量,使得正负样本比例均衡

8.2 解决类别不平衡数据方法介绍

8.2.1 过采样方法(Oversampling)

8.2.1.1 什么是过采样方法

对训练集里的少数类进行“过采样”(Oversampling),即增加一些少数类样本使得正、反例数目接近,然后再进行学习。

8.2.1.2 随机过采样方法(Random Oversampling)

随机过采样是在少数类 S min ⁡ S_{\min} Smin 中随机选择一些样本,然后通过复制所选择的样本生成样本集 E E E,将它们添加到 S min ⁡ S_{\min} Smin 中来扩大原始数据集,从而得到新的少数类集合 S n e w − m i n S_{\rm new-min} Snewmin。新的数据集 S n e w − m i n = S min ⁡ + E S_{\rm new-min} = S_{\min} + E Snewmin=Smin+E

通过代码实现随机过采样方法

from imblearn.over_sampling import RandomOverSampler


# 使用imblearn进行随机过采样
ros = RandomOverSampler(random_state=0)
X_resampled, y_resampled = ros.fit_resample(X, y)

# 查看结果
Counter(y_resampled)  # Counter({2: 4674, 1: 4674, 0: 4674})

# 数据集可视化
plt.figure(dpi=300)
plt.scatter(X_resampled[:, 0], X_resampled[:, 1], c=y_resampled)
plt.show()

在这里插入图片描述

和之前未经过采样(原始数据)的对比

在这里插入图片描述


随机过采样方法的优点

  • 简单易实现,能够快速平衡数据集

随机过采样方法的缺点

  • 由于它是通过复制少数类样本来增加少数类样本的数量,因此可能会导致过拟合问题
  • 如果数据集中存在噪声或异常值,随机过采样可能会放大这些样本的影响,从而影响模型的预测性能

因此,在使用随机过采样时,需要谨慎选择参数,并结合其他方法来解决类别不均衡问题。

为了解决随机过采样中造成模型过拟合问题,又能保证实现数据集均衡的目的,出现了过采样法代表性的算法 SMOTE 算法。

8.2.1.3 过采样代表性算法:SMOTE

SMOTE 全称是 Synthetic Minority Oversampling,即合成少数类过采样技术。

Synthetic:英[sɪnˈθetɪk]美[sɪnˈθetɪk]
adj. (人工)合成的; 人造的; 综合(型)的;
n. 合成物; 合成纤维(织物); 合成剂;

Minority:英[maɪˈnɒrəti]美[maɪˈnɔːrəti]
n. 少数民族(成员),少数群体; 少数(人); 少数派,少数党; 未成年,未到法定年龄,未成年身份;
adj. 少数人的,构成少数的; 少数党的,少数派的; 少数民族的;

SMOTE 算法是对随机过采样方法的一个改进算法,由于随机过采样方法是直接对少数类进行重采用,会使训练集中有很多重复的样本,容易造成产生的模型过拟合问题。而 SMOTE 算法的基本思想是对每个少数类样本 x i x_i xi,从它的最近邻中随机选择一个样本 x ^ i \hat{x}_i x^i x ^ i \hat{x}_i x^i 是少数类中的一个样本),然后在 x i x_i xi x ^ i \hat{x}_i x^i 之间的连线上随机选择一点作为新合成的少数类样本

SMOTE 算法合成新少数类样本的算法描述如下:

  1. 对于少数类中的每一个样本 x i x_i xi,以欧氏距离为标准计算它到少数类样本集 S min ⁡ S_{\min} Smin 中所有样本的距离,得到其 k k k 近邻。
  2. 根据样本不平衡比例,设置一个采样比例,以确定采样倍率 N N N,对于每一个少数类样本 x i x_i xi,从其 k k k 近邻中随机选择若干个样本,假设选择是 x ^ i \hat{x}_i x^i
  3. 对于每一个随机选出来的近邻 x ^ i \hat{x}_i x^i,分别与按照如下公式构建新的样本。

x n e w = x i + r a n d ( 0 , 1 ) × ( x ^ i − x i ) x_{\mathrm{new}} = x_i + \mathrm{rand}(0,1) \times (\hat{x}_i - x_i) xnew=xi+rand(0,1)×(x^ixi)


我们用图文表达的方式,再来描述一下 SMOTE 算法。

一、先随机选定一个少数类样本 x i x_i xi

在这里插入图片描述

二、找出这个少数类样本的 k k k 个近邻(假设 k = 5 k=5 k=5),5个近邻已经被圈出

在这里插入图片描述

三、随机从这 k k k 个近邻中选出一个样本 x ^ i \hat{x}_i x^i(用绿色圈出来了)

在这里插入图片描述

四、在少数类样本 x i x_i xi​ 和被选中的这个近邻样本 x ^ i \hat{x}_i x^i​ 之间的连线上,随机找一点。这个点就是人工合成的新的样本点(绿色正号标出)

在这里插入图片描述

SMOTE 算法摒弃了随机过采样复制样本的做法,可以防止随机过采样中容易过拟合的问题,实践证明此方法可以提高分类器的性能


SMOTE 算法的优点

  • 相比于简单的随机过采样方法,SMOTE 算法能够生成更多样化的新样本点,从而避免过拟合问题。
  • SMOTE 算法能够有效地平衡数据集,提高分类模型对少数类样本的预测性能。
  • SMOTE 算法简单易实现,且计算效率较高。

SMOTE 算法的缺点:SMOTE 过采样方法虽然能够有效地解决类别不均衡问题,但也存在一些缺点。

  • SMOTE 算法通过在少数类样本之间插值来合成新的样本点,这可能会导致过拟合
  • SMOTE 算法假设少数类样本是连续分布的,但在实际应用中,这个假设并不总是成立
  • SMOTE 算法只考虑了少数类样本之间的关系,而忽略了多数类样本对分类边界的影响

因此,在使用 SMOTE 算法时,需要谨慎选择参数,并结合其他方法来解决类别不均衡问题。


Q1:在第四步中,随机找一点,这个点是已经存在的点还是之前没有,SMOTE自己创造的点?
A1:在第四步中,SMOTE 算法会在少数类样本 x i x_i xi 和被选中的近邻样本 x ^ i \hat{x}_i x^i 之间的连线上随机找一点。这个点是人工合成的新的样本点,它之前并不存在。这个新的样本点是通过在 x i x_i xi x ^ i \hat{x}_i x^i 之间插值得到的。


Q2:SMOTE 算法是通过什么样的插值方法得到人工合成的新的样本点的呢?
A2:SMOTE 算法通过线性插值来合成新的样本点。具体来说,对于每个特征,新样本点的值是在少数类样本 x i x_i xi 和被选中的近邻样本 x ^ i \hat{x}_i x^i 之间的连线上随机选取的。例如,如果 x i x_i xi 的某个特征值为 a a a x ^ i \hat{x}_i x^i 的相应特征值为 b b b,则新样本点的该特征值为 a + ( b − a ) ∗ λ a + (b-a) * \lambda a+(ba)λ,其中 λ \lambda λ 是一个在 [ 0 , 1 ] [0,1] [0,1] 之间随机选取的数。


SMOTE 算法的代码实现

from imblearn.over_sampling import SMOTE

# SMOTE算法实现过采样
X_resampled, y_resampled = SMOTE().fit_resample(X, y)
Counter(y_resampled)  # Counter({2: 4674, 1: 4674, 0: 4674})

# 数据集可视化
plt.figure(dpi=300)
plt.scatter(X_resampled[:, 0], X_resampled[:, 1], c=y_resampled)
plt.show()

在这里插入图片描述

和原始数据的对比

在这里插入图片描述

三者对比

在这里插入图片描述

8.2.2 欠采样方法(Undersampling)

8.2.2.1 什么是欠采样方法

直接对训练集中多数类样本进行“欠采样”(Undersampling),即去除一些多数类中的样本使得正例、反例数目接近,然后再进行学习。

欠采样方法一般是不常用的,因为数据的收集是很难的,我们一般不会丢弃珍贵的数据 😢

8.2.2.2 随机欠采样方法(Random Undersampling)

随机欠采样顾名思义,即从多数类 S m a j S_{\mathrm{maj}} Smaj 中随机选择一些样样本组成样本集 E E E。然后将样本集 E E E S m a j S_{\mathrm{maj}} Smaj 中移除。新的数据集 S n e w − m a j = S m a j − E S_{\mathrm{new - maj}} = S_{\mathrm{maj}} - E Snewmaj=SmajE

maj:majority英[məˈdʒɒrəti]美[məˈdʒɔːrəti]
n. 大部分,大多数,过半数,大半; 〈英〉(获胜的)票数,(选举中的)多数票(指超出其余各方票数总和的票数); 多数党,多数派; 成年,(成年的)法定年龄,成年身份; 〈军〉少校级别(或职位、军衔);
adj. 多数(人)的,过半数的,大多数的; 多数党的,多数派的;

随机欠采样的代码实现

from imblearn.under_sampling import RandomUnderSampler


# 随机欠采样
rus = RandomUnderSampler(random_state=0)
X_sampled, y_sampled = rus.fit_resample(X, y)
Counter(y_sampled)  # Counter({0: 64, 1: 64, 2: 64})

# 数据集可视化
plt.figure(dpi=300)
plt.scatter(X_sampled[:, 0], X_sampled[:, 1], c=y_sampled)
plt.show()

在这里插入图片描述

和原始数据的对比

在这里插入图片描述

随机欠采样方法(Random Undersampling)通过改变多数类样本比例,以达到修改样本分布的目的,从而使样本分布较为均衡。但是这也存在一些问题:

  • 对于随机欠采样,由于采样的样本集合要少于原来的样本集合,因此会造成一些信息缺失,即将多数类样本删除有可能会导致分类器(模型)丢失有关多数类的重要信息

小结

  • 什么是类别不均衡数据?
    • 类别不均衡数据是指在分类问题中,不同类别的样本数量相差很大的数据集。例如,在一个二分类问题中,正类样本的数量远远少于负类样本的数量,这就是一个类别不均衡的数据集。类别不均衡数据会导致分类模型在训练过程中对多数类样本过拟合,而忽略少数类样本,从而影响模型的预测性能。
  • 解决类别不平衡数据方法
    • 过采样(Oversampling)
      • 随机过采样(Random Oversampling)
        • 原理:随机复制点从而实现增加数据的目的
        • 优点
          • 简单易实现,能够快速平衡数据集
        • 缺点
          • 数据重复度高
          • 容易使得模型复杂度增高,模型过拟合
          • 由于它是通过复制少数类样本来增加少数类样本的数量,因此可能会导致过拟合问题
          • 如果数据集中存在噪声或异常值,随机过采样可能会放大这些样本的影响,从而影响模型的预测性能
      • SMOTE过采样
        • 原理:利用k邻近和线性插值创造新的点从而能够有效地解决类别不均衡问题
        • 优点
          • 相比于简单的随机过采样方法,SMOTE 算法能够生成更多样化的新样本点,从而避免过拟合问题。
          • SMOTE 算法能够有效地平衡数据集,提高分类模型对少数类样本的预测性能。
          • SMOTE 算法简单易实现,且计算效率较高。
        • 缺点
          • SMOTE 算法通过在少数类样本之间插值来合成新的样本点,这可能会导致过拟合
          • SMOTE 算法假设少数类样本是连续分布的,但在实际应用中,这个假设并不总是成立
          • SMOTE 算法只考虑了少数类样本之间的关系,而忽略了多数类样本对分类边界的影响
          • 因此,在使用 SMOTE 算法时,需要谨慎选择参数,并结合其他方法来解决类别不均衡问题。
    • 欠采样(Undersampling)
      • 随机欠采样(Random Undersampling)
        • 原理:随机删除不均衡的样本
        • 优点
          • 简单易实现,能够快速减少数据集的大小,从而缩短模型训练时间。
        • 缺点
          • 由于它是随机删除多数类样本,因此可能会丢失一些重要信息
          • 如果数据集中存在噪声或异常值,随机欠采样可能会保留这些样本,从而影响模型的预测性能
          • 因此,在使用随机欠采样时,需要谨慎选择参数,并结合其他方法来解决类别不均衡问题。
  • Imblearn
  • 创造数据集:from sklearn.datasets import make_classification
  • 查看各个标签的样本:from collections import Counter
    来的样本集合,因此会造成一些信息缺失,即将多数类样本删除有可能会导致分类器(模型)丢失有关多数类的重要信息
;