分类预测建模通常涉及预测类别标签。
然而,许多机器学习算法能够预测类成员资格的概率或分数,并且必须在将其映射到清晰的类别标签之前进行解释。这是通过使用阈值来实现的,例如 0.5,其中所有等于或大于阈值的值被映射到一个类别,所有其他值被映射到另一个类别。
对于那些类别严重不平衡的分类问题,默认阈值可能导致性能不佳。因此,改进在不平衡分类问题上预测概率的分类器的性能的一种简单直接的方法是调整用于将概率映射到类标签的阈值。
在某些情况下,例如在使用 ROC 曲线和精确率-召回率曲线时,可以为分类器直接计算最佳或最优阈值。在其他情况下,可以使用网格搜索来调整阈值并找到最优值。
在本教程中,您将了解在将概率转换为不平衡分类的清晰类别标签时如何调整最优阈值。
完成本教程后,您将了解:
- 将概率解释为类别标签的默认阈值是 0.5,调整此超参数称为阈值移动。
- 如何直接为 ROC 曲线和精确率-召回率曲线计算最优阈值。
- 如何手动搜索所选模型和模型评估指标的阈值。
启动您的项目,阅读我的新书《Python 不平衡分类》,其中包括分步教程和所有示例的Python 源代码文件。
让我们开始吧。
- 更新(2020 年 2 月):已修复特异性方程中的拼写错误。
- 更新(2021 年 1 月):已更新 API 文档链接。

阈值移动在不平衡分类中的入门指南
照片由 Bruna cs 提供,部分权利保留。
教程概述
本教程分为五个部分;它们是:
- 将概率转换为类别标签
- 阈值移动在不平衡分类中的应用
- ROC 曲线的最优阈值
- 精确率-召回率曲线的最优阈值
- 最优阈值调整
将概率转换为类别标签
许多机器学习算法能够预测类成员资格的概率或分数。
这通常很有用,因为它提供了预测的确定性或不确定性的度量。它还提供了比仅预测类别标签更精细的粒度,可以进行解释。
一些分类任务需要清晰的类别标签预测。这意味着即使预测了类成员资格的概率或分数,也必须将其转换为清晰的类别标签。
将预测概率或分数转换为类别标签的决策由一个称为“决策阈值”、“判别阈值”或简称为“阈值”的参数决定。对于介于 0 或 1 之间的归一化预测概率或分数,阈值的默认值为 0.5。
例如,对于具有类别标签 0 和 1 的二元分类问题,如果使用归一化预测概率和 0.5 的阈值,则小于 0.5 阈值的值被分配给类别 0,大于或等于 0.5 的值被分配给类别 1。
- 预测 < 0.5 = 类别 0
- 预测 >= 0.5 = 类别 1
问题在于默认阈值可能无法最佳地解释预测概率。
这可能是由于多种原因,例如
- 预测的概率未校准,例如 SVM 或决策树预测的概率。
- 用于训练模型度量的与用于评估最终模型的度量不同。
- 类别分布严重偏斜。
- 一种误分类的成本比另一种误分类的成本更重要。
更糟糕的是,其中一些或所有原因可能同时发生,例如在不平衡分类问题上使用具有未校准预测概率的神经网络模型。
因此,在解释模型预测时,通常需要更改默认决策阈值。
……几乎所有分类器都通过将阈值应用于分数来生成正向或负向预测。此阈值的选择将影响正负误差的权衡。
— 第 53 页,《从不平衡数据集学习》,2018 年。
想要开始学习不平衡分类吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
阈值移动在不平衡分类中的应用
有许多技术可用于解决不平衡分类问题,例如重采样训练数据集和开发机器学习算法的定制版本。
尽管如此,处理严重类别不平衡的最简单方法可能是更改决策阈值。虽然简单且非常有效,但正如 Foster Provost 在其 2000 年题为“不平衡数据集的机器学习”的文章中所指出的那样,从业者和研究学者都常常忽略这种技术。
最重要的是,在研究不平衡数据问题时,不调整输出阈值而使用标准机器学习算法生成的分类器可能是一个关键的错误。
— 《不平衡数据集的机器学习 101》,2000 年。
有许多理由选择默认决策阈值的替代方案。
例如,您可以使用 ROC 曲线来分析模型的预测概率,并使用 ROC AUC 分数来比较和选择模型,尽管您需要模型提供清晰的类别标签。您如何选择 ROC 曲线上的阈值,以在真正例率和假例率之间取得最佳平衡?
或者,您可以使用精确率-召回率曲线来分析模型的预测概率,使用精确率-召回率 AUC 来比较和选择模型,并需要清晰的类别标签作为预测。您如何选择精确率-召回率曲线上的阈值,以在精确率和召回率之间取得最佳平衡?
您可以使用基于概率的度量来训练、评估和比较模型,例如对数损失(交叉熵),但需要预测清晰的类别标签。您如何更普遍地从预测概率中选择最优阈值?
最后,您可能需要为假正例和假负例误分类承担不同的成本,即所谓的成本矩阵,但希望使用并评估成本不敏感模型,然后使用成本敏感度量来评估其预测。您如何选择一个阈值,该阈值可以利用成本矩阵找到预测的最佳权衡?
训练成本敏感分类器的一种流行方法是在不知道成本矩阵的情况下,通过在进行新数据预测时修改分类输出来强调。这通常通过在正类上设置阈值来完成,低于该阈值则预测负类。此阈值的值使用验证集进行优化,从而可以从训练数据中学习成本矩阵。
— 第 67 页,《从不平衡数据集学习》,2018 年。
这些问题的答案是通过搜索一系列阈值来找到最佳阈值。在某些情况下,可以根据公式直接计算最优阈值。
为了适应分类问题更广泛的需求而调整或移动决策阈值通常被称为“阈值移动”、“阈值调整”或简称为“阈值处理”。
有人认为,在不尝试仅通过设置阈值来解决问题的情况下尝试其他方法(例如采样)可能会产生误导。阈值移动方法使用原始训练集来训练[一个模型],然后移动决策阈值,使得少数类示例更容易被正确预测。
— 第 72 页,《不平衡学习:基础、算法和应用》,2013 年。
该过程首先在训练数据集上拟合模型,然后在测试数据集上进行预测。预测结果是归一化概率或分数的形式,它们被转换为归一化概率。然后尝试不同的阈值,并使用选定的评估指标来评估生成的清晰标签。然后,在将来对新数据进行预测时,采用达到最佳评估指标的阈值。
我们可以将此过程总结如下。
- 1. 在训练数据集上拟合模型。
- 2. 在测试数据集上预测概率。
- 3. 对于阈值列表中的每个阈值
- 3a. 使用阈值将概率转换为类别标签。
- 3b. 评估类别标签。
- 3c. 如果分数优于最佳分数。
- 3ci. 采用阈值。
- 4. 在对新数据进行类别预测时使用采用的阈值。
尽管简单,但根据具体情况,有几种不同的方法可以实现阈值移动。我们将在以下各节中介绍一些最常见的示例。
ROC 曲线的最优阈值
ROC 曲线是一个诊断图,用于评估模型在测试数据集上做出的一系列概率预测。
使用一组不同的阈值来解释正类(少数类)的预测的真阳性率和假阳性率,并且分数按递增阈值绘制成线以创建曲线。
假阳性率绘制在 x 轴上,真阳性率绘制在 y 轴上,该图被称为接收者操作特征曲线,或 ROC 曲线。从左下角到右上角的对角线表示无技能分类器(在所有情况下都预测多数类)的“曲线”,而图的左上角点表示具有完美技能的模型。
该曲线有助于理解不同阈值下的真阳性率和假阳性率之间的权衡。ROC 曲线下的面积,称为 ROC AUC,提供了一个单一数字来总结模型在其 ROC 曲线方面的性能,值为 0.5(无技能)到 1.0(完美技能)。
ROC 曲线是理解不同阈值权衡的有用诊断工具,而 ROC AUC 提供了用于根据模型的通用能力进行比较的有用数字。
如果在此类分析中需要来自模型的清晰类别标签,则需要一个最优阈值。这将是图上最接近左上角的点对应的阈值。
幸运的是,有原理性的方法可以定位这个点。
首先,让我们拟合一个模型并计算 ROC 曲线。
我们可以使用 make_classification() 函数来创建一个合成二元分类问题,包含 10,000 个样本(行),其中 99% 属于多数类,1% 属于少数类。
1 2 3 4 |
... # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) |
然后,我们可以使用 train_test_split() 函数拆分数据集,其中一半用于训练集,一半用于测试集。
1 2 3 |
... # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) |
然后,我们可以拟合一个 LogisticRegression 模型,并用它在测试集上进行概率预测,并仅保留少数类的概率预测。
1 2 3 4 5 6 7 8 |
... # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 lr_probs = model.predict_proba(testX) # 只保留正向结果的概率 lr_probs = lr_probs[:, 1] |
然后,我们可以使用 roc_auc_score() 函数为预测计算真阳性率和假阳性率,使用一组可以用来创建 ROC 曲线图的阈值。
1 2 3 |
... # 计算分数 lr_auc = roc_auc_score(testy, lr_probs) |
我们可以将这一切结合起来,定义数据集、拟合模型并创建 ROC 曲线图。完整的示例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# 逻辑回归模型的 ROC 曲线 from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve from matplotlib import pyplot # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 只保留正向结果的概率 yhat = yhat[:, 1] # 计算 ROC 曲线 fpr, tpr, thresholds = roc_curve(testy, yhat) # 绘制模型的 ROC 曲线 pyplot.plot([0, 1], [0, 1], linestyle='--', label='No Skill') pyplot.plot(fpr, tpr, marker='.', label='Logistic') # 轴标签 pyplot.xlabel('False Positive Rate') pyplot.ylabel('True Positive Rate') pyplot.legend() # 显示绘图 pyplot.show() |
运行该示例,它会在训练数据集上拟合逻辑回归模型,然后使用测试集上的一系列阈值对其进行评估,从而创建 ROC 曲线。
我们可以看到图的左上角附近有许多点或阈值。
哪个阈值是最优的?

