天天看点

监督学习 | 决策树之Sklearn实现1. Sklearn中决策树的超参数2. 使用 Scikit-learn 实现决策树算法3. 实例:使用决策树探索泰坦尼克号乘客存活情况¶参考资料

文章目录

  • 1. Sklearn中决策树的超参数
    • 1.1 最大深度 max_depth
    • 1.2 每片叶子的最小样本数 min_samples_leaf
    • 1.3 每次分裂的最小样本数 min_samples_split
    • 1.4 最大特征数 max_features
    • 1.5 算法 criterion: string, optional (default=”gini”)
    • 1.6 模型复杂度系数 ccp_alpha: non-negative float, optional (default=0.0)
  • 2. 使用 Scikit-learn 实现决策树算法
  • 3. 实例:使用决策树探索泰坦尼克号乘客存活情况¶
    • 3.1 数据导入
    • 3.2 数据预处理
      • 3.2.1 One-Hot 编码
      • 3.2.2 Pandas 实现 One-Hot 编码
    • 3.3 训练模型
    • 3.4 测试模型
    • 3.5 决策树可视化
    • 3.6 预剪枝
  • 参考资料

相关文章:

机器学习 | 目录

监督学习 | ID3 决策树原理及Python实现

监督学习 | ID3 & C4.5 决策树原理

监督学习 | CART 分类回归树原理

监督学习 | 决策树之Sklearn实现

监督学习 | 决策树之网络搜索

1. Sklearn中决策树的超参数

1.1 最大深度 max_depth

  • 决策树的最大深度指树根和叶子之间的最大长度。当决策树的最大深度为 k k k时,它最多可以拥有 2 k 2^k 2k片叶子,

    最大深度的设置相当于预剪枝处理

  • 较大的深度往往会导致过拟合,这是因为过深的决策树可以记忆数据。而较小的深度会使得模型过于简单,导致欠拟合。
监督学习 | 决策树之Sklearn实现1. Sklearn中决策树的超参数2. 使用 Scikit-learn 实现决策树算法3. 实例:使用决策树探索泰坦尼克号乘客存活情况¶参考资料

1.2 每片叶子的最小样本数 min_samples_leaf

  • 在分裂节点时,很有可能一片叶子上有 99 个样本,而另一片叶子上只有 1 个样本。这将使我们陷入困境,并造成资源和时间的浪费。如果想避免这种问题,我们可以设置每片叶子允许的最小样本数。
  • 这个数字可以被指定为一个整数,也可以是一个浮点数。如果它是整数,它将表示这片叶子上的最小样本数。如果它是个浮点数,它将被视作每片叶子上的最小样本比例。比如,0.1 或 10% 表示如果一片叶子上的样本数量小于该节点中样本数量的 10%,这种分裂将不被允许。
  • 当每片叶子的样本数量较小时,叶子上的样本数量也有可能过于稀少,此时模型将记忆数据,也就是过拟合。当每片叶子的样本数量较大时,决策树能够获得足够的弹性进行构建,这也许会导致欠拟合。
监督学习 | 决策树之Sklearn实现1. Sklearn中决策树的超参数2. 使用 Scikit-learn 实现决策树算法3. 实例:使用决策树探索泰坦尼克号乘客存活情况¶参考资料

1.3 每次分裂的最小样本数 min_samples_split

  • 与每片叶子上的最小样本树相同,只不过是应用在节点的分裂当中。

1.4 最大特征数 max_features

  • 有时,我们会遇到特征数量过于庞大,而无法建立决策树的情况。在这种状况下,对于每一个分裂,我们都需要检查整个数据集中的每一个特征。这种过程极为繁琐。而解决方案之一是限制每个分裂中查找的特征数。如果这个数字足够庞大,我们很有可能在查找的特征中找到良好特征(尽管也许并不是完美特征)。然而,如果这个数字小于特征数,这将极大加快我们的计算速度。

更多关于SKlearn决策树算法的参数设定可以参考这篇文章:Python中决策树分类器DecisionTreeClassifier参数和经验总结

DecisionTreeClassifier官方文档

1.5 算法 criterion: string, optional (default=”gini”)

  • ID3: criterion=“entropy”
  • CART: “gini”

1.6 模型复杂度系数 ccp_alpha: non-negative float, optional (default=0.0)

即后剪枝中惩罚函数的系数 α \alpha α,当 α \alpha α 确定时,子树越大,往往与训练数据的拟合越好,但是模型的复杂度越高;相反,子树越小,模型复杂度就越低,但是往往与训练数据的拟合不好,损失函数正好表示了对两者的平衡。关于后剪枝的惩罚函数,可以参考这篇文章。

2. 使用 Scikit-learn 实现决策树算法

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
model = DecisionTreeClassifier(max_depth = 7, min_samples_leaf = 10)              # 创建决策树分类器
model.fit(x_values, y_values)                                                     # 模型拟合
y_pred = model.predict(X)                                                         # 模型预测
acc = accuracy_score(y,y_pred)                                                    # 模型评分
           

