跳至主要內容

决策树应用实战:泰坦尼克乘客生存预测

yczha大约 9 分钟Data AnalysisMachine LearningMachine LearningData Analysis

open in new window open in new window

陈旸老师极客时间《数据分析实战45讲》笔记

sklearn 中的决策树模型

首先,我们需要掌握 sklearn 中自带的决策树分类器 DecisionTreeClassifier,方法如下:

clf = DecisionTreeClassifier(criterion='entropy')

到目前为止,sklearn 中只实现了 ID3 与 CART 决策树,所以我们暂时只能使用这两种决策树,在构造 DecisionTreeClassifier 类时,其中有一个参数是 criterion,意为标准。它决定了构造的分类树是采用 ID3 分类树,还是 CART 分类树,对应的取值分别是 entropy 或者 gini

entropy: 基于信息熵,也就是 ID3 算法,实际结果与 C4.5 相差不大;

gini: 默认参数,基于基尼系数。CART 算法是基于基尼系数做属性划分的,所以 criterion=gini 时,实际上执行的是 CART 算法。

算法的参数代表的含义如下:

参数含义
criterion特征选择的标准,有信息增益和基尼系数两种,使用信息增益的是ID3和C4.5算法(使用信息增益比),使用基尼系数的CART算法,默认是gini系数。
splitter特征切分点选择标准,决策树是递归地选择最优切分点,spliter是用来指明在哪个集合上来递归,有“best”和“random”两种参数可以选择,best表示在所有特征上递归,适用于数据集较小的时候,random表示随机选择一部分特征进行递归,适用于数据集较大的时候。
max_depth决策树最大深度,决策树模型先对所有数据集进行切分,再在子数据集上继续循环这个切分过程,max_depth可以理解成用来限制这个循环次数。
max_features特征切分时考虑的最大特征数量,默认是对所有特征进行切分,也可以传入int类型的值,表示具体的特征个数;也可以是浮点数,则表示特征个数的百分比;还可以是sqrt,表示总特征数的平方根;也可以是log2,表示总特征数的log个特征。
random_page随机种子的设置,与LR中参数一致。
min_samples_split子数据集再切分需要的最小样本量,默认是2,如果子数据样本量小于2时,则不再进行下一步切分。如果数据量较小,使用默认值就可,如果数据量较大,为降低计算量,应该把这个值增大,即限制子数据集的切分次数。
min_samples_leaf叶节点(子数据集)最小样本数,如果子数据集中的样本数小于这个值,那么该叶节点和其兄弟节点都会被剪枝(去掉),该值默认为1。
min_weight_fraction_leaf在叶节点处的所有输入样本权重总和的最小加权分数,如果不输入则表示所有的叶节点的权重是一致的。
max_leaf_nodes最大叶节点个数,即数据集切分成子数据集的最大个数
min_impurity_decrease切分点不纯度最小减少程度,如果某个结点的不纯度减少小于这个值,那么该切分点就会被移除。
min_impurity_split切分点最小不纯度,用来限制数据集的继续切分(决策树的生成),如果某个节点的不纯度(可以理解为分类错误率)小于这个阈值,那么该点的数据将不再进行切分。
class_weight权重设置,主要是用于处理不平衡样本,与LR模型中的参数一致,可以自定义类别权重,也可以直接使用balanced参数值进行不平衡样本处理。
presort是否进行预排序,默认是False,所谓预排序就是提前对特征进行排序,我们知道,决策树分割数据集的依据是,优先按照信息增益/基尼系数大的特征来进行分割的,涉及的大小就需要比较,如果不进行预排序,则会在每次分割的时候需要重新把所有特征进行计算比较一次,如果进行了预排序以后,则每次分割的时候,只需要拿排名靠前的特征就可以了。

这里我们只使用默认参数进行训练。

Titanic乘客生存预测

问题描述

泰坦尼克海难是著名的十大灾难之一,究竟多少人遇难,各方统计的结果不一。现在我们可以得到部分的数据,具体数据你可以从 GitHub 上下载:https://github.com/cystanford/Titanic_Data

其中数据集格式为 csv,一共有两个文件:

train.csv 是训练数据集,包含特征信息和存活与否的标签;

test.csv: 测试数据集,只包含特征信息。

现在我们需要用决策树分类对训练集进行训练,针对测试集中的乘客进行生存预测,并告知分类器的准确率。

在训练集中,包括了以下字段,它们具体为:

字段描述
PassengerId乘客编号
Survived是否幸存
Pclass船票等级
Name乘客姓名
Sex乘客性别
SibSp亲戚数量(兄妹、配偶)
Parch亲戚数量(父母、子女)
Ticket船票号码
Fare船票价格
Cabin船舱
Embarked登陆港口

生存预测的关键流程

我们要对训练集中乘客的生存进行预测,这个过程可以划分为两个重要的阶段:

准备阶段: 我们首先需要对训练集、测试集的数据进行探索,分析数据质量,并对数据进行清洗,然后通过特征选择对数据进行降维,方便后续分类运算;

分类阶段: 首先通过训练集的特征矩阵、分类结果得到决策树分类器,然后将分类器应用于测试集。然后我们对决策树分类器的准确性进行分析,并对决策树模型进行可视化。