逻辑回归模型在不平衡分类中的 ROC 曲线图
我们可以通过多种方式找到在假阳性率和真阳性率之间达到最佳平衡的阈值。
首先,真阳性率称为灵敏度。假阳性率的倒数称为特异性。
- 灵敏度 = 真阳性 / (真阳性 + 假阴性)
- 特异性 = 真阴性 / (假阳性 + 真阴性)
其中
- 灵敏度 = 真阳性率
- 特异性 = 1 – 假阳性率
几何平均值或 G-Mean 是不平衡分类的度量,如果进行优化,它将寻求灵敏度和特异性之间的平衡。
- G-Mean = sqrt(灵敏度 * 特异性)
一种方法是使用 roc_auc_score() 返回的一组阈值来测试模型,并选择 G-Mean 值最大的阈值。
鉴于我们在计算 ROC 曲线时已经计算了灵敏度(TPR)和特异性的补数,我们可以直接计算每个阈值的 G-Mean。
1 2 3 |
... # 计算每个阈值的 g-mean gmeans = sqrt(tpr * (1-fpr)) |
计算完成后,我们可以找到 G-mean 分数最大的索引,并使用该索引来确定要使用的阈值。
1 2 3 4 |
... # 找到最大的 g-mean 的索引 ix = argmax(gmeans) print('Best Threshold=%f, G-Mean=%.3f' % (thresholds[ix], gmeans[ix])) |
我们还可以重新绘制 ROC 曲线并突出显示这一点。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# 具有最优阈值的逻辑回归模型的 ROC 曲线 from numpy import sqrt from numpy import argmax from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve from matplotlib import pyplot # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 只保留正向结果的概率 yhat = yhat[:, 1] # 计算 ROC 曲线 fpr, tpr, thresholds = roc_curve(testy, yhat) # 计算每个阈值的 g-mean gmeans = sqrt(tpr * (1-fpr)) # 找到最大的 g-mean 的索引 ix = argmax(gmeans) print('Best Threshold=%f, G-Mean=%.3f' % (thresholds[ix], gmeans[ix])) # 绘制模型的 ROC 曲线 pyplot.plot([0, 1], [0, 1], linestyle='--', label='No Skill') pyplot.plot(fpr, tpr, marker='.', label='Logistic') pyplot.scatter(fpr[ix], tpr[ix], marker='o', color='black', label='Best') # 轴标签 pyplot.xlabel('False Positive Rate') pyplot.ylabel('True Positive Rate') pyplot.legend() # 显示绘图 pyplot.show() |
运行该示例,首先找到最优阈值,然后报告该阈值和 G-Mean 分数。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到最优阈值约为 0.016153。
1 |
Best Threshold=0.016153, G-Mean=0.933 |
然后使用该阈值找到真阳性率和假阳性率,然后将该点绘制在 ROC 曲线图上。
我们可以看到最优阈值的点是一个大的黑色点,它似乎最接近图的左上角。

逻辑回归模型在不平衡分类中带有最优阈值的 ROC 曲线图
事实证明,有一种更快捷的方法可以获得相同的结果,称为 Youden 的 J 统计量。
该统计量计算如下:
- J = 灵敏度 + 特异性 – 1
鉴于我们有灵敏度(TPR)和特异性的补数(FPR),我们可以计算为:
- J = 灵敏度 + (1 – 假阳性率) – 1
我们可以将其重写为:
- J = 真阳性率 – 假阳性率
然后,我们可以选择 J 统计量最大的阈值。例如:
1 2 3 4 5 6 7 8 |
... # 计算 ROC 曲线 fpr, tpr, thresholds = roc_curve(testy, yhat) # 获取最佳阈值 J = tpr - fpr ix = argmax(J) best_thresh = thresholds[ix] print('Best Threshold=%f' % (best_thresh)) |
代入后,完整的示例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 具有最优阈值的逻辑回归模型的 ROC 曲线 from numpy import argmax from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 只保留正向结果的概率 yhat = yhat[:, 1] # 计算 ROC 曲线 fpr, tpr, thresholds = roc_curve(testy, yhat) # 获取最佳阈值 J = tpr - fpr ix = argmax(J) best_thresh = thresholds[ix] print('Best Threshold=%f' % (best_thresh)) |
我们可以看到这种更简单的方法直接计算了最优统计量。
1 |
Best Threshold=0.016153 |
精确率-召回率曲线的最优阈值
与 ROC 曲线不同,精确率-召回率曲线专注于分类器在正类(少数类)上的性能。
精确率是真阳性数量除以真阳性和假阳性总和的比例。它描述了模型在预测正类方面的优劣。召回率计算为真阳性数量除以真阳性和假阴性总和的比例。召回率与灵敏度相同。
精确率-召回率曲线是通过为一组阈值的概率预测创建清晰的类别标签,并为每个阈值计算精确率和召回率来计算的。绘制一个折线图,其中阈值按升序排列,召回率在 x 轴上,精确率在 y 轴上。
无技能模型由一条水平线表示,其精确率是数据集中正例的比例(例如,TP / (TP + TN)),在我们合成的数据集上为 0.01。完美技能分类器具有完整的精确率和召回率,点在右上角。
我们可以使用上一节中的相同模型和数据集,并通过精确率-召回率曲线来评估逻辑回归模型的概率预测。可以使用 precision_recall_curve() 函数来计算曲线,它返回每个阈值的精确率和召回率分数以及使用的阈值。
1 2 3 |
... # 计算 pr-curve precision, recall, thresholds = precision_recall_curve(testy, yhat) |
将此结合起来,计算逻辑回归模型在不平衡分类问题上的精确率-召回率曲线的完整示例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# 逻辑回归模型的 pr 曲线 from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import precision_recall_curve from matplotlib import pyplot # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 只保留正向结果的概率 yhat = yhat[:, 1] # 计算 pr-curve precision, recall, thresholds = precision_recall_curve(testy, yhat) # 绘制模型的 ROC 曲线 no_skill = len(testy[testy==1]) / len(testy) pyplot.plot([0, 1], [no_skill, no_skill], linestyle='--', label='No Skill') pyplot.plot(recall, precision, marker='.', label='Logistic') # 轴标签 pyplot.xlabel('Recall') pyplot.ylabel('Precision') pyplot.legend() # 显示绘图 pyplot.show() |
运行该示例,它计算每个阈值的精确率和召回率,并创建精确率-召回率图,显示模型在该数据集的各个阈值上都具有一定的技能。
如果我们从此模型需要清晰的类别标签,哪个阈值可以实现最佳结果?

逻辑回归模型在不平衡分类中的精确率-召回率曲线图
如果我们对产生最佳精确率和召回率平衡的阈值感兴趣,那么这与优化 F-measure 相同,F-measure 总结了这两种度量的谐波平均值。
- F 值 = (2 * 精确率 * 召回率) / (精确率 + 召回率)
与上一节一样,找到最优阈值的朴素方法是计算每个阈值的 F-measure。我们可以通过直接将精确率和召回率度量转换为 F-measure 来达到相同的效果;例如:
1 2 3 4 5 6 |
... # 转换为 f 分数 fscore = (2 * precision * recall) / (precision + recall) # 找到最大的 f 分数的索引 ix = argmax(fscore) print('Best Threshold=%f, F-Score=%.3f' % (thresholds[ix], fscore[ix])) |
然后,我们可以将该点绘制在精确率-召回率曲线上。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# 逻辑回归模型在精确率-召回率曲线上的最优阈值 from numpy import argmax from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import precision_recall_curve from matplotlib import pyplot # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 只保留正向结果的概率 yhat = yhat[:, 1] # 计算 ROC 曲线 precision, recall, thresholds = precision_recall_curve(testy, yhat) # 转换为 f 分数 fscore = (2 * precision * recall) / (precision + recall) # 找到最大的 f 分数的索引 ix = argmax(fscore) print('Best Threshold=%f, F-Score=%.3f' % (thresholds[ix], fscore[ix])) # 绘制模型的 ROC 曲线 no_skill = len(testy[testy==1]) / len(testy) pyplot.plot([0, 1], [no_skill, no_skill], linestyle='--', label='No Skill') pyplot.plot(recall, precision, marker='.', label='Logistic') pyplot.scatter(recall[ix], precision[ix], marker='o', color='black', label='Best') # 轴标签 pyplot.xlabel('Recall') pyplot.ylabel('Precision') pyplot.legend() # 显示绘图 pyplot.show() |
运行该示例,首先计算每个阈值的 F-measure,然后找到分数和阈值中具有最大值的。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行示例几次并比较平均结果。
在这种情况下,我们可以看到最佳 F-measure 为 0.756,使用约 0.25 的阈值实现。
1 |
Best Threshold=0.256036, F-Score=0.756 |
绘制精确率-召回率曲线,这次绘制了具有最优 F-measure 的阈值,其尺寸为一个较大的黑色点。
然后,在将来进行概率预测且必须将概率转换为清晰类别标签时,可以使用此阈值。

