sklearn LogisticRegression 和更改分类的默认阈值
sklearn LogisticRegression and changing the default threshold for classification
我正在使用 sklearn 包中的 LogisticRegression,并且有一个关于分类的快速问题。我为我的分类器构建了一条 ROC 曲线,结果证明我的训练数据的最佳阈值约为 0.25。我假设创建预测时的默认阈值是 0.5。在进行 10 折交叉验证时,如何更改此默认设置以了解我的模型的准确性?基本上,我希望我的模型为大于 0.25 而不是 0.5 的任何人预测“1”。我一直在查看所有文档,但似乎一无所获。
这不是内置功能。您可以 "add" 通过将 LogisticRegression class 包装在您自己的 class 中,并添加您在自定义 predict()
方法中使用的 threshold
属性。
但是,一些注意事项:
- 默认阈值实际上是 0。
LogisticRegression.decision_function()
returns 到所选分离超平面的有符号距离。如果您正在查看 predict_proba()
,那么您正在查看阈值为 0.5 的超平面距离的 logit()
。但这计算起来更昂贵。
- 通过像这样选择 "optimal" 阈值,您正在利用信息 post-learning,这会破坏您的测试集(即,您的测试或验证集不再提供对 out 的无偏估计样本误差)。因此,您可能会引发额外的过度拟合,除非您仅在训练集的交叉验证循环中选择阈值,然后将它和训练有素的 classifier 与您的测试集一起使用。
- 如果您遇到不平衡问题,请考虑使用
class_weight
,而不是手动设置阈值。这应该会迫使 classifier 选择一个远离 class 的超平面。
我想给出一个实用的答案
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, recall_score, roc_auc_score, precision_score
X, y = make_classification(
n_classes=2, class_sep=1.5, weights=[0.9, 0.1],
n_features=20, n_samples=1000, random_state=10
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
clf = LogisticRegression(class_weight="balanced")
clf.fit(X_train, y_train)
THRESHOLD = 0.25
preds = np.where(clf.predict_proba(X_test)[:,1] > THRESHOLD, 1, 0)
pd.DataFrame(data=[accuracy_score(y_test, preds), recall_score(y_test, preds),
precision_score(y_test, preds), roc_auc_score(y_test, preds)],
index=["accuracy", "recall", "precision", "roc_auc_score"])
将THRESHOLD
改为0.25
,可以发现recall
和precision
的分数在下降。
但是,通过删除 class_weight
参数,accuracy
增加但 recall
分数下降。
参考@accepted的回答
特例:一维逻辑回归
将样本 X
标记为 1
的区域与标记为 0
的区域分开的值使用以下公式计算:
from scipy.special import logit
thresh = 0.1
val = (logit(thresh)-clf.intercept_)/clf.coef_[0]
因此,可以使用
更直接地计算预测
preds = np.where(X>val, 1, 0)
为了完整起见,我想提一下另一种根据 scikit 的概率计算优雅地生成预测的方法 using binarize:
import numpy as np
from sklearn.preprocessing import binarize
THRESHOLD = 0.25
# This probabilities would come from logistic_regression.predict_proba()
y_logistic_prob = np.random.uniform(size=10)
predictions = binarize(y_logistic_prob.reshape(-1, 1), THRESHOLD).ravel()
另外,我同意,特别是2和3,一定要留意。
def find_best_threshold(threshould, fpr, tpr):
t = threshould[np.argmax(tpr*(1-fpr))]
# (tpr*(1-fpr)) will be maximum if your fpr is very low and tpr is very high
print("the maximum value of tpr*(1-fpr)", max(tpr*(1-fpr)), "for threshold", np.round(t,3))
return t
如果你想找到最好的True positive rate和nagatuve rate可以使用这个函数
您可以更改阈值,但它是 0.5,因此计算是正确的。如果你有一个不平衡的集合,分类如下图所示。
您可以看到类别 1 的预期很差。 Class1占总人口的2%。
在将结果变量平衡为 50% 到 50%(使用过采样)后,0.5 阈值到达图表的中心。
就我的算法而言:
threshold = 0.1
LR_Grid_ytest_THR = ((model.predict_proba(Xtest)[:, 1])>= threshold).astype(int)
和:
print('Valuation for test data only:')
print(classification_report(ytest, model.predict(Xtest)))
print("----------------------------------------------------------------------")
print('Valuation for test data only (new_threshold):')
print(classification_report(ytest, LR_Grid_ytest_THR))
我正在使用 sklearn 包中的 LogisticRegression,并且有一个关于分类的快速问题。我为我的分类器构建了一条 ROC 曲线,结果证明我的训练数据的最佳阈值约为 0.25。我假设创建预测时的默认阈值是 0.5。在进行 10 折交叉验证时,如何更改此默认设置以了解我的模型的准确性?基本上,我希望我的模型为大于 0.25 而不是 0.5 的任何人预测“1”。我一直在查看所有文档,但似乎一无所获。
这不是内置功能。您可以 "add" 通过将 LogisticRegression class 包装在您自己的 class 中,并添加您在自定义 predict()
方法中使用的 threshold
属性。
但是,一些注意事项:
- 默认阈值实际上是 0。
LogisticRegression.decision_function()
returns 到所选分离超平面的有符号距离。如果您正在查看predict_proba()
,那么您正在查看阈值为 0.5 的超平面距离的logit()
。但这计算起来更昂贵。 - 通过像这样选择 "optimal" 阈值,您正在利用信息 post-learning,这会破坏您的测试集(即,您的测试或验证集不再提供对 out 的无偏估计样本误差)。因此,您可能会引发额外的过度拟合,除非您仅在训练集的交叉验证循环中选择阈值,然后将它和训练有素的 classifier 与您的测试集一起使用。
- 如果您遇到不平衡问题,请考虑使用
class_weight
,而不是手动设置阈值。这应该会迫使 classifier 选择一个远离 class 的超平面。
我想给出一个实用的答案
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, recall_score, roc_auc_score, precision_score
X, y = make_classification(
n_classes=2, class_sep=1.5, weights=[0.9, 0.1],
n_features=20, n_samples=1000, random_state=10
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
clf = LogisticRegression(class_weight="balanced")
clf.fit(X_train, y_train)
THRESHOLD = 0.25
preds = np.where(clf.predict_proba(X_test)[:,1] > THRESHOLD, 1, 0)
pd.DataFrame(data=[accuracy_score(y_test, preds), recall_score(y_test, preds),
precision_score(y_test, preds), roc_auc_score(y_test, preds)],
index=["accuracy", "recall", "precision", "roc_auc_score"])
将THRESHOLD
改为0.25
,可以发现recall
和precision
的分数在下降。
但是,通过删除 class_weight
参数,accuracy
增加但 recall
分数下降。
参考@accepted的回答
特例:一维逻辑回归
将样本 X
标记为 1
的区域与标记为 0
的区域分开的值使用以下公式计算:
from scipy.special import logit
thresh = 0.1
val = (logit(thresh)-clf.intercept_)/clf.coef_[0]
因此,可以使用
更直接地计算预测preds = np.where(X>val, 1, 0)
为了完整起见,我想提一下另一种根据 scikit 的概率计算优雅地生成预测的方法 using binarize:
import numpy as np
from sklearn.preprocessing import binarize
THRESHOLD = 0.25
# This probabilities would come from logistic_regression.predict_proba()
y_logistic_prob = np.random.uniform(size=10)
predictions = binarize(y_logistic_prob.reshape(-1, 1), THRESHOLD).ravel()
另外,我同意
def find_best_threshold(threshould, fpr, tpr):
t = threshould[np.argmax(tpr*(1-fpr))]
# (tpr*(1-fpr)) will be maximum if your fpr is very low and tpr is very high
print("the maximum value of tpr*(1-fpr)", max(tpr*(1-fpr)), "for threshold", np.round(t,3))
return t
如果你想找到最好的True positive rate和nagatuve rate可以使用这个函数
您可以更改阈值,但它是 0.5,因此计算是正确的。如果你有一个不平衡的集合,分类如下图所示。
您可以看到类别 1 的预期很差。 Class1占总人口的2%。 在将结果变量平衡为 50% 到 50%(使用过采样)后,0.5 阈值到达图表的中心。
就我的算法而言:
threshold = 0.1
LR_Grid_ytest_THR = ((model.predict_proba(Xtest)[:, 1])>= threshold).astype(int)
和:
print('Valuation for test data only:')
print(classification_report(ytest, model.predict(Xtest)))
print("----------------------------------------------------------------------")
print('Valuation for test data only (new_threshold):')
print(classification_report(ytest, LR_Grid_ytest_THR))