欢迎来到 2912 年,需要您的数据科学技能来解开宇宙之谜。我们收到了来自四光年外的信号,情况看起来不太好。
泰坦尼克号宇宙飞船是一个月前下水的一艘星际客轮。船上载有近 13,000 名乘客,这艘船开始了它的处女航,将太阳系的移民运送到绕附近恒星运行的三颗新的宜居系外行星。
在绕行半人马座阿尔法星前往其第一个目的地(炎热的 55 Cancri E)途中,粗心的泰坦尼克号宇宙飞船与隐藏在尘埃云中的时空异常相撞。可悲的是,它遭遇了与 1000 年前同名的相似命运。虽然飞船完好无损,但几乎一半的乘客都被传送到了异次元!
为了帮助救援人员并找回失踪的乘客,您面临的挑战是使用从飞船损坏的计算机系统中恢复的记录来预测哪些乘客被异常现象运送。
帮助拯救他们并改变历史!
在本次比赛中,您的任务是预测泰坦尼克号宇宙飞船与时空异常相撞期间乘客是否被传送到另一个维度。为了帮助您做出这些预测,您将获得一组从船上损坏的计算机系统中恢复的个人记录。
import pandas as pd
train_data = pd.read_csv('/kaggle/input/spaceship-titanic/train.csv')
test_data = pd.read_csv('/kaggle/input/spaceship-titanic/test.csv')
train_data.shape, test_data.shape
打开数据文件,查看文件的一些基本信息,这里可用的函数有train_data.shape,train_data.head(),train_data.info()等等。
train_data.isna().sum(), test_data.isna().sum()
这里是查看数据集的缺失值。根据缺失值的数据以及类型去选择对缺失值进行删除或者填充操作。
import numpy as np
# 删除缺失值
train_data2 = train_data.dropna()
test_data2 = test_data
# 对需要转换的列进行对数转换
columns_to_transform = ['RoomService', 'FoodCourt', 'ShoppingMall', 'Spa', 'VRDeck']
# 为了避免对数运算中的负值或零,加上常数1,确保数据不为零
for col in columns_to_transform:
train_data2[col] = np.log(train_data2[col] + 1) # 对每一列进行对数转换,避免 log(0)
for col in columns_to_transform:
test_data2[col] = np.log(test_data2[col] + 1)
train_data2.shape,test_data2.shape
这里的对数转换操作是因为我提前知道这些列的分布情况,并不是很好的正态分布,因此进行对数转换能够增强模型的稳定性。
train_data2[['team', 'team_encoder']] = train_data2['PassengerId'].str.split('_', expand=True)
test_data2[['team', 'team_encoder']] = test_data2['PassengerId'].str.split('_', expand=True)
# 将该列转换为整数类型
train_data2['team'] = train_data2['team'].astype(int)
train_data2['team_encoder'] = train_data2['team'].astype(int)
test_data2['team'] = test_data2['team'].astype(int)
test_data2['team_encoder'] = test_data2['team'].astype(int)
train_data2.shape, test_data2.shape
根据字段的信息可以知道,每个 ID 均采用以下形式gggg_pp,其中gggg指示乘客随行的团体,并且pp是他们在团体中的编号。因此在这里进行了切分操作,而不是直接从特征中删除。一般的唯一标识符所含有的数据信息太少,在入模之前都是进行删除操作。
## 这里计算每个人在飞船上的总消费金额
train_data2.loc[:, 'total_fee'] = train_data2['RoomService'] + train_data2['FoodCourt'] + train_data2['ShoppingMall'] + train_data2['Spa'] + train_data2['VRDeck']
test_data2.loc[:, 'total_fee'] = test_data2['RoomService'] + test_data2['FoodCourt'] + test_data2['ShoppingMall'] + test_data2['Spa'] + test_data2['VRDeck']
# 使用 str.split() 拆分
train_data2[['deck', 'num', 'side']] = train_data2['Cabin'].str.split('/', expand=True)
test_data2[['deck', 'num', 'side']] = test_data2['Cabin'].str.split('/', expand=True)
# 删除多个列
train_data3 = train_data2.drop(['Cabin', 'Name','PassengerId'], axis=1)
test_data3 = test_data2.drop(['Cabin', 'Name','PassengerId'], axis=1)
train_data3.drop_duplicates(inplace = True)
PassengerId=test_data2['PassengerId']
# 使用 map() 方法进行编码
train_data3['Transported'] = train_data3['Transported'].map({True: 1, False: 0})
train_data3.shape, test_data3.shape
# 准备入模,划分好目标变量和特征
train_data4=train_data3.drop(['Transported'], axis=1)
label=train_data3['Transported']
以上都是一些数据处理和特征工程的操作,大家自行查看。
from sklearn.preprocessing import OneHotEncoder
import pandas as pd
def encode_categorical_features(train_df, test_df, categorical_columns):
"""
对指定的类别型特征进行 One-Hot 编码,并对训练集和测试集进行特征对齐。
"""
encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore') # 创建 OneHotEncoder
train_encoded = train_df.copy() # 使用传入的训练集数据
test_encoded = test_df.copy() # 使用传入的测试集数据
for column in categorical_columns:
# 对训练集进行 fit_transform
train_encoded_array = encoder.fit_transform(train_df[[column]])
# 对测试集使用训练集的规则进行 transform
test_encoded_array = encoder.transform(test_df[[column]])
# 将编码后的数据转换为 DataFrame
train_encoded_df = pd.DataFrame(train_encoded_array,
columns=encoder.get_feature_names_out([column]),
index=train_df.index)
test_encoded_df = pd.DataFrame(test_encoded_array,
columns=encoder.get_feature_names_out([column]),
index=test_df.index)
# 合并编码后的数据到原始 DataFrame 中
train_encoded = pd.concat([train_encoded, train_encoded_df], axis=1)
test_encoded = pd.concat([test_encoded, test_encoded_df], axis=1)
# 删除原始的类别型列
train_encoded.drop(column, axis=1, inplace=True)
test_encoded.drop(column, axis=1, inplace=True)
# 确保训练集和测试集的列顺序一致
test_encoded = test_encoded.reindex(columns=train_encoded.columns, fill_value=0)
return train_encoded, test_encoded
# 需要编码的列名
encoder_columns = ['HomePlanet', 'CryoSleep', 'Destination', 'VIP', 'deck', 'num', 'side']
# 调用函数进行编码,传入指定的类别列
train_data_encoded, test_data_encoded = encode_categorical_features(train_data4, test_data3, encoder_columns)
# 查看结果维度
print("训练集维度: ", train_data_encoded.shape)
print("测试集维度: ", test_data_encoded.shape)
以上属于编码的操作,这里使用的OneHotEncoder,一般情况下,模型更想要的是数值数据,因此需要转成数值数据。object类型的编码一般是独热编码用的较多,这里就要提一下分类变量的类型。
分类变量(Categorical Variables)是指那些数据值表示不同类别的变量。分类变量本身并没有数量上的顺序,通常用来表示某个对象或现象的类型、类别等。根据其具体的特点,分类变量可分为几种类型,且在机器学习与数据分析中,通常需要对分类变量进行编码,以便进行后续的建模和计算。
(1)名义型变量(Nominal Variables)
定义:名义型变量表示不同的类别或类型,但这些类别之间没有内在的顺序关系。例如:性别(男、女)、颜色(红色、蓝色、绿色)、城市(北京、上海、广州)。
示例:
性别:男、女
城市:纽约、伦敦、东京
(2)顺序型变量(Ordinal Variables)
定义:顺序型变量不仅表示不同的类别,而且这些类别之间具有某种自然的顺序关系,但顺序间的距离不一定是均等的。顺序型变量不仅告诉我们类别的不同,还给出了这些类别之间的相对顺序。例如:教育程度(小学、中学、大专、大学)、星级评价(1星、2星、3星、4星、5星)。
示例:
教育程度:小学 < 中学 < 大专 < 本科
星级评价:1星 < 2星 < 3星 < 4星 < 5星
2. 分类变量的编码方法
在机器学习和数据分析中,由于大多数算法无法直接处理非数值型的数据,我们需要将分类变量转换为数值型变量。常见的编码方法包括:
(1)独热编码(One-Hot Encoding)
适用范围:适用于名义型变量。
原理:将每个类别转换为一个新的二进制特征列。如果某个样本属于某个类别,则该类别对应的列为1,其他列为0。每个类别都会变成一个独立的列。
示例:
假设有一个“颜色”变量,类别为:红色、蓝色、绿色。
编码后的结果:
红色 蓝色 绿色
1 0 0
0 1 0
0 0 1
优点:不会引入任何顺序关系,避免了不必要的模型假设。适用于类别数量较少的变量。
缺点:如果类别数目很大,编码后的数据会变得非常稀疏(维度过高)。
(2)标签编码(Label Encoding)
适用范围:适用于顺序型变量(有内在顺序的分类变量),也可以应用于名义型变量,但对于名义型变量可能会引入不必要的顺序假设。
原理:将每个类别映射为一个整数值。例如,“红色” = 0, “蓝色” = 1, “绿色” = 2。
示例:
假设有一个“教育程度”变量,类别为:小学、中学、大专、本科。
编码后的结果:
教育程度 编码
小学 0
中学 1
大专 2
本科 3
优点:编码简单,维度低。
缺点:对于名义型变量使用时可能不合适,因为会误导模型认为这些数字之间存在某种顺序或距离(例如,模型可能认为"红色"比"蓝色"大)。
(3)目标编码(Target Encoding)
适用范围:适用于分类变量和回归问题,尤其是类别很多时。
原理:通过计算每个类别的目标变量(如标签)的均值或其他统计量来替代类别。例如,对于某个分类变量,每个类别的编码就是该类别对应的目标变量的平均值。
示例:
假设我们有一个“城市”变量和一个“收入”目标变量,通过计算每个城市的平均收入来替代“城市”变量。
优点:能够有效处理高基数(即类别数非常多)的情况。
缺点:可能会导致过拟合,尤其是在数据集较小的时候。
(4)频率编码(Frequency Encoding)
适用范围:适用于类别较多的变量。
原理:将每个类别用该类别在数据集中的出现频率来表示。频率高的类别对应较大的数值。
示例:
假设有一个“城市”变量,并且“纽约”出现了50次,“洛杉矶”出现了30次,“东京”出现了20次。
编码后的结果:
城市 频率编码
纽约 50
洛杉矶 30
东京 20
优点:简洁,且避免了独热编码带来的高维问题。
缺点:可能丧失类别的具体信息,且频率高的类别可能在某些算法中被错误地解释为重要。
(5)二进制编码(Binary Encoding)
适用范围:适用于类别数目较多且需要减少维度的情况。
原理:首先将类别用整数编码,然后将这些整数转换为二进制数,最终将二进制数拆分成多个列。
示例:
假设有5个类别,我们首先对其进行整数编码(例如:0, 1, 2, 3, 4),然后将这些整数转换为二进制:000, 001, 010, 011, 100。
编码后的结果:
类别 二进制编码
0 000
1 001
2 010
3 011
4 100
优点:相比独热编码,维度较低。
缺点:对模型来说,二进制编码可能难以解释,尤其是在类别很多时。
总结
对于 名义型变量,常用的编码方式是 独热编码,但如果类别很多也可以使用 频率编码 或 目标编码。
对于 顺序型变量,适合使用 标签编码,因为这些变量本身存在一定的顺序关系。
对于类别数目较多的变量,除了常规的独热编码和标签编码, 目标编码 和 频率编码 也可以作为有效选择。
选择哪种编码方式,主要取决于数据的特点、所选模型以及任务的具体要求。
from sklearn.model_selection import train_test_split
from sklearn.metrics import precision_score, recall_score, f1_score, roc_auc_score
import lightgbm as lgb
import optuna
import joblib # 用于保存模型
x = train_data_encoded
y = label
# 切分数据集
X_train, X_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
# 定义目标函数
def objective(trial):
params = {
'objective': 'binary',
'scale_pos_weight': 0.98557,
'boosting_type': 'gbdt',
'max_depth': trial.suggest_int('max_depth', 3, 10),
'num_leaves': trial.suggest_int('num_leaves', 20, 150),
'min_child_samples': trial.suggest_int('min_child_samples', 10, 100),
'min_child_weight': trial.suggest_loguniform('min_child_weight', 1e-3, 10.0),
'subsample': trial.suggest_uniform('subsample', 0.6, 1.0),
'colsample_bytree': trial.suggest_uniform('colsample_bytree', 0.6, 1.0),
'learning_rate': trial.suggest_loguniform('learning_rate', 1e-4, 0.1),
'reg_lambda': trial.suggest_loguniform('reg_lambda', 1e-3, 10.0),
'reg_alpha': trial.suggest_loguniform('reg_alpha', 1e-3, 10.0),
}
model = lgb.LGBMClassifier(**params, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
f1 = f1_score(y_test, y_pred)
return f1
# 启动优化
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=50)
# 使用最佳参数训练模型
best_params = study.best_trial.params
best_model = lgb.LGBMClassifier(**best_params, random_state=42)
best_model.fit(X_train, y_train)
# 保存模型
joblib.dump(best_model, "best_lightgbm_model.pkl")
# 评估
y_pred = best_model.predict(X_test)
y_proba = best_model.predict_proba(X_test)[:, 1]
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_proba)
# 输出结果
print("最佳参数: ", best_params)
print("查准率: {:.5f}".format(precision))
print("查全率: {:.5f}".format(recall))
print("F1分数: {:.5f}".format(f1))
print("AUC分数: {:.5f}".format(auc))
print("模型已保存为 'best_lightgbm_model.pkl'")
运行结果如下
最佳参数: {'max_depth': 5, 'num_leaves': 64, 'min_child_samples': 49, 'min_child_weight': 2.4205967592730935, 'subsample': 0.8284617968332849, 'colsample_bytree': 0.815004121704074, 'learning_rate': 0.052056911035826305, 'reg_lambda': 0.002421309962401076, 'reg_alpha': 9.088728193209626}
查准率: 0.77869
查全率: 0.83700
F1分数: 0.80679
AUC分数: 0.88882
模型已保存为 'best_lightgbm_model.pkl'
这里选择使用lightgbm完全是本人习惯,主要是因为其运行速度较其他算法更快。大家可以选择别的模型进行尝试,或者直接使用堆叠,堆叠在竞赛中是比较玩赖的算法,将多种模型的效果可以进行叠加。
LightGBM的主要优点包括训练速度快、内存使用高效、支持类别特征、能够处理大规模数据集、支持分布式训练和GPU加速、具有灵活的调参能力、强大的可解释性以及支持多种任务类型。这些优点使得LightGBM在解决机器学习问题时,特别是在数据量较大时,成为一种非常流行和高效的工具。
这里的参数优化使用了optuna,大家也可以使用网格搜索或者遗传算法和粒子群算法进行参数优化。
Optuna 是一个自动化的超参数优化框架,旨在帮助机器学习模型在不同的超参数配置下找到最优解。它提供了高效、灵活且易于使用的超参数调优工具,适用于各种机器学习任务,包括深度学习、传统机器学习以及强化学习等。Optuna的核心特点是通过高效的算法自动化调整模型的超参数,极大地减少了人工调参的时间和工作量。
Optuna的主要特点和优势
自动化超参数优化:
Optuna 旨在通过自动化搜索超参数空间来帮助优化机器学习模型。它通过定义一个目标函数(objective function),在该函数中评估模型的性能,并通过智能搜索来找到最优的超参数组合。
高效的优化算法:
Optuna 使用基于 TPE(Tree-structured Parzen Estimator) 算法的贝叶斯优化方法。TPE 是一种先进的序列模型优化方法,比传统的网格搜索和随机搜索更加高效,能够在较少的试验次数下找到优秀的超参数配置。
TPE(Tree-structured Parzen Estimator):一种贝叶斯优化技术,它根据已评估的超参数配置的性能来逐步优化后续的超参数选择。
灵活的搜索空间定义:
Optuna 支持通过用户自定义的搜索空间来定义超参数的范围,支持离散和连续类型的参数。用户可以灵活选择各种超参数的搜索策略(如uniform、loguniform等),同时支持多种搜索空间约束。
试验和算法的分布式支持:
Optuna 支持分布式训练,通过集成分布式计算框架(如Dask或MPI),可以在多台机器或多个进程中并行化超参数优化过程,加速搜索过程。
早停机制(Pruning):
在训练过程中,Optuna 提供了 早停机制,可以提前终止那些表现不佳的试验,从而节省计算资源。Optuna可以自动监控训练过程中的指标,并根据设定的阈值决定是否停止试验,进一步提高效率。
易于集成:
Optuna 与常见的机器学习库(如 Scikit-learn, XGBoost, LightGBM, TensorFlow, PyTorch)有很好的兼容性,可以方便地与现有的工作流集成。Optuna 提供了简单易用的接口,帮助用户快速上手。
可视化:
Optuna 提供了强大的可视化工具,可以帮助用户查看超参数搜索的过程、结果以及模型的性能趋势。例如,可以绘制超参数与目标值之间的关系图,或者查看优化过程中不同超参数组合的表现。
以上就是该次竞赛的所有内容,分享给大家更多是为了交流,本人以后的内容还是以实战的项目为主,项目中用到的知识点顺便进行一些分享,本次的分享到这里就结束了。
数据文件也已经上传,点关注不迷路,以后会更新更多机器学习实战的内容