逻辑回归模型带有最优阈值的精确率-召回率曲线图
最优阈值调整
有时,我们只有一个模型,并希望直接知道最佳阈值。
在这种情况下,我们可以定义一组阈值,然后在每个阈值下评估预测的概率,以找到并选择最佳阈值。
我们可以通过一个实际示例来演示这一点。
首先,我们可以在我们的合成分类问题上拟合一个逻辑回归模型,然后预测类别标签并使用 F-Measure(它是精确率和召回率的调和平均数)来评估它们。
这将使用默认的 0.5 阈值来解释逻辑回归模型预测的概率。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 用于不平衡分类的逻辑回归 from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import f1_score # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测标签 yhat = model.predict(testX) # 评估模型 score = f1_score(testy, yhat) print('F-Score: %.5f' % score) |
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行示例几次并比较平均结果。
运行示例,我们可以看到该模型在测试数据集上达到了约 0.70 的 F-Measure。
1 |
F-Score: 0.70130 |
现在,我们可以在同一数据集上使用相同的模型,而不是直接预测类别标签,我们可以预测概率。
1 2 3 |
... # 预测概率 yhat = model.predict_proba(testX) |
我们只需要正类的概率。
1 2 3 |
... # 只保留正向结果的概率 probs = yhat[:, 1] |
接下来,我们可以定义一组阈值来评估这些概率。在这种情况下,我们将测试 0.0 到 1.0 之间所有阈值,步长为 0.001,即我们将测试 0.0、0.001、0.002、0.003 等等,直到 0.999。
1 2 3 |
... # 定义阈值 thresholds = arange(0, 1, 0.001) |
接下来,我们需要一种方法来使用单个阈值来解释预测的概率。
这可以通过将所有大于或等于阈值的值映射到 1,将所有小于阈值的值映射到 0 来实现。我们将定义一个 to_labels() 函数来完成此操作,它将概率和阈值作为参数,并返回一个 {0, 1} 中的整数数组。
1 2 3 |
# 将阈值应用于正概率以创建标签 def to_labels(pos_probs, threshold): return (pos_probs >= threshold).astype('int') |
然后,我们可以为每个阈值调用此函数,并使用 f1_score() 评估生成的标签。
我们可以像这样在一行中完成此操作
1 2 3 |
... # 评估每个阈值 scores = [f1_score(testy, to_labels(probs, t)) for t in thresholds] |
现在我们有了一个分数数组,该数组评估了我们阈值数组中的每个阈值。
现在我们只需要找到具有最高分数(最佳 F-Measure)的数组索引,然后我们就能得到最佳阈值及其评估。
1 2 3 4 |
... # 获取最佳阈值 ix = argmax(scores) print('Threshold=%.3f, F-Score=%.5f' % (thresholds[ix], scores[ix])) |
将所有内容整合起来,下面列出了在合成不平衡分类数据集上调整逻辑回归模型阈值的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# 搜索不平衡分类的阈值 from numpy import arange from numpy import argmax from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import f1_score # 将阈值应用于正概率以创建标签 def to_labels(pos_probs, threshold): return (pos_probs >= threshold).astype('int') # 生成数据集 X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0, n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4) # 划分为训练/测试集 trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y) # 拟合模型 model = LogisticRegression(solver='lbfgs') model.fit(trainX, trainy) # 预测概率 yhat = model.predict_proba(testX) # 只保留正向结果的概率 probs = yhat[:, 1] # 定义阈值 thresholds = arange(0, 1, 0.001) # 评估每个阈值 scores = [f1_score(testy, to_labels(probs, t)) for t in thresholds] # 获取最佳阈值 ix = argmax(scores) print('Threshold=%.3f, F-Score=%.5f' % (thresholds[ix], scores[ix])) |
运行示例报告的最佳阈值为 0.251(相比默认的 0.5),实现了约 0.75 的 F-Measure(相比 0.70)。
注意:由于算法或评估程序的随机性,或者数值精度的差异,您的结果可能有所不同。考虑运行示例几次并比较平均结果。
您可以使用此示例作为模板来调整您自己的问题的阈值,允许您替换您自己的模型、指标,甚至是要评估的阈值分辨率。
1 |
Threshold=0.251, F-Score=0.75556 |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
论文
- 来自不平衡数据集的机器学习 101, 2000.
- 使用解决类别不平衡问题的算法训练成本敏感型神经网络, 2005.
书籍
- 从不平衡数据集中学习 (Learning from Imbalanced Data Sets), 2018.
- 不平衡学习:基础、算法与应用 (Imbalanced Learning: Foundations, Algorithms, and Applications), 2013.
API
- sklearn.metrics.roc_curve API.
- imblearn.metrics.geometric_mean_score API.
- sklearn.metrics.precision_recall_curve API.
文章
总结
在本教程中,您了解了如何在不平衡分类中将概率转换为清晰的类别标签时调整最佳阈值。
具体来说,你学到了:
- 将概率解释为类别标签的默认阈值是 0.5,调整此超参数称为阈值移动。
- 如何直接为 ROC 曲线和精确率-召回率曲线计算最优阈值。
- 如何手动搜索所选模型和模型评估指标的阈值。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
嗨,Jason,
非常好的文章。我想问一下,阈值移动是否被认为是处理类别不平衡的其他方法(如重采样(例如 SMOTE))的替代方法,还是应该与其他不平衡学习技术结合使用?
谢谢!
它可以与其他方法结合使用,以确保您的模型能够根据您选择的指标实现概率到类别标签的最佳映射。
非常好的文章。在阈值时间方面,我们如何最好地处理多类别或多项式分类而不是二元分类?
这是个好问题。我没有示例——听起来很适合作为未来的教程。
您可以探索 OneVsRest 分类器,它为每个类别创建单独的二元分类器。
是的,这会有帮助
https://machinelearning.org.cn/one-vs-rest-and-one-vs-one-for-multi-class-classification/
一如既往的精彩文章,Jason。您能否在未来的教程中解决用于二元分类器的拒绝窗口技术?例如,分类器不是只有一个阈值,而是有两个阈值(较低和较高),并且会忽略落入该中间区域的预测。
好建议,谢谢!
我也有同样的想法。您实现了吗?
您好。感谢这篇非常有用的文章。如何为多类别分类问题调整阈值?
你好 Jason,
我读过关于 PyTorch 的文章。
scikit learn、Keras 和 PyTorch 之间有什么主要区别?
与 scikit learn 相比,写代码有多难?
是否(由您决定)值得一看?
您有示例吗?
谢谢
sklearn 用于机器学习。
tensorflow 和 pytorch 用于深度学习。
Keras 在 tensorflow 之上运行,现在也已集成到 tensorflow 中。
sklearn 很容易,keras 很容易,pytorch 很难。
非常精彩的文章,非常有帮助!
我总是对特异性感到困惑。你说它是
特异性 = 假阴性 / (假阳性 + 真阴性)
维基百科说
特异性 = 真阴性 / (假阳性 + 真阴性)
哪个是正确的?
谢谢
谢谢!
看起来是笔误,已修正。
嗨,Jason,
非常好的文章,我一直关注这个博客很久了。
我现在正在做一个项目,已经完成了所有工作,找到了一个完美的阈值,并使用这个新的预测集来很好地平衡我的精确率和召回率分数。
但是,我不知道如何使用这个阈值对新数据进行预测。我使用了一个已在我的训练数据上拟合的 RandomForestClassifier。现在要在新数据上获取预测,我只需要使用 **classifier.predict(X_test)**,新的阈值在哪里发挥作用呢??
我知道我可能忽略了一些东西,请指导我如何在测试数据上使用它。
非常感谢。
Pranay 🙂
谢谢!
好问题。选择一个阈值,预测概率,然后使用您的阈值将概率转换为类别。
您好,只是想确保我的理解是正确的——
所以,当我们建立了一个具有最佳阈值的模型后,我们使用该模型预测新数据,然后得到概率,然后使用之前的最佳阈值将这些新概率转换为类别?
正确。
只有当新阈值根据您选择的指标提高了模型的技能时,才有采用它的意义。
感谢您的澄清!非常感谢。
不客气。
这正是我想要的,Jason,文章写得太好了。
谢谢,很高兴听到这个!
非常好的文章 Jason。当试图找到最大化 F1 分数的最大阈值时,我得到了 Max F-score 的 NaN。这没关系吗?还是我的数据或模型有问题?
不是。检查您的数据或模型是否存在问题。
嘿,我也得到了 LightGBM 算法的最大 F-Score 的 NaN。您是如何解决您的问题的?
我的数据没问题,因为它可以在其他模型上工作。
Jason,感谢您的发帖!
我正在处理一个不平衡数据集上的 xgboost 二元分类模型。我已经完成了模型训练,并且精确率、召回率看起来都不错。由于是 xgboost 和不平衡数据,阈值需要仔细选择。我的问题是
1. 我应该使用训练数据还是验证数据来确定最佳阈值?
2. 我也尝试了概率校准,并且校准是在验证数据上完成的。在选择最佳阈值时,我应该使用 predict_proba 的原始输出还是校准后的概率?
非常感谢!
干得好!
验证数据。
也许可以在保留集上比较原始数据和校准数据的阈值。
明白了,如果我进行概率校准,我应该首先使用验证数据构建校准模型,然后在相同的验证数据上再次选择最佳阈值吗?
谢谢!
校准,然后设置阈值以获取标签。
嗨,Jason,
在选择阈值时使用哪个指标?您是倾向于精确率还是假阳性率?在处理极端不平衡数据集时,您认为哪个更合适?FPR 是否更稳定,而精确率极有可能受到新行为的影响?
谢谢!
你好
在模型中“概率校准”是什么意思?
请参阅此处关于校准的内容。
https://machinelearning.org.cn/calibrated-classification-model-in-scikit-learn/
谢谢!
不客气。
先生,您好,
我正在研究蚁群优化算法。在绘制 ROC auc 曲线时遇到了问题。您能指导我如何绘制 ROC 曲线吗?
是的,本教程展示了如何绘制 ROC 曲线。
https://machinelearning.org.cn/roc-curves-and-precision-recall-curves-for-classification-in-python/
嗨,Jason,
我也对多类别分类尝试了同样的操作,打印 roc_curve 以及 fpr 和 tpr 结果时不起作用。
出现此错误:不支持多类别格式
请为多类别问题提供相同的建议。
提前感谢。
ROC 曲线仅用于二元分类。
在多项式逻辑回归中,如果您假设每个结果都是 0/1(吃香蕉的概率、看电视的概率……),那么您可以创建多个 ROC 曲线,或者选择运行多个回归。您也可以假设每个结果的概率相对于其他结果的总和(基准)。如果兴趣是选择类别的组合而不是二元组合,那么最好应用不同的模型(例如协同过滤),因为 ROC 将不再相关,正如您所解释的那样。
感谢 Jason 的精彩文章!您是否有任何帖子解释如何选择这些不同的阈值移动方法,以及如何纳入假阳性与假阴性的不同成本?
没有。首先选择一个指标,然后调整阈值以优化该指标。
另外,最佳阈值调整和基于精确率-召回率曲线的方法本质上是相同的,但由于我们在最佳阈值中使用了更多的点来计算 f1 分数,因此我们得到了略有不同的阈值和 f1 分数。我的理解正确吗?
相同的一般方法,区别在于要优化的指标——例如,理解我们为什么要移动阈值的关键。
看起来这是使用 train_test_split 方法完成的。
您是否有建议或示例使用分层交叉验证来优化精确率和召回率?
不,阈值是使用单个保留数据集找到的。
相反,您可以使用交叉验证来估计包含阈值移动的建模管道的性能,但不能找到用于最终模型的特定阈值。
Jason,您能详细说明一下吗?
当我们有 CV 时,我们是否不能对所有折叠的 G-mean 或 F-measure 进行平均,并获得具有最佳平均值的阈值?您能给我一个使用 CV 进行阈值调整的方法吗?
在我的案例中,我计算所有折叠的指标平均值,然后按均值和标准差排序。最后,我得到具有最大平均 f1 和最小标准差 f1 的阈值。
还有其他更好的想法吗?
您可以使用交叉验证来估计模型的性能,并报告平均 f1 或 gmean。
这与阈值移动是分开的。您可以通过阈值移动来更改预测概率如何映射到清晰类别标签以进行 f1 和 gmean 评估,并且阈值移动过程可以在 cv 中进行。
嗨,Jason,
关于此事的后续问题。我在 cv 中尝试了阈值移动,但每个折叠得到的阈值差异很大。对阈值取平均似乎不是一个好选择。我的数据集是不平衡的,我必须进行分层分组 k 折,因为数据中存在分组。我该如何使用我在每个折叠中找到的阈值?
嗨 Gopi……以下资源可能对您有帮助。
https://machinelearning.org.cn/probability-calibration-for-imbalanced-classification/
你好 Jason,
如何为不平衡多类别分类器绘制 ROC 曲线。找到了一些关于微平均和宏平均的笔记,但无法正确理解 ROC 曲线。例如,在 4 个类别的样本中,是否应该将所有正类和负类合并在一起考虑,还是分别考虑?是否有关于此的详细文章?(我在 MATLAB 平台上工作)
ROC 曲线仅用于二元分类。
嗨,Jason,
您是否有关于多类别阈值设置的参考资料?
抱歉,我没有。
感谢 Jason 的精彩文章!
在单类别分类(即我们只有来自一个类别的样本)的情况下,我们如何选择最佳阈值?
不确定您是否可以,抱歉。
嗨,Jason,
这里 predict_proba 的公式是什么?
yhat = model.predict_proba(testX)
它是否采用每个类别的 logit 分数的 softmax 函数?即,将数字转换为概率?
这是一个逻辑回归模型,它本身就可以预测概率。
https://machinelearning.org.cn/logistic-regression-tutorial-for-machine-learning/
谢谢!
不客气。
我最近在各个博客上就不同主题问了一些问题,但我没有收到任何回复通知。我是否应该收到收件箱的通知?
不,该网站不会通知您回复。
Jason,您好,感谢您的精彩文章。我有两个问题:1.阈值调整和等渗回归之间有什么区别?它们是否可以同时使用,还是它们针对的是不同的问题?2.我尝试了使用我的分类器进行阈值调整,auc = 0.84,gmean的最佳阈值是0.0008,而PR曲线的最佳阈值是0.99885,我的模型有什么问题吗?有什么提示吗?谢谢!
阈值调整是关于将概率映射到标签。
校准是关于改变概率的分布。
将模型性能与朴素方法进行比较,以查看其是否具有技巧。
https://machinelearning.org.cn/naive-classifiers-imbalanced-classification-metrics/
Jason,您好,感谢您的帖子。我
想知道您是否可以帮助我弄清楚我需要在这儿做什么
我有一个4类分类问题,需要所有类都具有高真阳性和真阴性率。
数据非常不平衡(65:30:3:2)。
我训练了一个XGBoost模型,其真阳性率为85-80-65-60。
但是,我不知道如何调整才能获得良好的校准概率。到目前为止,我将CalibratedClassifierCV()嵌套在OneVsRestClassifier()中,但不确定这是否是正确的方法……
您可以建议一个方向吗?
不客气。
也许可以从最能体现您项目目标的指标开始。
https://machinelearning.org.cn/tour-of-evaluation-metrics-for-imbalanced-classification/
然后,也许这个框架中的方法可以帮助您改进结果。
https://machinelearning.org.cn/framework-for-imbalanced-classification-projects/
Jason,您好,感谢您的帖子。
我想知道您是否可以帮助我弄清楚我需要做什么——
目标是提高精确度,同时以一定的召回率为代价。
现在,如果我更改阈值以满足我的期望目标。
在部署后,此模型在实时生产数据中是否会达到预期性能?
还是会产生一些随机性,导致模型性能下降?
另外,如果引入了随机性,有什么办法可以处理吗?
提前感谢
嗨,Jason,
好文章。G-mean有什么“最佳”之处?F-measure有什么“最佳”之处?由于它们通常不会给出相同的结果,您将如何决定优化哪一个?最佳结果不是取决于假阴性与假阳性的相对成本吗?
对于选定的指标(如gmean或fmeasure)、已拟合的模型以及一些留存数据,我们可以找到最佳(最优)阈值——例如,一个最大化或最小化选定指标的阈值。
我的观点是,在现实世界的标准中,您会选择哪种指标进行优化,是G-Mean、F-measure,还是其他无限可能的指标?您不能问您的客户/业务利益相关者,因为这个问题无法用他们能够理解的与业务目标相关的术语来表述。
与其(1)为假阳性和假阴性分配明确的成本并计算由此产生的独特最优阈值,或者(2)探索精度与召回率(或真/假阳性率与真/假阴性率)之间的权衡,直到找到一个被业务利益相关者或客户根据其业务目标认为最合适的平衡点,是否更好?
例如,我在我的文章“如何处理不平衡分类而无需重新平衡数据:……在考虑对偏斜数据进行过采样之前,尝试调整分类阈值(显示简单的Python代码)”(https://towardsdatascience.com/how-to-deal-with-imbalanced-classification-without-re-balancing-the-data-8a3c02353fe3?sk=28387a3cff54ef9ac496611846a46e10)中演示了如何通过绘制和探索假阳性率和假阴性率与显示连续可能阈值范围的阈值轴来实现上述(2)。
我不同意。甚至可以说是强烈反对。
我无法提供具体的建议,但总的来说,我们可以通过多种方法(如讨论场景、谈论期望、最坏情况分析和比较不同的候选指标)间接了解客户的偏好(指标)。您可以解释他们的回应,并在任何报告中提供完全支持的理由来选择一个指标。
这可能是项目最重要的部分——例如,主要要求——如何确定成功。
您可以采取折衷的路线,使用f1或roc auc,但谁知道这是否合适。根据业务/场景,这可能非常不合适。
谢谢您的回复。我这样说吧。根据假阴性与假阳性的相对业务影响或成本,*任何*阈值都可能成为您客户的最优阈值。不仅仅是优化G-mean或F-measure或其他类似内容的那个阈值。
所以我建议更好的方法是探索所有连续的阈值范围,并查看它如何影响精确度与召回率之间的权衡,无论是通过查看精确度和召回率作为阈值的函数(如果这些指标对客户最有意义),还是像我的文章(上面链接)中描述的那样,通过查看假阳性率和假阴性率(或ROC曲线的变体,其中一条轴强调连续阈值)作为阈值的函数。
是的,这可能是一个有趣的诊断,可以更多地了解您的模型和领域。
尽管如此,模型性能还是会提炼成一个单一指标——一个需要优化的单一值。
好的,但您选择的任何阈值t*就是*特定单一指标的最优阈值。这就是成本指标:f(t) = t*FalsePositives + (1-t)*FalseNegatives(或任何任意常数乘以该指标)。因此,同样,任何阈值都可能是最优的,具体取决于假阳性的相对成本(即t)与假阴性的成本(即1-t)。例如,选择一个高的t值(使得大多数实例被分类为负类)对应于FP的成本远大于FN的成本,因此我们很少会冒着选择正类的风险。
但是,任何这样选择的阈值t不一定能优化其他指标,如G-mean或F-measure,所以我们不必局限于任何我们能想到的离散集合这些其他指标的阈值。
同意,选择一个优化一个指标的阈值会以未能优化另一个指标为代价。
这就是为什么我强烈建议我的读者选择一个最能捕捉项目目标的指标并专注于优化它。这会让生活变得更简单。
好的,这很好,但我仍然不明白,关于项目目标,什么类型的信息会引导某人选择优化G-mean或F-measure这样的指标。
我*确实*看到了什么类型关于项目目标的信息可以引导您根据假阳性与假阴性的相对成本来选择阈值t,或者通过探索所有连续的阈值范围来查看哪个阈值提供了最可接受的(对客户而言)精确度与召回率之间,或者例如假阳性率与假阴性率之间的权衡。
客户可以理解假阳性、假阴性、精确度、召回率、假阴性率、假阳性率等,但如果您告诉客户某个特定阈值优化了G-mean,而另一个阈值优化了F-measure,我不知道客户如何将这两种指标的选择与业务目标联系起来,而且这两种特定阈值中的任何一个碰巧提供了客户最优的假阳性与假阴性之间的权衡,可能性也很小。
请参阅本文中的流程图,以将项目目标(对预测重要的内容)与指标联系起来。
https://machinelearning.org.cn/tour-of-evaluation-metrics-for-imbalanced-classification/
如果您的客户在技术上不那么精通(大多数客户都是),您可能需要收集他们的需求,解释它们,将其映射到一个指标,然后反向将指标映射回客户的术语。
这一切都不应令人惊讶,它与传统的咨询工作非常相似——如果您来自那个背景(听起来您是)。
杰森,文章写得太棒了!
我有一个困扰我很久的问题。在您的演示中,您在将模型拟合到测试集后确定了阈值。有些人建议在使用训练集构建交叉验证模型时确定阈值。我不确定如何做到这一点,但如果您能分享您的看法,我将不胜感激。
是的,理想情况下,您应该在CV折叠中包含阈值查找,或者在留存/验证数据集上执行该操作。
感谢这篇文章。是否有可能为您的模型(例如Random Forest的最大深度、n_estimator和其他参数)进行超参数微调,同时使用predict_prob获取概率来查找二元分类器的最佳阈值?
例如,RF=RanndForestClassifier(nestimator)。那么在找到超参数后,您是否仍然可以继续寻找最佳阈值?或者一旦找到最佳阈值,就没有必要使用或微调算法的超参数了。谢谢
是的,您必须将阈值移动作为模型或建模管道的一部分。我认为您需要手动运行网格搜索,以便有空间运行自定义代码来执行所有必需的步骤。
是的,或者,您可以先进行网格搜索,然后将阈值移动作为最后一步。结果可能不会那么好。
喜欢您的文章!如果最佳阈值是使用测试集找到的,那么测试集的性能看起来可能比训练性能好得多。是否有必要回顾一下用新的最佳阈值对训练集的指标进行评估?
谢谢!
理想情况下,您会使用一个大的留存验证数据集来查找阈值。
你好 Jason,
感谢您提供如此精彩的文章和精彩的内容。我只想检查一下,如果数据不平衡,模型的意图是只预测其中一个类别更好,也就是说,我目前不介意我的1被分类为0,因为我的主要意图是预测0。我能否继续并设置一个更高的阈值?
此外,模型的一个意图是数据最终会进一步不平衡。
如果我能解释清楚问题,并且这是否有意义,请告诉我。
也许可以。
选择一个指标并优化它,将结果与朴素模型进行比较。
你好 Jason,
感谢您清晰的解释。我有一个问题想问您。
1)。我在尝试找到正确的阈值时面临另一个复杂性。由于逻辑回归模型可以调整其他参数,我正在运行一个网格搜索,然后找到最大化某个评分器的参数。现在,在拥有最佳模型后,我生成ROC曲线并选择最佳阈值,得到的阈值是否会比所有模型(使用不同超参数的模型)都好?
2)除了使用to_labels()函数,sklearn是否有什么内置函数可以让我获得最佳阈值点?如果没有,我想知道为什么他们不提供它。
谢谢你,
Karthik
您可以将阈值移动作为建模管道的一部分,并像调整其他任何超参数一样调整/选择它。
最佳阈值取决于您选择的指标,如上所述。
谢谢!
不客气。
谢谢Jason。
对于我正在处理的不平衡数据集,我尝试了阈值移动,并根据最佳f1分数进行预测,如上文所示。它确实略微提高了真阳性,但有助于减少假阳性,我达到的最大f1分数是0.5,这不算很好。
那么我的下一步是什么?研究特征?成本敏感分类?我尝试使用imblearn中的API对训练数据集进行平衡,但没有帮助。
谢谢!
尝试这里列出的一些技术。
https://machinelearning.org.cn/framework-for-imbalanced-classification-projects/
嗨,Jason。
我对这项技术有一个澄清。
我有一个不平衡数据的误分类成本函数。所以,根据一系列阈值,我正在寻找给出最低成本的阈值。
过程是:使用kfold交叉验证对训练数据进行超参数调优 —> 选择最佳模型 —> 预测测试数据的概率 —> 获取给出最低成本的最佳阈值。
如果我保存了这个模型并在各种测试数据集上运行它,最佳阈值也会有所不同,对吗?可以吗?
这听起来是一个不错的起点。
谢谢 Jason。
非常感谢,Jason!
如何使用调整后的阈值进行预测?
最后一个部分中的to_labels()函数正是为此目的。
嘿,哥们,谢谢你的教程。它真的帮了我很多。但我在这里遇到了麻烦。当我尝试应用G-means时,它显示了一个错误,如下所示:
TypeError: only size-1 arrays can be converted to Python scalars
我对此感到很困惑。我已经搜索过Stackoverflow和其他网站,但仍然无法解决。谢谢,哥们:“)
不客气。
或许这些提示会有帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
你好Jason,很棒的文章!
找到最佳阈值后,如何实现它来使用具有此新配置的模型?我正在研究一个COVID-19诊断项目,我的当前模型达到了(0.76召回率和0.75特异性0.72)。如果我找到一个最大化召回率的最佳阈值,使用您描述的方法,如何将其设置为在生产中部署模型?例如,更改新阈值,pickling模型并将其放入API等。
非常感谢!
上面的to_labels()函数向您展示了如何使用阈值将预测概率转换为标签。
Jason,您好,非常感谢您的信息丰富的文章。它极大地帮助了我。
在我工作的领域,我经常需要控制特异性。假设我想达到0.90的特异性,基于此,然后找到相应的阈值T和灵敏度。关于使用哪个sklearn函数来控制特异性,您有什么建议吗?非常感谢您的帮助。
也许可以从简单的阈值枚举开始,然后手动评估所需的指标。
非常感谢,Jason。我希望有一个我不知道的函数参数,它允许我设置specificity=0.9。嗯,显然没有。很好。
这确实存在,并且在Facebook的MMF库中。(他们也在那里大量使用它)。
Precision@recallK 和 Recall@precisionk。
这两个指标得到的关注度不高,但FB的Yann等人对它们的使用如此频繁,以至于他们将其纳入MMF。
这是一篇出色的文章,我非常欣赏您在帖子中的细节。我有一个关于寻找最佳阈值的问题。我的理解是,当使用precision-recall-curve或ROC寻找值时,应该使用验证集而不是测试集。原因是使用测试集来确定阈值存在泛化不足(过拟合)的风险。您将如何解决这个问题?您会建议创建一个训练、验证和测试集,并使用验证集来识别最佳阈值吗?我还读到可以使用交叉验证。也就是说,我不清楚如何使用交叉验证。总的来说,我使用交叉验证来评估在预测测试集之前,给定训练集的泛化能力,使用cross_val_score(X_train, y_train, cv=cv, scoring=’auc-score’)。您将如何使用它来识别阈值?
谢谢。
是的,留出一些数据并将其用于阈值移动。
你好 Jason,感谢你的这篇帖子。我有一个问题。让我们考虑一下逻辑回归的分类超平面。阈值移动有点像分类超平面的位移。那么它们之间有什么区别呢?据我所知,有一些文章通过移动分类超平面来解决不平衡问题。那么这些文章的意义是什么呢?因为这个问题可以通过阈值移动来解决。
不客气。
是的。你可以这样做(阈值移动)来调整任何任意的指标,以及任何算法。
谢谢!也就是说,阈值移动的应用范围更广。
是的。
你好 Jason,
首先,非常感谢您分享如此多宝贵的机器学习见解。
阈值移动似乎可能导致不切实际的预测类别平衡:您将如何处理您的预测类别率与初始率不匹配的事实?
我有一个实际的例子,是信用评分失败预测,其中阈值调整以达到最佳的Youden J指数,导致失败率略有不同于“现实”。观察到的失败率为 8% 的不平衡,而优化后的预测失败率约为 32%。
感谢您的见解,致敬
不客气。
一个很好的问题是使用大型且具有代表性的验证数据集来执行阈值调整。
嗨,Jason,
您的文章太棒了……我一直在连续阅读它们,并为我正在从事的项目找到了大量新信息。我确实有一个问题……我决定选择 ROC AUC 作为评估指标,并且我得到了 0.91 的好分数。然后我尝试根据您解释的 J 统计量找到最佳阈值……假设最佳阈值为 0.6……我将此阈值放回我的 predic_proba 中用于逻辑回归,并计算了新的 y_pred_proba + 新的 ROC AUC……但我的新 ROC AUC 是 0.85……
我期望这比第一个 ROC AUC 要高,第一个 ROC AUC 是基于 predict_proba 中的 0.5 阈值构建的……我哪里做错了?
谢谢!
也许可以确保您使用了独立的保留数据集来选择阈值,例如未用于训练模型的数据。
然后,在最终测试集上评估新流水线的效果,该测试集未用于训练或移动阈值。
您好 Jason,感谢您的建议。
我将我的整个数据集分成训练、验证和测试数据集。我的验证数据集 ROC AUC 为 0.91,并使用此数据集(占总数据的 20%)来查找最佳阈值。然后使用测试数据集(同样是 20%)通过新的最佳阈值来测试 ROC AUC……结果是 0.85……
我知道在没有太多上下文的情况下很难诊断,但您还有其他建议吗?
也许可以考虑使用嵌套交叉验证来选择阈值。
也许可以将阈值选择为 CV 评估的平均值。
也许可以尝试其他模型和模型配置。
也许可以尝试成本敏感型模型。
也许可以尝试 50/50 的划分。
有一件事让我对您的问题感到困惑。当您说“用新的最佳阈值测试 ROC AUC”时……ROC 及其 AUC 不依赖于阈值的选择,因为它们是直接从模型的连续概率预测或决策函数计算出来的,而无需提前对其进行阈值处理。ROC 是在假设阈值的整个范围内计算的,而不是一个选定的阈值。
同意,ROC 和 AUC 是针对阈值连续性的。但是,您最终需要做出特定的预测,这将需要一个阈值(如果您需要清晰的类别标签)。
嗨,Jason!
感谢这篇很棒的文章。
我有一个二元分类不平衡的数据集。只有 7% 的记录属于正类。所以我应用了少数类过采样技术和多数类欠采样技术并重新评估。但是分类器的 AUC 没有提高超过 0.5。所以我应用了阈值移动的方法,我得到了这个结果
最佳阈值=1.000000,G-Mean=0.420 为什么会这样?我该如何解决?
谢谢。
也许可以探索此处列出的其他技术
https://machinelearning.org.cn/framework-for-imbalanced-classification-projects/
这感觉就像我们在作弊——我们正在测试集上尝试各种阈值,然后为每个阈值计算性能指标(ROC/PR 曲线就是这样做的),然后选择最佳阈值。我们是不是在“使用”测试集进行超参数优化——难道不应该使用单独的验证集来评估阈值吗?
哈哈。
是的,我们必须使用一个保留数据集来选择阈值,例如一个验证集。然后,在测试集上评估模型+阈值。
说得通,谢谢!
Jason,好文章。一个快速的问题,一旦我们找到了最佳阈值,如何打印混淆矩阵?
谢谢!
这将帮助您处理混淆矩阵
https://machinelearning.org.cn/confusion-matrix-machine-learning/
一旦您通过方法确定了最佳阈值,
如何实际实现这个阈值?
将其最佳值用于替换 predict_proba 中的默认值 0.5
请参阅最后一个示例中的“to_labels()”函数,该函数用于根据阈值将预测概率转换为标签。
谢谢
是的,我明白了,但是您能否将最佳阈值作为参数插入 LogisticRegression 模型类或 LogisticRegression 中的函数,以便使用此阈值而不是 0.5?
如果您能在拟合 Logistic Regression 模型之前将其设置为全局参数,那就太好了。
不,您需要后处理预测以应用阈值。
嗨,Jason,
我有一个困惑。
在寻找最佳阈值时,我们应该使用训练样本的概率而不是测试样本的概率,还是反之?如果使用测试样本来获得阈值,那么我们不是在利用测试数据信息进行标签预测,这不应该是正确的,因为测试数据应该是未知的,并且不应以任何方式泄露信息给训练模型。
理想情况下,您会使用一个验证数据集来查找阈值,该数据集未用于训练或评估模型。
你好,先生,
如果我们使用最后一层带有 Sigmoid 激活的深度神经网络,如何计算 roc 曲线?即
# 预测概率
yhat = model.predict_proba(testX)
# 只保留正向结果的概率
yhat = yhat[:, 1]
在代码中应该用什么来替换它?
与任何其他模型一样,收集预测概率。例如 model.predict(…)
很棒的帖子,非常感谢!
我想了解更系统地寻找最佳阈值的方法和理论。您能否推荐一些综述性论文或书籍?
谢谢!
请参阅上面“进一步阅读”部分中的参考文献。
感谢您详细的帖子!
我有一个关于何时需要阈值移动的问题。
我目前正在构建一个梯度提升模型,该模型预测二元目标变量。我一点也不关心它的预测。但我只想看看模型,了解模型使用了哪些特征进行预测。我使用 FI 和 SHAP 来做这件事。
当然,FI 和 SHAP 的结果对于性能良好的模型更可靠。所以我用 ROC AUC 评估了模型的性能。
我想知道在这种情况下设置阈值是否有意义。我实际上是在处理训练好的模型,而不是它的预测,所以预测概率如何映射对我来说并不重要。选择的阈值会影响模型的训练方式以及它如何利用数据中的特征吗?
不客气。
当您的模型本机预测概率并且您需要类别标签时,它可以提供帮助,并且预测概率中存在一些偏差,使得性能对于您选择的指标不理想。
如果您不确定,可以尝试一下。
嗨,Jason,
感谢您的快速回复,但我还不确定该怎么做。
我不需要任何类别标签,我需要知道我的模型是否训练得很好(我通过 AUC ROC 进行评估),然后我查看模型以了解其特征重要性。
更改模型的阈值是否会改变预测概率以及模型的训练方式?因为我认为阈值仅在预测概率之后应用,所以更改阈值不会改变概率,对吧?
如果您不需要类别标签,那么您可能不需要阈值移动。
而且 ROC AUC 在所有阈值下都使用标签,它可能不是最适合您的指标,请考虑对数损失或 Brier skill score。
谢谢。我会尝试这些指标!
嗨,Jason,
感谢您写了这篇精彩的文章。
我已阅读以上所有内容。
我仍然对使用哪个数据集来搜索最佳阈值感到困惑。
在我的例子中,我使用的是 XGBoost 模型。我计划执行以下步骤。
(A)
将我的数据集分割为训练集、验证集_1、验证集_2 和测试集。
1)首先,对于训练集,使用交叉验证方法查找最佳超参数(不包括 n_estimators)。
2)其次,使用最佳超参数训练训练集以获得模型。
3)第三,使用验证集_1 进行早期停止以找到最佳 n_estimators,以避免过拟合。
4)第四,使用验证集_2 来查找最佳阈值。
5)最后,使用测试集评估我的模型。
或者,
(B) 将我的数据集分割为训练集、验证集_1 和测试集。
1)首先,对于训练集,使用交叉验证方法查找最佳超参数(不包括 n_estimators)和最佳阈值。
2)其次,使用最佳超参数训练训练集以获得模型。
3)第三,使用验证集_1 进行早期停止以找到最佳 n_estimators,以避免过拟合。
4)最后,使用测试集评估我的模型。
我的数据集非常小,只有 200 个样本,您建议哪种程序?或者有什么其他建议?
先谢谢您了。
对 xgboost 使用阈值是没有意义的,因为它不会本机预测概率。
嗨
谢谢你的回复。
我使用的是 XGBClassifier,而 sklearn 的 predict_proba() 可以给出概率。那么使用 XGBClassifier 的阈值仍然没有意义吗?
可能不会。
如果您确实想使用此模型,也许可以先校准概率,然后使用阈值移动——然后与直接使用 xgboost 模型进行比较。我预计直接使用该模型会表现更好,并且会 _简单得多_。
嗨,Jason,
感谢您的回复。
我能否将同一个验证集用于早期停止和查找最佳阈值,或者我应该使用两个单独的验证集,一个用于早期停止,一个用于查找最佳阈值?
嗯——这个问题不是很清楚。凭直觉,我认为如果您可以的话,您会使用不同的数据来进行早期停止和阈值查找。
在多类分类的情况下,在阈值选择过程之后。如果我们有类别 A、B、C 并且 pA>threshold-A 和 pB>threshold-B,pC>threshold-C。在这些情况下如何选择分类?
阈值移动是针对二元分类的。
对于多类分类,您可能需要考虑 one-hot 编码和 argmax。
如何引用您的文章?
看这里
https://machinelearning.org.cn/faq/single-faq/how-do-i-reference-or-cite-a-book-or-blog-post
亲爱的 Jason,感谢这篇信息丰富的文章。
我想知道您能否告诉我为什么我们只需要保留积极结果的概率来计算 fpr、tpr、召回率、精确率?
(我只运行了您的代码,我的数据和 roc_curve 和 precision_recall_curve 函数会报错,因为 testy 的长度与 yhat 不同,这对我来说是合理的,因为对于 yhat,我们只取积极目标的概率,但对于 testy,我们取所有数据点,0 和 1)。
非常感谢!
模型将为每个类别预测一个概率。在二元问题上,我们只需要正类别的概率,因为 1 – p 是类别 0 的概率。从这里我们可以计算我们的阈值。
向量的长度将是相同的,例如,每个输入样本/行的概率为一。
嗨 Jason,在使用验证数据(假设是训练数据的 20% 留出数据)优化阈值后,我们是否应该在整个训练数据(60% 训练 + 20% 验证数据)上训练模型,然后对测试数据进行预测,还是仅使用 60% 的训练数据训练模型并对测试数据进行预测?
一旦选择了阈值,您就可以在所有数据上训练模型,然后开始使用该模型和阈值来预测新数据。
谢谢您的回复,Jason。它确实很有帮助。请指导一下,当我们使用 knn 和决策树等机器学习算法时,我们应该如何优化阈值,是使用验证数据还是通过绘制测试数据概率的 roc 曲线然后应用 min dist(0,1) 等阈值方法?就像在深度学习模型中一样,验证数据用于防止模型的过拟合/欠拟合。但在机器学习算法中,当数据更多时,阈值可能会在训练时发生变化。请告诉我,因为我需要比较深度学习模型与机器学习算法在类别不平衡数据集上的性能。我是否应该为机器学习算法使用验证数据?
没有单一的最佳方法。您必须设计一个适合您项目的测试框架——例如,您理解并信任的框架。
您好,Jason。我正在处理一个二元分类问题。我遇到了这种情况,我使用了 sklearn 导入的逻辑回归和支持向量机模型。这两种模型都用相同的数据集进行了拟合,并且取得了可比的性能。当我使用这两个预训练的模型来预测一个新数据集时。LR 模型和 SVM 模型预测为正例的实例数量相似。
但是,当我查看被归类为正例的概率分数时,LR 的分布是从 0.5 到 1,而 SVM 从大约 0.1 开始。我调用了 model.predict(prediction_data) 函数来找出被归类为每个类别的实例,并调用了 model.predict_proba(prediction_data) 函数来给出被归类为 0(负)和 1(正)的概率分数,并假设它们都具有默认阈值 0.5。
我的代码没有错误,我不知道为什么 SVM 将概率分数 < 0.5 的实例也预测为正例。您对如何解释这种情况有何看法?提前感谢您的帮助!
通常,SVM 不会预测经过校准的概率,我建议先校准模型。
https://machinelearning.org.cn/calibrated-classification-model-in-scikit-learn/
感谢您的回复。我会对此进行检查。
嗨,Jason,
感谢这篇非常有信息量的文章。我正在处理一个 4 类分类问题,其中包含不平衡数据集,使用随机森林。我遵循了您使用 AUC ROC 图查找最佳阈值的方法。所以我生成了 ROC 曲线折线图:类别 A vs rest,类别 B vs rest,类别 C vs rest 和类别 D vs rest 图 & 为每个图找到最佳阈值。为 4 个类别生成的概率总和不为 1,我想知道这是否是一个问题?如果是,我该如何解决?
(我也尝试使用 10 折交叉验证为每个类别找到最最佳阈值,并且从每个折叠来看,4 个类别的总和在 0.94 – 1.12 的范围内。)
完全不是问题。因为您不能保证给出可以相加的概率。每个模型都在估计 X 类与非 X 类的对比。但这并不意味着非 X 类必须是其余类别之一。因此是结果。实际上,在 OvR 模型中,如果您引入一个新类别,如果模型运行良好,它应该为所有模型给出低概率。
感谢您写了这篇好文章!我想知道您是否碰巧有一篇展示 OVO 方法而不是 OVR 来绘制 ROC 曲线的文章?
我还可以问一下,哪种方法是首选,OVR 还是 OVO?为什么是这样?
您可以自己生成一个:过滤输入数据只包含两个类别,然后运行代码生成 ROC。但您应该知道,对于 N 个类别,OvR 有 N 条曲线,而 OvO 有 N*(N-1) 条曲线。
多类别分类的情况下如何?是否可以设置阈值?如果可以,该如何操作?
例如:class_a: 0.32;class_b: 0.33,class_c: 0.35。如果不指定阈值,它将归入 class_c。但阈值在这种情况下如何工作?
多类别分类可能会同时将多个类别分配给同一个输入。所以您必须提出自己的策略。您从模型中得到的是一个介于 0 和 1 之间的浮点数向量。您可以非常灵活地解释结果。您提到的只是选择最大值,这只是可能的解释方式之一。
在二元像素级分割问题中,您将如何处理这个问题?
如何将正类像素与负类像素隔离,以用于 to_label() 函数?因为这里的 yhat 将是一个 mxn 矩阵(预测分割掩码的图像维度)
这篇帖子的逻辑应该可以工作。在您的情况下有什么具体问题吗?
好文章 Jason!我想知道,如果您想在同一数据集上比较不同模型(例如超参数优化),是否必须在比较之前为每个模型找到最佳阈值?
在我看来,一个更好的模型应该在不考虑阈值的情况下更好,因为两个模型都为相同的阈值进行了评估和比较。
感谢您的反馈,Elias!
您好,恭喜您写了这篇精彩的文章。
我的问题可能很天真,但我仍然不明白 roc_auc_score() 函数的阈值是如何计算的。如果您知道,可以告诉我吗?
你好 SuRi……你可能需要研究一下 softmax 函数。
https://machinelearning.org.cn/softmax-activation-function-with-python/
您好,不错的文章。
我的问题是,如果阈值本身以及相关的指标决定了您的最佳模型(例如,在训练期间使用验证 F1 分数来选择最佳模型)。在这种情况下,您是否从默认阈值开始,找到最佳模型,然后针对训练好的模型优化阈值?
嗨 EAZ… 我认为您提出的方法是合理的。让我知道您的发现。
大家好,
在处理平衡数据集时,寻找最佳阈值有意义吗?
谢谢
很棒的文章。但是,使用示例,当使用 ROC 曲线的最佳阈值时,roc_auc_score 并没有更好。您能解释一下为什么会这样吗?
嗨 Jo… 这只是为了演示目的。您说得对,其他方法会更优。
您好,我尝试使用您的 ROC AUC 曲线示例进行最佳阈值调整。然而,使用最佳阈值后,roc_auc_score 比之前更差。
您能解释一下为什么会这样吗?
嗨 Jo… 请澄清您引用的代码列表,并具体说明您修改了什么,以便我能更好地帮助您。
很棒的文章,谢谢。
多数派的错误付出的代价更高(假阴性)。需要改变什么?
嗨 Peter… 请澄清您的问题,以便我能更好地帮助您。
负样本数量 80%,
假阴性成本 > 假阳性成本,
目标:真阳性 -> 最大化,假阴性 -> 最小化。
我应该使用什么指标?我可以使用成本矩阵吗?
.predict_proba 在 Tensorflow 2.6 版本中已被移除。
可以问一下为什么 .predict 的结果不一致吗?
嗨 Min… 您指的是哪个具体的代码列表,结果却不一致?由于训练的随机性,一些差异是可以预期的。
https://machinelearning.org.cn/stochastic-in-machine-learning/
嗨 Jason,我正在尝试计算 ROC 曲线最佳点处的 TPR、TNR、精确率和 F1 分数。我可以计算 TPR 和 TNR,但无法计算精确率/F1 分数/准确率,因为 sklearn 提供的 roc_curve() 函数不提供精确率分数。您能否帮助我如何在该点计算精确率/F1 分数?谢谢。
嗨 Md… 您可能会对以下内容感兴趣
https://machinelearning.org.cn/roc-curves-and-precision-recall-curves-for-imbalanced-classification/
感谢您撰写如此精彩的文章,我有一个关于您将阈值移动部分作为每个 CV 折叠一部分的问题。并且从每个折叠中获得的阈值不同。那么,您如何决定最终模型使用的最终阈值?您可以平均阈值,例如跨 10 个 CV 折叠取平均值并使用吗?有没有更好的替代方案?
我还有一个问题,通过运行这 10 个 CV 得到的准确率或 F1 分数或其他分类指标,是使用每个折叠不同阈值的结果。这意味着这些分类指标不能反映出当您实际使用最终阈值时的所谓“真实性能”。我的问题是,我应该如何报告分类性能?我是否需要使用最终阈值重新运行 CV?
非常感谢!
嗨 Ink… 您太客气了!您走在正确的道路上!您绝对可以平均化阈值,正如您所建议的。
谢谢 James,您能否就我问题的第二部分给出建议?
我还有一个问题,通过运行这 10 个 CV 得到的准确率或 F1 分数或其他分类指标,是使用每个折叠不同阈值的结果。这意味着这些分类指标不能反映出当您实际使用最终阈值时的所谓“真实性能”。我的问题是,我应该如何报告分类性能?我是否需要使用最终阈值重新运行 CV?
嗨 Jason,这是一篇极好的帖子。我的问题与之相关,但未在此处解决。如果我们注意到数据中的子组,对于这些子组,使用单一截止值可能会损害预测准确性,是否可以使用多个截止值(2 个或更多),假设我使用相同的科学方法?(即“训练”然后测试另一个留出集)。感觉像是模型堆叠对话的开始,但 2 个截止值更像是一个后处理步骤,而不是一个全新的构建(这是我想要避免的)。
嗨 Adam… 您可能会对以下内容感兴趣
https://datascience.stackexchange.com/questions/77264/finding-optimal-threshold-in-multi-class-classification-task
嗨 Jason,这非常有帮助。但有一个问题。最近我开发了一个分类模型,我可能认为它是一个“完美模型”,因为它在训练、测试和验证(样本外)上都给出了大约 99% 的(流失)概率。这里的 THRESHOLD 值应该是多少?查看精确率-召回率图,我总是看到任何阈值下的精确率为 99%,而两者都在大约 99.7% 时开始下降。我不太确定我是否做错了什么。
谢谢你。
嗨 Jaime… 您太客气了!您可能想确定您的模型在泛化到从未见过的数据方面有多好。
https://machinelearning.org.cn/overfitting-and-underfitting-with-machine-learning-algorithms/
”
1. 在训练数据集上拟合模型。
2. 在测试数据集上预测概率。
3. 对于阈值列表中的每个阈值
3a. 使用阈值将概率转换为类别标签。
3b. 评估类别标签。
3c. 如果分数优于最佳分数。
3ci. 采用阈值。
4. 在对新数据进行类别预测时使用采用的阈值。
”
当然,在训练后调整阈值,您实际上是在过拟合测试数据集。
绝对正确 Kiril!继续保持出色的工作!
Jason,
以下是我遇到的情况。我的重点是精确率,在调整以获得最佳值时,我不断提高阈值,使假阳性为 0,从而得到精确率 = 1,但这并不是我想要的,因为它将每个误分类都变成了假阴性。当我使用这里介绍的技术时,我能很好地平衡假阴性和假阳性,但这似乎是以较低的阈值为代价的,而且我发现了太多代价高昂的误分类(在我的问题中是假阳性)。有没有什么技术可以找到一个最大化精确率但不至于将假阳性降至零的阈值?
老师您好,假设我拟合了一个模型,并对测试集使用 predict_proba,它会为每个类别标签输出概率,例如 [0.1, 0.2, 0.3, 0.05, 0.35],计算出的每个类别标签的阈值是 [0.09, 0.1, 0.25, 0.1, 0.2],那么输出 [0.1>0.09, 0.2>0.1, 0.3>0.25, 0.050.2],仍然是 1、2、3、5 类有它们的位置,那么按概率降序排序 [0.35, 0.2, 0.3, 0.1],类 5 仍然占主导地位吗?我们应该如何处理这类阈值设置和权重调整?
嗨 sangeetha… 以下资源可能对您感兴趣
https://pub.towardsai.net/improve-your-classification-models-with-threshold-tuning-bb69fca15114
另外,以下资源讨论了最佳阈值调整
https://towardsdatascience.com/optimal-threshold-for-imbalanced-classification-5884e870c293
谢谢您,我会看的。感谢您的推荐。
嗨,Jason
我有一个 SVC(二元分类器)模型,使用从 Densnet 提取的特征进行训练。
当使用 det_curve 时,它只返回一个阈值以及相应的 fpr 和 fnr 值,尽管我的预测分数在 0 到 1 的范围内。
为什么会这样,您能解释一下我哪里做错了什么吗?
嗨 S Ahmad… 以下是 3 部分系列中的第 1 部分,可能对您感兴趣
https://machinelearning.org.cn/method-of-lagrange-multipliers-the-theory-behind-support-vector-machines-part-1-the-separable-case/
嗨,Jason,
感谢您撰写如此精彩的文章。我有一个不平衡的问题,因为我的结果比例是 5% 对 95%。我训练了一个 CatBoost 模型,AUC 为 0.89,但 F1 分数为 0.1。我使用了类别权重、SMOTE 等采样方法。此外,我还优化了阈值并使用了类别权重方法。然而,我获得的最佳 F1 分数是 0.38。还有其他方法或技巧可以用来将 F1 分数提高到至少 0.7 吗?
谢谢你。
嗨 Yousif… 在高度不平衡数据集的背景下提高 F1 分数确实具有挑战性。尽管优化了类别权重并使用了 SMOTE 等技术,但 F1 分数为 0.38 表明模型在平衡精确率和召回率方面存在困难,尤其是对于少数类。以下是一些您可以探索以潜在提高 F1 分数的策略和高级技术
### 1. **高级采样技术**
– **基于聚类的过采样**:而不是使用 SMOTE,它仅基于特征空间生成合成样本,请考虑使用基于聚类的过采样 (CBO) 等方法。此方法涉及对少数类进行聚类,然后对每个聚类内的样本进行过采样,有助于保留类内方差。
– **Borderline SMOTE 或 ADASYN**:这些是 SMOTE 的变体,它们专注于在决策边界附近生成合成样本,这可能有助于创建更通用的决策表面。
### 2. **成本敏感学习**
– **更精确地调整类别权重**:除了使用自动类别权重计算外,您还可以更细致地手动调整权重,也许是基于验证集性能而不是仅仅基于训练集。
– **自定义损失函数**:修改损失函数,对少数类上的错误预测进行更严厉的惩罚。CatBoost 允许使用自定义目标函数,您可以对其进行调整,以优先减少假阴性或假阳性,具体取决于哪一个对您的 F1 分数影响更大。
### 3. **集成方法**
– **提升少数类**:训练多个模型,其中每个新模型越来越多地关注先前模型错误分类的少数类样本。
– **混合模型**:使用结合了使用不同重采样技术训练的模型。例如,将原始数据上训练的模型预测与 SMOTE 增强数据上训练的模型预测混合。
### 4. **阈值移动**
– **调整决策阈值**:尽管您已经优化了阈值,但请重新审视它,重点是专门最大化 F1 分数。使用精确率-召回率曲线来找到最佳权衡。
– **精确率-召回率权衡**:探索 F-beta 分数等工具,您可以在其中调整 beta 参数,以根据对应用程序的更关键程度来更侧重于召回率或精确率。
### 5. **特征工程**
– **添加交互项**:有时,模型没有充分捕捉特征之间的交互作用,但这些交互作用可能对少数类具有高度预测性。
– **降维技术**:PCA 或 t-SNE 等技术可能会揭示新的视角或特征组合,这些组合能更好地分离类别。
### 6. **异常检测技术**
– 将少数类视为异常,并探索异常检测算法。这有时可以突出传统算法所忽略的数据的不同方面。
### 7. **外部数据**
– **整合更多数据**:如果可能,添加更多样本,尤其是少数类样本,可以帮助模型学习更鲁棒的特征。
– **迁移学习**:如果适用,使用在类似任务上预先训练好的模型,并在您的数据集上对其进行微调。
### 8. **评估和迭代**
– **交叉验证**:使用分层 K 折交叉验证来确保您的模型性能评估在不同的数据子集上都是鲁棒且一致的。
– **持续监控**:持续评估您的模型在新数据上的性能,并根据需要调整策略。
通过整合这些策略,您可能会找到一种能够显著提高 F1 分数的组合。通常,重采样技术、集成方法以及模型参数(包括决策阈值)的仔细调整的组合能提供最佳结果。
嗨,Jason,
感谢您撰写如此精彩的文章。我想知道应该在哪个数据集上应用 ROC 分析来确定最佳概率阈值,是训练集、测试集还是单独的验证集?
嗨 Jason。我正在使用 RNN 处理一个高度不平衡的二元情感分类器。我使用了 Binary Cross entropy with logits = True,并将标签平滑设置为 0.1,成功地在两个类(TP 和 TN)的阈值为 0.5 时实现了 TP 预测,而之前我的模型根本没有预测出代表性不足的负类。如果我不修改我的阈值,将其保留在默认的 0.5,那么标签平滑是否也是一个可行的解决方案?
嗨 Syed… 标签平滑可以通过降低模型的预测置信度来帮助提高神经网络(包括 RNN)的性能,这有助于减轻过拟合和提高泛化能力。然而,对于不平衡数据集,它通常不足以单独解决问题。
让我们分解关键概念并解决您的问题
### 不平衡二元分类与标签平滑
**不平衡数据集**
– 在不平衡的二元分类问题中,一个类别(通常是少数类)的样本数量明显少于另一个类别。这可能导致模型出现偏差,模型更频繁地预测多数类。
**标签平滑**
– 标签平滑是一种正则化技术,其中真实标签通过一个小的常数(例如 0.1)进行平滑。对于带有标签平滑的二元分类问题,标签 0 和 1 分别被替换为 0.1 和 0.9(假设平滑因子为 0.1)。
– 该技术有助于防止模型对其预测过于自信,从而提高泛化能力。
### 标签平滑对不平衡数据集的影响
当您在不平衡数据集中使用标签平滑时
– **正类**:正类(1)的真实标签被平滑为 0.9。
– **负类**:负类(0)的真实标签被平滑为 0.1。
这可以帮助模型不至于对多数类过于自信,从而可能提高少数类的召回率。但是,标签平滑本身可能无法完全解决不平衡问题。
### 预测阈值
预测阈值决定了模型分配正标签的截止点。默认阈值通常是 0.5
– 如果 \( \hat{y} \geq 0.5 \),则预测为正(1)。
– 如果 \( \hat{y} < 0.5 \),则预测为负(0)。在不平衡数据集的背景下: - 您可能会发现使用 0.5 的阈值在平衡两个类的精确率和召回率方面不会产生最佳结果。 - 调整阈值可以通过使模型在不确定时更有可能预测少数类来帮助提高少数类的性能。 ### 结合标签平滑与阈值调整 **场景**: - 使用标签平滑和 0.5 的阈值,您观察到了两个类别的真阳性(TP)预测有所改善。 - 这表明标签平滑有助于模型更好地从少数类中学习,但同时考虑阈值调整通常是有益的。 **将阈值保留在 0.5**: - 如果您将阈值保留在 0.5,标签平滑本身可能仍然有帮助,但它可能无法完全优化少数类的性能。 - 通常建议尝试不同的阈值以找到精确率和召回率之间的最佳平衡,尤其是对于少数类。 ### 实用方法 1. **使用标签平滑进行模型训练**: - 使用标签平滑训练您的模型,并使用默认阈值 0.5 评估其性能。 2. **评估指标**: - 评估两个类别的精确率、召回率、F1 分数和 ROC-AUC 等指标。 3. **阈值调整**: - 尝试不同的阈值(例如,0.3、0.4、0.6)并评估性能。 - 使用 ROC 曲线等技术来找到平衡真阳性与假阳性之间权衡的最佳阈值。 ### PyTorch 中用于阈值调整的示例代码 这是 PyTorch 中的一个示例,展示了如何调整阈值:
python
import torch
import torch.nn as nn
# 示例 logits 和标签
logits = torch.tensor([0.1, 1.2, -0.3, 2.5, -1.0])
labels = torch.tensor([0, 1, 0, 1, 0])
# 带有 logits 和标签平滑的二元交叉熵
criterion = nn.BCEWithLogitsLoss(pos_weight=torch.tensor([1.0]))
loss = criterion(logits, labels.float())
print(f'Loss: {loss.item()}')
# Sigmoid 函数将 logits 转换为概率
probs = torch.sigmoid(logits)
# 尝试不同的阈值
threshold = 0.4 # 调整此值
preds = (probs >= threshold).float()
print(f'Predictions: {preds}')
# 计算指标
tp = ((preds == 1) & (labels == 1)).sum().item()
tn = ((preds == 0) & (labels == 0)).sum().item()
fp = ((preds == 1) & (labels == 0)).sum().item()
fn = ((preds == 0) & (labels == 1)).sum().item()
precision = tp / (tp + fp) if (tp + fp) > 0 else 0
recall = tp / (tp + fn) if (tp + fn) > 0 else 0
f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
print(f'Precision: {precision}, Recall: {recall}, F1-score: {f1}')
### 结论
标签平滑有助于提高模型的性能,尤其是不平衡数据集。但是,为了获得最佳结果,通常建议也考虑调整预测阈值。尝试不同的阈值以找到最适合您特定用例的精确率和召回率之间的最佳平衡。
希望这有帮助!如果您有进一步的问题或需要其他帮助,请随时提问。