一遍计算精度、召回率和F1分数 - Python

11

准确率,精确率,召回率和 F1 分数 是机器学习系统中评估模型质量的指标。它基于 True/False Positives/Negatives 的混淆矩阵。

给定二元分类任务,我已尝试以下方法来获得一个能够返回准确率,精确率,召回率和 F1 分数的函数:

gold = [1] + [0] * 9
predicted = [1] * 10

def evaluation(gold, predicted):
  true_pos = sum(1 for p,g in zip(predicted, gold) if p==1 and g==1)
  true_neg = sum(1 for p,g in zip(predicted, gold) if p==0 and g==0)
  false_pos = sum(1 for p,g in zip(predicted, gold) if p==1 and g==0)
  false_neg = sum(1 for p,g in zip(predicted, gold) if p==0 and g==1)
  try:
    recall = true_pos / float(true_pos + false_neg)
  except:
    recall = 0
  try:
    precision = true_pos / float(true_pos + false_pos)
  except:
    precision = 0
  try:
    fscore = 2*precision*recall / (precision + recall)
  except:
    fscore = 0
  try:
    accuracy = (true_pos + true_neg) / float(len(gold))
  except:
    accuracy = 0
  return accuracy, precision, recall, fscore

但是似乎我已经通过数据集进行了冗余循环4次以获取真/假阳性/阴性。

此外,为了捕获ZeroDivisionError,多个try-except有点冗余。

那么,有没有Pythonic的方法可以在不通过数据集进行多次循环的情况下获取True/False Positives/Negatives的计数?

如何使用Pythonic方式捕获ZeroDivisionError而不使用多个try-except?


我也可以通过以下方式在一个循环中计算True/False Positives/Negatives,但是否有另一种不需要多个if的替代方法?

for p,g in zip(predicted, gold):
    if p==1 and g==1:
        true_pos+=1
    if p==0 and g==0:
        true_neg+=1
    if p==1 and g==0:
        false_pos+=1
    if p==0 and g==1:
        false_neg+=1
3个回答

13
什么是Pythonic的方式来获取True/False、正例/反例的计数,而不需要通过数据集进行多次循环遍历?
我会使用collections.Counter,大致上你正在使用所有的if(应该使用elif,因为你的条件是相互排斥的),最后得到的结果:
counts = Counter(zip(predicted, gold))

那么例如:true_pos = counts [1, 1]

我如何在Python中捕获ZeroDivisionError,而不使用多个try-except语句呢?

首先,你几乎永远不应该使用裸的 except:。如果你要捕获 ZeroDivisionError,那么写成 except ZeroDivisionError。你也可以考虑 "三思而后行" 的方法,在尝试除法之前检查分母是否为 0,例如:

accuracy = (true_pos + true_neg) / float(len(gold)) if gold else 0

太酷了!!!我从来没有想过要计算元组以进行精确度/召回率的计算。 - alvas
@alvas,我看到你开了一个悬赏,但实际上并没有改变问题;我的回答有问题吗? - jonrsharpe
@jonsharpe,我想看看其他人能提出什么解决方案。目前你的回答最好,很可能会给你发放悬赏或者至少把答案选中标记给你 =) - alvas

5
这是一个很自然的使用场景,可以使用bitarray包来实现。
import bitarray as bt

tp = (bt.bitarray(p) & bt.bitarray(g)).count()
tn = (~bt.bitarray(p) & ~bt.bitarray(g)).count()
fp = (bt.bitarray(p) & ~bt.bitarray(g)).count()
fn = (~bt.bitarray(p) & bt.bitarray(g)).count()

有一些类型转换的开销,但之后,位运算会快得多。

在我的电脑上,对于100个实例,在1000次迭代中,您的方法需要0.036秒,而使用bitarray只需要0.017秒。对于1000个实例,它分别为0.291和0.093。对于10000个实例,它们分别为3.177和0.863。你懂的。

它的扩展性非常好,不使用循环,并且不必存储一个大型中间表示来构建zip中的临时元组列表。


4

根据您的需求,有几个库可以计算精度、召回率、F-分数等。我使用过的一个是 scikit-learn。假设您已经对实际值和预测值进行了对齐,那么只需要...

from sklearn.metrics import precision_recall_fscore_support as pr
bPrecis, bRecall, bFscore, bSupport = pr(gold, predicted, average='binary')

使用这个库的一个优点是,不同类型的度量标准(如微平均、宏平均、加权、二进制等)都可以轻松获得。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接