numpy 分类模型评估 混淆矩阵

先来看最基础的二分类模型的混淆矩阵

Prediction
PositiveNegative
TargetTrue真阳性 - TP假阴性 - FN
False假阳性 - FP真阴性 - TN

根据这个矩阵,可以计算得到分类模型的几个指标:

准确率gif.latex?Accuracy%20%3D%20%5Cfrac%7BTP%20+%20TN%7D%7BTP%20+%20FP%20+%20FN%20+%20TN%7D预测正确样本在所有样本中的占比

查准率

(精确度) 

gif.latex?Precision%20%3D%20%5Cfrac%7BTP%7D%7BTP%20+%20FP%7D

真阳性样本在所有预测为阳性的样本

中的占比

查全率

(召回率)

gif.latex?Recall%20%3D%20%5Cfrac%7BTP%7D%7BTP%20+%20FN%7D

真阳性样本在所有真实为阳性的样本

中的占比,自动驾驶的目标检测因为

在遗漏目标时有巨大的安全隐患,

所以要求接近 100% 的召回率

Fβ-scoregif.latex?F_%7B%5Cbeta%7D%20%3D%20%281+%5Cbeta%5E2%29%20%5Ccdot%20%5Cfrac%7BPrecision%20%5Ccdot%20Recall%7D%7B%5Cbeta%5E2%20%5Ccdot%20Precision%20+%20Recall%7D

查准率和查全率的调和平均数,

β 表示了查准率的权值

(查全率的权值为 1),

比较常用的是:

F1-score、F2-score、F0.5-score

 对于多分类模型,以 dog 为正样本 (其它分类为负样本),可得到如下的混淆矩阵:

Prediction
catdogcarstudent
TargetcatFP
dogFNTPFNFN
carFP
studentFP

由此可知,对于每一个类别,都可以由混淆矩阵各计算出 Precision、Recall

代码实现

使用一个类来创建混淆矩阵,并需要多个类方法来求解 Accuracy、Precision、Recall、Fβ-score

  • _div:类方法,定义了防止除零、对结果保留 4 位小数的除法,具体计算为:gif.latex?%5Cfrac%7Ba%7D%7Bb%20+%20eps%7D
  • __init__:根据 pred 和 target (支持 numpy 和 torch,不支持 list,在传参后调用 flatten 函数展平成行向量) 计算混淆矩阵
  • _tp:使用 property 管理的类变量,每次访问都进行一次计算,对应每个类别的 TP
  • accuracy、precision、recall:使用 property 管理的类变量,每次访问都进行一次计算
  • f_score:可指定 β 的值,在此避免了 precision、recall 的二次计算
  • eval:输出 Accuracy、Precision、Recall、F-Score 构成的字典
  • __add__:支持 Crosstab 类的累加
import numpy as np


class Crosstab:
    _div = lambda self, a, b, decimal=4, eps=1e-8: np.round(a / (b + eps), decimal)

    def __init__(self, pred, target, num_classes=None):
        num_classes = max(target) + 1 if num_classes is None else num_classes
        empty = sum(len(y) for y in (pred, target)) == 0
        assert empty or all('int' in str(y.dtype) for y in (pred, target)), \
            'Only integer can be used to represent categories'
        if empty:
            self.data = np.zeros((num_classes,) * 2, dtype=np.int32)
        else:
            pred, target = map(lambda x: x.flatten(), (pred, target))
            self.data = np.bincount(pred + num_classes * target, minlength=num_classes ** 2).reshape((num_classes,) * 2)

    _tp = property(fget=lambda self: np.diag(self.data))
    accuracy = property(fget=lambda self: self._div(self._tp.sum(), self.data.sum()))
    precision = property(fget=lambda self: self._div(self._tp, self.data.sum(axis=0)))
    recall = property(fget=lambda self: self._div(self._tp, self.data.sum(axis=1)))

    def f_score(self, beta=1.):
        alpha = beta ** 2
        precision, recall = self.precision, self.recall
        return self._div((1 + alpha) * precision * recall, alpha * precision + recall)

    @property
    def kappa(self):
        x = self.data / self.data.sum()
        pe = (x.sum(0) * x.sum(1)).sum()
        return max(0, (self.accuracy - pe) / (1 - pe))

    def eval(self, beta=1.):
        return {'Accuracy': self.accuracy, 'Precision': self.precision,
                'Recall': self.recall, 'kappa': self.kappa, f'F{beta:.1f}-Score': self.f_score(beta)}

    def __add__(self, other):
        if isinstance(other, type(self)):
            self.data += other.data
            return self
        types = tuple(map(lambda x: type(x).__name__, (self, other)))
        raise TypeError(f'unsupported operand type(s) for +: \'{types[0]}\' and \'{types[1]}\'')

    def __repr__(self):
        return str(self.data)
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

荷碧TongZJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值