Tensorflow 2.4 完成 Titanic 救援预测

1,630 阅读4分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

前言

本文主要是使用 cpu 版本的 tensorflow 2.4 完成对 Titanic 乘客的救援预测任务。我们用到的数据是 Titanic 乘客数据,先搭建深度学习模型,模型会根据乘客的年龄、性别、舱室等特征来预测乘客被救援生还的可能性。

本文大纲

  1. 获取 Titanic 数据
  2. 处理特征数据
  3. 搭建、编译、训练模型
  4. 评估模型
  5. 使用模型预测

实现过程

1. 获取 Titanic 数据

(1)本文的用到的 Titanic 数据需要使用 tensorflow 的内置函数从网络上进行下载,下载好的数据一般会放在主用户目录下面的 .keras/datasets 文件见之内,我们可以发现多了一个 train.csv 和一个 eval.csv 。

(2)需要注意的是这里的 train_file_path 和 test_file_path 变量中保存的就是下载好的训练数据和测试数据分别在本地磁盘的绝对路径。

(3)这里为了方便用户阅读,我们使用了 set_printoptions 将数据集中的浮点数都只保留 3 位小数,并且对极大/极小数不使用科学计数法。

import functools
import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
train_filePath = tf.keras.utils.get_file("train.csv", "https://storage.googleapis.com/tf-datasets/titanic/train.csv")
test_filePath = tf.keras.utils.get_file("eval.csv", "https://storage.googleapis.com/tf-datasets/titanic/eval.csv")
np.set_printoptions(precision=3, suppress=True)

(4)通过观察数据我们数据中第一列 survived 是标签表示该乘客是否能被获救,后面列都是特征,包括 sex、age、n_siblings_spouses、parch、are、class、deck、embark_town、alone 。

! head  -n 3 {train_filePath}

输出结果:

survived,sex,age,n_siblings_spouses,parch,fare,class,deck,embark_town,alone
0,male,22.0,1,0,7.25,Third,unknown,Southampton,n
1,female,38.0,1,0,71.2833,First,C,Cherbourg,n

2. 处理特征数据

(1)因为我们目前只是得到了训练数据文件和测试数据文件的绝对路径,所以我们要通过 tensorflow 内置函数 make_csv_dataset ,从指定的 csv 文件中加载数据,该函数会将指定文件中的所有列数据都加载。

(2)我们将 batch_size 设置为 32 ,也就是说 train_data 或者 test_data 中的一个对象包含的就是一个 batch_size 大小的特征值和标签,将数据的标签设置为 survived 列,也就是其他列是特征,并且对于空值都设置为“?”字符串。

def get_dataset(filePath):
    dataset = tf.data.experimental.make_csv_dataset(  filePath,
                                                      batch_size=32, 
                                                      label_name='survived',
                                                      na_value="?",
                                                      num_epochs=1,
                                                      ignore_errors=True,
                                                      shuffle=False)
    return dataset

train_data = get_dataset(train_filePath).shuffle(500)
test_data = get_dataset(test_filePath)

(3)因为有些特征列的值都是离散的,比如 sex、class、deck、embark_town、alone 等,我们使用变量 d 保存了对应列中的离散值,然后使用函数 categorical_column_with_vocabulary_list 和 indicator_column 将它们转换成可用的形式。

d = {
    'sex': ['male', 'female'],
    'class' : ['First', 'Second', 'Third'],
    'deck' : ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
    'embark_town' : ['Cherbourg', 'Southhampton', 'Queenstown'],
    'alone' : ['y', 'n']
}
cls_cols = []
for k, v in d.items():
    col = tf.feature_column.categorical_column_with_vocabulary_list(key=k, vocabulary_list=v)
    cls_cols.append(tf.feature_column.indicator_column(col))
    

(4)因为有些特征列的值都是连续值,比如 age、 n_siblings_spouses、parch、fare ,所以我们要先将它们进行标准化,并且从一维转换成二维向量。这里我们用 means 保存每一列的平均值,在进行 process 标准化计算的时候可以直接拿来用。需要使用函数 numeric_column 将它们转换成可用的形式。

def process(mean, data):
    data = tf.cast(data, tf.float32) * 1/(2*mean)
    return tf.reshape(data, [-1, 1])
means = {'age' : 29.631308,
         'n_siblings_spouses' : 0.545455,
         'parch' : 0.379585,
         'fare' : 34.385399}
num_cols = []
for f in means.keys():
    num_col = tf.feature_column.numeric_column(f, normalizer_fn=functools.partial(process, means[f]))
    num_cols.append(num_col)

3. 搭建、编译、训练模型

(1)第一层是将所有的特征拼接起来,使用 DenseFeatures 创建一个进行预处理的输入层。

(2)第二层是一个输出 256 维向量的全连接层,并使用了激活函数 relu 进行非线性变化。

(3)第三层是一个输出 128 维向量的全连接层,并使用了激活函数 relu 进行非线性变化。

(4)第四层是一个输出 1 维向量的全连接输出层,并使用了激活函数 sigmoid ,这其实就是输出该乘客被救的概率。

(5)选择损失值为 binary_crossentropy 。

(6)选择优化器为 adam 。

(7)选择评估指标为 accuracy 。

(8)使用训练数据训练 20 个 epoch 。

model = tf.keras.Sequential([ tf.keras.layers.DenseFeatures(cls_cols + num_cols),
                              tf.keras.layers.Dense(256, activation='relu'),
                              tf.keras.layers.Dense(128, activation='relu'),
                              tf.keras.layers.Dense(1, activation='sigmoid')])

model.compile( loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(train_data, epochs=20, verbose=2)

训练过程如下:

Epoch 1/20
20/20 - 1s - loss: 0.5738 - accuracy: 0.7560
Epoch 2/20
20/20 - 0s - loss: 0.4440 - accuracy: 0.8086
...
Epoch 20/20
20/20 - 0s - loss: 0.3313 - accuracy: 0.8612

4. 评估模型

使用测试数据对训练好的模型进行指标评估。

test_loss, test_accuracy = model.evaluate(test_data)
print('\n测试数据的 Loss 为 {}, Accuracy 为 {}'.format(test_loss, test_accuracy))

输出结果为:

测试数据的 Loss 为 0.4356554448604584, Accuracy 为 0.8295454382896423

5. 使用模型预测

取出前 5 条测试数据进行救援概率的预测。

predictions = model.predict(test_data)
for prediction, survived in zip(predictions[:5], list(test_data)[0][1][:5]):
    print("预测的获救概率为: {:.2%}".format(prediction[0]),
        ",实际情况该乘客: ",
        ("被救" if bool(survived) else "死亡"))

结果输出为:

预测的获救概率为: 8.64% ,实际情况该乘客:  死亡
预测的获救概率为: 45.76% ,实际情况该乘客:  死亡
预测的获救概率为: 82.74% ,实际情况该乘客:  被救
预测的获救概率为: 64.96% ,实际情况该乘客:  被救
预测的获救概率为: 3.34% ,实际情况该乘客:  被救