决策树算法总结
算法思想
给定一个样本集合D,其中每个样本由若干个属性表示,决策树通过贪心策略(如 ID3 / C4.5 / CART)不断挑选最优的属性,将每个样本划分到不同的子树,再在各棵子树上通过递归对子树上的样本进行划分,直到满足一定的终止条件为止。
决策树的每个叶节点对应一个分类,非叶节点对应某个属性上的划分,根据样本在该属性上的不同取值将其划分为若干子集。
算法基本框架(伪代码)
输入:训练集D = {(X1,Y1),(X2,Y2), ..., (Xn,Yn)}
属性集A = {A1, A2, ..., Am} (即Xi含m个特征属性,i=1,2,...,n)
输出:构建好的决策树
def BuildDecisionTree(D, A){
node = New Node(); // 新键一个节点
if (D为空集){
return node;
}
else if(Y1 == Y2 == ... ==Yn ==C){ // D中的所有样本都属于同一类别 C
node为叶节点,其标记为C;
return node;
}
else if( "A为空" or "D中的样本在A上的取值都相同"){ // A为空表示:已没有可以用于继续划分的属性
node 为叶节点, 标记为 D 中最频繁的类别;
return node;
}
else{
从 A 中选择最优划分属性 A* (使用信息增益或其他准则,应用贪心算法策略有不同的效果,也是构建决策树的关键步骤)
对于 A* 的每一个值v{
为 D 中 A* = v 的样本创建一个分支节点
Dv = {(Xj,Yj) | Xj_A* = v, j=1,2,...,n} //令 Dv 为 D 中 A* = v 的样本集合(Dv为D的子集 ,Xj_A*为第j个样本的属性A*的属性值
if Dv 为空:
添加一个叶节点到分支节点,标记为 D 中最频繁的类别
else:
添加 BuildDecisionTree(Dv, A - {A*}) 到分支节点
}
}
返回 生成的树
}
策略对比
采用不同的贪心策略(属性选择度量标准不同)生成最终的决策树会引起BuildDecisionTree的不同;另外在实际应用中,可能还需要考虑处理缺失值、剪枝(避免过拟合)等问题。
ID3:采用信息增益
# 计算属性a的信息增益(信息增益表示划分前后数据集纯度的提升程度,信息增益越大,表示使用属性 a 划分数据集的效果越好。)
def CalculateInformationGain(D, A, a):
计算 D 的熵 H(D) = CalculateEntropy(D)
对于 a 的每一个值 v:
计算 Dv 的大小 |Dv|
计算条件熵 H(D|a) # 给定某个属性 a 后,数据集 D 的条件熵表示了按 a 划分后数据集的纯度。
信息增益 Gain(D, a) = H(D) - H(D|a)
return Gain(D, a)
# 计算样本集 D 的熵(Entropy,是衡量数据集纯度的一个指标,熵越小,数据集的纯度越高)
def CalculateEntropy(D):
对于 D 中的每个类别 C:
计算 C 的概率 p(C)
H(D) = -Σ p(C) * log2(p(C))
return H(D)
优点:
方法简单,学习能力强
缺点:
选择具有大量值的属性的倾向;没有考虑连续特征;属性相互关系强调不够,容易导致子树重复或某些属性被重复检验;容易过拟合
C4.5:采用增益率
C4.5是ID3算法的改进版本,采用增益率(Gain Ratio)作为分支指标,旨在克服信息增益偏向于选择取值数目较多的属性的问题。增益率通过引入一个分裂信息(Split Information)的项来惩罚取值数目多的属性
def CalculateGainRatio(D, A, a):
信息增益 Gain(D, a) = CalculateInformationGain(D, A, a)
# 计算 a 的分裂信息 SplitInfo(D, a),用来衡量根据属性a将数据集D划分成多个子集时的不确定性
对于 a 的每一个唯一值 v:
计算 Dv 的大小 |Dv|
计算该分支的概率 p(v) = |Dv| / |D|
SplitInfo(D, a) = -Σ p(v) * log2(p(v))
if SplitInfo(D, a) == 0:
return 0 # 避免除以零
计算增益率 GainRatio(D, a) = Gain(D, a) / SplitInfo(D, a)
return GainRatio(D, a)
优点:尽量克服了ID3算法的缺点;能处理非离散数据或不完整数据
缺点:大量的运算耗费资源,且只能用于分类
CART:采用基尼指数
CART全称为Classification and Regression Tree,分类回归树,是如今主要使用的用于实现决策树的算法。
基尼指数用来衡量数据的不纯度或不确定性。
决策树中的基尼指数计算:
注:一般情况下,CART算法实现的决策树是一棵二叉树,而前两种算法生成的决策树一般是多叉树
实验-决策树模型实现葡萄酒分类
相关库的导入与说明
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, export_graphviz
import numpy as np
import os
np.random.seed(0) # 为了保证每次随机实验的结果一致
sklearn.tree.DecisionTreeClassifier
DecisionTreeClassifier是 scikit-learn 库中用于分类任务的决策树模型,实现了决策树的构建、分类预测、决策树评价。该模型通过递归地将特征空间划分为若干个简单的区域来做出预测,每个区域都输出一个简单的预测值(通常是该区域内训练样本中最常见的类别)。
核心参数(构造函数参数):
criterion : {"gini", "entropy", "log_loss"}, default="gini"
规定了该决策树所采用的最佳分割属性的判决方法(采用的度量标准)
"gini", "entropy", "log_loss" 分别对应 CART、ID3、C4.5算法
max_depth : int, default=None
限制决策树的最大深度,默认不限制;进行限制有助于防止过拟合
min_samples_leaf : int or float, default=1
限定了叶节点包含的最小样本数
- If float, then `min_samples_leaf` is a fraction and
`ceil(min_samples_leaf * n_samples)` are the minimum
number of samples for each node
常用实例方法:
fit(XTrain,YTrain):
根据给定的样本特征数据和对应的标签数据构建决策树
score(XTest,YTest):
使用构建好的决策树对若干样本特征XTest进行标签预测,并与真实标签YTest对比最后返回准确率。
predict(XTest):
使用构建好的决策树对若干样本特征XTest进行标签预测并返回预测的标签数据
sklearn.tree的export_graphviz()方法
常用调用形式:
# model为成功执行fit()方法后的决策树对象
export_graphviz(model, out_file='xxxxxxTree.dot')
该函数将决策树模型以 DOT 格式导出,通过生成的 DOT 文件,用户可以使用 Graphviz 的工具(如 dot 命令行工具,需要自行另外安装在系统中)将决策树转换为PNG图片或PDF文件,这样可直观地看到决策树的结构,可视化决策树的决策过程。
加载葡萄酒数据
# 获取训练集、测试集
wines = load_wine()
X, Y = wines.data, wines.target
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.2)
通过PyCharm的调试功能可以看看sklearn内置的葡萄酒数据的相关信息。共有178个样本,每个样本13个特征,样本标签共3类。
决策树三种构建策略的对比实验
def decisionTreeExperiment(criterion = "gini"):
# 获取决策树模型
model = DecisionTreeClassifier(criterion=criterion)
model.fit(X_train, y_train)
# 可视化生成的决策树
dot_file = f"DecisionTreeWithCriterion={criterion}.dot"
export_graphviz(model, out_file=dot_file)
p = os.path.join(os.getcwd(),dot_file)
commandText = f"dot -Tpng {p} -o {dot_file[:-4]}.png" # 注意,需要去 Graphviz 的官方网站下载适用于你操作系统的安装程序
print('命令终端执行如下命令可获取可视化的决策树效果:\n',commandText)
# 模型评估
trainScore = model.score(X_train, y_train)
testScore = model.score(X_test, y_test)
print("DecisionTree experiment with 'criterion={}' ".format(criterion))
print(f'\t trainScore:{trainScore} \n\t testScore:{testScore}\n')
对比实验,三种决策树算法的应用:
dict_algorithm2criterion = {'ID3':'entropy', 'C4.5':'log_loss', "CART":'gini'}
for k, criterion in dict_algorithm2criterion.items():
print(k)
decisionTreeExperiment(criterion)
运行结果:
初步来看,ID3、C4.5、CART三种策略的效果依次增强。
决策树可视化
在安装了的系统的命令终端上执行dot命令将代码生成的三个dot文件转为png图片(将-Tpng替换为-Tpdf即将dot文件转为pdf文件):
dot -Tpng xxxxxxTree.dot -o xxxxxxTree.png
生成的三棵决策树中,除了使用gini标准生成的决策树外,其它两棵决策树的总节点数为样本的特征树目(13个)。
图中每个非叶子节点(下同)包含四个数据:决策条件(样本的某个特征的特征值,或者某个属性的属性值与特定数值的比较)、度量标准及其值、样本数、每个类别的个数。叶子节点没有“决策条件”这个数据是因为叶子节点不用再根据条件再进行分裂了。
- 注:叶节点所包含的样本都属于同一类别,如value = [0, 0, 42]表示该叶节点只有42个标签为第三类的样本。这决定了决策树预测新样本的过程。
决策树预测新样本
构建好后的决策树对新样本进行预测的过程相对直观且系统化,一般过程:
- 从根节点开始:
- 将新样本的特征值输入到决策树的根节点。
- 特征值测试(决策条件判断):
- 在当前节点上,根据该节点的特征属性对新样本进行相应的测试。
- 测试结果将决定下一步应该走向哪个子节点。
- 递归遍历:
- 根据测试结果,移动到相应的子节点上。
- 在新的子节点上重复进行特征值测试,直到达到一个叶节点。
- 输出预测结果:
- 当达到叶节点时,该叶节点所代表的类别或数值即为对新样本的预测结果。
上一篇:k-近邻算法实现鸢尾花分类-sklearn接口调用