Kaggle | Titanic - Machine Learning from Disaster【泰坦尼克号生存预测】 | baseline及优秀notebook总结

魏明亮
2023-12-01

一、数据介绍

  Titanic - Machine Learning from Disaster是主要针对机器学习初学者开展的比赛,数据格式比较简单,为结构化数据。数据的数量较少(训练集892条,测试集419条),因此,就算找到有效的特征有良好的准确度,但很有可能因为一些小变动就让准确度下降。事实上,Public Leaderboard分数较高的notebook,未必对未知数据有良好的预测能力,可能只是过度比对测试数据碰巧得到吻合的结果罢了。在泰坦尼克号公开资料集中,每个用户有如下特征:

  • Survived: 是否存活(label)
  • PassengerId: (乘客ID)
  • Pclass(用户阶级):1 - 1st class,高等用户;2 - 2nd class,中等用户;3 - 3rd class,低等用户;
  • Name(名字)
  • Sex(性别)
  • Age(年龄)
  • SibSp:描述了泰坦尼克号上与乘客同行的兄弟姐妹(Siblings)和配偶(Spouse)数目;
  • Parch:描述了泰坦尼克号上与乘客同行的家长(Parents)和孩子(Children)数目;
  • Ticket(船票号)
  • Fare(乘客费用)
  • Cabin(船舱)
  • Embarked(港口):用户上船时的港口

二、代码

  代码实现包含如下基本步骤:

  • 特征处理
  • 模型搭建
  • 模型调参
  • 模型集成(融合)
#!usr/bin/env python
# -*- coding:utf-8 -*-
"""
@author: liujie
@file: titanic.py
@time: 2022/09/08
@desc:Kaggle案例——泰坦尼克号
"""
import numpy as np
import pandas as pd
from xgboost import XGBClassifier
from sklearn.model_selection import KFold
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import log_loss, accuracy_score

# TODO:1.构造训练集与测试集
train = pd.read_csv("data/train.csv", sep=",", header=0)
test = pd.read_csv("data/test.csv", sep=",", header=0)
x_train = train.drop(['Survived'], axis=1)
y_train = train['Survived']
x_test = test.copy()

# TODO:2.建立特征
# 去除 PassengerId,Name, Ticket, Cabin
x_train = x_train.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1)
x_test = x_test.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1)
# 对Sex、Embarked进行label encoding
for c in ["Sex", "Embarked"]:
    le = LabelEncoder()
    le.fit(x_train[c].fillna("NA"))

    x_train[c] = le.transform(x_train[c].fillna("NA"))
    x_test[c] = le.transform(x_test[c].fillna("NA"))

# TODO:3.建立模型
xgb = XGBClassifier(n_estimators=20, random_state=2022)
xgb.fit(x_train, y_train)  # 训练
pred = xgb.predict_proba(x_test)[:, 1]  # 预测
# 将结果进行转换
pred = np.where(pred > 0.5, 1, 0)

# TODO:4.K折交叉验证
# 用 List 保存各 fold 的 accuracy 与 logloss 分数
scores_accuracy = []
scores_logloss = []

kf = KFold(n_splits=4, shuffle=True, random_state=2022)
for tr_idx, va_idx in kf.split(x_train):
    # 分为训练集与验证集
    tr_x, va_x = x_train.iloc[tr_idx], x_train.iloc[va_idx]
    tr_y, va_y = y_train.iloc[tr_idx], y_train.iloc[va_idx]
    # 建立XGBoost模型
    xgb = XGBClassifier(n_estimators=20, random_state=2022)
    xgb.fit(tr_x, tr_y)
    # 对验证集进行预测
    va_pred = xgb.predict_proba(va_x)[:, 1]
    # 评测logloss与acc
    logloss = log_loss(va_y, va_pred)
    acc = accuracy_score(va_y, va_pred > 0.5)

    scores_accuracy.append(acc)
    scores_logloss.append(logloss)

