【问题标题】:scikit-learn .predict() 默认阈值
【发布时间】:2013-11-27 21:28:09
【问题描述】:

我正在处理不平衡类(5% 1)的分类问题。我想预测类别,而不是概率。

在一个二元分类问题中,scikit 的classifier.predict() 是否默认使用0.5? 如果没有,默认方法是什么?如果是,我该如何更改?

在 scikit 中,一些分类器具有 class_weight='auto' 选项,但并非所有分类器都有。使用class_weight='auto'.predict()会不会以实际人口比例为阈值?

在像 MultinomialNB 这样不支持 class_weight 的分类器中执行此操作的方法是什么?除了使用predict_proba()然后自己计算类。

【问题讨论】:

    标签: python machine-learning classification scikit-learn


    【解决方案1】:

    可以使用clf.predict_proba()设置阈值

    例如:

    from sklearn.tree import DecisionTreeClassifier
    clf = DecisionTreeClassifier(random_state = 2)
    clf.fit(X_train,y_train)
    # y_pred = clf.predict(X_test)  # default threshold is 0.5
    y_pred = (clf.predict_proba(X_test)[:,1] >= 0.3).astype(bool) # set threshold as 0.3
    

    【讨论】:

    • 为了澄清,您没有设置阈值,因为这意味着您将永久更改clf.predict() 的行为,而您没有。跨度>
    • 这是正确答案。我无法在 MLP 源代码中看到他们执行 0.5 阈值的位置...
    • 您如何将其与 GridSearchCV 联系起来,其中正在执行的预测是内部的并且您无法访问?假设阈值 0.3 会给我带来不同的最佳模型选择。
    • 我认为 GridSearchCV 只会使用默认阈值 0.5。在训练期间改变这个阈值是不合理的,因为我们希望一切都是公平的。只有在最后的预测阶段,我们调整概率阈值以支持更多的正面或负面结果。例如,为了获得更大的捕获率(以更高的误报为代价),我们可以手动降低阈值。
    • 嗨,我使用svm.predict(prediction_data) 来预测给定的数据集。但是,当我查看预测为阳性的那些实例的概率分数时,一些概率分数低于预期的 0.5,并且似乎是 0.1、0.2 等。关于我为什么会有这个结果的任何可能的想法?谢谢!
    【解决方案2】:

    scikit 的classifier.predict() 默认使用0.5吗?

    在概率分类器中,是的。正如其他人所解释的那样,从数学角度来看,这是唯一合理的阈值。

    在像 MultinomialNB 这样不支持 class_weight 的分类器中执行此操作的方法是什么?

    您可以设置class_prior,即每个类y的先验概率P(y)。这有效地改变了决策边界。例如

    # minimal dataset
    >>> X = [[1, 0], [1, 0], [0, 1]]
    >>> y = [0, 0, 1]
    # use empirical prior, learned from y
    >>> MultinomialNB().fit(X,y).predict([1,1])
    array([0])
    # use custom prior to make 1 more likely
    >>> MultinomialNB(class_prior=[.1, .9]).fit(X,y).predict([1,1])
    array([1])
    

    【讨论】:

    • 看来 RandomForestClassifier 没有 class_prior。该怎么做呢?
    • RandomForestClassifier 没有 class_prior 参数,但有一个可以使用的 class_weight 参数。
    • 实际上 0.5 默认值是任意的,不一定是最优的,例如注意到in this answer on CV by Frank Harrell谁是被切除的权威。
    • “在概率分类器中,是的。从数学的角度来看,这是唯一合理的阈值,正如其他人所解释的那样。” - 这似乎完全不合时宜。例如,如果您想权衡召回率而不是精度怎么办?
    【解决方案3】:

    scikit learn 中二元分类的阈值为 0.5,并且哪个类具有最大的多类分类概率。在许多问题中,通过调整阈值可以获得更好的结果。但是,这必须小心完成,而不是在保留测试数据上,而是通过对训练数据的交叉验证。如果您对测试数据的阈值进行任何调整,那么您只是过度拟合了测试数据。

    大多数调整阈值的方法是基于receiver operating characteristics (ROC)Youden's J statistic,但也可以通过其他方法来完成,例如使用遗传算法进行搜索。

    这是一篇同行评议期刊文章,描述了在医学中这样做:

    http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2515362/

    据我所知,没有在 Python 中执行此操作的包,但在 Python 中通过蛮力搜索找到它相对简单(但效率低下)。

    这是执行此操作的一些 R 代码。

    ## load data
    DD73OP <- read.table("/my_probabilites.txt", header=T, quote="\"")
    
    library("pROC")
    # No smoothing
    roc_OP <- roc(DD73OP$tc, DD73OP$prob)
    auc_OP <- auc(roc_OP)
    auc_OP
    Area under the curve: 0.8909
    plot(roc_OP)
    
    # Best threshold
    # Method: Youden
    #Youden's J statistic (Youden, 1950) is employed. The optimal cut-off is the threshold that maximizes the distance to the identity (diagonal) line. Can be shortened to "y".
    #The optimality criterion is:
    #max(sensitivities + specificities)
    coords(roc_OP, "best", ret=c("threshold", "specificity", "sensitivity"), best.method="youden")
    #threshold specificity sensitivity 
    #0.7276835   0.9092466   0.7559022
    

    【讨论】:

    • 好帖子!最重要的一点:“如果您对测试数据的阈值进行任何调整,那么您只是过度拟合了测试数据。”
    • 是大于等于 0.5 舍入到 1,还是刚好大于 0.5???
    • SFAIK,在 scikit learn 和大多数其他包中 >= 0.5 是正类,
    【解决方案4】:

    您似乎在这里混淆了概念。阈值不是“通用分类器”的概念——最基本的方法是基于一些可调阈值,但大多数现有方法为分类创建了复杂的规则,不能(或至少不应该)被视为阈值。

    所以首先 - 因为没有这样的东西,所以无法回答你关于 scikit 分类器默认阈值的问题。

    第二 - 类权重与阈值无关,与分类器处理不平衡类的能力有关,它取决于特定分类器。例如 - 在 SVM 情况下,它是优化问题中松弛变量的加权方式,或者如果您愿意 - 与特定类相关的拉格朗日乘数值的上限。将此设置为“自动”意味着使用一些默认启发式,但再一次 - 它不能简单地转换为一些阈值。

    另一方面,朴素贝叶斯直接估计训练集中的类概率。它被称为“类先验”,您可以在构造函数中使用“class_prior”变量对其进行设置。

    来自documentation

    类的先验概率。如果指定,则不会根据数据调整先验。

    【讨论】:

    • 让我以不同的方式解释这一点,然后随意说我仍然感到困惑:-)。假设我有两节课。大多数分类器会预测概率。我可以使用概率来评估我的模型,比如使用 ROC。但是如果我想预测一个类别,我需要选择一个截止值,比如 0.5,然后说“p0.5 的观察值进入第 1 类。这通常是一个很好的选择如果您的先验是 0.5-0.5,则可以选择。但是对于不平衡的问题,我需要一个不同的截止值。我的问题实际上是在询问使用 .predict() 时如何在 scikit 中处理该截止值。
    • 大多数分类器不是概率分类器。他们可以以某种方式“产生”这个概率(估计)这一事实并不意味着他们实际上“使用它”来进行预测。这就是为什么我将其称为可能的混淆。 Predict 调用用于进行预测的原始模型的例程,它可以是概率 (NB)、几何 (SVM)、基于回归 (NN) 或基于规则 (Trees),因此 predict() 中的概率值问题似乎是一个概念混乱。
    • @lejlot,如果是这样的话,那么用 predict_proba 绘制的 roc 曲线的整个概念不会变得无关紧要吗?不同阈值绘制的 roc 曲线的不同点不是应用于 predict_proba 的结果吗?
    【解决方案5】:

    如果有人访问此线程希望使用现成的功能(python 2.7)。在此示例中,截止值旨在反映原始数据集 df 中事件与非事件的比率,而 y_prob 可能是 .predict_proba 方法的结果(假设分层训练/测试分裂)。

    def predict_with_cutoff(colname, y_prob, df):
        n_events = df[colname].values
        event_rate = sum(n_events) / float(df.shape[0]) * 100
        threshold = np.percentile(y_prob[:, 1], 100 - event_rate)
        print "Cutoff/threshold at: " + str(threshold)
        y_pred = [1 if x >= threshold else 0 for x in y_prob[:, 1]]
        return y_pred
    

    随时批评/修改。希望在极少数情况下,当无法进行类平衡并且数据集本身高度不平衡时,它会有所帮助。

    【讨论】:

      猜你喜欢
      • 2017-02-27
      • 2021-11-22
      • 2017-01-02
      • 2018-02-03
      • 2016-09-03
      • 2020-07-16
      • 2019-04-19
      • 2020-07-25
      • 2020-10-20
      相关资源
      最近更新 更多