我正在研究一个不平衡类的分类问题(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_val
和y_val
)集; estimator
安装在X_train
和y_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)