本文介绍Kaggle入门比赛Titanic泰坦尼克号幸存预测的解题思路,使用的是神经网络和Tensorflow框架。提交代码附在本文的最后部分。
基本思路为:
Kaggle是一个数据建模和数据分析竞赛平台。
泰坦尼克号幸存预测是Kaggle上的一个入门级比赛,利于平台给的数据集,预测泰坦尼克号上不同乘客的幸存情况,并熟悉机器学习基础知识。
比赛地址:Titanic - Machine Learning from Disaster
Titanic - Machine Learning from Disaster
https://www.kaggle.com/competitions/titanic
Kaggle的主页面如上,我们主要关注 Overview、Data 和 Submit Predictions 三个板块
其他板块如 Code 可以查看其他团队分享的代码,Discussion 是解题思路的分享,Leaderboard 是分数排行榜。
比赛很简单:创建一个模型,预测哪些乘客在泰坦尼克号沉船中幸存下来。
下载下来的数据包括三个文件:训练集(train.csv), 测试集(test.csv), 提交示例(gender_submission.csv)
- # 提前将所有包引入
- import pandas as pd
- import numpy as np
- import tensorflow as tf
- from tensorflow.python.keras import activations
- import matplotlib.pyplot as plt
- from tensorflow.keras import regularizers
- import datetime
-
- # 导入数据
- def load_data(path_url, test_path_url):
- raw_train_dataset = pd.read_csv(path_url)
- raw_test_dataset = pd.read_csv(test_path_url)
- return raw_train_dataset, raw_test_dataset
-
- if __name__ == "__main__":
- # 1. 导入数据
- path_url = r"kaggle\titanic\train.csv"
- test_path_url = r"kaggle\titanic\test.csv"
- raw_train_dataset, raw_test_dataset = load_data(path_url, test_path_url)
首先我们确定数据集 features_test
训练集 train.csv 中共包含 12 列,分别是 passengerId,survival,pclass,name,sex,Age,sibsp,parch,ticket,fare,cabin,embarked。
通过分析可知对预测结果有影响的列为 Pclass,Sex,Fare,Age,SibSp,Parch,Embarked,而Survived为真实标签列。PassengerId在测试集中会用到。
根据以上分析确定所需的列,形成features_test,将训练集裁剪为所需列。
接下来对数据的缺失值进行填补
用pandas的describe()和isnull().sum()方法可以对训练集进行数值统计,考虑以中位数来替代缺失值。其中缺失较多的列为Age和Fare列。
数据类型统一为数字编码
考虑Sex和Embarked列是字符串,将其修改为每一个可能性字符单独一列,并去除原列。
归一化输入(标准化)
第一步是零均值化,每个训练数据𝑥减去均值𝜇。第二步是归一化方差。
分别处理训练集和测试集
对于训练集,我们要把Survived列单独提出来作为标签,即真实值。
对于测试集,对比提交示例(gender_submission.csv)文件,我们保留PassengerId列作为索引id。
- # 数据预处理的基础方法
- def preprocess(raw_dataset, features, train=True):
- """用于predict的数据预处理
- Args:
- input: dataset = pandas.DataFrame对象
- """
- # 以中位数来替代
- if "Age" in features:
- raw_dataset["Age"].fillna(raw_dataset["Age"].median(), inplace=True)
- raw_dataset["Fare"].fillna(raw_dataset["Fare"].median(), inplace=True)
- # Embarked也可以为最多值
- raw_dataset["Embarked"].fillna(raw_dataset["Fare"].median(), inplace=True)
- dataset = raw_dataset[features]
- dataset = dataset.copy()
-
- # 由于 embarked=登船港口, Port of Embarkation C = Cherbourg, Q = Queenstown, S = Southampton
-
- Embarked = dataset.pop("Embarked")
-
- # 根据 embarked 列来写入新的 3 个列
- dataset["S"] = (Embarked == "S") * 1.0
- dataset["C"] = (Embarked == "C") * 1.0
- dataset["Q"] = (Embarked == "Q") * 1.0
-
- # 根据 sex 列来写入新的 2 个列
- Sex = dataset.pop("Sex")
- dataset["Male"] = (Sex == "male") * 1.0
- dataset["Female"] = (Sex == "female") * 1.0
-
- dataset_withoutna = dataset
- if train:
- labels = dataset_withoutna["Survived"]
- dataset_withoutna.pop("PassengerId")
- dataset_withoutna.pop("Survived")
- # 标准化,归一化输入
- train_stats = dataset_withoutna.describe()
- train_stats = train_stats.transpose()
- normed_train_data = (dataset_withoutna - train_stats["mean"]) / train_stats[
- "std"
- ]
- return np.array(normed_train_data), np.array(labels)
- else:
- labels = dataset.pop("PassengerId")
- dataset.fillna(0, inplace=True)
- test_stats = dataset.describe()
- test_stats = test_stats.transpose()
- normed_test_data = (dataset - test_stats["mean"]) / test_stats["std"]
- return np.array(normed_test_data), np.array(labels)
-
- if __name__ == "__main__":
-
- # 1. 导入数据
- path_url = r"kaggle\titanic\train.csv"
- test_path_url = r"kaggle\titanic\test.csv"
-
- raw_train_dataset, raw_test_dataset = load_data(path_url, test_path_url)
-
- # 2. 数据预处理
- features_test = [
- "PassengerId",
- "Pclass",
- "Sex",
- "Fare",
- "Age",
- "SibSp",
- "Parch",
- "Embarked",
- ]
- features_train = features_test + ["Survived"]
-
- # 获取预处理后的训练集和标签
- train_dataset, labels = preprocess(raw_train_dataset, features_train)
采用Sequential构建网络模型
通过Kera 提供的网络容器Sequential生成网络模型,模型的深度为4层,分别有64、32、16和1个的神经单元,前三层使用relu激活函数并应用𝐿2正则化。
compile,fit,evaluate和predict
在Sequential搭建好网络结构后,compile,fit,evaluate和predict是常用的keras训练和验证的方法。
首先是compile,即模型装配。用于设置网络使用的优化器对象、损失函数类型,评价指标等设定。这里我们选定Adam优化器,binary_crossentropy损失函数,设置测量指标为准确率。
接下来是fit,即模型训练。模型装配完成后,即可通过 fit()函数送入待训练的数据集和验证用的数据集,这一步称为模型训练。此处可选定epochs数量,并设定从训练集中切分出的验证集的比例。
evaluate,即模型测试,测试在训练所有样本上的性能表现。
predict,即模型预测,可使用训练好的模型去预测新样本的标签信息。predict方法将在下一节预测输出中使用。
evaluate和predict的区别是,evaluate是用已有的真实结果进行检测,而predict是单纯输出预测结果。
- # 训练
- def train(train_dataset, labels, epochs=120, batch_size=512, is_plot=False):
- model = tf.keras.Sequential(
- [
- # 1. input_shape = 输入形状
- # ND 张量的形状:. 最常见的情况是带有 shape 的 2D 输入。
- # (batch_size, ..., input_dim)(batch_size, input_dim)
- # train_dataset.shape[1] = 4
- # 现在模型就会以尺寸为 (*, 4) 的数组作为输入,
- # 2. kernel_regularizer = 应用于kernel权重矩阵的正则化函数。
- # 𝐿2正则化:范数的平方
- tf.keras.layers.Dense(
- 64,
- activation="relu",
- input_shape=(train_dataset.shape[1],),
- kernel_regularizer=regularizers.l2(0.001),
- ),
- tf.keras.layers.Dense(
- 32, activation="relu", kernel_regularizer=regularizers.l2(0.001)
- ),
- tf.keras.layers.Dense(16, activation="relu"),
- tf.keras.layers.Dense(1, name="prediction"),
- ]
- )
-
- # 在 Keras 中提供了 compile()和 fit()函数方便实现逻辑。
- # compile:首先通过compile 函数指定网络使用的优化器对象、损失函数类型,评价指标等设定,这一步称为装配
- # fit: 模型装配完成后,即可通过 fit()函数送入待训练的数据集和验证用的数据集
- model.compile(
- # Adam的学习律默认为0.001
- optimizer=tf.keras.optimizers.Adam(),
- # BinaryCrossentropy:计算真实标签和预测标签之间的交叉熵损失
- loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
- # 设置测量指标为准确率
- metrics=["accuracy"],
- )
-
- # early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)
- # 模型装配完成后,即可通过 fit()函数送入待训练的数据集和验证用的数据集,这一步称为模型训练
- # fit函数的参数:
- # x = Input data
- # y = Target data
- # verbose = 'auto'、0、1 或 2 详细模式。
- # 0 = 静音,1 = 进度条,2 = 每个 epoch 一行。'auto' 在大多数情况下默认为 1
- history = model.fit(
- x=train_dataset,
- y=labels,
- epochs=epochs,
- validation_split=0.01,
- batch_size=batch_size,
- verbose="auto",
- # callbacks=[early_stop]
- )
-
- # 显示训练情况
- if is_plot:
- plot_history(history)
-
- # 可以通过 Model.evaluate(db)循环测试完 db 数据集上所有样本
- loss, accuracy = model.evaluate(train_dataset, labels, verbose=2)
- print("Accuracy:", accuracy)
-
- return model
-
- if __name__ == "__main__":
-
- # 1. 导入数据
- path_url = r"kaggle\titanic\train.csv"
- test_path_url = r"kaggle\titanic\test.csv"
-
- raw_train_dataset, raw_test_dataset = load_data(path_url, test_path_url)
-
- # 2. 数据预处理
- features_test = [
- "PassengerId",
- "Pclass",
- "Sex",
- "Fare",
- "Age",
- "SibSp",
- "Parch",
- "Embarked",
- ]
- features_train = features_test + ["Survived"]
-
- # 获取预处理后的训练集和标签
- train_dataset, labels = preprocess(raw_train_dataset, features_train)
-
- # 3. 训练
- model = train(train_dataset, labels, epochs=256, is_plot=True)
-
- # 真实预测并输出csv
- def predict_out(model, csv_path):
- # model.evaluate 和 model.predict 的区别
- # https://blog.csdn.net/DoReAGON/article/details/88552348
- # 两者差异:
- # 1
- # 输入输出不同
- # model.evaluate输入数据(data)和金标准(label),然后将预测结果与金标准相比较,得到两者误差并输出.
- # model.predict输入数据(data),输出预测结果
- # 2
- # 是否需要真实标签(金标准)
- # model.evaluate需要,因为需要比较预测结果与真实标签的误差
- # model.predict不需要,只是单纯输出预测结果,全程不需要金标准的参与.
- predictions = model.predict(test_dataset)
- # 通过astype()方法可以强制转换数据的类型。
- predictions = (tf.sigmoid(predictions).numpy().flatten() > 0.5).astype(int)
- print(predictions.shape, predictions)
- # 输出结果
- output = pd.DataFrame({"PassengerId": passenger_id, "Survived": predictions})
- # index=False 不保存行索引,index=是否保留行索引
- output.to_csv(csv_path, index=False)
- print(f"您的提交文件保存成功! 位置在{csv_path}")
- return predictions
-
-
- if __name__ == "__main__":
-
- # 1. 导入数据
- path_url = r"kaggle\titanic\train.csv"
- test_path_url = r"kaggle\titanic\test.csv"
-
- raw_train_dataset, raw_test_dataset = load_data(path_url, test_path_url)
-
- # 2. 数据预处理
- features_test = [
- "PassengerId",
- "Pclass",
- "Sex",
- "Fare",
- "Age",
- "SibSp",
- "Parch",
- "Embarked",
- ]
- features_train = features_test + ["Survived"]
-
- # 获取预处理后的训练集和标签
- train_dataset, labels = preprocess(raw_train_dataset, features_train)
-
- # 3. 训练
- model = train(train_dataset, labels, epochs=256, is_plot=True)
-
- # 获取预处理后的测试集和序号
- test_dataset, passenger_id = preprocess(
- raw_test_dataset, features_test, train=False
- )
-
- csv_path = f"./submission_{get_time()}.csv"
- prediction = predict_out(model, csv_path)
显示训练情况 plot_history
使用model.fit的返回值History.history即训练数据记录,读取accuracy和val_accuracy来绘制准确率曲线。
- # 显示训练情况
- def plot_history(history):
- # histoty的返回值
- # A History object. Its History.history attribute is a record of training loss values and metrics values at successive epochs, as well as validation loss values and validation metrics values (if applicable).
- # 一个历史对象。它的 History.history 属性是连续 epoch 的训练损失值和指标值的记录,
- # 以及验证损失值和验证指标值(如果适用)。
- # history.history:loss、accuracy、val loss、val accuracy
- hist = pd.DataFrame(history.history)
- hist["epoch"] = history.epoch
-
- plt.figure()
- plt.xlabel("Num of Epochs")
- plt.ylabel("value")
- # plt.plot(hist["epoch"], hist["loss"], label="Loss")
- # plt.plot(hist["epoch"], hist["val_loss"], label="val_loss")
- plt.plot(hist["epoch"], hist["accuracy"], label="accuracy")
- plt.plot(hist["epoch"], hist["val_accuracy"], label="val_accuracy")
- plt.ylim([0, 1])
- plt.legend()
- plt.show()
获取年月日时分秒 get_time
在输出预测结果csv的时候附上时间,方便区别。
- # 获取年月日时分秒
- def get_time():
- return datetime.datetime.now().strftime("%Y%m%d%H%M")
- # #### Titanic - Machine Learning from Disaster
-
- if __name__ == "__main__":
-
- # 1. 导入数据
- path_url = r"kaggle\titanic\train.csv"
- test_path_url = r"kaggle\titanic\test.csv"
-
- raw_train_dataset, raw_test_dataset = load_data(path_url, test_path_url)
-
- # 2. 数据预处理
- features_test = [
- "PassengerId",
- "Pclass",
- "Sex",
- "Fare",
- "Age",
- "SibSp",
- "Parch",
- "Embarked",
- ]
- features_train = features_test + ["Survived"]
-
- # 获取预处理后的训练集和标签
- train_dataset, labels = preprocess(raw_train_dataset, features_train)
-
- # 3. 训练
- model = train(train_dataset, labels, epochs=256, is_plot=True)
-
- # 获取预处理后的测试集和序号
- test_dataset, passenger_id = preprocess(
- raw_test_dataset, features_test, train=False
- )
-
- csv_path = f"./submission_{get_time()}.csv"
- prediction = predict_out(model, csv_path)
-
- # 验证与原始数据raw_test_dataset长度是否一致
- if prediction.shape[0] == raw_test_dataset.shape[0]:
- print(f"--预测长度={prediction.shape[0]}校验通过 √")
- else:
- print(
- f"--预测长度与raw_test_dataset长度不一致 ×,prediction.shape={prediction.shape},raw_test_dataset.shape={raw_test_dataset.shape}"
- )
-
调整validation_split的比例为0.01后,最后最高分数为0.78947,排名前8%。基本达到学习完成Titanic泰坦尼克号幸存预测比赛的程度。
比赛中比较重要的是需要对数据进行仔细分析,可以利用Excel和pandas等工具分析数据,提取出有用的数据列,并对缺失值进行填补。
1. 机器学习项目实战(一): Kaggle Titanic机器学习项目实战(一): Kaggle Titanic
https://wangxin1248.github.io/machinelearning/2018/10/titanic.html
2. Kaggle入门系列(三)Titanic初试身手
3. Titanic Tutorial
Titanic Tutorial
https://www.kaggle.com/code/alexisbcook/titanic-tutorial
4. 用TensorFlow完成Kaggle Titanic竞赛
用TensorFlow完成Kaggle Titanic竞赛
https://www.jianshu.com/p/f5a7487e6ff2
titanic_predict.py
- import pandas as pd
- import numpy as np
- import tensorflow as tf
- from tensorflow.python.keras import activations
- import matplotlib.pyplot as plt
- from tensorflow.keras import regularizers
- import datetime
-
- # 显示训练情况
- def plot_history(history):
- # histoty的返回值
- # 一个历史对象。它的 History.history 属性是连续 epoch 的训练损失值和指标值的记录,以及验证损失值和验证指标值(如果适用)。
- # history.history:loss、accuracy、val loss、val accuracy
- hist = pd.DataFrame(history.history)
- hist["epoch"] = history.epoch
-
- plt.figure()
- plt.xlabel("Num of Epochs")
- plt.ylabel("value")
- plt.plot(hist["epoch"], hist["accuracy"], label="accuracy")
- plt.plot(hist["epoch"], hist["val_accuracy"], label="val_accuracy")
- plt.ylim([0, 1])
- plt.legend()
- plt.show()
-
-
- # 导入数据
- def load_data(path_url, test_path_url):
- raw_train_dataset = pd.read_csv(path_url)
- raw_test_dataset = pd.read_csv(test_path_url)
- return raw_train_dataset, raw_test_dataset
-
-
- # 数据预处理的基础方法
- def preprocess(raw_dataset, features, train=True):
- """用于predict的数据预处理
- Args:
- input: dataset = pandas.DataFrame对象
- """
- # 以中位数来替代
- if "Age" in features:
- raw_dataset["Age"].fillna(raw_dataset["Age"].median(), inplace=True)
- raw_dataset["Fare"].fillna(raw_dataset["Fare"].median(), inplace=True)
- raw_dataset["Embarked"].fillna(raw_dataset["Fare"].median(), inplace=True)
- dataset = raw_dataset[features]
- dataset = dataset.copy()
-
- # 由于 embarked=登船港口, Port of Embarkation C = Cherbourg, Q = Queenstown, S = Southampton
-
- Embarked = dataset.pop("Embarked")
-
- # 根据 embarked 列来写入新的 3 个列
- dataset["S"] = (Embarked == "S") * 1.0
- dataset["C"] = (Embarked == "C") * 1.0
- dataset["Q"] = (Embarked == "Q") * 1.0
-
- # 根据 sex 列来写入新的 2 个列
- Sex = dataset.pop("Sex")
- dataset["Male"] = (Sex == "male") * 1.0
- dataset["Female"] = (Sex == "female") * 1.0
-
- dataset_withoutna = dataset
- if train:
- labels = dataset_withoutna["Survived"]
- dataset_withoutna.pop("PassengerId")
- dataset_withoutna.pop("Survived")
- # 标准化
- train_stats = dataset_withoutna.describe()
- train_stats = train_stats.transpose()
- normed_train_data = (dataset_withoutna - train_stats["mean"]) / train_stats[
- "std"
- ]
- return np.array(normed_train_data), np.array(labels)
- else:
- labels = dataset.pop("PassengerId")
- dataset.fillna(0, inplace=True)
- test_stats = dataset.describe()
- test_stats = test_stats.transpose()
- normed_test_data = (dataset - test_stats["mean"]) / test_stats["std"]
- return np.array(normed_test_data), np.array(labels)
-
-
- # 训练
- def train(train_dataset, labels, epochs=120, batch_size=512, is_plot=False):
- model = tf.keras.Sequential(
- [
- # 1. input_shape = 输入形状
- # ND 张量的形状:. 最常见的情况是带有 shape 的 2D 输入。
- # 2. kernel_regularizer = 应用于kernel权重矩阵的正则化函数。
- # 𝐿2正则化:范数的平方
- tf.keras.layers.Dense(
- 64,
- activation="relu",
- input_shape=(train_dataset.shape[1],),
- kernel_regularizer=regularizers.l2(0.001),
- ),
- tf.keras.layers.Dense(
- 32, activation="relu", kernel_regularizer=regularizers.l2(0.001)
- ),
- tf.keras.layers.Dense(16, activation="relu"),
- tf.keras.layers.Dense(1, name="prediction"),
- ]
- )
-
- # 在 Keras 中提供了 compile()和 fit()函数方便实现逻辑。
- # compile:首先通过compile 函数指定网络使用的优化器对象、损失函数类型,评价指标等设定,这一步称为装配
- # fit: 模型装配完成后,即可通过 fit()函数送入待训练的数据集和验证用的数据集
- model.compile(
- # Adam的学习律默认为0.001
- optimizer=tf.keras.optimizers.Adam(),
- # BinaryCrossentropy:计算真实标签和预测标签之间的交叉熵损失
- loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
- # 设置测量指标为准确率
- metrics=["accuracy"],
- )
-
-
- # 模型装配完成后,即可通过 fit()函数送入待训练的数据集和验证用的数据集,这一步称为模型训练
- # verbose = 'auto'、0、1 或 2 详细模式。
- # 0 = 静音,1 = 进度条,2 = 每个 epoch 一行。'auto' 在大多数情况下默认为 1
- history = model.fit(
- x=train_dataset,
- y=labels,
- epochs=epochs,
- validation_split=0.3,
- batch_size=batch_size,
- verbose="auto",
- # callbacks=[early_stop]
- )
-
- # 显示训练情况
- if is_plot:
- plot_history(history)
-
- # 可以通过 Model.evaluate(db)循环测试完 db 数据集上所有样本
- loss, accuracy = model.evaluate(train_dataset, labels, verbose=2)
- print("Accuracy:", accuracy)
-
- return model
-
-
- # 真实预测并输出csv
- def predict_out(model, csv_path):
- # model.evaluate 和 model.predict 的区别
- # https://blog.csdn.net/DoReAGON/article/details/88552348
- # 两者差异:
- # 1
- # 输入输出不同
- # model.evaluate输入数据(data)和金标准(label),然后将预测结果与金标准相比较,得到两者误差并输出.
- # model.predict输入数据(data),输出预测结果
- # 2
- # 是否需要真实标签(金标准)
- # model.evaluate需要,因为需要比较预测结果与真实标签的误差
- # model.predict不需要,只是单纯输出预测结果,全程不需要金标准的参与.
- predictions = model.predict(test_dataset)
- # 通过astype()方法可以强制转换数据的类型。
- predictions = (tf.sigmoid(predictions).numpy().flatten() > 0.5).astype(int)
- print(predictions.shape, predictions)
- # 输出结果
- output = pd.DataFrame({"PassengerId": passenger_id, "Survived": predictions})
- # index=False 不保存行索引,index=是否保留行索引
- output.to_csv(csv_path, index=False)
- print(f"您的提交文件保存成功! 位置在{csv_path}")
- return predictions
-
-
- # 获取年月日时分秒
- def get_time():
- return datetime.datetime.now().strftime("%Y%m%d%H%M")
-
-
- # #### Titanic - Machine Learning from Disaster
-
- if __name__ == "__main__":
-
- # 1. 导入数据
- path_url = r"kaggle\titanic\train.csv"
- test_path_url = r"kaggle\titanic\test.csv"
-
- raw_train_dataset, raw_test_dataset = load_data(path_url, test_path_url)
-
- # 2. 数据预处理
- features_test = [
- "PassengerId",
- "Pclass",
- "Sex",
- "Fare",
- "Age",
- "SibSp",
- "Parch",
- "Embarked",
- ]
- features_train = features_test + ["Survived"]
-
- # 获取预处理后的训练集和标签
- train_dataset, labels = preprocess(raw_train_dataset, features_train)
-
- # 3. 训练
- model = train(train_dataset, labels, epochs=256, is_plot=True)
-
- # 获取预处理后的测试集和序号
- test_dataset, passenger_id = preprocess(
- raw_test_dataset, features_test, train=False
- )
-
- # 输出预测结果
- csv_path = f"./submission_{get_time()}.csv"
- prediction = predict_out(model, csv_path)
-
- # 验证与原始数据raw_test_dataset长度是否一致
- if prediction.shape[0] == raw_test_dataset.shape[0]:
- print(f"--预测长度={prediction.shape[0]}校验通过 √")
- else:
- print(
- f"--预测长度与raw_test_dataset长度不一致 ×,prediction.shape={prediction.shape},raw_test_dataset.shape={raw_test_dataset.shape}"
- )
依赖库 requirements.txt
- pandas==1.4.3
- numpy==1.20.3
- matplotlib==3.4.3
- tensorflow-cpu==2.9.1