# 输出每折评价指标的平均值
logloss = np.mean(scores_logloss)
accuracy = np.mean(scores_accuracy)
print(f'logloss: {logloss:.4f}, accuracy: {accuracy:.4f}')
# logloss: 0.4300, accuracy: 0.8137

# TODO:5.调整超参数
import itertools

# 准备用于调整的超参数
param_space = {"max_depth": [3, 5, 7], "min_child_weight": [1.0, 2.0, 4.0]}
# 产生所有超参数组合
param_combinations = itertools.product(param_space["max_depth"], param_space["min_child_weight"])
# 用 List 保存各参数组合的logloss 分数
params = []
scores = []

for max_depth, min_child_weight in param_combinations:
    # 保存每个fold的分数
    scores_fold = []
    kf = KFold(n_splits=4, shuffle=True, random_state=2022)
    for tr_idx, va_idx in kf.split(x_train):
        # 分为训练集与验证集
        tr_x, va_x = x_train.iloc[tr_idx], x_train.iloc[va_idx]
        tr_y, va_y = y_train.iloc[tr_idx], y_train.iloc[va_idx]
        # 建立XGBoost模型
        xgb = XGBClassifier(n_estimators=20, max_depth=max_depth, min_child_weight=min_child_weight, random_state=2022)
        xgb.fit(tr_x, tr_y)
        # 对验证集进行预测
        va_pred = xgb.predict_proba(va_x)[:, 1]
        # 评测logloss与acc
        logloss = log_loss(va_y, va_pred)
        scores_fold.append(logloss)

    score_mean = np.mean(scores_fold)
    params.append((max_depth, min_child_weight))
    scores.append(score_mean)

# 找出评价指标最佳的组合
best_idx = np.argsort(scores)[0]
best_param = params[best_idx]
print(f'best_param={best_param},best_score={scores[best_idx]}')
# best_param=(7, 2.0),best_score=0.4212539335124341

# TODO:6.建立逻辑回归模型所需特征,为后续模型集成做准备
from sklearn.preprocessing import OneHotEncoder

x2_train = train.drop(['Survived'], axis=1)
y2_train = train['Survived']
x2_test = test.copy()

# 去除训练、测试资料中的PassengerId、Name、Ticket、Cabin
x2_train = x2_train.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1)
x2_test = x2_test.drop(['PassengerId', 'Name', 'Ticket', 'Cabin'], axis=1)

# 对类别特征进行oneHot编码
cat_cols = ['Sex', 'Embarked', 'Pclass']
ohe = OneHotEncoder(categories='auto', sparse=False)
ohe.fit(x2_train[cat_cols].fillna('NA'))

# 构建one_hot编码后的特征名
ohe_columns = []
for i, c in enumerate(cat_cols):
    # .categories_:表示该特征包含哪些类别的值
    ohe_columns += [f'{c}_{v}' for v in ohe.categories_[i]]

# 将one_hot编码后的结果保存到dataframe中
ohe_train_x2 = pd.DataFrame(ohe.transform(x2_train[cat_cols].fillna('NA')), columns=ohe_columns)
ohe_test_x2 = pd.DataFrame(ohe.transform(x2_test[cat_cols].fillna('NA')), columns=ohe_columns)

# 去除原数据中已经onehot编码的特征
x2_train = x2_train.drop(cat_cols, axis=1)
x2_test = x2_test.drop(cat_cols, axis=1)

# 将onehot编码后的dataframe与原数据合并
x2_train = pd.concat([x2_train, ohe_train_x2], axis=1)
x2_test = pd.concat([x2_test, ohe_test_x2], axis=1)

# 填充缺失值
num_cols = ['Age', 'SibSp', 'Parch', 'Fare']
for col in num_cols:
    x2_train[col].fillna(x2_train[col].mean(), inplace=True)
    x2_test[col].fillna(x2_train[col].mean(), inplace=True)

