基于决策树的分类预测

[Data Whale - 2020年8月组队打卡任务 - by Golden (wechat: freealafeimax)]

Part1 - 决策树实验介绍

(原文Link: https://developer.aliyun.com/ai/scenario/bb2fe211e5e94017840ce42cc31fe621?accounttraceid=c0f41f9c5acf49debaf004b9a8333ffdyyst

1. 决策树的介绍

决策树是一种常见的分类模型,在金融分控、医疗辅助诊断等诸多行业具有较为广泛的应用。决策树的核心思想是基于树结构对数据进行划分,这种思想是人类处理问题时的本能方法。例如在婚恋市场中,女方通常会先看男方是否有房产,如果有房产再看是否有车产,如果有车产再看是否有稳定工作……最后得出是否要深入了解的判断。

决策树的主要优点

  1. 具有很好的解释性,模型可以生成可以理解的规则
  2. 可以发现特征的重要程度
  3. 模型的计算复杂度较低。

决策树的主要缺点

  1. 模型容易**过拟合,需要采用减枝技术处理**。
  2. 不能很好利用连续型特征
  3. 预测能力有限,无法达到其他强监督模型效果。
  4. 方差较高,数据分布的轻微改变很容易造成树结构完全不同。

2. 决策树的应用

由于决策树模型中自变量与因变量的非线性关系以及决策树简单的计算方法,使得它成为集成学习中最为广泛使用的基模型。梯度提升树(GBDT),XGBoost以及LightGBM等先进的集成模型都采用了决策树作为基模型,在广告计算、CTR预估、金融风控等领域大放异彩,成为当今与神经网络相提并论的复杂模型,更是数据挖掘比赛中的常客。在新的研究中,南京大学周志华老师提出一种多粒度级联森林模型,创造了一种全新的基于决策树的深度集成方法,为我们提供了决策树发展的另一种可能。

同时决策树在一些需要明确可解释甚至提取分类规则的场景中被广泛应用,而其他机器学习模型在这一点很难做到。例如在医疗辅助系统中,为了方便专业人员发现错误,常常将决策树算法用于辅助病症检测。例如在一个预测哮喘患者的模型中,医生发现测试的许多高级模型的效果非常差。所以他们在数据上运行了一个决策树的模型,发现算法认为剧烈咳嗽的病人患哮喘的风险很小。但医生非常清楚剧烈咳嗽一般都会被立刻检查治疗,这意味着患有剧烈咳嗽的哮喘病人都会马上得到收治。用于建模的数据认为这类病人风险很小,是因为所有这类病人都得到了及时治疗,所以极少有人在此之后患病或死亡。

3. 实验手册

学习目的

  • 了解决策树的理论知识
  • 掌握决策树的sklearn函数调用并将其运用到企鹅数据集预测

Part2 - 3个实验

  • Demo实验
  • 数据分析
  • 建模预测

    2.1 Demo实验

    Step1: 库函数导入

    ```python

    基础函数库

    import numpy as np

导入画图库

import matplotlib.pyplot as plt import seaborn as sns

导入决策树模型函数

from sklearn.tree import DecisionTreeClassifier from sklearn import tree

  1. <a name="mT8ZQ"></a>
  2. ####
  3. <a name="M0M5Z"></a>
  4. #### Step2: 模型训练
  5. ```python
  6. ## Demo演示LogisticRegression分类
  7. ## 构造数据集
  8. x_features = np.array([[-1,-2],[-2,-1],[-3,-2],[1,3],[2,1],[3,2]])
  9. y_label = np.array([0,1,0,1,0,1])
  10. ## 调用决策树模型
  11. tree_clf = DecisionTreeClassifier()
  12. ## 用决策树模型拟合构造的数据集
  13. tree_clf = tree_clf.fit(x_features, y_label)

Step3:数据和模型可视化

  1. ## 可视化构造的数据样本点
  2. plt.figure()
  3. plt.scatter(x_features[:,0],x_features[:,1],c=y_label,s=50,cmap='viridis')
  4. plt.title('Dataset')
  5. plt.show()

Output:
image.png

  1. ## 本段代码实际效果为本地生成PDF可视化文档,在体验过程中可以不运行,可能无法正常展示结果;
  2. ## 代码生成的可视化结果会截图展示实际效果
  3. !pip install graphviz
  4. import graphviz
  5. dot_data = tree.export_graphviz(tree_clf, out_file=None)
  6. graph = graphviz.Source(dot_data)
  7. graph.render("pengunis")
  8. ## 'pengunis.pdf'

Output:
image.png

Step4:模型预测

  1. ## 创建新样本
  2. x_features_new1 = np.array([[0, -1]])
  3. x_features_new2 = np.array([[2, 1]])
  4. ## 在训练集和测试集上分别利用练好的模型进行预测
  5. y_label_new1_predict = tree_clf.predict(x_features_new1)
  6. y_label_new2_predict = tree_clf.predict(x_features_new2)
  7. print('The New point 1 predict class:\n', y_label_new1_predict)
  8. print('The New point 2 predict class:\n', y_label_new2_predict)

Output:
image.png

2.2 数据分析

在实践的最开始,我们首先需要导入一些基础的函数库包括:numpy (Python进行科学计算的基础软件包),pandas(pandas是一种快速,强大,灵活且易于使用的开源数据分析和处理工具),matplotlib和seaborn绘图。

Step1: 库函数导入

  1. ## 基础函数库
  2. import numpy as np
  3. import pandas as pd
  4. ## 绘图函数库
  5. import matplotlib.pyplot as plt
  6. import seaborn as sns

本次我们选择企鹅数据(palmerpenguins)进行方法的尝试训练,该数据集一共包含8个变量,其中7个特征变量,1个目标分类变量。

共有150个样本,目标变量为:企鹅的类别 其都属于企鹅类的三个亚属,分别是Adelie, Chinstrap, and Gentoo.

包含的三种企鹅的7个特征,分别是:所在岛屿, 嘴巴长度, 嘴巴深度, 脚蹼长度, 身体体积, 性别以及年龄。

Adelie Penguins 阿德利企鹅 label:0号
image.png
Gentoo Penguins 巴布亚企鹅 label: 1号
image.png
Chinastrap Penguins 南极企鹅 label: 2号
image.png

Step2: 数据读取/载入

  1. ## 我们利用Pandas自带的read_csv函数读取并转化为DataFrame格式
  2. data = pd.read_csv('penguins_raw.csv')
  3. ## 为了方便,我们仅选取4个简单的特征,有兴趣的同学可以研究下其他特征的含义以及使用方法
  4. data = data[['Species', 'Culmen Length(mm)', 'Culmen Depth (mm)','Flipper Length (mm)', 'Body Mass (g)']]

Step3: 数据信息简单查看

  1. ## 利用.info()查看数据的整体信息
  2. data.info()

Output:
image.png

  1. ## 进行简单的数据查看,我们可以利用.head()头部 .tail()尾部
  2. data.head()

Output:
image.png
这里我们发现数据集中存在NaN,一般的我们认为NaN在数据集中代表了缺失值,可能是数据采集或处理时产生的一种错误。
这里我们采用-1将缺失值进行填补,还有其他例如“中位数填补”、“平均数填补”的缺失值处理方法有兴趣的同学也可以尝试。

  1. data = data.fillna(-1)
  2. data.tail()

Output:
image.png

  1. data.head()

Output:image.png

  1. ## 其对应的类别标签为"Adelie Penguin", "Gentoo penguin", "Chinstrap penguin"三种不同企鹅的类别。
  2. data['Species'].unique()

Output:
image.png

  1. ## 利用value_counts函数查看每个类别数量
  2. pd.Series(data['Species']).value_counts()

Output:
image.png

  1. ## 对于特征进行一些统计描述
  2. data.describe()

Output:
image.png

Step4: 可视化描述

  1. ## 特征与标签组合的散点可视化
  2. sns.pairplot(data=data, diag_kind='hist',hue='Species')
  3. plt.show()

Output:
image.png
从上图可以发现,在2D情况下不同的特征组合对于不同类别企鹅的散点分布,以及大概的区分能力。
我们发现:

  1. '''为了方便我们将标签转化为数字
  2. 'Adelie Penguin (Pygoscelis adeliae)' ---0
  3. 'Gentoo penguin (Pygoscelis papua)' ---1
  4. 'Chinastrap penguin (Pygoscelis antar) ---2'''
  5. def trans(x):
  6. if x == data['Species'].unique()[0]:
  7. return 0
  8. if x == data['Species'].unique()[1]:
  9. return 1
  10. if x == data['Species'].unique()[2]:
  11. return 2
  12. data['Species'] = data['Species'].apply(trans)
  13. for col in data.columns:
  14. if col != 'Species':
  15. sns.boxplot(x='Species', y=col, saturation=0.5, palette='pastel', data=data)
  16. plt.title(col)
  17. plt.show()

Output:
image.pngimage.png
利用箱型图,我们也可以得到不同类别不同特征上分布差异情况

  1. # 选取其前三个特征绘制三维散点图
  2. from mpl_toolkits.mplot3d import Axes3D
  3. fig = plt.figure(figsize=(10,8))
  4. ax = fig.add_subplot(111, projection='3d')
  5. data_class0 = data[data['Species']==0].values
  6. data_class1 = data[data['Species']==1].values
  7. data_class2 = data[data['Species']==2].values
  8. # 'setosa'(0), 'versicolor'(1), 'virginica'(2)
  9. ax.scatter(data_class0[:,0], data_class0[:,1], data_class0[:,2], label=data['Species'].unique()[0])
  10. ax.scatter(data_class1[:,0], data_class1[:,1], data_class1[:,2], label=data['Species'].unique()[1])
  11. ax.scatter(data_class2[:,0], data_class2[:,1], data_class2[:,2], label=data['Species'].unique()[2])
  12. plt.legend()
  13. plt.show()

Output:
image.png

2.3 建模预测

Step1:利用决策树模型在二分类上进行训练和预测

  1. ## 为了正确评估模型性能,将数据划分为训练集和测试集,并在训练集上训练模型,在测试集上验证模型性能。
  2. from sklearn.model_selection import train_test_split
  3. ## 选择其类别为0和1的样本 (不包括类别为2的样本)
  4. data_target_part = data[data['Species'].isin([0,1])][['Species']]
  5. data_features_part = data[data['Species'].isin([0,1])][['Culmen Length (mm)', 'Culmen Depth (mm)',
  6. 'Flipper Length (mm)', 'Body Mass (g)']]
  7. ## 测试集大小为20%, 80%/20%分
  8. x_train, x_test, y_train, y_test = train_test_split(data_features_part, data_target_part, test_size=0.2,random_state=2020)
  1. ## 从sklearn中导入决策树模型
  2. from sklearn.tree import DecisionTreeClassifier
  3. from sklearn import tree
  4. ## 定义决策树模型
  5. clf = DecisionTreeClassifier(criterion = 'entropy')
  6. ## 在训练集上训练决策树模型
  7. clf.fit(x_train, y_train)

Output:
image.png

  1. ## 可视化
  2. ## 本段代码实际效果为本地生成PDF可视化文档,在体验过程中可以不运行,可能无法正常展示结果
  3. ## 代码生成的可视化结果会截屏展示实际效果
  4. import graphviz
  5. dot_data = tree.export_graphviz(clf, out_file=None)
  6. graph = graphviz.Source(dot_data)
  7. graph.render('penguins')
  8. ## 'penguins.pdf'

Output:
打开penguins.pdf
image.png

  1. ## 在训练集合测试集上分别利用训练好的模型进行预测
  2. train_predict = clf.predict(x_train)
  3. test_predict = clf.predict(x_test)
  4. from sklearn import metrics
  5. ## 利用accuracy(准确度)【预测正确的样本数目占总预测样本数目的比例】 评估模型效果
  6. print('The accuracy of the Logistic Regression is:', metrics.accuracy_score(y_train, train_predict))
  7. print('The accuracy of the Logistic Regression is:', metrics.accuracy_score(y_test, test_predict))
  8. ## 查看混淆矩阵(预测值和真实值的各类情况统计矩阵)
  9. confusion_matrix_result = metrics.confusion_matrix(test_predict, y_test)
  10. print('The confusion matrix result:\n', confusion_matrix_result)
  11. ## 利用热力图对于结果进行可视化
  12. plt.figure(figsize = (8,6))
  13. ax = sns.heatmap(confusion_matrix_result, annot=True, cmap='Blues')
  14. ax.set_ylim([2, 0])
  15. plt.xlabel('Predicted labels')
  16. plt.ylabel('True labels')
  17. plt.show()

Output:
image.png
我们可以发现其准确度为1,代表所有的样本都预测正确了。

Step2:利用决策树模型在三分类(多分类)上进行训练和预测

  1. ## 测试集大小为20%, 80%/20%分
  2. x_train, x_test, y_train, y_test = train_test_split(data[['Culmen Length (mm)','Culmen Depth (mm)',
  3. 'Flipper Length (mm)','Body Mass (g)']], data[['Species']], test_size = 0.2, random_state = 2020)
  4. ## 定义 决策树模型
  5. clf = DecisionTreeClassifier()
  6. ## 在训练集上训练决策树模型
  7. clf.fit(x_train, y_train)

Output:
image.png

  1. ## 在训练集合测试集上分别利用训练好的模型进行预测
  2. train_predict = clf.predict(x_train)
  3. test_predict = clf.predict(x_test)
  4. ## 由于逻辑回归模型是概率预测模型(前文介绍的 p = p(y=1|x,\theta)),所以我们可以利用 predict_proba 函数预测其概率
  5. train_predict_proba = clf.predict_proba(x_train)
  6. test_predict_proba = clf.predict_proba(x_test)
  7. print('The test predict Probability of each class:\n', test_predict_proba)
  8. ## 其中第一列代表预测为0类的概率,第二列代表预测为1类的概率,第三列代表预测为2类的概率
  9. ## 利用accuracy(准确度)【预测正确的样本数目占总预测样本数目的比例】评估模型效果
  10. print('The accuracy of the Logistic Regression is:',metrics.accuracy_score(y_train,train_predict))
  11. print('The accuracy of the Logistic Regression is:',metrics.accuracy_score(y_test,test_predict))

Output:
image.png
image.png

  1. ## 查看混淆矩阵
  2. confusion_matrix_result = metrics.confusion_matrix(test_predict, y_test)
  3. print('The confusion matrix result:\n', confusion_matrix_result)
  4. # 利用热力图对于结果进行可视化
  5. plt.figure(figsize=(8, 6))
  6. ax = sns.heatmap(confusion_matrix_result, annot=True, cmap='Blues')
  7. ax.set_ylim([3,0])
  8. plt.xlabel('Predicted labels')
  9. plt.ylabel('True labels')
  10. plt.show()

Output:
image.png


Part3 - 理论补充

3.1 决策树构建的伪代码

image.png

决策树的构建过程是一个递归过程
函数存在三种返回状态
(1)当前节点包含的样本全部属于同一类别,无需继续划分;
(2)当前属性集为空或者所有样本在某个属性上的取值相同,无法继续划分;
(3)当前节点包含的样本集合为空,无法划分。

3.2 划分选择

从上述伪代码中我们发现,决策树的关键在于line6.从A中选择最优划分属性a∗,一般我们希望决策树每次划分节点中包含的样本尽量属于同一类别,也就是节点的“纯度”更高

3.2.1 信息增益

信息熵是一种衡量数据混乱程度的指标信息熵越小**,则数据的**“纯度”越高
image.png
其中pk代表了第k类样本在D中占有的比例。
image.png
image.png
一般的信息增益越大,则意味着使用特征a来进行划分的效果越好。

3.2.2 基尼指数

image.png
基尼指数反映了从数据集D中随机抽取两个的类别标记不一致的概率。
image.png
使用特征a对数据集D划分的基尼指数定义为上。

3.3 重要参数

3.3.1 criterion

Criterion这个参数正是用来决定模型特征选择的计算方法的。
sklearn提供了两种选择:

  • 输入”entropy“,使用信息熵(Entropy)
  • 输入”gini“,使用基尼系数(Gini Impurity)

3.3.2 random_state & splitter

randomstate用来设置分枝中的随机模式的参数,默认None,在高维度时随机性会表现更明显。
splitter也是用来控制决策树中的随机选项的,有两种输入值,输入”best”,决策树在分枝时虽然随机,但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances
查看),输入“random”,决策树在分枝时会更加随机,树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合。

3.3.3 max_depth

限制树的最大深度,超过设定深度的树枝全部剪掉。这是用得最广泛的剪枝参数,在高维度低样本量时非常有效。决策树多生长一层,对样本量的需求会增加一倍,所以限制树深度能够有效地限制过拟合。

3.3.4 min_samples_leaf

min_samples_leaf 限定,一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本,否则分枝就不会发生,或者,分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生。一般搭配max_depth使用,在回归树中有神奇的效果,可以让模型变得更加平滑。这个参数的数量设置得太小会引起过拟合,设置得太大就会阻止模型学习数据