分类是机器学习中比较常见的任务,对于分类任务常见的评价指标有准确率(Accuracy)、精确率(Precision)、召回率(Recall)、F1 score、ROC曲线(Receiver Operating Characteristic Curve)等
这篇文章将结合sklearn对准确率、精确率、召回率、F1-score进行讲解
混淆矩阵
如上图所示,要了解各个评价指标,首先需要知道混淆矩阵,混淆矩阵中的P表示Positive,即正例或者阳性,N表示Negative,即负例或者阴性。你也可以把P和N分别理解为二分类中的1-0
- TP:实际为正,预测为正的样本数量
- FP:实际为负,预测为正的样本数量
- FN:实际为正,预测为负的样本数量
- TN:实际为负,预测为负的样本数量
另外
- TP FP:表示所有预测为正的样本数量
- TN FN:表示所有预测为负的样本数量
- TP FN:表示实际为正的样本数量
- TN FP:表示实际为负的样本数量
准确率
准确率是分类正确的样本占总样本个数,即
$$ accuracy = frac{n_{correct}}{n_{total}} $$
其中,$n_{correct}$表示被正确分类的样本个数,$n_{total}$表示样本总数
综合上面的混淆矩阵,公式还可以这样写
$$ accuracy=frac{TP TN}{TP TN FP FN} $$
准确率是分类问题中最简单最直观的评价指标,但存在明显的缺陷。比如正负样本的比例不均衡,假设样本中正样本占90%,负样本占10%,那分类器只需要一直预测为正,就可以得到90%的准确率,但其实际性能是非常低下的
下面看一下sklearn中计算准确率的示例
代码语言:javascript复制import numpy as np
from sklearn.metrics import accuracy_score as accu
y_true = [0, 1, 2, 3]
y_pred = [0, 2, 1, 3]
print(accu(y_true, y_pred)) # 0.5
print(accu(y_true, y_pred, normalize=False)) # 2
# normalize=False 返回分类正确的样本数量
# 在具有二元标签指示符的多标签分类问题中
print(accu(np.array([0, 1], [1, 1]), np.ones((2, 2)))) # 0.5
对于最后两行代码
$$ y_{true}=begin{bmatrix} {0}&{1}\ {1}&{1} end{bmatrix}, y_{pred}=begin{bmatrix} {1}&{1}\ {1}&{1} end{bmatrix} $$
矩阵的每一行表示一个样本,列表示标签(每个样本具有两个标签,这两个标签共同确定样本类别)。对于这种情况,此时实际上只有一个样本是预测正确的,因此准确率为0.5
精确率
精确率指模型预测为正的样本中实际也为正的样本 占 被预测为正的样本的比例。计算公式为
$$ precision=frac{TP}{TP FP} $$
代码如下
代码语言:javascript复制from sklearn.metrics import precision_score as ps
y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 2, 1, 0, 0, 1]
print(ps(y_true, y_pred, average="macro")) # 0.22222
print(ps(y_true, y_pred, average="micro")) # 0.33333
print(ps(y_true, y_pred, average="weighted")) # 0.22222
print(ps(y_true, y_pred, average=None)) # [0.6666 0. 0.]
average的参数可选值有None, binary(默认), micro, macro, samples, weighted
上面的$y_{true}$有3个类别,分别为类0,类1,类2。我们将每个类别的TP、FP、FN列在下表中
类别 | TP | FP | FN |
---|---|---|---|
0 | 2 | 1 | 0 |
1 | 0 | 2 | 2 |
2 | 0 | 1 | 1 |
那么每个类别的precision也就得到了
$$ begin{align*} P_0&=frac{2}{1 2}=frac{2}{3}approx 0.667\ P_1&=frac{0}{0 2}=0\ P_2&=frac{0}{0 1}=0 end{align*} $$
于是Macro Precision也就知道了,就是$frac{P_0 P_1 P_2}{3}approx 0.222$
Micro Precision的计算要从每个样本考虑,所有样本中预测正确的有两个,那么TP就是2,剩下的4个预测结果都可以看作FP,于是Micro Precision就是$frac{2}{2 4}=frac{1}{3}approx 0.333$
最后还有一个weighted,因为这里每个类别的数量恰好占比都是1/3,所以结果是
$$ P_w=frac{1}{3}*P_0 frac{1}{3}*P_1 frac{1}{3}*P_2approx 0.222 $$
- 如果每个类别的样本数量不多,那么Macro和Micro没有太大差异
- 如果每个类别的样本数量差异很大,那么注重样本量多的类时用Micro,注重样本量少的类时用Macro
- 如果Macro>>Micro的值,那么检查样本量多的类来确定指标表现差的原因
- 如果Micro>>Macro的值,那么检查样本量少的类来确定指标表现差的原因
召回率
召回率指实际为正的样本中,预测也为正的样本 占 实际为正的样本的比例。计算公式为
$$ recall=frac{TP}{TP FN} $$
代码如下
代码语言:javascript复制from sklearn.metrics import recall_score as rs
y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 2, 1, 0, 0, 1]
print(rs(y_true, y_pred, average="macro")) # 0.3333
print(rs(y_true, y_pred, average="micro")) # 0.3333
print(rs(y_true, y_pred, average="weighted")) # 0.3333
print(rs(y_true, y_pred, average=None)) # [1. 0. 0.]
Recall和Precision只有计算公式不同,它们average参数的计算方式都是相同的,这里不再赘述
F1-score
F1-score是精确率和召回率的加权平均值,计算公式为
$$ F1-score=frac{2*precision*recall}{precision revall} $$
Precision体现了模型对负样本的区分能力,Precision越高,模型对负样本的区分能力越强
Recall体现了模型对正样本的识别能力,Recall越高,模型对正样本的识别能力越强
F1-score是两者的综合,F1-score越高,说明模型越稳健
代码如下
代码语言:javascript复制from sklearn.metrics import f1_score as fs
y_true = [0, 1, 2, 0, 1, 2]
y_pred = [0, 2, 1, 0, 0, 1]
print(fs(y_true, y_pred, average="macro")) # 0.2666
print(fs(y_true, y_pred, average="micro")) # 0.3333
print(fs(y_true, y_pred, average="weighted")) # 0.2666
print(fs(y_true, y_pred, averge=None)) # [0.8 0. 0.]
Worked Example
对于正常的邮件
$$ begin{align*} precision&=frac{16}{16 2}=frac{8}{9}\ Recall&=frac{16}{16 4}=frac{4}{5}\ F1-score&=frac{2*frac{8}{9}*0.8}{frac{8}{9} 0.8} = a end{align*} $$
对于垃圾邮件
$$ begin{align*} precision&=frac{3}{3 4}=frac{3}{7}\ Recall&=frac{3}{3 2}=frac{3}{5}\ F1-score&=frac{2*frac{3}{7}*0.6}{frac{3}{7} 0.6} = b end{align*} $$
对于整体的所有邮件
$$ begin{align*} P &= frac{frac{8}{9} frac{3}{7}}{2} \ R &= frac{0.8 0.6}{2}\ F1 &= frac{a b}{2} end{align*} $$