机器学习第四课之决策树
目录
简介
一.决策树算法简介
二. 决策树分类原理
1.ID3算法
1.1 熵值
1.2 信息增益
1.3 案例分析
编辑 2.C4.5
2.1 信息增益率
2.2.案例分析
3.CART决策树
3.1基尼值和基尼指数
3.2案例分析
三、决策树剪枝
四、决策树API
五、电信客户流失
六、回归树
七. 回归树API
简介
决策树的核心逻辑就像我们日常生活中的选择过程:从一个初始问题出发,根据不同的答案走向不同的分支,最终抵达一个明确的结论。这种 “层层设问、逐步拆分” 的思路,让它成为机器学习中最容易理解和解释的模型之一,哪怕是没有太多算法基础的人,也能快速看懂它的决策路径。
一.决策树算法简介
-
是⼀种树形结构,本质是⼀颗由多个判断节点组成的树
-
其中每个内部节点表示⼀个属性上的判断,
-
每个分⽀代表⼀个判断结果的输出,
-
最后每个叶节点代表⼀种分类结果。
核心:所有数据从根节点一步一步落到叶子节点。
怎么理解这句话?通过⼀个对话例⼦
决策树分类标准:
- ID3算法
- C4.5算法
- CART决策树
二. 决策树分类原理
1.ID3算法
1.1 熵值

