DataWhale基于决策树的分类预测
学习目标
- 了解 决策树 的理论知识
- 掌握 决策树 的 sklearn 函数调用使用并将其运用到企鹅数据集预测
理论知识
1.决策树(Decision Tree)的介绍
-
决策树是一种常见的分类模型,在金融风控、医疗辅助诊断等诸多行业具有较为广泛的应用。
-
决策树的核心思想是基于树结构对数据进行划分,这种思想是人类处理问题时的本能方法。
-
例如在相亲行为中,女方通常会先看男方年龄是否大于30,如果是30岁以下再看是长相,如果不丑再看收入……最后得出是否要见面的决策。
-
决策树的主要优点:
- 具有很好的解释性,模型可以生成可以理解的规则
- 可以发现特征的重要程度。
- 模型的计算复杂度较低。
-
决策树的主要缺点:
- 模型容易过拟合,需要采用减枝技术处理。
- 不能很好利用连续型特征。
- 预测能力有限,无法达到其他强监督模型效果。
- 方差较高,数据分布的轻微改变很容易造成树结构完全不同。
2.决策树的应用
由于决策树模型中自变量与因变量的非线性关系以及决策树简单的计算方法,使得它成为集成学习中最为广泛使用的基模型。梯度提升树(GBDT),XGBoost以及LightGBM等先进的集成模型都采用了决策树作为基模型,在广告计算、CTR预估、金融风控等领域大放异彩,成为当今与神经网络相提并论的复杂模型,更是数据挖掘比赛中的常客。
在新的研究中,南京大学周志华老师提出一种多粒度级联森林模型,创造了一种全新的基于决策树的深度集成方法,为我们提供了决策树发展的另一种可能。
同时决策树在一些需要明确可解释甚至提取分类规则的场景中被广泛应用,而其他机器学习模型在这一点很难做到。例如在医疗辅助系统中,为了方便专业人员发现错误,常常将决策树算法用于辅助病症检测。例如在一个预测哮喘患者的模型中,医生发现测试的许多高级模型的效果非常差。所以他们在数据上运行了一个决策树的模型,发现算法认为剧烈咳嗽的病人患哮喘的风险很小。但医生非常清楚剧烈咳嗽一般都会被立刻检查治疗,这意味着患有剧烈咳嗽的哮喘病人都会马上得到收治。用于建模的数据认为这类病人风险很小,是因为所有这类病人都得到了及时治疗,所以极少有人在此之后患病或死亡。
3.决策树的原理
决策树本质是一个树状图结构:
在整个决策的过程中,一直在对相亲对象的特征提问题。对于树结构,最开始的年龄是否大于30的问题是树的根结点,而长相、收入、公务员等做出决策前的问题叫做中间结点,最后黄色的见或者不见叫做叶子结点。
结点类型 | 特征 | 决策树对应 |
---|---|---|
根结点 | 无进边,有出边。是最开始的提问。 | 第一次划分数据的地方 |
中间结点 | 既有进边也有出边,进边只有一条,出边可以有很多条。 | 都是针对特征的提问。,是中间过程的各个节点 |
叶子结点 | 有进边,没有出边,每个叶子结点都是一个类别标签。 | 数据最终的决策结果 |
子结点和结节点 | 在两个相连的结点中,更接近根结点的是父结点 | ———— |
那么问题来了
1、根结点应该选择那个特征来划分?
2、年龄划分,为什么选择30,换言之就是判断条件的阈值如何设置?
3、年龄之后的长相、收入等特征,应该先选择哪一个?
我们需要定一个量化标准,那么多的特征,哪一个特征最好用。
对于决策树,优先选择的都是剩余数据集中效果最好的特征。
这里的评价标准就是熵
熵
熵(Entropy),是一个热力学的概念,表征物质状态的参量之一,用符号S表示,其物理意义是物体或体系内部混乱程度的度量。
熵值越高,混乱程度越高。对于分类模型而言,可以理解为不断的通过熵值最高的特征对数据进行区分,最终得到的分类结果熵值应该为0。
熵的公式
H
(
X
)
=
−
∑
i
=
1
n
p
i
∗
l
o
g
p
i
\ H(X) = -\displaystyle\sum_{i=1}^n p_i*log p_i
H(X)=−i=1∑npi∗logpi
上式中,n为当前数据集中总的类别的数量,(整体的熵由所有的类别共同决定),若某个节点中所有的数据都属于同一类,此时
p
i
\ p_i
pi值为1,
l
o
g
p
i
\ logp_i
logpi则为0,熵也为0,也就是该数据不混乱。
举例
有两个集合甲={子,丑,寅,卯},乙={天,地,天,地}。在决策树分类中乙集合相对纯净,各自类别出现概率相对较大,此时熵值就偏低,而在甲集合中,各类别概率较低,熵值就相对较高。
发散
在构建分类决策树时,除熵外还可使用Gini作为衡量标准:
G
i
n
i
(
p
)
=
∑
i
=
1
n
p
i
(
1
−
p
i
)
=
1
−
∑
i
=
1
n
p
i
2
\ Gini(p)=\displaystyle\sum_{i=1}^np_i(1-p_i)=1-\displaystyle\sum_{i=1}^np_i^2
Gini(p)=i=1∑npi(1−pi)=1−i=1∑npi2
信息增益
前面提到,每次划分数据集的熵应该降低,反过来说就是,划分数据集之后的熵值比划分前小,就说明划分有价值,可以用信息增益来衡量这个价值的大小:
G
a
i
n
(
Y
∣
X
)
=
H
(
Y
)
−
H
(
Y
∣
X
)
\ Gain(Y|X)=H(Y)-H(Y|X)
Gain(Y∣X)=H(Y)−H(Y∣X)
其
中
H
(
Y
∣
X
)
=
∑
x
p
(
x
)
H
(
Y
∣
X
=
x
)
叫
做
条
件
熵
。
其中\ H(Y|X)=\displaystyle\sum_{x}p(x)H(Y|X=x)叫做条件熵。
其中 H(Y∣X)=x∑p(x)H(Y∣X=x)叫做条件熵。
寻找决策树的根节点就是,遍历数据集中的所有特征,看那个特征的信息增益最大,最大的就是要寻找的根节点。然后递归寻找剩余信息增益最大的特征依次作为子节点。
使用算法ID3, C4.5和C5.0生成树算法使用熵。
剪枝
上面介绍了怎么生成一颗决策树,理想情况下,决策树是不是可以把数据集中所有的样本完全分开,每个样本就是一个叶子结点,但这时就存在过拟合的问题。所以需要限制树的深度(规模),这就是剪枝。
预剪枝
在生成决策树的同时进行剪枝,设置停止条件,如树的层数、叶子节点个数、信息增益阈值等。
后剪枝
完成决策树构建后,再通过一定的标准进行判断。比较麻烦,不过多介绍。(其实是没理解-_-!)
代码流程
Part1 Demo实践
Step1:库函数导入
## 基础函数库
import numpy as np
import pandas as pd
## 导入画图库
import matplotlib.pyplot as plt
import seaborn as sns
## 导入决策树模型函数
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
#可视化决策树
import graphviz
#windows导入比较麻烦,需要提前下载graphviz2.38版本的MSI安装文件,安装后并添加环境变量后,再pip install graphviz才可用
Step2:构建数据及其可视化
## 构造数据集
x_fearures = np.array([[-1, -2], [-2, -1], [-3, -2], [1, 3], [2, 1], [3, 2]])
y_label = np.array([0, 1, 0, 1, 0, 1])
plt.figure()
plt.scatter(x_fearures[:,0],x_fearures[:,1], c=y_label, s=50, cmap='viridis')
plt.title('Dataset')
plt.show()
Step3:模型构建及可视化
## 调用决策树模型
tree_clf = DecisionTreeClassifier()
tree_clf = tree_clf.fit(x_fearures, y_label)
#可视化二维决策边界
def plot_decision_boundary(model, x_fearures,y_label,level=100):
plt.scatter(x_fearures[:,0],x_fearures[:,1], c=y_label, s=50, cmap='viridis')
x_min, x_max = plt.xlim()#获取或者是设定x座标轴的范围,当前axes上的x座标轴。
y_min, y_max = plt.ylim()#获取或者是设定x座标轴的范围,当前axes上的x座标轴。
x1= np.linspace(x_min, x_max, level)
x2= np.linspace(y_min, y_max, level)
xx, yy = np.meshgrid(x1, x2)
x_new = np.c_[xx.ravel(), yy.ravel()]
y_pred = model.predict(x_new).reshape(xx.shape)
plt.contourf(xx, yy, y_pred, alpha = 0.4, cmap=plt.cm.brg)
plt.title('Decision Boundary')#设置图像标题
plot_decision_boundary(tree_clf, x_fearures,y_label,200)
#可视化决策树
dot_data = tree.export_graphviz(tree_clf, out_file=None)
graph = graphviz.Source(dot_data)
graph.render("pengunis.pdf")
## 'pengunis.pdf'
上述决策边界仅适用于二维与三维情况,三维以上存在难以展示的问题,对于实际的决策树应用亦不能仅选择其中最重要的(信息增益最大的)三个或者两个变量进行展示,这样会改变模型本身
Step4:模型预测
## 创建新样本
x_fearures_new1 = np.array([[0, -1]])
x_fearures_new2 = np.array([[2, 1]])
## 在训练集和测试集上分布利用训练好的模型进行预测
y_label_new1_predict = tree_clf.predict(x_fearures_new1)
y_label_new2_predict = tree_clf.predict(x_fearures_new2)
print('The New point 1 predict class:\n',y_label_new1_predict)
print('The New point 2 predict class:\n',y_label_new2_predict)
'''
The New point 1 predict class:
[1]
The New point 2 predict class:
[0]
'''
从上图可以看出,决策树每一个决策过程都是与某一维度正交的(与坐标轴垂直),这也就导致了决策树对数据的旋转很敏感。
Part2 数据分析
Step1:数据读取/载入、数据信息简单查看和可视化描述
df1=pd.read_csv("D:/python_project/企鹅数据集/table_219.csv")
df2=pd.read_csv("D:/python_project/企鹅数据集/table_220.csv")
df3=pd.read_csv("D:/python_project/企鹅数据集/table_221.csv")
df=pd.concat([df1,df2,df3],join='outer',axis=0)
df.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 344 entries, 0 to 343
# Data columns (total 5 columns):
# Species 344 non-null object
# Culmen Length (mm) 342 non-null float64
# Culmen Depth (mm) 342 non-null float64
# Flipper Length (mm) 342 non-null float64
# Body Mass (g) 342 non-null float64
# dtypes: float64(4), object(1)
# memory usage: 13.6+ KB
#选择'Species','Culmen Length (mm)','Culmen Depth (mm)','Flipper Length (mm)','Body Mass (g)'四个特征进行本次训练
data=df[['Species','Culmen Length (mm)','Culmen Depth (mm)','Flipper Length (mm)','Body Mass (g)']]
data = data.fillna(-1)#对缺失值进行填充
data['Species'].unique()
## array(['Adelie Penguin (Pygoscelis adeliae)',
## 'Gentoo penguin (Pygoscelis papua)',
## 'Chinstrap penguin (Pygoscelis antarctica)'], dtype=object)
## 利用value_counts函数查看每个类别数量
pd.Series(data['Species']).value_counts()
## Adelie Penguin (Pygoscelis adeliae) 152
## Gentoo penguin (Pygoscelis papua) 124
## Chinstrap penguin (Pygoscelis antarctica) 68
## Name: Species, dtype: int64
## 特征与标签组合的散点可视化
sns.pairplot(data=data, diag_kind='hist', hue= 'Species')
plt.show()
data['Species'] = data['Species'].apply(trans)
for col in data.columns:
if col != 'Species':
sns.boxplot(x='Species', y=col, saturation=0.5, palette='pastel', data=data)
plt.title(col)
plt.show()
# 选取其前三个特征绘制三维散点图
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(111, projection='3d')
data_class0 = data[data['Species']==0].values
data_class1 = data[data['Species']==1].values
data_class2 = data[data['Species']==2].values
# 'setosa'(0), 'versicolor'(1), 'virginica'(2)
ax.scatter(data_class0[:,0], data_class0[:,1], data_class0[:,2],label=data['Species'].unique()[0])
ax.scatter(data_class1[:,0], data_class1[:,1], data_class1[:,2],label=data['Species'].unique()[1])
ax.scatter(data_class2[:,0], data_class2[:,1], data_class2[:,2],label=data['Species'].unique()[2])
plt.legend()
plt.show()
Part3 建模预测
## 为了正确评估模型性能,将数据划分为训练集和测试集,并在训练集上训练模型,在测试集上验证模型性能。
from sklearn.model_selection import train_test_split
## 选择其类别为0和1的样本 (不包括类别为2的样本)
data_target_part = data[data['Species'].isin([0,1])][['Species']]
data_features_part = data[data['Species'].isin([0,1])][['Culmen Length (mm)','Culmen Depth (mm)',
'Flipper Length (mm)','Body Mass (g)']]
## 测试集大小为20%, 80%/20%分
x_train, x_test, y_train, y_test = train_test_split(data_features_part, data_target_part, test_size = 0.2, random_state = 2020)#random_state 随机种子需要固定
## 从sklearn中导入决策树模型
from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
## 定义 决策树模型
clf = DecisionTreeClassifier(criterion='entropy')
## 在训练集上训练决策树模型
clf.fit(x_train, y_train)
## DecisionTreeClassifier(class_weight=None, criterion='entropy', 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=False, random_state=None,
## splitter='best')
#可视化新的决策树
dot_data = tree.export_graphviz(clf, out_file=None,class_names=True,filled=True)
graph = graphviz.Source(dot_data)
graph.render("penguins1")
Step1:利用 决策树模型 在二分类上 进行训练和预测
## 在训练集和测试集上分布利用训练好的模型进行预测
train_predict = clf.predict(x_train)
test_predict = clf.predict(x_test)
from sklearn import metrics
## 利用accuracy(准确度)【预测正确的样本数目占总预测样本数目的比例】评估模型效果
print('在训练集上决策树模型的准确度为:',metrics.accuracy_score(y_train,train_predict))
print('在测试集上决策树模型的准确度为:',metrics.accuracy_score(y_test,test_predict))
'''
在训练集上决策树模型的准确度为: 0.9954545454545455
在测试集上决策树模型的准确度为: 1.0
'''
## 查看混淆矩阵 (预测值和真实值的各类情况统计矩阵)
confusion_matrix_result = metrics.confusion_matrix(test_predict,y_test)
print('The confusion matrix result:\n',confusion_matrix_result)
'''
[[31 0]
[ 0 25]]
'''
# 利用热力图对于结果进行可视化
plt.figure(figsize=(8, 6))
sns.heatmap(confusion_matrix_result, annot=True, cmap='Blues')
plt.xlabel('Predicted labels')
plt.ylabel('True labels')
plt.show()
Step2:利用 决策树模型 在三分类(多分类)上 进行训练和预测
## 测试集大小为20%, 80%/20%分
x_train, x_test, y_train, y_test = train_test_split(data[['Culmen Length (mm)','Culmen Depth (mm)',
'Flipper Length (mm)','Body Mass (g)']], data[['Species']], test_size = 0.2, random_state = 2020)
## 定义 决策树模型
clf = DecisionTreeClassifier()
# 在训练集上训练决策树模型
clf.fit(x_train, y_train)
## DecisionTreeClassifier(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=False, random_state=None,
## splitter='best')
## 在训练集和测试集上分布利用训练好的模型进行预测
train_predict = clf.predict(x_train)#预测分类
test_predict = clf.predict(x_test)
train_predict_proba = clf.predict_proba(x_train)#预测概率
test_predict_proba = clf.predict_proba(x_test)
print('The test predict Probability of each class:\n',test_predict_proba)
## 其中第一列代表预测为0类的概率,第二列代表预测为1类的概率,第三列代表预测为2类的概率。
## 利用accuracy(准确度)【预测正确的样本数目占总预测样本数目的比例】评估模型效果
print('在训练集上决策树模型的准确度为:',metrics.accuracy_score(y_train,train_predict))
print('在测试集上决策树模型的准确度为:',metrics.accuracy_score(y_test,test_predict))
# The test predict Probability of each class:
# [[0. 0. 1.]
# [0. 1. 0.]
# [0. 1. 0.]
# [1. 0. 0.]
# …………
# [0. 1. 0.]
# [0. 0. 1.]
# [1. 0. 0.]
# [1. 0. 0.]]
#在训练集上决策树模型的准确度为: 0.9963636363636363
#在测试集上决策树模型的准确度为: 0.9710144927536232
## 查看混淆矩阵
confusion_matrix_result = metrics.confusion_matrix(test_predict,y_test)
print('The confusion matrix result:\n',confusion_matrix_result)
# The confusion matrix result:
# [[30 1 0]
# [ 0 23 0]
# [ 2 0 13]]
# 利用热力图对于结果进行可视化
plt.figure(figsize=(8, 6))
sns.heatmap(confusion_matrix_result, annot=True, cmap='Blues')
plt.xlabel('Predicted labels')
plt.ylabel('True labels')
plt.show()
Part4 参数设置
参数 | 解释 |
---|---|
criterion | 可选‘gini’和‘entropy’,默认‘gini’ |
splitter | 特征划分点选择标准,可选‘best’和‘random’,前者为全局最优解,后者为随机划分后在局部寻找最优解,数据量大的情况下可设置‘random’ |
max_features | 划分时考虑的最大特征数,默认‘None’为考虑所有特征数,当特征特别多时可限制特征数 |
max_depth | 决策树的最大深度,防止过拟合 |
min_samples_split | 内部节点在划分所需最小样本数,可限制子树继续划分,默认为2,是重点调参对象 |
min_samples_leaf | 叶子节点最小样本数,限制叶子节点最少的样本数,如果某叶子节点小于该参数值,则会和兄弟节点一起被剪枝 |
min_weight_fraction_leaf | 叶子节点最小的样本权重和,限制叶子节点所有样本权重和的最小值,若某叶子节点小于该参数值,则会和兄弟节点一起被剪枝,样本不均衡时可以考虑 |
max_leaf_node | 最大叶子节点数,防止过拟合 |
class_weight | 指定各类别的权重,样本不均衡时使用,‘balanced’令算法自己平衡各类别权重 |
min_impurity_split | 节点划分最小不纯度,如果某节点不纯度小于参数阈值,则该节点不再生成子节点 |