我需要specificity
为我的分类,定义为:TN/(TN+FP)
我正在写一个自定义分数函数:
from sklearn.metrics import make_scorer
def specificity_loss_func(ground_truth, predictions):
print predictions
tp, tn, fn, fp = 0.0,0.0,0.0,0.0
for l,m in enumerate(ground_truth):
if m==predictions[l] and m==1:
tp+=1
if m==predictions[l] and m==0:
tn+=1
if m!=predictions[l] and m==1:
fn+=1
if m!=predictions[l] and m==0:
fp+=1
`return tn/(tn+fp)
score = make_scorer(specificity_loss_func, greater_is_better=True)
,
from sklearn.dummy import DummyClassifier
clf_dummy = DummyClassifier(strategy='most_frequent', random_state=0)
ground_truth = [0,0,1,0,1,1,1,0,0,1,0,0,1]
p = [0,0,0,1,0,1,1,1,1,0,0,1,0]
clf_dummy = clf_dummy.fit(ground_truth, p)
score(clf_dummy, ground_truth, p)
当我运行这些命令时,我得到p
打印为:
[0 0 0 0 0 0 0 0 0 0 0 0 0]
1.0
当我输入p = [0,0,0,1,0,1,1,1,1,0,0,1,0]
p
变为一系列零您可以从confusion matrix
中获得specificity
。对于二元分类问题,它类似于:
from sklearn.metrics import confusion_matrix
y_true = [0, 0, 0, 1, 1, 1, 1, 1]
y_pred = [0, 1, 0, 1, 0, 1, 0, 1]
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
specificity = tn / (tn+fp)
正如在其他回答中提到的,特异性是对否定类的回忆。您可以通过设置pos_label
参数来实现:
from sklearn.metrics import recall_score
y_true = [0, 1, 0, 0, 1, 0]
y_pred = [0, 0, 1, 1, 1, 1]
recall_score(y_true, y_pred, pos_label=0)
返回.25
首先你需要知道:
DummyClassifier(strategy='most_frequent'...
将给你从你的训练集中返回最频繁标签的分类器。它甚至不考虑x中的样本。您可以在这一行传递任何东西而不是ground_truth:
clf_dummy = clf_dummy.fit(ground_truth, p)
训练结果,和预测结果将保持不变,因为p内的大多数标签都是标签"0"。
第二个你需要知道的事情:make_scorer返回接口为scorer(estimator, X, y)
的函数,该函数将调用集合X上estimator的predict方法,并计算预测标签与y之间的特异性函数。
所以它在任何数据集上调用clf_dummy(不管哪个数据集,它总是返回0),并返回0的向量,然后它计算ground_truth和预测之间的特异性损失。你的预测是0因为0是训练集中的多数类。你的分数等于1,因为没有假阳性预测。
我修改了你的代码,以增加更多的方便。
from sklearn.dummy import DummyClassifier
clf_dummy = DummyClassifier(strategy='most_frequent', random_state=0)
X = [[0],[0],[1],[0],[1],[1],[1],[0],[0],[1],[0],[0],[1]]
p = [0,0,0,1,0,1,1,1,1,0,0,1,0]
clf_dummy = clf_dummy.fit(X, p)
score(clf_dummy, X, p)
在我的理解中,"专一性"只是"回忆"的一个特例。召回率是针对实际阳性类别(TP/[TP+FN])计算的,而"特异性"是相同类型的计算,但针对实际阴性类别(TN/[TN+FP])。
只有在二元分类问题中使用这种特定的术语才有意义。对于一个多类分类问题,讨论每个类的召回率会更方便。即使在处理二元分类问题(例如,类0的召回,类1的召回)时,也没有理由不能以这种方式谈论召回。
例如,回忆告诉我们实际患有癌症的患者被成功诊断为患有癌症的比例。然而,概括地说,你可以说X类召回率告诉我们实际属于X类的样本的比例,被成功地预测为属于X类。
考虑到这一点,您可以使用from sklearn.metrics import classification_report
生成每个标签/类的精度、召回率、f1-score和支持度的字典。您也可以依赖from sklearn.metrics import precision_recall_fscore_support
,这取决于您的偏好。文档在这里。
from sklearn.metrics import precision_recall_fscore_support
labels = ['dog', 'cat', 'pig']
y_true = np.array(['cat', 'dog', 'pig', 'cat', 'dog', 'pig'])
y_pred = np.array(['cat', 'pig', 'dog', 'cat', 'cat', 'dog'])
prfs = precision_recall_fscore_support(y_true, y_pred, average=None, labels=labels)
precisions = prfs[0]
recalls = prfs[1] #Specificity in Binary Classification
fbeta_scores = prfs[2]
supports = prfs[3]
print(recalls) # Note the order of this array is dependent on the order of your labels array
记住,在二元分类中,正类的召回也被称为"灵敏度";回忆的否定类是"专一性",我用这个:
unique, counts = np.unique(y_test, return_counts=True)
for i in unique:
score = precision_score(y_true, y_pred, labels=unique, pos_label=i)
print('score ' + str(i) + ' ' + str(score))
我个人非常依赖使用sklearn的classification_report
,所以想用特定的值来扩展它,所以想出了下面的代码。
请注意,我只将其添加到macro avg
中,尽管将其扩展到加权平均输出也应该很容易
import random
import numpy as np
from sklearn.metrics import classification_report
def extended_classification_report(y_true: np.array, y_pred: np.array, classes: set = None):
report = classification_report(y_true, y_pred, output_dict=True, zero_division=0)
report['macro avg']['specificity'] = specificity(y_true, y_pred, classes=classes)
return report
def specificity(y_true: np.array, y_pred: np.array, classes: set = None):
if classes is None: # Determine classes from the values
classes = set(np.concatenate((np.unique(y_true), np.unique(y_pred))))
specs = []
for cls in classes:
y_true_cls = (y_true == cls).astype(int)
y_pred_cls = (y_pred == cls).astype(int)
fp = sum(y_pred_cls[y_true_cls != 1])
tn = sum(y_pred_cls[y_true_cls == 0] == False)
specificity_val = tn / (tn + fp)
specs.append(specificity_val)
return np.mean(specs)