面对 ValueError: 目标是多类别但平均值='二进制'

42

我正在尝试使用朴素贝叶斯算法处理我的数据集。我能够找到准确性,但是尝试找出相同的精确度和召回率时却出现以下错误:

ValueError: Target is multiclass but average='binary'. Please choose another average setting.

请问是否有人能够建议我如何进行。我尝试在精度和召回率分数中使用 average ='micro',它可以正常工作但是却为准确性、精度和召回率给出了相同的分数。

我的数据集:

train_data.csv:

review,label
Colors & clarity is superb,positive
Sadly the picture is not nearly as clear or bright as my 40 inch Samsung,negative

test_data.csv:

review,label
The picture is clear and beautiful,positive
Picture is not clear,negative

我的代码:

import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import confusion_matrix

X_train, y_train = pd.read_csv('train_data.csv')
X_test, y_test = pd.read_csv('test_data.csv')

vec = CountVectorizer() 
X_train_transformed = vec.fit_transform(X_train) 
X_test_transformed = vec.transform(X_test)

clf = MultinomialNB()
clf.fit(X_train_transformed, y_train)

score = clf.score(X_test_transformed, y_test)

y_pred = clf.predict(X_test_transformed)
cm = confusion_matrix(y_test, y_pred)

precision = precision_score(y_test, y_pred, pos_label='positive')
recall = recall_score(y_test, y_pred, pos_label='positive')
2个回答

49

您需要添加'average'参数。根据文档

average:字符串,[无,'binary'(默认),'micro','macro','samples','weighted']

这个参数是多类/多标签目标的必需品。如果为None,则返回每个类别的分数。否则,这将确定在数据上执行的平均类型:

请执行以下操作:

print("Precision Score : ",precision_score(y_test, y_pred, 
                                           pos_label='positive'
                                           average='micro'))
print("Recall Score : ",recall_score(y_test, y_pred, 
                                           pos_label='positive'
                                           average='micro'))

'micro'替换为上述任何一个选项,但不包括'binary'。此外,在多类别设置中,无需提供'pos_label',因为它会被忽略。
评论更新:
是的,它们可以相等。在用户指南中给出了说明:
请注意,在包括所有标签的多类别设置中,“微观”平均值将产生相等的精度、召回率和F值,而“加权”平均可能会产生一个不在精度和召回率之间的F分数。

9
我已经在精确率和召回率分数中添加了 average ='micro',但准确率、精确率和召回率的得分仍然相同。这三个参数的得分是否可能相同? - Intrigue777
非常感谢您的建议。真的很有帮助。 - Intrigue777
还有一个与同一数据集相关的查询。我已经在同一数据集上使用了onehot_enc、BernoulliNB分类器,它给出了90%的准确率。但是上述使用的算法只给出了46%的准确率。这怎么可能呢? - Intrigue777
@Intrigue777 这个问题太宽泛了。这取决于数据集、预处理和代码的处理方式。你说你在其他代码中使用了onehot_enc,但在这里却使用了countvectorizer。也许这就是区别所在。无论如何,您应该发布一个新问题,包含这两个代码和数据集以供检查。 - Vivek Kumar

5

这个错误非常明显。它表示对于超过2个类别的问题,需要有某种平均规则。有效的规则包括:'micro''macro''weighted'None(文档列出了'samples',但不适用于多类目标)。

如果我们看一下源代码,在精度和召回率计算方面,多类问题被视为多标签问题,因为使用的基础混淆矩阵(multilabel_confusion_matrix)是相同的。1 这个混淆矩阵创建一个3D数组,其中每个“子矩阵”都是2x2的混淆矩阵,其中正值是标签之一。

每种平均规则之间有什么区别?

  • average=None时,将返回每个类别的精确度/召回率分数(没有任何平均值),因此我们得到一个分数数组,其长度等于类别数量。2

  • average='macro'时,会计算每个类别的精确度/召回率,然后取平均值。其公式如下:

    macro

  • average='micro'时,将累加所有类别的贡献来计算平均精确度/召回率。其公式如下:

    micro

  • average='weighted'实际上是带权重的宏平均,其中权重为实际正类。其公式如下:

    weighted


让我们考虑一个例子。

import numpy as np
from sklearn import metrics
y_true, y_pred = np.random.default_rng(0).choice(list('abc'), size=(2,100), p=[.8,.1,.1])
mcm = metrics.multilabel_confusion_matrix(y_true, y_pred)

