scikit-learn .predict() default threshold



我正在研究一个不平衡类的分类问题(5% 1)。我想预测类,而不是概率

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

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

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

可以使用

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

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

大多数调整阈值的方法都是基于受试者工作特性(ROC)和Youden的J统计量,但也可以通过其他方法完成,例如使用遗传算法进行搜索。

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

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

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])

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

所以首先 - 不能回答你关于scikit分类器默认阈值的问题,因为没有这样的事情。

第二类权重不是关于阈值,而是关于分类器处理不平衡类的能力,它依赖于特定的分类器。例如 - 在 SVM 情况下,它是在优化问题中对松弛变量进行加权的方式,或者如果您愿意,还可以使用与特定类相关的拉格朗日乘数值的上限。将其设置为"auto"意味着使用一些默认的启发式方法,但同样 - 它不能简单地转换为一些阈值。

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

从文档中:

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

如果有人访问此线程希望获得现成的功能(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

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

Scikit-learn分类器通常通过获取分数/概率的argmax来选择预测类(参见LogisticRegression和DecisionTreeClassifier)。

对于二元分类问题argmax等于对概率使用 0.5 阈值。在这种情况下,改变阈值会改变您对预测类的信心。

您可以根据自己的目标调整/更改阈值(即最大化精度或召回率)。这篇文章清楚地解释了这个概念。通过检索预测概率并优化验证集上感兴趣的指标,可以自动查找任何分类器的最佳阈值。这是由ThresholdClassifier完成的:

import numpy as np
from sklearn.metrics import fbeta_score
from sklearn.model_selection import train_test_split
from sklearn.base import clone, BaseEstimator, ClassifierMixin

class ThresholdClassifier(BaseEstimator, ClassifierMixin):
    
    def __init__(self, estimator, refit=True, val_size=0.3):
        self.estimator = estimator
        self.refit = refit
        self.val_size = val_size
        
    def fit(self, X, y):
        
        def scoring(th, y, prob):
            pred = (prob > th).astype(int)
            return 0 if not pred.any() else 
                -fbeta_score(y, pred, beta=0.1) 
        
        X_train, X_val, y_train, y_val = train_test_split(
            X, y, stratify=y, test_size=self.val_size, 
            shuffle=True, random_state=1234
        )
        
        self.estimator_ = clone(self.estimator)
        self.estimator_.fit(X_train, y_train)
        
        prob_val = self.estimator_.predict_proba(X_val)[:,1]
        thresholds = np.linspace(0,1, 200)[1:-1]
        scores = [scoring(th, y_val, prob_val) 
                    for th in thresholds]
        self.score_ = np.min(scores)
        self.th_ = thresholds[np.argmin(scores)]
        
        if self.refit:
            self.estimator_.fit(X, y)
        if hasattr(self.estimator_, 'classes_'):
            self.classes_ = self.estimator_.classes_
            
        return self
    
    def predict(self, X):
        proba = self.estimator_.predict_proba(X)[:,1]
        return (proba > self.th_).astype(int)
    
    def predict_proba(self, X):
        return self.estimator_.predict_proba(X)

拨打fit时:

  • 从接收到的数据随机生成验证(X_valy_val)集;
  • estimator安装在X_trainy_train上;
  • 概率
  • prob_val)在X_val上检索到1的类;
  • 通过优化选择的指标(在我们的例子中fbeta_score),可以在X_val上找到最佳阈值。

调用 predict 时:1 类的概率由找到的最佳阈值生成并转换为二进制类。

model = ThresholdClassifier(RandomForestClassifier()).fit(X_train, y_train)
pred_clas = model.predict(X_test)

ThresholdClassifier可以与任何产生概率的 sklearn 分类器一起使用。它可以根据不同的需求轻松定制。与GridSearchCV/RandomSearchCV结合使用非常有用,可以将参数搜索与分类阈值的调谐联系起来。

model = RandomizedSearchCV(
    ThresholdClassifier(RandomForestClassifier()), 
    dict(n_estimators=stats.randint(50,300)), 
    n_iter=20, random_state=1234,
    cv=5, n_jobs=-1,
).fit(X_train, y_train)

相关内容

  • 没有找到相关文章

最新更新