第15关 预测泰坦尼克号乘客生还情况_人工智能课程 - 小象学院

课程目录 小象学院 - 人工智能

关注公众号【Python家庭】领取1024G整套教材交流群学习商务合作。整理分享了数套四位数培训机构的教材,现免费分享交流学习,并提供解答、交流群

你要的白嫖教程,这里可能都有喔~

 

本关内容概述

欢迎来到本关的学习,本关先介绍sklearn中决策树的使用以及几个重要参数的对模型的影响;然后利用决策树模型对泰坦尼克号乘客的生还情况进行预测。准备好了吗?Let's go!

我们先简单看一下sklearn的官方文档对决策树模型的介绍,链接为 https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html 。

可以看到,DecisionTreeClassifier这个类位于sklearn.tree这个包下,并且有许多可以调整的超参数。接下来我们先通过一个简单的例子介绍一下sklearn中决策树模型的使用,并对几个典型参数的调整进行介绍。

sklearn中的决策树

我们采用sklearn自带的函数来生成一些模拟的数据,为了直观起见,我们将数据用散点图展示出来:

AI_15_0_1_使用make_moons函数生成一些模拟的数据

# 使用make_moons函数生成一些模拟的数据
from sklearn.datasets import make_moons
X, y = make_moons(noise=0.25, random_state=666)

%matplotlib inline
import matplotlib.pyplot as plt

plt.scatter(X[y==0, 0], X[y==0, 1])
plt.scatter(X[y==1, 0], X[y==1, 1])
plt.show();
y

array([1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0,
       1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1,
       1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0,
       0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
       0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0])

接下来我们将用决策树算法对这个数据集进行分类,并通过对不同参数的调整来观察最终分类效果。

AI_15_0_2

from sys import path
path.append(r"../data/course_util")
from ai_course_15_1 import *

# 引入决策树分类器
from sklearn.tree import DecisionTreeClassifier

# 使用默认参数实例化一个决策树分类器
dt_clf = DecisionTreeClassifier()

# 训练数据
dt_clf.fit(X, y)
DecisionTreeClassifier(ccp_alpha=0.0, class_weight=None, criterion='gini',
                       max_depth=None, max_features=None, max_leaf_nodes=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=2,
                       min_weight_fraction_leaf=0.0, presort='deprecated',
                       random_state=None, splitter='best')

上面的程序中这里,我们实例化了一个决策树分类器,并且没有传入任何参数,这表示我们使用默认的参数对数据进行训练。

为了将训练得到的分类边界更加直观地画出来,我们提前准备好了一个plot_decision_boundary的函数,你直接调用就可以了:

AI_15_0_3

from sys import path
path.append(r"../data/course_util")
from ai_course_15_2 import *
%matplotlib inline

# 传入训练好的模型dt_clf,axis参数控制横纵坐标的范围
plot_decision_boundary(dt_clf, axis=[-1.5, 2.5, -1.0, 2.5])

plt.scatter(X[y==0, 0], X[y==0, 1])
plt.scatter(X[y==1, 0], X[y==1, 1])
plt.show();

我们来分析一下上述程序输出的分类边界:

  • 首先,通过观察发现,我们得到了一个不规则的决策边界,但每条分界线都只能是“横平竖直”的,这也算是决策树模型的一个缺点;
  • 其次,可以看出目前的决策边界对于数据的拟合并不是非常的理想。

max_depth参数

先来调整一下 max_depth 参数,该参数控制的是决策树的最大深度。

AI_15_0_4

from sys import path
path.append(r"../data/course_util")
from ai_course_15_3 import *
%matplotlib inline

# 使用max_depth参数实例化一个决策树分类器
dt_clf2 = DecisionTreeClassifier(max_depth=2)
dt_clf2.fit(X, y)

plot_decision_boundary(dt_clf2, axis=[-1.5, 2.5, -1.0, 2.5])
plt.scatter(X[y==0, 0], X[y==0, 1])
plt.scatter(X[y==1, 0], X[y==1, 1])
plt.show();

我们来分析一下程序的输出结果:

  • 与上面使用默认参数得到的决策边界进行比较可以发现,前面得到的决策边界更加复杂,分类准确率更高,但模型对数据的拟合是过度的,换句话说,模型应用到新数据上的效果不一定好;
  • 使用max_depth参数得到的这个决策边界更加简单,但分类错误的样本数量明显增多。

