Skip to main content

决策树

决策树

决策树是一种基本的分类与回归方法,是最经常使用的数据挖掘算法之一。

决策树模型呈树形结构,在分类问题中,表示基于特征对实例进行分类的过程。它可以认为是 if-else 规则的集合,也可以认为是定义在特征空间与类空间上的条件概率分布。

决策树学习通常包括 3 个步骤:特征选择、决策树的生成和决策树的修剪。

决策树学习的损失函数通常是正则化的极大似然函数,决策树学习属于监督学习,可以认为是学习一个分类规则。

from sklearn.datasets import load_iris
from sklearn import tree
import numpy as np
iris = load_iris()

clf = tree.DecisionTreeClassifier(min_samples_leaf=15)
clf = clf.fit(iris.data, iris.target)
# 决策树模型为:先左后右,先上后下 负数表示没有
print("树结构-左节点:"+str(clf.tree_.children_left))
print("树结构-右节点:"+str(clf.tree_.children_right))
print("节点分裂特征:"+str(clf.tree_.feature))
print("节点分裂阈值:"+str(np.round(clf.tree_.threshold,2)))
print("节点类别:"+str(clf.classes_.take( [ np.argmax(i) for i in clf.tree_.value])))
索引012345678
左节点1-134-1-17-1-1
右节点2-165-1-18-1-1
分类特征2-232-2-20-2-2
分裂阈值2.45-21.754.45-2-26.35-2-2
节点类别001111222

那么,给定一组样本数据,决策树如何确定第一个条件呢?

决策树的基尼不纯度(Gini impurity)是衡量一个数据集的不纯度或不一致性的一种指标。基尼不纯度越小,数据越纯。如果所有样本属于同一个类别,基尼不纯度为0。

IG(p)=1i=1Jpi2I_G(p) = 1 - \sum_{i=1}^{J} p_i^2

假设有10个水果,其中4个香蕉、3个苹果、3个梨子,则基尼不纯度为:

IG(0.4,0.3,0.3)=1(0.42+0.32+0.32)=0.66I_G(0.4, 0.3, 0.3) = 1 - (0.4^2 + 0.3^2 + 0.3^2) = 0.66\\

简单示例

import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

clf = DecisionTreeClassifier(random_state=0)
# 计算最小成本复杂性修剪期间的修剪路径
path = clf.cost_complexity_pruning_path(X_train, y_train)
# 从路径中提取alpha值和相应的决策树
# 剪枝期间子树的有效 alpha
# 代价复杂度剪枝法,实质就是在树的复杂度与准确性之间取得一个平衡点。
# 原理参考:https://blog.csdn.net/ywj_1991/article/details/126846155
ccp_alphas, impurities = path.ccp_alphas, path.impurities

# 在 DecisionTreeClassifier中, 这种剪枝技术是通过成本复杂度参数ccp_alpha来参数化的。更大的ccp_alpha值增加被剪枝的节点数。
clfs = []
# ccp_alphas的值是通过cost_complexity_pruning_path获得的
for ccp_alpha in ccp_alphas:
# 每个循环创建一个决策树并将其添加到列表中
clf = DecisionTreeClassifier(random_state=0, ccp_alpha=ccp_alpha)
# 拟合决策树
clf.fit(X_train, y_train)
# 决策树模型为:先左后右,先上后下 负数表示没有
print("{} | 树结构-左节点长度:{},树结构-右节点长度:{}".format(ccp_alpha,len(clf.tree_.children_right),len(clf.tree_.children_left)))

# 将决策树添加到列表中
clfs.append(clf)
print("Number of nodes in the last tree is: {} with ccp_alpha: {}".format(
clfs[-1].tree_.node_count, ccp_alphas[-1]))
# 删除 ccp_alphas的最后一个值, 因为它对应于完全未剪枝的树
clfs = clfs[:-1]
# ccp_alphas也需要删除最后一个值, 因为它是完全未剪枝的树对应的值
ccp_alphas = ccp_alphas[:-1]

效果评估

import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.tree import DecisionTreeClassifier

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

clf = DecisionTreeClassifier(random_state=0)
# 计算最小成本复杂性修剪期间的修剪路径
path = clf.cost_complexity_pruning_path(X_train, y_train)
# 从路径中提取alpha值和相应的决策树
# 剪枝期间子树的有效 alpha
# 代价复杂度剪枝法,实质就是在树的复杂度与准确性之间取得一个平衡点。
# 原理参考:https://blog.csdn.net/ywj_1991/article/details/126846155
ccp_alphas, impurities = path.ccp_alphas, path.impurities

# 在 DecisionTreeClassifier中, 这种剪枝技术是通过成本复杂度参数ccp_alpha来参数化的。更大的ccp_alpha值增加被剪枝的节点数。
clfs = []
# ccp_alphas的值是通过cost_complexity_pruning_path获得的
for ccp_alpha in ccp_alphas:
# 每个循环创建一个决策树并将其添加到列表中
clf = DecisionTreeClassifier(random_state=0, ccp_alpha=ccp_alpha)
# 拟合决策树
clf.fit(X_train, y_train)
# 决策树模型为:先左后右,先上后下 负数表示没有
print("{} | 树结构-左节点长度:{},树结构-右节点长度:{}".format(ccp_alpha,len(clf.tree_.children_right),len(clf.tree_.children_left)))

# 将决策树添加到列表中
clfs.append(clf)
print("Number of nodes in the last tree is: {} with ccp_alpha: {}".format(
clfs[-1].tree_.node_count, ccp_alphas[-1]))
# 删除 ccp_alphas的最后一个值, 因为它对应于完全未剪枝的树
clfs = clfs[:-1]
# ccp_alphas也需要删除最后一个值, 因为它是完全未剪枝的树对应的值
ccp_alphas = ccp_alphas[:-1]
# 绘制每个alpha值的树的测试集精度和训练集精度
node_counts = [clf.tree_.node_count for clf in clfs]
# 计算每个树的测试集精度和训练集精度
depth = [clf.tree_.max_depth for clf in clfs]


# 绘制精度与alpha的关系
train_scores = [clf.score(X_train, y_train) for clf in clfs]
# 绘制测试集精度
test_scores = [clf.score(X_test, y_test) for clf in clfs]

fig, ax = plt.subplots()
ax.plot(ccp_alphas, train_scores, marker='o', label="train",
drawstyle="steps-post")
ax.plot(ccp_alphas, test_scores, marker='o', label="test",
drawstyle="steps-post")
ax.legend()
plt.show()
'''
当 ccp_alpha 设置为0, 并保留DecisionTreeClassifier的其他默认参数时, 树就过拟合了,
使训练的准确率达到100%,测试的准确率达到88%。
随着alpha的增加,更多的树被剪枝,从而创建了一个泛化更好的决策树。
在本例中,设置 ccp_alpha=0.015可以最大限度地提高测试的准确率。
'''

模型保存

import pickle
import os
# 保存模型
with open('clf_model_v1.pickle','wb') as f:
pickle.dump(clf,f)
# 加载模型
with open('clf_model_v1.pickle','rb') as f:
clf2 = pickle.load(f)

# 删除模型
os.remove('clf_model_v1.pickle')

import joblib

# 保存模型
joblib.dump(clf, 'new_app_model_v1.pkl')

# 加载模型
clf3 = joblib.load('new_app_model_v1.pkl')

# 删除模型
os.remove('new_app_model_v1.pkl')