上面计算出的多标签混淆矩阵如下所示。

confusion matrix

相应的精确度/召回率得分如下:

  • average='macro' 的精确率/召回率为:

    recall_macro = (57 / (57 + 16) + 1 / (1 + 10) + 6 / (6 + 10)) / 3
    precision_macro = (57 / (57 + 15) + 1 / (1 + 13) + 6 / (6 + 8)) / 3
    
    # 使用 sklearn.metrics.precision_score 和 sklearn.metrics.recall_score 进行验证
    recall_macro == metrics.recall_score(y_true, y_pred, average='macro')        # True
    precision_macro == metrics.precision_score(y_true, y_pred, average='macro')  # True
    
  • average='micro' 的精确率/召回率为:

    recall_micro = (57 + 1 + 6) / (57 + 16 + 1 + 10 + 6 + 10)
    precision_micro = (57 + 1 + 6) / (57 + 15 + 1 + 13 + 6 + 8)
    
    # 使用 sklearn.metrics.precision_score 和 sklearn.metrics.recall_score 进行验证
    recall_micro == metrics.recall_score(y_true, y_pred, average='micro')        # True
    precision_micro == metrics.precision_score(y_true, y_pred, average='micro')  # True
    
  • average='weighted' 的精确率/召回率为:

    recall_weighted = (57 / (57 + 16) * (57 + 16) + 1 / (1 + 10) * (1 + 10) + 6 / (6 + 10) * (6 + 10)) / (57 + 16 + 1 + 10 + 6 + 10)
    precision_weighted = (57 / (57 + 15) * (57 + 16) + 1 / (1 + 13) * (1 + 10) + 6 / (6 + 8) * (6 + 10)) / (57 + 16 + 1 + 10 + 6 + 10)
    
    # 使用 sklearn.metrics.precision_score 和 sklearn.metrics.recall_score 进行验证
    recall_weighted == metrics.recall_score(y_true, y_pred, average='weighted')        # True
    precision_weighted == metrics.precision_score(y_true, y_pred, average='weighted')  # True
    

正如你所看到的,这个例子是不平衡的(类别a的频率为80%,而bc每个都为10%)。平均规则之间的主要区别在于'macro'平均不考虑类别的不平衡,而'micro''weighted'则考虑。因此,'macro'对类别的不平衡比较敏感,可能会导致得分过高或过低,具体取决于不平衡的情况。

此外,从公式可以很容易地看出,'micro''weighted'的召回率相等。

为什么average='micro'时准确率==召回率==精确度==F1得分?

用视觉方式理解可能更容易。

如果我们看一下上面构建的多标签混淆矩阵,每个子矩阵对应于一个One vs Rest分类问题;即在子矩阵的非列/行中,另外两个标签已经计入。

例如,对于第一个子矩阵,有

  • 57个真正例(a
  • 16个假负例(bc
  • 15个假正例(bc
  • 12个真负例

对于精度/召回率的计算,只有TP、FN和FP才有意义。如上所述,FN和FP计数可以是bc中的任何一个;由于它是二进制的,因此这个子矩阵本身无法说明预测了多少个每个类别;但是,我们可以通过调用confusion_matrix()方法来计算多类混淆矩阵,从而确定每个类别被正确分类的数量。

mccm = metrics.confusion_matrix(y_true, y_pred)

下图绘制了相同的混淆矩阵(mccm),但使用不同的背景颜色进行区分(黄色背景对应于TP,第一个子矩阵中的红色背景对应于假阴性,橙色对应于第三个子矩阵中的假阳性等)。因此,这些实际上是多标签混淆矩阵中的TP、FN和FP“扩展”到考虑负类别的情况。左图的颜色方案与多标签混淆矩阵中TP和FN计数的颜色相匹配(用于确定召回率),右图的颜色方案则与TP和FP的颜色相匹配(用于确定精确度)。

confusion matrices

使用 average='micro',左侧图表中黄色背景数字与所有数字的比率确定了召回率,右侧图表中黄色背景数字与所有数字的比率确定了精确度。如果我们仔细观察,相同的比率也确定了准确度。此外,由于f1得分精确度召回率的调和平均值,并且它们相等,因此我们有关系式recall == precision == accuracy == f1-score


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