min_samples_split参数

再来看另外一个参数:min_samples_split,该参数的含义是决策树的某个节点至少要包含min_samples_split个样本才可以继续进行划分。

AI_15_0_5

from sys import path
path.append(r"../data/course_util")
from ai_course_15_4 import *
%matplotlib inline

# 使用min_samples_split参数实例化一个决策树分类器
dt_clf3 = DecisionTreeClassifier(min_samples_split=10)
dt_clf3.fit(X, y)

plot_decision_boundary(dt_clf3, axis=[-1.5, 2.5, -1.0, 2.5])
plt.scatter(X[y==0, 0], X[y==0, 1])
plt.scatter(X[y==1, 0], X[y==1, 1])
plt.show();

从输出结果来看,这个决策边界介于之前得到的两个分类边界之间,既没有过度地拟合数据,也没有过于简单地对数据进行划分,是一个比较理想的决策边界。

sklearn还给我们提供了许多可以调节的参数,比如:

  • min_samples_leaf
  • max_leaf_nodes
  • min_weight_fraction_leaf
  • min_features

在这里我们就不一一进行演示了。在实际应用时,我们可以使用网格搜索等技术选出一组最优的超参数,从而得到一棵最佳的决策树。

案例:预测乘客的生还情况

接下来,我们将利用决策树算法来解决一个实际问题:预测泰坦尼克号乘客的生还情况。

在1912年,泰坦尼克号在第一次航行中,就因不幸撞上了冰山而沉没,造成了一场历史性的悲剧。在随后的事故调查中,船上乘客的信息逐渐被披露出来,我们的任务是通过分析这些数据,试图找出这次事故中乘客的生还逻辑。

获取数据与字段含义

我们先来看一下乘客的数据:数据可以在 https://chinahadoop-xc.oss-cn-beijing.aliyuncs.com/titanic.txt 这个网址上找到,利用 pandas 包中的 read_csv() 函数可以直接读取到这个文件中的数据,程序如下所示:

AI_15_0_6_导入pandas包

# 导入pandas包
import pandas as pd

# 从互联网上直接读取乘客数据
titanic = pd.read_csv('http://biostat.mc.vanderbilt.edu/wiki/pub/Main/DataSets/titanic.txt')

# 观察乘客数据的前5行
titanic.head(5)
  row.names pclass survived name age embarked home.dest room ticket boat sex
0 1 1st 1 Allen, Miss Elisabeth Walton 29.0000 Southampton St Louis, MO B-5 24160 L221 2 female
1 2 1st 0 Allison, Miss Helen Loraine 2.0000 Southampton Montreal, PQ / Chesterville, ON C26 NaN NaN female
2 3 1st 0 Allison, Mr Hudson Joshua Creighton 30.0000 Southampton Montreal, PQ / Chesterville, ON C26 NaN (135) male
3 4 1st 0 Allison, Mrs Hudson J.C. (Bessie Waldo Daniels) 25.0000 Southampton Montreal, PQ / Chesterville, ON C26 NaN NaN female
4 5 1st 1 Allison, Master Hudson Trevor 0.9167 Southampton Montreal, PQ / Chesterville, ON C22 NaN 11 male

不难发现,我们得到的乘客数据是一个表格,每一行表示一个乘客的信息,每一列表示一个字段,这种表格在pandas中叫作DataFrame

与numpy中的 ndarray 相比,pandas中的 DataFrame 在数据之外,每一行增加了行号,每一列增加了列名。这样的设计使用起来是非常方便的,比如,我们可以通过 titanic['age'] 的方式直接取出 age 这一列的数据。

此外,pandas是基于numpy建立的包,因此,pandas中的 DataFrame 可以轻松地转换为numpy中的 ndarray,比如,通过 titanic.values 就得到了一个numpy中的 ndarray。通过前面的学习,我们了解到sklearn这个机器学习库处理的都是ndarray类型的数据,实际上,如果我们输入的是DataFrame类型的数据,sklearn库也会自动将其转换为ndarray类型,因此我们完全不用担心数据类型的问题。

pandas提供了很多实用的内置方法,非常适合完成数据分析的任务。接下来让我们通过泰坦尼克号这个案例,一起感受利用pandas进行数据分析的魅力吧!

