返回列表

# 1 solution - stacked NN

624. Playground Series - Season 4, Episode 9 | playground-series-s4e9

开始: 2024-09-01 结束: 2024-09-30 定价与促销 数据算法赛
# 1 solution - stacked NN

# 第一名解决方案 - 堆叠神经网络 (stacked NN)

作者: Mart Preusse (EXPERT)
发布时间: 2024-10-01
竞赛排名: 1

我真的登顶了吗?我仍然感到惊讶和兴奋。

解决方案之路:

前两周我一直在阅读讨论,尝试 catboost 并发布了一个集成模型。计划是收集多样的模型并用 Ridge 进行集成,这与我在这个笔记本中使用的管道相同。最终的集成模型(我选作第一个最终提交)本会让我获得第二名,它与笔记本的不同之处在于以下几点:

  • 我使用了 20 折交叉验证 (cv folds)
  • 我在某些模型中包含了原始数据(甚至在 LGBM 中包含了两次)
  • 我计算了一个带有 rbf 核的 SVR,这是由 broccoli beef这个讨论帖中建议的,而不是线性 SVR。
  • 我将所有分类特征 additionally 作为目标编码(target encoded)包含在 catboost 中,但我使用的是中位数而不是均值进行目标编码。我是无泄漏地做的,意味着我在每一折中重新计算了目标编码列。此外,我将 Catboost 用作分类器而不是回归器。Catboost 预测异常值价格(见函数 bin_price)。超参数是通过 optuna 找到的。OOF 预测未用于集成。它们被用作 LGBM(或我第二个最终提交中的 NN)的附加特征。
def bin_price(data):
    df = data.copy()
    # Calculate Q1 (25th percentile) and Q3 (75th percentile)
    Q1 = np.percentile(df['price'], 25)
    Q3 = np.percentile(df['price'], 75)
    IQR = Q3 - Q1

    # Define the lower and upper bounds for outliers
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    # Identify outliers
    outliers = df[(df['price'] > upper_bound)]
    df['price_bin'] = (df['price'] < upper_bound).astype(int)
    
    return df

cat_params2 = {
    'early_stopping_rounds':25,
    'use_best_model': True,
    "verbose": False ,
    'cat_features': cat_cols,
    'min_data_in_leaf': 16, 
    'learning_rate': 0.03355311405703999, 
    'random_strength': 11.663619399375248, 
    'l2_leaf_reg': 17.703146378123996, 
    'max_depth': 10, 
    'subsample': 0.9479174100256215, 
     'border_count': 130, 
    'bagging_temperature': 24.032067560148384
}
  • 我将 catboost 的 oof 预测作为 LGBM 的附加特征
  • 我使用了第二个 LGBM (LGBM5),其中我对所有分类数据进行标签编码(稀有类别总结为类别 "rare",如同笔记本中的 NN 所做的那样)并提高了 max_bin。
lgb_params = {
    'verbose' : -1,
    'early_stopping_rounds':25,
    'loss_function':"RMSE",
    'n_estimators': 2000, 
    'max_bin': 30000,
}
  • 我包含了来自 Autogluon 的 fastai 计算(通过 20 折的嵌套 cv 确保 100% 无泄漏)
predictor = TabularPredictor(label='price',
                             eval_metric='rmse',
                             problem_type="regression").fit(X_train,
                                                       pseudo_data = data_original, 
                                                       num_bag_folds = 10,
                                                       num_bag_sets = 2,
                                                       time_limit=1800,
                                                       included_model_types = ['FASTAI'], 
                                                       keep_only_best = True,
                                                       presets="best_quality",
                                                      )

我最终得到了 72300 的交叉验证分数,以及以下模型(_st 表示包含了 catboost oofs):
Ensemble weights final

各个模型的交叉验证分数如下:
Ensemble scores

第一名解决方案:

我需要第二个最终提交,所以在最后一天我决定临时提交 一个 fork 的笔记本,来自 Vladimir Demidov。我注意到他的 NN 对变化具有鲁棒性,所以我添加了四个数值特征:SVR oof 预测、LGBM5 oof 预测、CatboostClassifier oof 预测和 XGB 预测(源自公开可用的超参数,不幸的是我忘记了来源)。NN 集成的交叉验证分数为 72468,但最终比 Ridge 集成更好。

当然,我要感谢 @yekenot@siukeitin@noodl35 (LGBM 超参数),他们直接提供了我使用的部分代码。我也从讨论中受益匪浅,特别是 AutoML 解决方案线程以及 @tilii7@roberthatch 的帖子,我从那里获得了异常值分类的想法,还有 @cdeotte 让我简单进入了 NN 的世界。我也非常感激所有积极参与讨论的人,让学习成为一种充实的体验。

无效尝试:

我实验了很多模型,但大多数并没有帮助我获得更好的交叉验证分数。特别是 XGB 对我不起作用,虽然它偶然包含在我的最终提交中,但我不认为它是集成的关键部分。
特征工程至少部分起作用,但 Chris Deotte 在这个帖子中引入的所有惊人特征对我都没有起作用。

相应的交叉验证 - 排行榜分数散点图:
CV LB Scores

编辑:

我将 CatBoostClassifier、LGBM1_st 和 LGBM5_st 包含在我的笔记本中。这个笔记本的私有分数为 62957.83525,本可以进入前三名。

编辑 2:

Autogluon 在使用 TabularPredictor.fit() 时,并没有使用原始数据。要使用原始数据,必须调用 TabularPredictor.fit_pseudolabel()。所以我的 Fastai - AutoGluon 预测并没有使用原始数据。关于如何在下一个 playground 竞赛中使用带有原始数据和预定义分层的 AutoGluon 的精彩参考,请参阅 Mahdi Ravaghi 的这个笔记本

同比赛其他方案