返回列表

#9 Solution | 24 Models + Hill Climbing

619. Playground Series - Season 4, Episode 7 | playground-series-s4e7

开始: 2024-06-30 结束: 2024-07-31 保险科技 数据算法赛
#9 解决方案 | 24 模型 + 爬山算法
作者: Oscar Aguilar (Grandmaster)
排名: 第 9 名
发布时间: 2024-08-01

#9 解决方案 | 24 模型 + 爬山算法

首先,我要感谢 Kaggle 团队组织这次比赛。这是我第一次处理如此大的数据集,这让项目从一开始就令人兴奋。在这篇文章中,我将简要概述我的方法。

初始建模

在比赛初期,由于数据量巨大,我选择使用 Logistic Regression(逻辑回归)对数据进行建模。这种方法提供了一些关于数据的初步见解。例如,当我使用 TargetEncoding(目标编码)时,Logistic Regression 模型的性能显著提高。这表明某些特征虽然是数字存储的,但应被视为类别特征。您可以在这篇 帖子 中找到更多细节。

数据

我同时使用了比赛数据集和原始数据集。

数据预处理

为了减少内存使用,我考虑了以下预处理步骤:

def converting_datatypes(df, df_train=False):
    
    df = df.copy()
    
    if df_train==False:
        
        df['Policy_Sales_Channel'] = np.where(df['Policy_Sales_Channel']==144., 145., df['Policy_Sales_Channel'])
        df['Policy_Sales_Channel'] = np.where(df['Policy_Sales_Channel']==149., 150., df['Policy_Sales_Channel'])

    df['Age'] = df['Age'].astype('int8')
    df['Driving_License'] = df['Driving_License'].astype('int8')
    df['Region_Code'] = df['Region_Code'].astype('int8')
    df['Previously_Insured'] = df['Previously_Insured'].astype('int8')
    df['Annual_Premium'] = df['Annual_Premium'].astype('int32')
    df['Policy_Sales_Channel'] = df['Policy_Sales_Channel'].astype('int16')
    df['Vintage'] = df['Vintage'].astype('int16')
    df['Gender'] = df['Gender'].map({'Female': 0, 'Male': 1}).astype('int8')

    df['Vehicle_Age'] = df['Vehicle_Age'].map({'< 1 Year': 0, 
                                               '1-2 Year': 1,
                                               '> 2 Years': 2}).astype('int8')

    df['Vehicle_Damage'] = df['Vehicle_Damage'].map({'No': 0, 'Yes': 1}).astype('int8')

    if df_train==True:

        df['Response'] = df['Response'].astype('int8')

    return df

特征工程

我考虑了以下特征:

def fe(df_train, df_test, df_original):

    n = df_train.shape[0]
    m = df_test.shape[0]
    p = n+m
    df_tot = pd.concat([df_train, df_test, df_original], axis=0).reset_index(drop=True)
    
    df_tot['interaction_1'] = pd.factorize((df_tot['Previously_Insured'] + df_tot['Vehicle_Age']).to_numpy())[0]
    df_tot['interaction_2'] = pd.factorize((df_tot['Previously_Insured'] + df_tot['Vehicle_Damage']).to_numpy())[0]
    df_tot['interaction_3'] = pd.factorize((df_tot['Previously_Insured'] + df_tot['Vintage']).to_numpy())[0]
    df_tot['interaction_4'] = pd.factorize((df_tot['Previously_Insured'] + df_tot['Annual_Premium']).to_numpy())[0]
    df_tot['interaction_5'] = pd.factorize((df_tot['Previously_Insured'] + df_tot['Gender']).to_numpy())[0]
    df_tot['interaction_6'] = pd.factorize((df_tot['Previously_Insured'] + df_tot['Driving_License']).to_numpy())[0]
    df_tot['interaction_7'] = pd.factorize((df_tot['Vehicle_Age'] + df_tot['Vehicle_Damage']).to_numpy())[0]
    df_tot['interaction_8'] = pd.factorize((df_tot['Vehicle_Age'] + df_tot['Driving_License']).to_numpy())[0]

    return [df_tot[:n], df_tot.iloc[n:p].drop(columns=['Response'], axis=1), df_tot[p:]]

模型

我考虑了以下模型:

  • 随机森林 (Random Forest)(在 LGBM 框架下,因为它运行得更快)。更多细节请参阅这篇 帖子
  • LGBMClassifier
  • XGBClassifier
  • TensorFlow。我构建的第一个版本的 TensorFlow 在 10 折交叉验证中达到了 0.882 的 OOF ROC-AUC。后来,我使用了 @paddykb 的 TensorFlow 模型(参见此 笔记本),因为它优于我最初的 TensorFlow 模型。
  • Catboost。我想这次比赛中的每个人都使用了某种版本的 @rohanrao Catboost 模型。更多细节请参阅此 笔记本

下表显示了所考虑的每个模型在 10 折交叉验证中的最佳性能。

模型 比赛数据 比赛 + 原始数据
CatBoost 0.895311 0.895819
LGBM 0.892605 0.892753
TensorFlow 0.892083 0.892213
XGBoost 0.890959 0.89105
Random Forest 0.873091 0.875128

集成学习

在 Kaggle 社区中,众所周知可以通过集成不同模型的预测来优化 ROC-AUC 分数。为了实现这一点,我采用了爬山策略(Hill Climbing)。这种策略类似于爬山,涉及以线性方式组合模型预测,只要目标指标改善,就始终向上移动。最终的集成包括 24 个模型:

模型 比赛数据 比赛 + 原始数据
CatBoost 4 个模型 4 个模型
LGBM 3 个模型 3 个模型
TensorFlow 3 个模型 3 个模型
XGBoost 1 个模型 1 个模型
Random Forest 1 个模型 1 个模型

从上表可以看出,重要的是要注意并非所有模型都是在相同的数据上训练的。例如,4 个 CatBoost 模型是在比赛数据上训练的;然而,不同的特征被用来训练这些模型。

后处理

最后,我使用 @paddykb 在此 帖子 中提出的建议对爬山集成的预测进行了后处理。

期待下一期比赛。

同比赛其他方案