我们先介绍一下读取的数据中每个字段的含义:

  • row.names:乘客的编号,可忽略;

  • pclass:船舱的等级,1st表示头等舱,2nd表示二等舱,3rd表示三等舱;

  • survived:是否生还,1表示是,0表示否;

  • name:乘客的姓名;

  • age:乘客的年龄,婴儿的年龄用小数表示;

  • embarked:登船的港口;

  • home.dest:出发地及目的地;

  • room:房间号;

  • ticket:船票号;

  • boat:救生艇编号;

  • sex:乘客的性别

在上面这些字段中,survived(是否生还)是我们想要预测的结果,我们将其作为标签;其余的字段都可以看作是特征

得到了这些数据之后,是不是可以用这些数据直接训练我们的模型了呢?

先别急。在我们把数据“喂”给模型之前,要先确保这些数据是模型所需要的“好”数据。为了达到这个目的,我们至少还需要对数据进行三步操作:特征选择数据清洗以及数据预处理

特征选择

特征选择,顾名思义,就是要去掉不重要的特征,将重要的特征保留下来。

对于这个案例,我们可以根据对这场事故的了解,简单地保留pclassagesex这三个特征,而其他特征我们认为与最终的预测结果关联不大,直接舍弃掉。

AI_15_0_7

from sys import path
path.append(r"../data/course_util")
from ai_course_15_5 import *

# 选取pclass,age,sex3个特征作为数据集X;将survived字段作为标签y
X = titanic[['pclass', 'age', 'sex']]
y = titanic['survived']

# 查看数据集X的维度信息
X.shape

# 查看数据集X最后5行的数据
X.tail(5)

# 查看数据集X的整体统计信息
X.info()

我们来分别分析一下程序输出的三个结果:

  • X的维度为(1313,3),表示数据集中有1313条乘客的信息,每条信息包含3个特征;
  • 观察X的末尾5行数据,我们发现age特征中出现了很多NaN(Not a Number),即age特征中有很多的缺失值;
  • 观察X的信息统计,我们重点关注一下age特征,age中只有633个非空的值,这也说明了age特征中存在很多缺失值,需要在下一步中进行处理。

数据清洗

数据清洗,通常包括对异常值的删除、对缺失值的填补以及对错误值的纠正。

在这个案例中,我们需要对age特征中的缺失值进行填补,这里我们采用“平均值”进行填补,分为两步:

  • 先使用 mean() 方法计算出age特征的平均值

  • 然后使用 fillna() 方法将age中的NaN值替换成平均值

程序如下所示:

AI_15_0_8

from sys import path
path.append(r"../data/course_util")
from ai_course_15_6 import *

# 计算age特征的平均值
mean_age = X['age'].mean()
print(mean_age)

# 将NaN值替换成平均值
X['age'] = X['age'].fillna(mean_age)

# 查看X的最后5行数据
X.tail(5)
31.19418104265403
  pclass age sex
1308 3rd 31.194181 male
1309 3rd 31.194181 male
1310 3rd 31.194181 male
1311 3rd 31.194181 female
1312 3rd 31.194181 male

从程序的输出结果中可以看出,age特征中的NaN值已经替换成平均值。

数据预处理

经过前面的处理,现在的数据集看上去“美观”多了!在我们开始训练模型之前,请你再思考一下,还有哪些问题需要我们处理?

至少还有两个问题需要我们处理:

  • pclass和sex特征是字符串类型,必须转换成数值类型,后续才能代入模型进行计算,我们可以采用 map() 方法实现文字和数值之间的转换;

  • pclass、age和sex特征的取值范围差别较大,需要将这3个特征的取值都归一化到0到1之间,这种处理在之前案例中已经出现过很多次了,相信你对这种处理不会很陌生。

AI_15_0_9

from sys import path
path.append(r"../data/course_util")
from ai_course_15_7 import *

# 将'lst'转换为1,'2nd'转换为2,'3rd'转换为3
# 将'female'转换为0,'male'转换为1 
X['pclass'] = X['pclass'].map({'1st':1, '2nd':2, '3rd':3})
X['sex'] = X['sex'].map({'female':0, 'male':1})

# 查看X的最后5行数据
print(X.tail(5))

# 将所有特征都归一化到0到1之间
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)

