UndefinedMetricWarning:F 分数定义不明确,在没有预测样本的标签中被设置为 0.0
- 2024-12-26 08:43:00
- admin 原创
- 164
问题描述:
我收到这个奇怪的错误:
classification.py:1113: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no predicted samples.
'precision', 'predicted', average, warn_for)`
但是第一次运行时它也会打印 f 分数:
metrics.f1_score(y_test, y_pred, average='weighted')
第二次运行时,它提供了正确的分数。这是为什么?
>>> y_pred = test.predict(X_test)
>>> y_test
array([ 1, 10, 35, 9, 7, 29, 26, 3, 8, 23, 39, 11, 20, 2, 5, 23, 28,
30, 32, 18, 5, 34, 4, 25, 12, 24, 13, 21, 38, 19, 33, 33, 16, 20,
18, 27, 39, 20, 37, 17, 31, 29, 36, 7, 6, 24, 37, 22, 30, 0, 22,
11, 35, 30, 31, 14, 32, 21, 34, 38, 5, 11, 10, 6, 1, 14, 12, 36,
25, 8, 30, 3, 12, 7, 4, 10, 15, 12, 34, 25, 26, 29, 14, 37, 23,
12, 19, 19, 3, 2, 31, 30, 11, 2, 24, 19, 27, 22, 13, 6, 18, 20,
6, 34, 33, 2, 37, 17, 30, 24, 2, 36, 9, 36, 19, 33, 35, 0, 4,
1])
>>> y_pred
array([ 1, 10, 35, 7, 7, 29, 26, 3, 8, 23, 39, 11, 20, 4, 5, 23, 28,
30, 32, 18, 5, 39, 4, 25, 0, 24, 13, 21, 38, 19, 33, 33, 16, 20,
18, 27, 39, 20, 37, 17, 31, 29, 36, 7, 6, 24, 37, 22, 30, 0, 22,
11, 35, 30, 31, 14, 32, 21, 34, 38, 5, 11, 10, 6, 1, 14, 30, 36,
25, 8, 30, 3, 12, 7, 4, 10, 15, 12, 4, 22, 26, 29, 14, 37, 23,
12, 19, 19, 3, 25, 31, 30, 11, 25, 24, 19, 27, 22, 13, 6, 18, 20,
6, 39, 33, 9, 37, 17, 30, 24, 9, 36, 39, 36, 19, 33, 35, 0, 4,
1])
>>> metrics.f1_score(y_test, y_pred, average='weighted')
C:UsersMichaelMiniconda3envssnowflakeslibsite-packagessklearnmetricsclassification.py:1113: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 in labels with no predicted samples.
'precision', 'predicted', average, warn_for)
0.87282051282051276
>>> metrics.f1_score(y_test, y_pred, average='weighted')
0.87282051282051276
>>> metrics.f1_score(y_test, y_pred, average='weighted')
0.87282051282051276
另外,为什么会有尾随'precision', 'predicted', average, warn_for)
错误消息?没有左括号,那么为什么它以右括号结尾?我在 Windows 10 上的 conda 环境中使用 Python 3.6.0 运行 sklearn 0.18.1。
我也看了这里,不知道是不是同样的错误。这篇SO 帖子也没有解决方案。
解决方案 1:
正如评论中提到的,中的一些标签y_test
没有出现在中y_pred
。具体来说,在这种情况下,标签“2”从未被预测到:
>>> set(y_test) - set(y_pred)
{2}
这意味着没有要为该标签计算的 F 分数,因此此案例的 F 分数被视为 0.0。由于您要求计算分数的平均值,因此您必须考虑到计算中包含了 0 分,这就是 scikit-learn 向您显示该警告的原因。
这让我不会再看到错误。正如我所提到的,这是一个警告,它与 Python 中的错误处理方式不同。大多数环境中的默认行为是仅显示一次特定警告。此行为可以更改:
import warnings
warnings.filterwarnings('always') # "error", "ignore", "always", "default", "module" or "once"
如果在导入其他模块之前设置此项,则每次运行代码时都会看到警告。
除了设置之外,没有其他方法可以避免第一次看到此警告warnings.filterwarnings('ignore')
。您可以做的是决定您对未预测的标签分数不感兴趣,然后明确指定您感兴趣的标签(至少被预测过一次的标签):
>>> metrics.f1_score(y_test, y_pred, average='weighted', labels=np.unique(y_pred))
0.91076923076923078
警告将消失。
解决方案 2:
我在训练分类模型时也遇到了同样的问题。导致此问题的原因正如警告消息所述“在无预测样本的标签中”,它将在计算 f1-score 时导致零除。我在阅读sklearn.metrics.f1_score文档时找到了另一个解决方案,其中有一条注释如下:
当真阳性 + 假阳性 == 0 时,精度未定义;当真阳性 + 假阴性 == 0 时,召回率未定义。在这种情况下,默认情况下,指标将设置为 0,f-score 也将设置为 0,并且会引发 UndefinedMetricWarning。可以使用 zero_division 修改此行为
默认值zero_division
是"warn"
,您可以将其设置为0
或1
以避免UndefinedMetricWarning
。它对我有用 ;) 哦,等等,当我使用 时还有另一个问题zero_division
,我的 sklearn 报告说使用 scikit-learn 0.21.3 没有这样的关键字参数。只需通过运行将 sklearn 更新到最新版本即可pip install scikit-learn -U
解决方案 3:
我最终遇到了同样的错误,但在阅读了@Shovalt 的回答后,我意识到我的测试/训练分割相当低。我一开始有一个很大的数据集,但把它分割了,其中一个组相当小。通过增加样本量,这个警告就消失了,我得到了我的 f1 分数。从这个
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=0)
对此
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
解决方案 4:
我注意到这个错误在两种情况下发生,
如果您已使用 train_test_split() 分割数据,则必须确保重置数据的索引(特别是使用 pandas 系列对象时):y_train、y_test 索引应重置。问题是当您尝试使用 sklearn.metrics 中的一个分数(例如 precision_score)时,这将尝试匹配您从 train_test_split() 获得的 y_test 的打乱索引。
因此,要么使用np.array(y_test) for y_true in scores
,要么y_test.reset_index(drop=True)
然后,如果您预测的“真阳性”为 0(用于精度、召回率和 f1_scores),您仍然会遇到此错误。您可以使用 Confusion_matrix 将其可视化。如果分类是多标签的,并且您设置了参数:average='weighted'/micro/macro,只要矩阵中的对角线不为 0,您就会得到答案
希望这有帮助。
解决方案 5:
接受的答案已经很好地解释了为什么会出现警告。如果您只是想控制警告,可以使用precision_recall_fscore_support
。它提供了一个(半官方)论点,warn_for
可用于静音警告。
(_, _, f1, _) = metrics.precision_recall_fscore_support(y_test, y_pred,
average='weighted',
warn_for=tuple())
正如一些评论中所提到的,请谨慎使用。
解决方案 6:
按照Shovalt 的建议,我检查了多标签情况下真值集和预测之间的差异,但这并没有帮助我解决问题。
因此,我搜索了sklearn.metrics.precision_recall_fscore_support源代码(由 f1_score 调用)以检查其工作原理。
触发警告的代码如下:
precision = _prf_divide(
tp_sum, pred_sum, "precision", "predicted", average, warn_for, zero_division
)
recall = _prf_divide(
tp_sum, true_sum, "recall", "true", average, warn_for, zero_division
)
tpsum
对应于 TP (True Positives)pred_sum
对应于 TP + FP(假阳性)true_sum
对应于 TP + FN(假阴性)第一个参数
_prf_divide
是除法的分子的第二个参数
_prf_divide
是除法的分母
一旦 pred_sum 或 true_sum 等于 0,就会触发警告,因为不允许除以 0。
为了获取这些不同的值,请使用sklearn.metrics.multilabel_confusion_matrix。结果是一个三维数组。您可以将其视为 2x2 矩阵列表,其中每个矩阵代表每个标签的真阴性 (TN)、假阳性 (FP)、假阴性 (FP) 和真阳性 (TP),结构如下:
就我而言,问题来自于模型由于训练不佳或缺乏样本而无法预测某些标签。
解决方案 7:
正如错误消息所述,获取 F 分数的方法来自 sklearn 的“分类”部分 - 因此谈论“标签”。
您有回归问题吗?Sklearn 在“特征选择”组下为回归提供了一种“F 分数”方法:http ://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.f_regression.html
如果您确实存在分类问题,@Shovalt 的答案对我来说似乎是正确的。
解决方案 8:
小心 Seqeval 的单字母标签!
使用 Seqeval pakcage 计算指标时,我遇到了类似的问题。事实证明,当我为 NER 使用单字符标签(如“U”或“0”)时,脚本seqeval/metrics/sequence_labeling.py
会在处理过程中截断一个字母,因此用于比较的标签为空。将标签更改为“UU”和“00”解决了该问题。
解决方案 9:
您遇到的错误是由于 used_actions 中的某些标签在 y_true 列表中没有任何真实样本。在计算这些标签的召回率(或其他指标)时,它变得不确定,因为召回率计算中的分母(该标签的真实样本数量)为零。
要解决此问题,您可以使用分类报告函数中的 zero_division 参数来控制此行为。将 zero_division 设置为 1 或 0 将通过明确定义发生零除时要执行的操作来避免警告。
class_report = classification_report(y_true, y_pred, labels=used_actions, zero_division=1)```
解决方案 10:
此命令对我有用
sklearn.metrics.f1_score(y_true, y_pred,average='weighted',zero_division=0)