文章目录
- 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片叶子,
。最大深度的设置相当于预剪枝处理
- 较大的深度往往会导致过拟合,这是因为过深的决策树可以记忆数据。而较小的深度会使得模型过于简单,导致欠拟合。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLzkTQ4cDOBdTQ3E0NtgDNElTLyAzM00iN4cTQtcjR1QEM0ETMvwVbvNmLzNmb1lXasFmLuVGa65WZoNXLuNWLzN3buM3cvlGbh1SYxg3Lc9CX6MHc0RHaiojIsJye.png)
1.2 每片叶子的最小样本数 min_samples_leaf
- 在分裂节点时,很有可能一片叶子上有 99 个样本,而另一片叶子上只有 1 个样本。这将使我们陷入困境,并造成资源和时间的浪费。如果想避免这种问题,我们可以设置每片叶子允许的最小样本数。
- 这个数字可以被指定为一个整数,也可以是一个浮点数。如果它是整数,它将表示这片叶子上的最小样本数。如果它是个浮点数,它将被视作每片叶子上的最小样本比例。比如,0.1 或 10% 表示如果一片叶子上的样本数量小于该节点中样本数量的 10%,这种分裂将不被允许。
- 当每片叶子的样本数量较小时,叶子上的样本数量也有可能过于稀少,此时模型将记忆数据,也就是过拟合。当每片叶子的样本数量较大时,决策树能够获得足够的弹性进行构建,这也许会导致欠拟合。
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]
这样做的好处主要有:
- 解决了分类器不好处理属性数据的问题
- 在一定程度上也起到了扩充特征的作用
为什么不使用 BOW ?
BOW 适用于每一个对象内包含多个单词,使用 BOW 后,对象内的单词将会被分散开来。
更多关于 One-Hot 编码的知识,可以参考这一篇博文:独热编码(One-Hot Encoding)和 LabelEncoder标签编码
3.2.2 Pandas 实现 One-Hot 编码
- pd.get_dummies(data)
该方法可以讲类别变量转换成新增的虚拟变量/指示变量。
常用参数:
-
(输入的数据): array-like, Series, or DataFramedata
-
(转换后列名的前缀): string, list of strings, or dict of strings, default Noneprefix
-
(指定需要实现类别转换的列名): list-like, default Nonecolumns
-
(增加一列表示空缺值,默认忽略空缺值): True, Falsedummy_na
-
(获得k中的k-1个类别值,去除第一个): bool, default Falsedrop_first
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
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%,如果想要更高的准确率,可以通过网络搜索来确定最优参数。
从图片中可以看出,在泰坦尼克号的逃生中,大多数的女性都活了下来,而大多数的男性都没有存活下来.
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.