# 将Fare取对数,变为正态分布
x2_train['Fare'] = np.log1p(x2_train['Fare'])
x2_test['Fare'] = np.log1p(x2_test['Fare'])

# TODO:7.模型集成
from sklearn.linear_model import LogisticRegression

xgb_model = XGBClassifier(n_estimators=20, max_depth=7, min_child_weight=2.0, random_state=2022)
xgb_model.fit(x_train, y_train)
xgb_pred = xgb_model.predict_proba(x_test)[:, 1]

lr_model = LogisticRegression(solver='lbfgs', max_iter=300)
lr_model.fit(x2_train, y2_train)
lr_pred = lr_model.predict_proba(x2_test)[:, 1]

# 取多个模型预测结果的加权值
pred = xgb_pred * 0.8 + lr_pred * 0.2
label_pred = np.where(pred > 0.5, 1, 0)

# 将预测结果进行保存
submission = pd.DataFrame({"PassengerId": test['PassengerId'], "Survived": label_pred})
submission.to_csv("submission.csv", index=False)

三、代码优化方向

  How am I doing with my score?这个notebook将参赛者们使用了什么手法,出现了什么样的分数整合起来。可进行参考。

  • 0.77990 Gender + Class + Embarked LightGBM in Python.

    这个notebook仅使用Sex,Embarked,Pclass这三个特征来进行预测;使用的模型是LightGBM。

    • 对Embarked进行缺失值填充
    • 对Sex与Embarked进行标签编码
    • 利用网格搜索对lgb模型进行10折交叉验证,寻找最佳模型,得到预测结果
  • 0.78468 Name-only text vectorization and PCA with a 3D interactive plot.

    这个notebook仅使用Name特征来进行预测,使用的模型是KNeighborsClassifier。

    • 利用单词计数+PCA来矢量化名称特征,
    • 使用 KNeighborsClassifier模型并使用 GridSearchCV 对其进行调整参数,最后得到预测结果
  • 0.78947 Gender + Class + Embarked + Age using SVM

    这个notebook仅使用Sex,Embarked,Pclass、Age这三个特征来进行预测;使用的模型是SVM。

    • 读取数据,并处理Embarked特征中的缺失值
    • 针对Age特征,只保留年轻人,以及给Age缺失的人创建一个指标标志
    • 针对Embarked,Pclass特征,采用oneHot编码;针对Sex特征,采用Lable 编码
    • 使用 SVC模型并使用 GridSearchCV 对其进行调整参数,最后得到预测结果
  • 0.79904 Neural network (keras) by Rafael Vicente Leite.

    这个notebook使用ANN模型

    • 读取数据
      • 提取姓名中的性别标志
      • 丢弃不相关特征’Ticket’, ‘Cabin’
      • 填充’Embarked’特征列中的缺失值
      • 对Name特征中的性别标志进行标签编码
      • 对Sex与Embarked进行标签编码
      • 得到对应性别与用户等级的平均票价与年龄,用于缺失值填充
      • 对’Embarked’, ‘Name’, 'Pclass’特征进行oneHot编码
      • 对所有特征进行标准化
    • 构建ANN模型
    • 模型预测和评估
    • 生成提交文件
  • 0.79904 Using ethnicity feature by Frederik Hasecke.

    这个notebook使用

    • 数据准备
      • 填充Embarkment、Fare、Age中的缺失值,Age填充规则比较复杂
      • 从Name特征中分析出种族
      • 将Sex、处理后Name标志、Embarked、Fare、Ethnicity、Age都进行标签编码
    • 模型训练
      Gauss、KNN、Log Reg、RandomF、SVM
    • 模型融合
  • 0.80861 Simple stacking by Anisotropic stacking is ubiquitous in competitions.

    这个notebook使用stacking集成方法,这种方法容易过拟合

    • 数据准备
      • 构造FamilySize与IsAlone的特征
      • 填充Embarked(港口)的缺失值
      • 填充Fare的缺失值
      • 填充Age,并用其置信区间中的值(平均值加减标准差)
      • 构造Name的Title特征
      • 对Sex、Title、Embarked、Fare、Age进行分箱
    • stacking
      • RandomForestClassifier, AdaBoostClassifier, GradientBoostingClassifier, ExtraTreesClassifier与SVC模型都进行5折交叉验证,保留训练集与测试集的预测值
      • 利用XGBClassifier模型,输入为预测值,输出为真实值来构建模型,得到预测结果(提交文件)
  • 0.80861 Voting/ensembling by Nick Brooks An impressive number of models is packed in almost one hour of running time!

    这个notebook使用stacking模型

    • 数据加载,数据预处理及特征工程
      • 构建FamilySize、Name_length、IsAlone、Title
      • 利用姓名Title来填充Age中的缺失值
      • 用Embarked的众数来填充Embarked(港口)的缺失值
      • 用Fare的均值来填充Fare中的缺失值
      • 对Sex特征进行标签编码,对Title特征进行标签编码,并用其众数来填充缺失值
      • 对Embarked特征进行标签编码
      • 丢弃不相关特征’Ticket’, 'Cabin
      • 将连续变量缩放到-1到1之间
      • 将数据集分为训练集与测试集
    • 模型训练
      • K-Nearest Neighbors
      • SGDClassifier
      • Decision Trees
      • Random Forest
      • AdaBoostClassifier
      • GradientBoostingClassifier
      • XGBClassifier
      • CatBoost
      • lgb
      • LogisticRegression
      • MLPClassifier
      • SVC
    • 将上述几种模型的结果进行stacking
      • Logistic Regression
  • 0.80861 Kaggle Titanic with Tensorflow by nme-42 It is quite an interesting kernel.

    这个notebook使用DNN

    • 数据加载
    • 数据预处理
      • 构造船舱级别特征deck level
      • 丢弃船舱特征Cabin
      • 填充Embarked特征中的缺失值,用’N’填充表示缺失
      • 填充Fare中的缺失值,用对应Pclass的Fare的众数
      • 填充Age中的缺失值
    • 特征工程
      • 构建Family Size特征

    后续部分没咋见过,感兴趣自己看!

  • 0.81339 Titanic Using Ticket Grouping by Jack Roberts.

    基于规则进行预测

  • 0.82775 Frank Sylla engineers several features.

    • 数据加载
    • 特征工程
      • 从Name特征中构建surname与Title特征,并对Title特征进行标签编码-TitleCat
      • 构建家庭人数(FamilySize)特征并切分后进行标签编码
      • 构建Name长度特征-NameLength
      • 填充Fare特征中的缺失值
      • 将Sex特征进行哑编码
      • 将Embarked特征与Cabin特征的第一位进行标签编码
      • 针对Cabin特征的数字部分构建CabinType特征,表示船舱号是奇数、偶数还是空
      • 构建person特征,用于区分CHILD/FEMALE ADULT/MALE ADULT,并对这个特征进行哑编码后再与元特征进行拼接
  • 0.83253 Konstantin brings attention to feature scaling, which is essential when working with the kNN algorithm.

    这个Notebook使用KNN算法,达到了非常好的效果

    • 特征工程
      • 基于Name特征中构建Title特征,基于Title特征来估算Age中的缺失值
      • 构建Family_Size特征
      • 构建姓氏特征Last_Name
      • 对船票价格特征Fare中的缺失值用均值进行填充
      • 基于Last_Name、Fare、Survived、Ticket特征构建Family_Survival特征
      • 将Fare特征中的缺失值用Fare中的中位数进行填充,并先进行等位分桶,再进行标签编码
      • 将Age特征先进行等位分桶,再进行标签编码
      • 将性别特征进行标签编码
      • 将特征缩放到-1到1之间
    • 模型训练
      利用Grid Search CV对KNN进行超参数调参找到KNN模型的最优参数来进行预测

参考

 类似资料: