返回列表

4th place solution: Stacking with XGB + Pseudo labeling + metric optimizing.

597. Playground Series - Season 4, Episode 2 | playground-series-s4e2

开始: 2024-02-01 结束: 2024-02-29 临床决策支持 数据算法赛

第4名解决方案:使用XGB堆叠 + 伪标签 + 指标优化

作者:Kirderf(Kaggle Grandmaster)
排名:第4名
发布日期:2024-03-01

再次感谢这个有趣的系列挑战赛,总是很有趣,也是测试和实验新模型与想法、与旧模型进行基准测试的好地方!

这次比赛出现了大变动,我个人上升了255个名次。但这在测试集比例为20/80的情况下是意料之中的,在这种情况下,应该高度重视最佳的本地交叉验证结果。

在这个挑战中,我们面临的是一个多分类问题,不久前我们在"肝硬化结果的多类别预测"比赛中遇到过类似问题,因此我从那场比赛的第4名解决方案中借鉴了一些思路。

虽然不完全相同——特征、评估指标、数据等都不同——但概念仍然可以复用。结果非常理想,我在这里也取得了第4名的成绩。

解决方案

概述

使用XGB作为元学习器的堆叠方法,并加入不同SOTA解决方案的预测作为额外的堆叠特征。最终推理包括优化的准确率指标和9个不同版本堆叠代码及其他多样化解决方案的预测,取每行中出现次数最多的类别。

数据和特征工程

同时使用了比赛数据集和额外的数据集。我使用了两种不同的特征工程方法:

方法1:
train_df = pd.read_csv('/kaggle/input/playground-series-s4e2/train.csv')
original = pd.read_csv('/kaggle/input/obesity-or-cvd-risk-classifyregressorcluster/ObesityDataSet.csv')
train_df = pd.concat([train_df, original]).drop(['id'], axis=1).drop_duplicates().reset_index(drop=True)

# 高级特征工程
train_df['Age_Group'] = pd.cut(train_df['Age'], bins=[20, 30, 40, 50, 55], labels=['A', 'B', 'C', 'D'])
train_df['Log_Age'] = np.log1p(train_df['Age'])
scaler = MinMaxScaler()
train_df['Scaled_Age'] = scaler.fit_transform(train_df['Age'].values.reshape(-1, 1))

test_df = pd.read_csv('/kaggle/input/playground-series-s4e2/test.csv')

# 测试数据特征工程
test_df['Age_Group'] = pd.cut(test_df['Age'], bins=[20, 30, 40, 50, 55], labels=['A', 'B', 'C', 'D'])
test_df['Log_Age'] = np.log1p(test_df['Age'])
test_df['Scaled_Age'] = scaler.transform(test_df['Age'].values.reshape(-1, 1))

test_df = test_df.drop(columns=['id'])

label_encoder = LabelEncoder()
label_encoder.fit(train_df['NObeyesdad'].unique())
train_df['NObeyesdad'] = label_encoder.transform(train_df['NObeyesdad'])

以及:

方法2:
train['Age group'] = pd.cut(train['Age'], bins=[0, 18, 30, 45, 60, train['Age'].max()], labels=['0-18', '19-30', '31-45', '46-60', '60+'])
train['BMI'] = train['Weight'] / (train['Height'] ** 2)
test['Age group'] = pd.cut(test['Age'], bins=[0, 18, 30, 45, 60, test['Age'].max()], labels=['0-18', '19-30', '31-45', '46-60', '60+'])
test['BMI'] = test['Weight'] / (test['Height'] ** 2)
original['Age group'] = pd.cut(original['Age'], bins=[0, 18, 30, 45, 60, original['Age'].max()], labels=['0-18', '19-30', '31-45', '46-60', '60+'])
original['BMI'] = original['Weight'] / (original['Height'] ** 2)

train['Age * Gender'] = train['Age'] * train['Gender']
test['Age * Gender'] = test['Age'] * test['Gender']
original['Age * Gender'] = original['Age'] * original['Gender']

categorical_features = ['Gender', 'family_history_with_overweight', 'Age group', 'FAVC','CAEC', 'SMOKE','SCC', 'CALC', 'MTRANS']
train = pd.get_dummies(train, columns=categorical_features)
test = pd.get_dummies(test, columns=categorical_features)
original = pd.get_dummies(original, columns=categorical_features)

polynomial_features = PolynomialFeatures(degree=2)
X_poly = polynomial_features.fit_transform(train[['Age', 'BMI']])
train = pd.concat([train, pd.DataFrame(X_poly, columns=['Age^2', 'Age^3', 'BMI^2', 'Age * BMI', 'Age * BMI2', 'Age * BMI3'])], axis=1)
X_poly = polynomial_features.transform(test[['Age', 'BMI']])
test = pd.concat([test, pd.DataFrame(X_poly, columns=['Age^2', 'Age^3', 'BMI^2', 'Age * BMI', 'Age * BMI2', 'Age * BMI3'])], axis=1)
X_poly = polynomial_features.transform(original[['Age', 'BMI']])
original = pd.concat([original, pd.DataFrame(X_poly, columns=['Age^2', 'Age^3', 'BMI^2', 'Age * BMI', 'Age * BMI2', 'Age * BMI3'])], axis=1)

模型和框架

比赛指标是准确率,但训练时使用了log_loss,并保存每个解决方案的概率供后续使用。

  • AutoXGB
  • AutoGluon(带有新的零样本HPO训练)
    集成权重:{'CatBoost_r9_BAG_L1': 0.363, 'LightGBM_r131_BAG_L1': 0.253, 'XGBoost_BAG_L1': 0.099, 'XGBoost_r33_BAG_L1': 0.099, 'NeuralNetTorch_BAG_L2': 0.077, 'ExtraTreesEntr_BAG_L2': 0.033, 'NeuralNetFastAI_BAG_L2': 0.022, 'CatBoost_BAG_L2': 0.022, 'NeuralNetTorch_BAG_L1': 0.011, 'RandomForestGini_BAG_L2': 0.011, 'ExtraTreesGini_BAG_L2': 0.011}
  • LightAutoml(使用LGBM和Catboost)
  • 自定义XGB + LGBM训练代码(有/无伪标签训练)

堆叠

使用训练集的特征,并加入不同解决方案的概率作为额外特征。元模型是XGB。堆叠在分类问题中是非常有效的方法。

指标优化和最终推理

我使用了公开的指标优化代码,并使用了之前保存的每个类别的概率。对于最终推理,我使用了9个不同预测结果的集合(包括自己训练的解决方案和2个公开笔记本),并计算每行中出现次数最多的类别。


就是这样!祝Kaggle愉快!

同比赛其他方案