print(X_scaled) 
X_scaled
pclass        age  sex
1308     NaN  31.194181  NaN
1309     NaN  31.194181  NaN
1310     NaN  31.194181  NaN
1311     NaN  31.194181  NaN
1312     NaN  31.194181  NaN
[[       nan 0.40705854        nan]
 [       nan 0.02588189        nan]
 [       nan 0.4211762         nan]
 ...
 [       nan 0.43803523        nan]
 [       nan 0.43803523        nan]
 [       nan 0.43803523        nan]]
array([[       nan, 0.40705854,        nan],
       [       nan, 0.02588189,        nan],
       [       nan, 0.4211762 ,        nan],
       ...,
       [       nan, 0.43803523,        nan],
       [       nan, 0.43803523,        nan],
       [       nan, 0.43803523,        nan]])

经过上述三步的处理,我们终于得到了我们想要的数据:X_scaledy,从这里我们可以看出,在整个机器学习中,准备数据这一步往往需要消耗大量时间和精力,其重要性不亚于对模型的训练和调整。

训练模型

接下来我们使用sklearn库中的决策树分类器来进行训练。

AI_15_0_10

from sys import path
path.append(r"../data/course_util")
from ai_course_15_8 import *

# 将数据划分为训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=100)

# 从sklearn中导入决策树分类器
from sklearn.tree import DecisionTreeClassifier

# 使用默认参数初始化决策树分类器
dt_clf = DecisionTreeClassifier()

# 使用训练集来训练决策树模型
dt_clf.fit(X_train, y_train)

# 计算该模型对测试集的预测准确率
dt_clf.score(X_test, y_test)
0.779467680608365
从程序的输出结果可以看出,我们使用默认参数训练的决策树模型在测试集上的预测准确率约为78%,看上去还不错。现在我们希望这个预测的准确率能提高到80%以上,你能想到什么好办法吗?

调参

我们可以尝试一下使用不同的超参数来构建决策树模型,也就是前面我们学到的网格搜索的方法!下面我们直接调用sklearn中的 GridSearchCV 类来搜索一下最优的参数配置。

AI_15_0_11

from sys import path
path.append(r"../data/course_util")
from ai_course_15_8 import *

# 使用默认参数初始化决策树分类器
dt_clf = DecisionTreeClassifier()

# 从sklearn中导入网格搜索GridSearchCV类
from sklearn.model_selection import GridSearchCV

# 配置一下需要搜索的参数名称和取值范围,用字典进行保存
params = {'max_depth':[2,3,4,5], 'min_samples_split':[2,4,6,8,10]}

# 将决策树分类器和需要搜索的参数传给GridSearchCV,它将自动为我们寻找出最优参数组合
dt_best = GridSearchCV(dt_clf, params)
dt_best.fit(X_train, y_train)

# 将寻找到的最优参数组合打印出来
print(dt_best.best_params_)
{'max_depth': 2, 'min_samples_split': 2}

在上面的程序中,我们分别对 max_depthmin_samples_split 这两个超参数进行了搜索,其中对max_depth搜索了2,3,4,5这四种取值,对min_samples_split搜索了2,4,6,8,10这五种取值。网格搜索的结果显示,max_depth=2,min_samples_split=2是一组最优的参数组合,让我们赶快编写程序试一下:

AI_15_0_12

from sys import path
path.append(r"../data/course_util")
from ai_course_15_8 import *

# 使用默认参数初始化决策树分类器
dt_clf = DecisionTreeClassifier(max_depth=2, min_samples_split=2)

# 使用训练集来训练决策树模型
dt_clf.fit(X_train, y_train)

# 计算该模型对测试集的预测准确率
dt_clf.score(X_test, y_test)
0.8403041825095057

我们使用网格搜索得到的参数重新构建的决策树模型,在测试集上可以得到 84% 左右的预测准确率,看起来网格搜索的威力真的不容小视!

利用模型对数据进行挖掘

现在我们已经训练好了一个效果还不错的决策树模型,接下来我们要利用这个模型做一些有意思的探索:

预测男女主角生还概率

在泰坦尼克号的电影中,男主角Jack和女主角Rose是虚构的两个人物,根据电影的情节我们假设:

  • Jack住的是三等舱,年龄为23岁,性别当然为男性
  • Rose住的是头等舱,年龄为20岁,性别当然为女性

由此,我们可以构建出Jack和Rose的数据如下:

  1. import numpy as np
  2.  
  3. jack = np.array([[3, 23, 1]])
  4. rose = np.array([[1, 20, 0]])

