具有不平衡 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")