numpy 分类模型评估 混淆矩阵

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

Prediction
Positive Negative
Target True 真阳性 - TP 假阴性 - FN
False 假阳性 - FP 真阴性 - TN

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

准确率 Accuracy = \frac{TP + TN}{TP + FP + FN + TN} 预测正确样本在所有样本中的占比

查准率

(精确度) 

Precision = \frac{TP}{TP + FP}

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

中的占比

查全率

(召回率)

Recall = \frac{TP}{TP + FN}

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

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

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

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

Fβ-score F_{\beta} = (1+\beta^2) \cdot \frac{Precision \cdot Recall}{\beta^2 \cdot Precision + Recall}

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

β 表示了查准率的权值

(查全率的权值为 1),

比较常用的是:

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

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

Prediction
cat dog car student
Target cat FP
dog FN TP FN FN
car FP
student FP

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

代码实现

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

  • _div:类方法,定义了防止除零、对结果保留 4 位小数的除法,具体计算为:\frac{a}{b + eps}
  • __init__:初始化混淆矩阵为 n × n 的零矩阵,并记录类别数
  • update:接收新的 pred 和 target  (支持 numpy 和 torch,不支持 list,在传参后调用 flatten 函数展平成行向量) 对混淆矩阵进行更新
  • _tp:使用 property 管理的类变量,每次访问都进行一次计算,对应每个类别的 TP
  • accuracy、precision、recall:使用 property 管理的类变量,每次访问都进行一次计算
  • f_score:可指定 β 的值,在此避免了 precision、recall 的二次计算
  • eval:输出 Accuracy、Precision、Recall、F-Score 构成的字典
class Crosstab:
    _div = lambda self, a, b, decimal=4, eps=1e-5: np.round(a / (b + eps), decimal)

    def __init__(self, classes=2):
        self._classes = classes
        self.data = np.zeros([classes] * 2, dtype=np.int32)

    def update(self, pred, target):
        assert all(['int' in str(y.dtype) for y in (pred, target)]), 'Only integer can be used to represent categories'
        pred, target = map(lambda x: x.flatten(), [pred, target])
        for r, c in zip(target, pred): self.data[r, c] += 1
        return self

    _tp = property(fget=lambda self: np.array([self.data[i][i] for i in range(self._classes)]))
    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)

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

    def __str__(self):
        return str(self.data)

    __repr__ = __str__

你可能感兴趣的:(数学建模,numpy,矩阵,机器学习,python)