下面,我分别对这些模块进行介绍。

模块 1:数据探索

数据探索这部分虽然对分类器没有实质作用,但是不可忽略。我们只有足够了解这些数据的特性,才能帮助我们做数据清洗、特征选择。

那么如何进行数据探索呢?这里有一些函数你需要了解:

使用 info() 了解数据表的基本情况:行数、列数、每列的数据类型、数据完整度;

使用 describe() 了解数据表的统计情况:总数、平均值、标准差、最小值、最大值等;

使用 describe(include=[‘O’]) 查看字符串类型(非数字)的整体情况;

使用 head 查看前几行数据(默认是前 5 行);

使用 tail 查看后几行数据(默认是最后 5 行)。

我们可以使用 Pandas 便捷地处理这些问题:

import pandas as pd
import numpy as np
import os

base_dir=r'./titanic/'
train_dir=os.path.join(base_dir,'train.csv')
test_dir=os.path.join(base_dir,'test.csv')

train_data=pd.read_csv(train_dir)
test_data=pd.read_csv(test_dir)

print('train_data shape:',train_data.shape)
print('test_data shape:',test_data.shape)

# 数据信息
print(train_data.info())
print('-'*30)
print(test_data.info())
print('-'*30)
print(train_data.describe())
print('-'*30)
print(train_data.describe(include=['O']))
print('-'*30)
print(train_data.head())
print('-'*30)
print(train_data.tail())

模块2:数据清洗

通过上面程序得到的结果,发现Age、Fare、Cabin三个字段的数据有缺失。其中Age为年龄字段,为数值型,可以通过平均值进行补齐;Fare为船票价格,数值型,也可以通过平均价格补齐;而Cabin为船舱,缺失率很大(77%和78%),无法补齐,直接丢弃;Embarked为登陆港口有少量缺失值,使用众数补齐。

# 对年龄使用均值填充
train_data['Age'].fillna(train_data['Age'].mean(),inplace=True)
test_data['Age'].fillna(test_data['Age'].mean(),inplace=True)

# 对Fare使用均值填充
train_data['Fare'].fillna(train_data['Fare'].mean(),inplace=True)
test_data['Fare'].fillna(test_data['Fare'].mean(),inplace=True)

# 对Embarked使用众数填充
train_data['Embarked'].fillna('S',inplace=True)
test_data['Embarked'].fillna('S',inplace=True)

模块3:特征选择

特征选择是分类器的关键,特征选择不同,得到的分类器也不同。

通过数据探索发现,PassengerId为乘客编号,对分类没有作用,可以放弃;另外,Name、Cabin、Ticket这些字段也对分类没有逻辑上的贡献,都放弃,这样就挑出了特征字段:

features=['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked']
train_features=train_data[features]
test_features=test_data[features]
train_labels=train_data['Survived']

由于特征中的Embarked和Sex这些字段为字符串,因而需要转换为数值类型。使用sklearn中的DictVectorizer类可以将字符串类型的特征转换为数值类型:

from sklearn.feature_extraction import DictVectorizer
dvec=DictVectorizer(sparse=False)
train_features=dvec.fit_transform(train_features.to_dict(orient='record'))

print(dvec.feature_names_)

模块4:决策树模型

创建决策树模型来进行分类:

from sklearn.tree import DecisionTreeClassifier

clf=DecisionTreeClassifier(criterion='entropy')
clf.fit(train_features,train_labels)

模块5:模型预测&评估

test_features=dvec.fit_transform(test_features.to_dict(orient='record'))
pred_labels=clf.predict(test_features)

acc_decision_tree=round(clf.score(train_features,train_labels),6)
print('score 准确率为 %.4lf' %acc_decision_tree)

这里我们使用训练集做训练,再用训练集自身做准确率评估,故而准确率会很高,但无法对分类器在实际环境下的表现作出评估,因而改进为使用K折交叉验证来进行评估

from sklearn.model_selection import cross_val_score

score=np.mean(cross_val_score(clf,train_features,train_labels,cv=10))
print('{:.4f}'.format(score))

此时发现正确率大约为78%左右。

为了直观查看模型性能,绘制出ROC曲线:

train_labels=train_labels.values
train_labels=train_labels.reshape((train_labels.shape[0],1))
train_labels.shape

from sklearn.preprocessing import label_binarize

y_true=label_binarize(train_labels,classes=[3,2,1])
y_true=y_true[:,1:3]
print(y_true.shape)
print(y_true[:10,:])

from sklearn.metrics import roc_curve, auc

y_score=clf.predict_proba(train_features)

fpr,tpr,threshold = roc_curve(y_true.ravel(), y_score.ravel()) 
roc_auc = auc(fpr,tpr)
 
plt.figure()
plt.plot(fpr, tpr, color='darkorange',label='ROC curve (area = %0.2f)' % roc_auc) 
plt.plot([0, 1], [0, 1], color='navy', linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic example')
plt.legend(loc="lower right")
plt.show()

模块6:模型可视化

sklearn的决策树模型可以使用Graphviz工具进行可视化:

流程图