具有不平衡 class(正数是少数 class)、低精度和怪异分数分布的随机森林
Random forest with unbalanced class (positive is minority class), low precision and weird score distributions
我有一个非常不平衡的数据集(5000 个正值,300000 个负值)。我正在使用 sklearn RandomForestClassifier 来尝试预测正 class 的概率。我有多年的数据,我设计的功能之一是前一年的 class,所以除了这些年的测试集外,我还保留了数据集的最后一年进行测试我正在训练。
这是我尝试过的(和结果):
使用 SMOTE 和 SMOTEENN 进行上采样(奇怪的分数分布,请参见第一张图片,正负概率的预测值 class 都相同,即模型预测大多数正值的概率非常低 class)
下采样到平衡数据集(测试集的召回率约为 0.80,但年外测试集的召回率为 0.07,原因是不平衡的年外测试集中的总负数,请参见第二张图片)
保持不平衡(再次出现奇怪的得分分布,精度上升到 ~0.60,召回率下降到 0.05 和 0.10,用于测试和年外测试集)
XGBoost(年外测试集的召回率略高,0.11)
接下来我应该尝试什么?我想针对 F1 进行优化,因为在我的案例中,误报和漏报同样糟糕。我想合并 k-fold 交叉验证并且已经阅读过我应该在上采样之前这样做,a)我应该这样做 this/is 它可能会有所帮助和 b)我如何将它合并到类似于此的管道中:
from imblearn.pipeline import make_pipeline, Pipeline
clf_rf = RandomForestClassifier(n_estimators=25, random_state=1)
smote_enn = SMOTEENN(smote = sm)
kf = StratifiedKFold(n_splits=5)
pipeline = make_pipeline(??)
pipeline.fit(X_train, ytrain)
ypred = pipeline.predict(Xtest)
ypredooy = pipeline.predict(Xtestooy)
- 使用 SMOTE 和 SMOTEENN 进行上采样:我远不是这方面的专家,但通过对数据集进行上采样,您可能会放大现有的噪音,从而导致过度拟合。这可以解释您的算法无法正确分类的事实,从而在第一张图中给出结果。
我在这里找到了更多信息,也许还有如何改进您的结果:
https://sci2s.ugr.es/sites/default/files/ficherosPublicaciones/1773_ver14_ASOC_SMOTE_FRPS.pdf
当你下采样时,你似乎遇到了我理解的同样的过拟合问题(至少对于前一年的目标结果)。但是,如果不查看数据,很难推断出其背后的原因。
您的过度拟合问题可能来自您使用的特征数量,这些特征可能会增加不必要的噪音。您可以尝试减少使用的功能数量并逐渐增加(使用 RFE 模型)。更多信息在这里:
https://machinelearningmastery.com/feature-selection-in-python-with-scikit-learn/
对于你使用的模型,你提到了随机森林和XGBoost,但你没有提到使用了更简单的模型。您可以尝试更简单的模型并专注于您的数据工程。
如果你还没有尝试过,也许你可以:
- 对数据进行缩减采样
- 使用 StandardScaler 规范化所有数据
测试 "brute force" 朴素贝叶斯和逻辑回归等简单模型的调优
# Define steps of the pipeline
steps = [('scaler', StandardScaler()),
('log_reg', LogisticRegression())]
pipeline = Pipeline(steps)
# Specify the hyperparameters
parameters = {'C':[1, 10, 100],
'penalty':['l1', 'l2']}
# Create train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33,
random_state=42)
# Instantiate a GridSearchCV object: cv
cv = GridSearchCV(pipeline, param_grid=parameters)
# Fit to the training set
cv.fit(X_train, y_train)
无论如何,对于您的示例,管道可能是(我使用逻辑回归制作它,但您可以使用其他 ML 算法更改它并因此更改参数网格):
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV, StratifiedKFold, cross_val_score
from imblearn.combine import SMOTEENN
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline
param_grid = {'C': [1, 10, 100]}
clf = LogisticRegression(solver='lbfgs', multi_class = 'auto')
sme = SMOTEENN(smote = SMOTE(k_neighbors = 2), random_state=42)
grid = GridSearchCV(estimator=clf, param_grid = param_grid, score = "f1")
pipeline = Pipeline([('scale', StandardScaler()),
('SMOTEENN', sme),
('grid', grid)])
cv = StratifiedKFold(n_splits = 4, random_state=42)
score = cross_val_score(pipeline, X, y, cv=cv)
希望对您有所帮助。
(编辑:我在 GridSearchCV 中添加了 score = "f1")
我有一个非常不平衡的数据集(5000 个正值,300000 个负值)。我正在使用 sklearn RandomForestClassifier 来尝试预测正 class 的概率。我有多年的数据,我设计的功能之一是前一年的 class,所以除了这些年的测试集外,我还保留了数据集的最后一年进行测试我正在训练。
这是我尝试过的(和结果):
使用 SMOTE 和 SMOTEENN 进行上采样(奇怪的分数分布,请参见第一张图片,正负概率的预测值 class 都相同,即模型预测大多数正值的概率非常低 class)
下采样到平衡数据集(测试集的召回率约为 0.80,但年外测试集的召回率为 0.07,原因是不平衡的年外测试集中的总负数,请参见第二张图片)
保持不平衡(再次出现奇怪的得分分布,精度上升到 ~0.60,召回率下降到 0.05 和 0.10,用于测试和年外测试集)
XGBoost(年外测试集的召回率略高,0.11)
接下来我应该尝试什么?我想针对 F1 进行优化,因为在我的案例中,误报和漏报同样糟糕。我想合并 k-fold 交叉验证并且已经阅读过我应该在上采样之前这样做,a)我应该这样做 this/is 它可能会有所帮助和 b)我如何将它合并到类似于此的管道中:
from imblearn.pipeline import make_pipeline, Pipeline
clf_rf = RandomForestClassifier(n_estimators=25, random_state=1)
smote_enn = SMOTEENN(smote = sm)
kf = StratifiedKFold(n_splits=5)
pipeline = make_pipeline(??)
pipeline.fit(X_train, ytrain)
ypred = pipeline.predict(Xtest)
ypredooy = pipeline.predict(Xtestooy)
- 使用 SMOTE 和 SMOTEENN 进行上采样:我远不是这方面的专家,但通过对数据集进行上采样,您可能会放大现有的噪音,从而导致过度拟合。这可以解释您的算法无法正确分类的事实,从而在第一张图中给出结果。
我在这里找到了更多信息,也许还有如何改进您的结果: https://sci2s.ugr.es/sites/default/files/ficherosPublicaciones/1773_ver14_ASOC_SMOTE_FRPS.pdf
当你下采样时,你似乎遇到了我理解的同样的过拟合问题(至少对于前一年的目标结果)。但是,如果不查看数据,很难推断出其背后的原因。
您的过度拟合问题可能来自您使用的特征数量,这些特征可能会增加不必要的噪音。您可以尝试减少使用的功能数量并逐渐增加(使用 RFE 模型)。更多信息在这里:
https://machinelearningmastery.com/feature-selection-in-python-with-scikit-learn/
对于你使用的模型,你提到了随机森林和XGBoost,但你没有提到使用了更简单的模型。您可以尝试更简单的模型并专注于您的数据工程。 如果你还没有尝试过,也许你可以:
- 对数据进行缩减采样
- 使用 StandardScaler 规范化所有数据
测试 "brute force" 朴素贝叶斯和逻辑回归等简单模型的调优
# Define steps of the pipeline steps = [('scaler', StandardScaler()), ('log_reg', LogisticRegression())] pipeline = Pipeline(steps) # Specify the hyperparameters parameters = {'C':[1, 10, 100], 'penalty':['l1', 'l2']} # Create train and test sets X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42) # Instantiate a GridSearchCV object: cv cv = GridSearchCV(pipeline, param_grid=parameters) # Fit to the training set cv.fit(X_train, y_train)
无论如何,对于您的示例,管道可能是(我使用逻辑回归制作它,但您可以使用其他 ML 算法更改它并因此更改参数网格):
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV, StratifiedKFold, cross_val_score
from imblearn.combine import SMOTEENN
from imblearn.over_sampling import SMOTE
from imblearn.pipeline import Pipeline
param_grid = {'C': [1, 10, 100]}
clf = LogisticRegression(solver='lbfgs', multi_class = 'auto')
sme = SMOTEENN(smote = SMOTE(k_neighbors = 2), random_state=42)
grid = GridSearchCV(estimator=clf, param_grid = param_grid, score = "f1")
pipeline = Pipeline([('scale', StandardScaler()),
('SMOTEENN', sme),
('grid', grid)])
cv = StratifiedKFold(n_splits = 4, random_state=42)
score = cross_val_score(pipeline, X, y, cv=cv)
希望对您有所帮助。
(编辑:我在 GridSearchCV 中添加了 score = "f1")