3. 实例:使用决策树探索泰坦尼克号乘客存活情况¶

3.1 数据导入

数据可以在我的Github上下载

# Import libraries necessary for this project
import numpy as np
import pandas as pd
from IPython.display import display # Allows the use of display() for DataFrames

# Pretty display for notebooks
%matplotlib inline

# Set a random seed
import random
random.seed(42)

# Load the dataset
in_file = 'data/titanic_data.csv'
full_data = pd.read_csv(in_file)

# Print the first few entries of the RMS Titanic data
display(full_data.head())
           
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
1 3 Braund, Mr. Owen Harris male 22.0 1 A/5 21171 7.2500 NaN S
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, Miss. Laina female 26.0 STON/O2. 3101282 7.9250 NaN S
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 113803 53.1000 C123 S
4 5 3 Allen, Mr. William Henry male 35.0 373450 8.0500 NaN S

下面是每位乘客具备的各种特征:

  • PassengerID:乘客编号
  • Survived:存活结果(0 = 存活;1 = 未存活)
  • Pclass:社会阶层(1 = 上层;2 = 中层;3 = 底层)
  • Name:乘客姓名
  • Sex:乘客性别
  • Age:乘客年龄(某些条目为

    NaN

  • SibSp:一起上船的兄弟姐妹和配偶人数
  • Parch:一起上船的父母和子女人数
  • Ticket:乘客的票号
  • Fare:乘客支付的票价
  • Cabin:乘客的客舱号(某些条目为

    NaN

  • Embarked:乘客的登船港(C = 瑟堡;Q = 皇后镇;S = 南安普顿)

因为我们对每位乘客或船员的存活情况感兴趣,因此我们可以从此数据集中删除 Survived 特征,并将其存储在单独的变量

outcome

中。我们将使用这些结果作为预测目标。

从数据集中删除特征 Survived 并将其存储到

outcome

中:

# Store the 'Survived' feature in a new variable and remove it from the dataset
outcomes = full_data['Survived']
features_raw = full_data.drop('Survived', axis = 1)

# Show the new dataset with 'Survived' removed
display(features_raw.head())
           
PassengerId Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
1 3 Braund, Mr. Owen Harris male 22.0 1 A/5 21171 7.2500 NaN S
1 2 1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1 PC 17599 71.2833 C85 C
2 3 3 Heikkinen, Miss. Laina female 26.0 STON/O2. 3101282 7.9250 NaN S
3 4 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 113803 53.1000 C123 S
4 5 3 Allen, Mr. William Henry male 35.0 373450 8.0500 NaN S

3.2 数据预处理

我们在之前的朴素贝叶斯之Sklearn实现中讲过词袋(Bag of Words)的概念,BOW的目的是将文本转换为单词频率表,原因是Scikit-learn 只处理数字值。类似的,在决策树中,对于“Name”、“Sex”等离散特征,其取值之间没有大小的意义,因此可以使用独热编码(One-Hot Encoding)将分类值转换为数值型。

3.2.1 One-Hot 编码

独热编码即 One-Hot 编码,又称一位有效编码。其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效。

可以这样理解,对于每一个特征,如果它有 m m m个可能值,那么经过独热编码后,就变成了 m m m个二元特征(如成绩这个特征有好,中,差变成 one-hot 就是100, 010, 001)。并且,这些特征互斥,每次只有一个激活。因此,数据会变成稀疏的。[1]

这样做的好处主要有:

  1. 解决了分类器不好处理属性数据的问题
  2. 在一定程度上也起到了扩充特征的作用
为什么不使用 BOW ?
BOW 适用于每一个对象内包含多个单词,使用 BOW 后,对象内的单词将会被分散开来。
           

更多关于 One-Hot 编码的知识,可以参考这一篇博文:独热编码(One-Hot Encoding)和 LabelEncoder标签编码

3.2.2 Pandas 实现 One-Hot 编码

  • pd.get_dummies(data)

该方法可以讲类别变量转换成新增的虚拟变量/指示变量。

常用参数:

  • data

    (输入的数据): array-like, Series, or DataFrame
  • prefix

    (转换后列名的前缀): string, list of strings, or dict of strings, default None
  • columns

    (指定需要实现类别转换的列名): list-like, default None
  • dummy_na

    (增加一列表示空缺值,默认忽略空缺值): True, False
  • drop_first

    (获得k中的k-1个类别值,去除第一个): bool, default False
features = pd.get_dummies(features_raw)      # 独热编码 将不是数字类型的数据转换为数字类型,如Embarked分为了Embarked_S、Embarked_C...
display(features.head())
           
PassengerId Pclass Age SibSp Parch Fare Name_Abbing, Mr. Anthony Name_Abbott, Mr. Rossmore Edward Name_Abbott, Mrs. Stanton (Rosa Hunt) Name_Abelson, Mr. Samuel ... Cabin_F G73 Cabin_F2 Cabin_F33 Cabin_F38 Cabin_F4 Cabin_G6 Cabin_T Embarked_C Embarked_Q Embarked_S
1 3 22.0 1 7.2500 ... 1
1 2 1 38.0 1 71.2833 ... 1
2 3 3 26.0 7.9250 ... 1
3 4 1 35.0 1 53.1000 ... 1
4 5 3 35.0 8.0500 ... 1

5 rows × 1730 columns

现在用 0 填充任何空白处:

features = features.fillna(0.0)
display(features.head())
           
PassengerId Pclass Age SibSp Parch Fare Name_Abbing, Mr. Anthony Name_Abbott, Mr. Rossmore Edward Name_Abbott, Mrs. Stanton (Rosa Hunt) Name_Abelson, Mr. Samuel ... Cabin_F G73 Cabin_F2 Cabin_F33 Cabin_F38 Cabin_F4 Cabin_G6 Cabin_T Embarked_C Embarked_Q Embarked_S
1 3 22.0 1 7.2500 ... 1
1 2 1 38.0 1 71.2833 ... 1
2 3 3 26.0 7.9250 ... 1
3 4 1 35.0 1 53.1000 ... 1
4 5 3 35.0 8.0500 ... 1

5 rows × 1730 columns

3.3 训练模型

我们已经准备好在 sklearn 中训练模型了。首先,将数据拆分为训练集和测试集。然后用训练集训练模型。

保留20%的数据作为测试集:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(features, outcomes, test_size=0.2, random_state=42)
           

导入决策树模型并训练模型:

# Import the classifier from sklearn
from sklearn.tree import DecisionTreeClassifier

# TODO: Define the classifier, and fit it to the data
model = DecisionTreeClassifier()
model.fit(X_train,y_train)
           
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=6,
                       max_features=None, max_leaf_nodes=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=6, min_samples_split=10,
                       min_weight_fraction_leaf=0.0, presort=False,
                       random_state=None, splitter='best')
           

3.4 测试模型

现在看看模型的效果。我们计算下训练集和测试集的准确率:

# Making predictions
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

# Calculate the accuracy
from sklearn.metrics import accuracy_score
train_accuracy = accuracy_score(y_train, y_train_pred)
test_accuracy = accuracy_score(y_test, y_test_pred)
print('The training accuracy is', train_accuracy)
print('The test accuracy is', test_accuracy)
           
The training accuracy is 0.8707865168539326
The test accuracy is 0.8547486033519553
           

可以看到训练集准确率为100%,但测试集准确率只有81%,这是因为我们没有对模型进行任何预剪枝处理,因此此时的模型有点过拟合了,这个模型偏向于“记住数据”,因此在测试剂上表现并不好。

我们将通过可视化决策树来直观的确定是否过拟合:

3.5 决策树可视化

# 可视化
from sklearn.tree import export_graphviz
export_graphviz(model,out_file="tree.dot",class_names=['Survived','Dead'],impurity=False,filled=True)

# 通过 Graphviz 打开.dot文件

# 或者通过terminal cd到文件夹
# 运行 dot -Tpdf Tree.dot -o output.pdf 直接输出为pdf
           
监督学习 | 决策树之Sklearn实现1. Sklearn中决策树的超参数2. 使用 Scikit-learn 实现决策树算法3. 实例:使用决策树探索泰坦尼克号乘客存活情况¶参考资料

3.6 预剪枝

从图片中可以看出,决策树确实是过拟合了,因此我们可以通过预剪枝,来防止过拟合:

model = DecisionTreeClassifier(max_depth=6, min_samples_leaf=6, min_samples_split=10)
model.fit(X_train,y_train)
# Making predictions
y_train_pred = model.predict(X_train)
y_test_pred = model.predict(X_test)

# Calculate the accuracy
from sklearn.metrics import accuracy_score
train_accuracy = accuracy_score(y_train, y_train_pred)
test_accuracy = accuracy_score(y_test, y_test_pred)
print('The training accuracy is', train_accuracy)
print('The test accuracy is', test_accuracy)
export_graphviz(model,out_file="tree.dot",class_names=['Survived','Dead'],impurity=False,filled=True)
           
The training accuracy is 0.8707865168539326
The test accuracy is 0.8547486033519553
           

可以看到,经过预剪枝后,该模型在测试集上的正确率已经达到了85%,如果想要更高的准确率,可以通过网络搜索来确定最优参数。

监督学习 | 决策树之Sklearn实现1. Sklearn中决策树的超参数2. 使用 Scikit-learn 实现决策树算法3. 实例:使用决策树探索泰坦尼克号乘客存活情况¶参考资料

从图片中可以看出,在泰坦尼克号的逃生中,大多数的女性都活了下来,而大多数的男性都没有存活下来.

Survived 1
Sex
female 81 233
male 468 109

参考资料

[1] 理想几岁.数据预处理:独热编码(One-Hot Encoding)和 LabelEncoder标签编码 [EB/OL].https://www.cnblogs.com/zongfa/p/9305657.html, 2018-07-13.