sklearn的metrics,如何原理上理解预测结果的得分好坏?
发布于 作者:苏南大叔 来源:程序如此灵动~ 我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...
从原理上分析模型预测结果得分的算法,这些预测模型的得分算法包括:查准率precision
、召回率recall
、以及整体的f1
得分,文章的主要内容就是模拟这些得分是怎么计算的。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的编程感想感悟。本文的模型得分算法分析实验的测试环境:win10
,python@3.11.0
,sklearn@1.2.2
。本文的基本思路前提是:二分法(非此即彼),而不是多分类。
基本数据
这里还是以二分类数据为例子,非零即一。场景假设为某种进口海鱼的安全合格率,这里假设1
为目标检查数据,即不合格的海鱼。海关要尽力把数据为1
的海鱼,即受污染的海鱼给找出来。
某一批海洋鱼类产品,
- 它的实际情况是:
[0 0 0 1 1 1]
- 大数据模型根据机器学习的结果预测的结果是:
[1 1 1 0 0 1]
。
基础分类
专业术语里面使用"阳性/Positive"来表示有问题,"阴性/Negative"表示没有问题。使用"真实/True"来表示符合情况,"虚假/False"来表示不符合情况。所以,这里对数据分成了分类,即:
变量 | 含义 | 预测 | 实际 | 是否准确 | 解释 |
---|---|---|---|---|---|
tp | 正确的阳性结论 | 1 | 1 | 准确 | 有问题,还被预测出来了 |
fp | 虚假的阳性结论 | 1 | 0 | 不准确 | 没问题,却被错误的预测出来了 |
tn | 正确的阴性结论 | 0 | 0 | 准确 | 没问题,同时也确实被预测出来 |
fn | 虚假的阴性结论 | 0 | 1 | 不准确 | 有问题,但是没被预测出来 |
算法上实际计算的是上述分类的样本总数:
tp = sum( (y_pred == 1) & (y_true == 1) )
fp = sum( (y_pred == 1) & (y_true == 0) )
tn = sum( (y_pred == 0) & (y_true == 0) )
fn = sum( (y_pred == 0) & (y_true == 1) )
查准率 precision_score
精准度可以有三种表述:
- 阳性有关的样本中,预测准确的样本比率。【查准率】
- 阴性有关的样本中,预测准确的样本比率。
- 所有样本中,预测准确的样本比率。
精准度,实际上计算的是阳性精准度。并不是阴性精准度,也不是整体的精准度【重大误解】。
尽量不冤枉好人,也不放过每个坏人。
算法是:
p = tp / (tp + fp)
p_n = tn / (tn + fn)
p_all = (tp + tn) / (tp + fp + tn + fn)
本文的例子中:
官方查准率: 0.25
阳性查准率[tp / tp + fp]: 0.25
阴性查准率[tn / tn + fp]: 0.0
整体查准率[(tp + tn) / (tp + fp + tn + fn)]: 0.16666666666666666
对比代码:
p = precision_score(y_true, y_pred)
p_ = (tp / (tp + fp)) if (tp + fp > 0) else 0
召回率recall_score
召回率就是:有问题的样本被召回(预测)的比率,无论有多少被错误召回的样本,只计算被正确召回的比率。
宁可错杀一千,不可放过敌军一个。
涉及的就是阳性相关的预测值:
- 预测为阳性,[==>]真实阳性,准确(tp)。真实阴性,不准确(fp)。
- 预测为阴性,真实阴性,准确(tn)。[==>]真实阳性,不准确(fn)。
算法是:
r = tp / (tp + fn)
对比代码:
r = recall_score(y_true, y_pred)
r_ = (tp / (tp + fn)) if (tp + fn > 0) else 0
f1得分f1_score
f1
得分是综合上述两者得分的,f1
得分越低,证明模型选的越不好。计算方法是:
f1 = 2 * p * r / (p + r)
对比代码:
f1 = f1_score(y_true, y_pred)
f1_ = 2 * p_ * r_ / (p_ + r_) if (p_ + r_ > 0) else 0
测试代码
from sklearn.metrics import precision_score, recall_score, f1_score
import numpy as np
def analyze(y_true, y_pred):
print("实际值", y_true)
print("预测值", y_pred)
print("对不对", y_true == y_pred)
tp = sum((y_pred == 1) & (y_true == 1))
fp = sum((y_pred == 1) & (y_true == 0))
tn = sum((y_pred == 0) & (y_true == 0))
fn = sum((y_pred == 0) & (y_true == 1))
print("有问题被发现了(tp):%s" % tp)
print("有问题没被发现(fn):%s" % fn)
print("没有问题被误诊(fp):%s" % fp)
print("放心啥事都没有(tn):%s" % tn)
p = precision_score(y_true, y_pred)
r = recall_score(y_true, y_pred)
f1 = f1_score(y_true, y_pred)
print("官方查准率:%s" % p)
print("官方召回率:%s" % r)
print("官方F1得分:%s" % f1)
p_ = (tp / (tp + fp)) if (tp + fp > 0) else 0
r_ = (tp / (tp + fn)) if (tp + fn > 0) else 0
f1_ = 2 * p_ * r_ / (p_ + r_) if (p_ + r_ > 0) else 0
print("查准率自定义:%s" % p_)
print("召回率自定义:%s" % r_)
print("F1得分自定义:%s" % f1_)
p_2 = (tn / (tn + fn)) if (tn + fn > 0) else 0
p_3 = ((tp + tn) / (tp + fp + tn + fn)) if (tp + fp + tn + fn > 0) else 0
print("阳性查准率[tp / tp + fp]:%s" % p_)
print("阴性查准率[tn / tn + fp]:%s" % p_2)
print("整体查准率[(tp + tn) / (tp + fp + tn + fn)]:%s" % p_3)
def analyze2(y_test, predictions):
y_true = np.array(y_test) # 1 有问题的,应该被召回 0 没有问题的,不应该被召回
y_pred = np.array(predictions) # 1 预测为有问题,被召回 0 预测为没有问题,没有被召回
analyze(y_true, y_pred)
print("=" * 20)
analyze2([0, 0, 0, 1, 1, 1], [1, 1, 1, 0, 0, 1])
输出值为:
实际值 [0 0 0 1 1 1]
预测值 [1 1 1 0 0 1]
对不对 [False False False False False True]
有问题被发现了(tp):1
有问题没被发现(fn):2
没有问题被误诊(fp):3
放心啥事都没有(tn):0
官方查准率:0.25
官方召回率:0.3333333333333333
官方F1得分:0.28571428571428575
查准率自定义:0.25
召回率自定义:0.3333333333333333
F1得分自定义:0.28571428571428575
阳性查准率[tp / tp + fp]:0.25
阴性查准率[tn / tn + fp]:0.0
整体查准率[(tp + tn) / (tp + fp + tn + fn)]:0.16666666666666666
从预测结果上来看,f1
得分实在是低的可怜。“这个”模型很失败,不合适。
结束语
查准率原来是阳性查准率,召回率居然可以容错,f1
得分才是最终的模型评判标准。更多机器学习文章,可以参考:
如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。