于是,我们可以利用我们训练的模型来预测一下男女主角生还的概率

AI_15_0_13

from sys import path
path.append(r"../data/course_util")
from ai_course_15_9 import *

# 将所有特征都归一化到0到1之间
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)

# 构建男女主角的数据,并进行归一化处理
import numpy as np
jack = np.array([[3, 23, 1]])
rose = np.array([[1, 20, 0]])

jack_scaled = scaler.transform(jack)
rose_scaled = scaler.transform(rose)

# 将数据划分为训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=100)

# 使用默认参数初始化决策树分类器
dt_clf = DecisionTreeClassifier(max_depth=2, min_samples_split=2)

# 使用训练集来训练决策树模型
dt_clf.fit(X_train, y_train)

# 预测男女主角的生还概率
dt_clf.predict_proba(jack_scaled)[0][1]
print(dt_clf.predict_proba(rose_scaled))
[[0.11055276 0.88944724]]

从程序输出结果可以看出,男主角Jack的生存概率只有15.6%左右,而女主角的生存概率高达88.9%,符合电影的结局。

模型预测错误的样本分析

我们对于模型预测错误的那些人会非常好奇:为什么模型预测一个乘客生存下来的概率很高,但实际中却没有存活呢?是因为我们的模型不够准确,还是背后另有隐情呢?

为了找出这些冷冰冰的数据背后的故事,我们可以这样做:

  • 先利用模型计算出所有人生存下来的概率
  • 将生存概率这一列数据合并到原始数据中
  • 对那些模型预测生存概率很高,但是实际中却没有存活下来的乘客数据进行分析

AI_15_0_14

from sys import path
path.append(r"../data/course_util")
from ai_course_15_8 import *

# 使用默认参数初始化决策树分类器
dt_clf = DecisionTreeClassifier(max_depth=2, min_samples_split=2)

# 使用训练集来训练决策树模型
dt_clf.fit(X_train, y_train)

# 计算出所有人生存下来的概率
prob = dt_clf.predict_proba(X_scaled)[:,1]

# 将生存概率添加到原表数据中
titanic['probability'] = prob

# 查看Allison家族的人的信息
titanic[1:5]
  row.names pclass survived name age embarked home.dest room ticket boat sex probability
1 2 1st 0 Allison, Miss Helen Loraine 2.0000 Southampton Montreal, PQ / Chesterville, ON C26 NaN NaN female 0.889447
2 3 1st 0 Allison, Mr Hudson Joshua Creighton 30.0000 Southampton Montreal, PQ / Chesterville, ON C26 NaN (135) male 0.156342
3 4 1st 0 Allison, Mrs Hudson J.C. (Bessie Waldo Daniels) 25.0000 Southampton Montreal, PQ / Chesterville, ON C26 NaN NaN female 0.889447
4 5 1st 1 Allison, Master Hudson Trevor 0.9167 Southampton Montreal, PQ / Chesterville, ON C22 NaN 11 male 1.000000

从程序输出结果来看,第1行和第3行的乘客都有很高的生存概率,但实际中却都没有存活下来,这是为什么呢?

真实的情况是这样的:这4条记录是Allison一家人的信息,即爸爸(30岁)、妈妈(25岁)、女儿(2岁)以及一个不满一岁的男婴。

由于在救援时遵守女士优先的原则,因此原本妈妈可以带着女儿和小婴儿坐上救生艇离开,但是因为当时突然找不到小婴儿,于是妈妈坚持不肯上救生艇,在船上寻找婴儿;然而命运总是爱捉弄人,原来婴儿早就被一个护士带上了救生艇离开,但慌乱中忘记通知它的家人,从而造成这个悲伤的故事。

本关总结:在本关,我们先介绍了sklearn中决策树模型的使用,然后利用决策树实现了对泰坦尼克号乘客生还情况的预测。

今天我们就先学到这里吧,下一关见,拜拜~

 

联系我们,一起学Python吧

分享Python实战代码,入门资料,进阶资料,基础语法,爬虫,数据分析,web网站,机器学习,深度学习等等。


​关注公众号「Python家庭领取1024G整套教材交流群学习商务合作

猜你喜欢

转载自blog.csdn.net/qq_34409973/article/details/115210531
今日推荐