熵值计算公式:
A 集合:[1, 1, 1, 1, 1, 1, 1, 1, 2, 2]
B 集合:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
A 集合熵值:
B 集合熵值:
1.2 信息增益
信息增益表示得知特征X的信息⽽使得类Y的信息熵减少的程度
1.3 案例分析
现在有个表格
表格中显示在 Outlook、temperature、humidity、windy的影响下到底出不出去的情况。
第一遍遍历:
1. 标签(结果是否外出打球)的熵(类别熵):
14 天中,9 天打球,5 天不打球,熵为:
import math
result = -9/14*math.log(9/14, 2) - 5/14*math.log(5/14, 2)
2. 基于天气的划分
属性熵:
晴天【5 天】的熵:
Overcast(阴天)【4 天】的熵:
雨天【5 天】的熵:
整体天气都熵值:
则信息增益为:0.940-0.693=0.247
3. 基于温度的划分
Hot【4 天】的熵:
Mild【6 天】的熵:
Cool【4 天】的熵:
熵值计算:
信息增益为:
4. 基于湿度的划分
5. 基于风的划分
以上两个按照步骤就可以算出来
最终:天气:0.247
温度:0.029
湿度:0.151
有风:0.048
显然,信息增益最大的是:天气 > 湿度 > 有风 >温度
所有就以天气为根节点 ,然后天气下面有三种情况sunny、rainy、overcast,然后再对剩下的温度、湿度、有风三种情况求熵值然后求信息增益,找出各自的根节点
例如在sunny相同下求根节点,以此类推
最终得到的结果如下图:
2.C4.5
2.1 信息增益率
息增益率:特征 A 对训练数据集 D 的信息增益比,为其信息增益
与训练数据集 D 的经验熵
之比:
C4.5 算法是一种决策树生成算法,它使用信息增益比(gain ratio)来选择最优分裂属性,具体步骤如下:
1、计算所有样本的类别熵(H)。
2、对于每一个属性,计算该属性的熵【也为自身熵】(Hi)。
3、对于每一个属性,计算该属性对于分类所能够带来的信息增益(Gi = H - Hi)。
4、计算每个属性的信息增益比(gain ratio = Gi / Hi),即信息增益与类别自身熵的比值。
选择具有最大信息增益比的属性作为分裂属性。
2.2.案例分析
在前面我们算出信息增益:
第一遍计算:【找首要节点】
天气的信息增益为: 0.247,
天气的自身熵值:
5 天晴天、4 天多云、5 天有雨。
信息增益率:
2. 温度的自身熵值:
信息增益率:
3. 湿度的自身熵值:
信息增益率:
4. 有风的自身熵值:
信息增益率:
信息增益率排序:天气(0.1566)湿度(0.151)有风(0.049)温度(0.0186)天气 > 湿度 > 有风 > 温度,然后后面以此类推
3.CART决策树
3.1基尼值和基尼指数
一看概念介绍就懵,我们自己上案例
3.2案例分析
现有一张贷款申请表如下:
青年(5 人,2 人贷款)的基尼系数:
如果是类别是二分类,则基尼系数:
非青年(10 人,7 人贷款)的基尼系数:
总公式:
在(中年)条件下,D 的基尼指数:
在条件下,D 的基尼:
由于和
相等,都可以选作
的最优切分点
求特征和
的基尼指数:
由于和
只有一个切分点,所以它们就是最优切分点。
求特征的基尼指数:
最小,所以
为
的最优切分点。
在几个特征中,
最小,所以选择特征
为最优特征,
为其最优切分点。于是根结点生成两个子结点,一个是叶结点。对另一个结点继续使用以上方法在
中选择最优特征及其最优切分点,结果是
。依此计算得知,所得结点都是叶结点。
三、决策树剪枝
为什么要剪枝?
防止过拟合。
如何剪枝?
预剪枝和后剪枝。
预剪枝策略:
- 1. 限制树的深度;
- 2. 限制叶子节点的个数以及叶子节点的样本数;
- 3. 基尼系数:
总体来说就是不想让我们的决策树变得更深更大,也就是类似与使得xn中n变小。因为在训练中,如果每一中特征都是生成一个路径,那重新拿来测试集精确率非常低导致过拟合。
四、决策树API
class sklearn.tree.DecisionTreeClassifier(criterion='gini',
splitter='best', max_depth=None, min_samples_split=2,
min_samples_leaf=1, min_weight_fraction_leaf=0.0,
max_features=None, random_state=None, max_leaf_nodes=None,min_impurity_decrease=0.0, min_impurity_split=None, class_weight=None, presort=False)
-
1.criterion: gini or entropy【采用基尼系数还是熵值衡量,默认基尼系数】
-
2.splitter: best or random 前者是在所有特征中找最好的切分点 后者是在部分特征中(数据量大的时候)【默认 best,无需更改】
-
3.max_features:(表示寻找最优分裂时需要考虑的特征数量,默认为 None,表示考虑所有特征。),log2,sqrt,N 特征小于 50 的时候一般使用所有的【默认取所有特征,无需更改】
-
4.max_depth: 表示树的最大深度,数据少或者特征少的时候可以不管这个值,如果模型样本量多,特征也多的情况下,可以尝试限制下。如果没有设置,那么将会把节点完全展开,直到所有的叶子节点都是纯的,或者达到最小叶子节点的个数阈值设置。
-
5.min_samples_split : (表示分裂一个内部节点需要的最小样本数,默认为 2),如果某节点的样本数少于
min_samples_split
,则不会继续再尝试选择最优特征来进行划分,如果样本量不大,不需要管这个值。如果样本量数量级非常大,则推荐增大这个值。【控制内部节点分裂的情况:假设 < 10,那么分裂的数量小于 10 就不会再次分裂了,默认 2 个】 -
6.min_samples_leaf : (叶子节点最少样本数),这个值限制了叶子节点最少的样本数,如果某叶子节点数目小于样本数,则会和兄弟节点一起被剪枝,如果样本量不大,不需要管这个值【先构成决策树,再剪枝,当小于某个设定值后,则除此节点以及此节点的分支节点】
五、电信客户流失
库导入部分
import pandas as pd # 数据处理库,用于读取和操作数据
import numpy as np # 数值计算库,用于数组和矩阵操作
from sklearn.model_selection import train_test_split # 数据集拆分工具
from sklearn.tree import DecisionTreeClassifier # 决策树分类器
from sklearn import metrics # 模型评估指标库
from sklearn.model_selection import cross_val_score # 交叉验证工具
from imblearn.over_sampling import SMOTE # 处理类别不平衡的SMOTE算法
数据加载与预处理
# 读取Excel数据
data = pd.read_excel('电信客户流失数据.xlsx')# 提取特征和目标变量
x = data.iloc[:, 1:-1] # 特征数据:取第2列到倒数第2列(排除第一列ID和最后一列标签)
y = data.iloc[:, -1] # 目标变量:最后一列(客户是否流失的标签)
数据集拆分
# 第一次拆分:将原始数据分为训练集(80%)和测试集(20%)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42
)
处理类别不平衡问题
# 使用SMOTE算法对训练集进行过采样
oversampler = SMOTE(random_state=0)
os_x_train, os_y_train = oversampler.fit_resample(x_train, y_train)# 第二次拆分:将过采样后的训练集再分为7:3的训练子集和验证子集
os_x_train_w, os_x_test_w, os_y_train_w, os_y_test_w = train_test_split(os_x_train, os_y_train, test_size=0.3, random_state=0
)
- 客户流失数据通常存在类别不平衡(流失客户占比低),SMOTE 通过生成少数类样本解决此问题
- 二次拆分是为了用验证集进行超参数调优,避免直接使用测试集导致的过拟合
超参数网格搜索(核心部分)
# 定义参数搜索范围
max_depth_param_range = range(2, 25, 2) # 树的最大深度:2到24,步长2
min_samples_leaf_param_range = range(2, 25, 2) # 叶子节点最小样本数
min_samples_split_param_range = range(2, 25, 2) # 内部节点分裂最小样本数
max_leaf_param_range = range(2, 25, 2) # 最大叶子节点数# 存储参数组合和对应的分数
scores = []
params_list = []# 四重重循环遍历所有参数组合(网格搜索)
for i in max_depth_param_range:for j in min_samples_leaf_param_range:for k in min_samples_split_param_range:for n in max_leaf_param_range:params = (i, j, k, n)params_list.append(params)# 创建决策树模型dtr = DecisionTreeClassifier(criterion='gini', # 使用基尼系数作为分裂标准max_depth=i, # 树的最大深度min_samples_leaf=j, # 叶子节点最小样本数min_samples_split=k, # 内部节点分裂最小样本数max_leaf_nodes=n, # 最大叶子节点数random_state=42)# 5折交叉验证计算召回率(recall)score = cross_val_score(dtr, os_x_train_w, os_y_train_w, cv=5, scoring="recall")score_mean = score.mean() # 取5折的平均召回率scores.append(score_mean)# 找到找最佳参数组合(召回率最高的参数)
best_index = np.argmax(scores)
best_i, best_j, best_k, best_n = params_list[best_index]
- 这里采用了四重循环进行网格搜索,计算量较大(12×12×12×12=20736 种组合)
- 选择召回率(recall)作为评价指标,适合流失预测场景(更关注尽可能捕捉所有流失客户)
- 5 折交叉验证可以更稳定地评估模型性能,避免单次拆分的随机性影响
最佳模型训练与评估
# 输出最佳参数
print(f"max_depth: {best_i}")
print(f"min_samples_leaf: {best_j}")
print(f"min_samples_split: {best_k}")
print(f"max_leaf_nodes: {best_n}")# 使用最佳参数创建建模型并在原始训练集上训练
dtr = DecisionTreeClassifier(criterion='gini',max_depth=best_i,min_samples_leaf=best_j,min_samples_split=best_k,max_leaf_nodes=best_n,random_state=42
)
dtr.fit(x_train, y_train) # 注意这里用了原始训练集,而非过采样的训练集# 用测试集进行预测
test_predicted = dtr.predict(x_test)# 评估模型性能
score = dtr.score(x_train, y_train) # 训练集准确率
print(score)
# 输出详细分类报告(包含精确率、召回率、F1分数等)
print(metrics.classification_report(y_test, test_predicted))
六、回归树
何为回归树?
解决回归问题的决策树模型即为回归树。
特点:
必须是二叉树
(1) 计算最优切分点
因为只有一个变量,所以切分变量必然是 x,可以考虑如下 9 个切分点:
【原因:实际上考虑两个变量间任意一个位置为切分点均可】一个一个切分点计算
<1> 切分点 1.5 的计算
第一部分:(1,5.56)
第二部分:(2,5.7)、(3,5.91)、(4,6.4)...(10,9.05)
(2) 计算损失
(3) 同理计算其他分割点的损失
容易看出,当时,loss=1.93最小,所以第一个划分点s=6.5。
(4) 对于小于 6.5 部分
<1> 切分点 1.5 的计算
当s=1.5时,将数据分为两个部分:
第一部分:(1,5.56)
第二部分:(2,5.7)\)、(3,5.91)、(4,6.4)、(5,6.8)、(6,7.05)
(5) 因此得到:
容易看出:
<1> 当s=3.5时,loss=0.2771最小,所以第一个划分点s=3.5。
<2> 当s=8.5时,loss=0.021最小,所以第一个划分点s=8.5。
(6) 假设只分裂我们计算的这几次:
那么分段函数为:
<1> 当 时 ,
<2> 当 时,
<3> 当 时,
<4> 当时,
(7) 对于预测来说:
特征 x 必然位于其中某个区间内,所以,即可得到回归的结果,比如说:
如果 x=11, 那么对应的回归值为 9.025.
(8) 决策树构建
七. 回归树API
class sklearn.tree.DecisionTreeRegressor(criterion='mse',
splitter='best', max_depth=None, min_samples_split=2,
min_samples_leaf=1, min_weight_fraction_leaf=0.0,
max_features=None, random_state=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None, presort=False)[source]
-
criterion:节点分裂依据。默认:
mse
-
可选择:
mae
(平均绝对误差) -> 使用绝对值\(\sum|y_i - c_1| + \sum|y_i - c_2|\) -
【按默认选择
mse
即可】
-
-
splitter:默认
best
,表示以最优的方式切分节点。决定了树构建过程中的节点分裂策略。值为best
,意味着在每个节点上,算法会找出最好的分割点来尽量降低信息熵或者减少均方误差。如果设置为random
,则算法会随机选择一个特征进行分裂。-
【按默认选择
best
即可】
-
-
max_depth:树的最大深度。过深的树可能导致过拟合。
-
【通过交叉验证来进行选择】
-
-
min_samples_split:默认值是 2. 分裂一个内部节点需要的最小样本数,
-
【含义与分类相同】
-
-
min_samples_leaf:默认值是 1,叶子节点最少样本数
-
【含义与分类相同】
-
-
max_leaf_nodes:设置最多的叶子节点个数,达到要求就停止分裂【控制过拟合】
-
【设置此参数之后
max_depth
失效】★重要
-
回归树的代码部分比较简单,重点理解一下它的逻辑过程就行,知道是这样计算的,不需要实际硬背。
代码部分
reg = tree.DecisionTreeRegressor()
reg = reg.fit(x,y)