机器学习第四课之决策树

发布于:2025-08-04 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

简介

一.决策树算法简介

二. 决策树分类原理

1.ID3算法

1.1 熵值

1.2 信息增益

1.3 案例分析

​编辑 2.C4.5

2.1 信息增益率

 2.2.案例分析

 3.CART决策树

3.1基尼值和基尼指数

3.2案例分析

三、决策树剪枝

四、决策树API

 五、电信客户流失

 六、回归树

七. 回归树API


简介

        决策树的核心逻辑就像我们日常生活中的选择过程:从一个初始问题出发,根据不同的答案走向不同的分支,最终抵达一个明确的结论。这种 “层层设问、逐步拆分” 的思路,让它成为机器学习中最容易理解和解释的模型之一,哪怕是没有太多算法基础的人,也能快速看懂它的决策路径。

一.决策树算法简介

决策树:
        决策树通过对训练样本的学习,并建立分类规则,然后依据分类规则,对新样本数据进行分类预测,属于有监督学习。
        监督学习:就是对于x特征有y结果
        无监督学习:没有y结果
  • 是⼀种树形结构,本质是⼀颗由多个判断节点组成的树

  • 其中每个内部节点表示⼀个属性上的判断,

  • 每个分⽀代表⼀个判断结果的输出,

  • 最后每个叶节点代表⼀种分类结果

 核心:所有数据从根节点一步一步落到叶子节点。

怎么理解这句话?通过⼀个对话例⼦

 决策树分类标准:

  1. ID3算法
  2. C4.5算法
  3. CART决策树

二. 决策树分类原理

1.ID3算法

1.1 熵值

        物理学上, Entropy 混乱 ”程度的量度。系统越有序,熵值越低;系统越混乱或者分散,熵值越⾼

熵值计算公式:

 H(U)=E\left [ -logp_{i} \right ]=-\sum_{i=1}^{n}p_{i}logp_{i}

 A 集合:[1, 1, 1, 1, 1, 1, 1, 1, 2, 2]

B 集合:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

A 集合熵值: -2/10*\log 2(2/10)-8/10*\log 2(8/10) = 0.722

B 集合熵值: -1/10*\log 2(1/10)*10 = 3.322

1.2 信息增益

        信息增益:以某特征划分数据集前后的熵的差值。熵可以表示样本集合的不确定性,熵越⼤,样本的不确定性就越⼤。 因此可以使⽤划分前后集合熵的差值来衡量使⽤当前特征对于样本集合 D 划分效果的好坏
信息增益 = entroy( ) - entroy( )

 信息增益表示得知特征X的信息⽽使得类Y的信息熵减少的程度

1.3 案例分析

现在有个表格

表格中显示在 Outlook、temperature、humidity、windy的影响下到底出不出去的情况。

 第一遍遍历:

1. 标签(结果是否外出打球)的熵(类别熵)
        14 天中,9 天打球,5 天不打球,熵为:


\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad-\frac{9}{14}log_{2}\frac{9}{14}-\frac{5}{14}log_{2}\frac{5}{14}=0.940

import math
result = -9/14*math.log(9/14, 2) - 5/14*math.log(5/14, 2)

 2. 基于天气的划分

 属性熵:
晴天【5 天】的熵:

 -\frac{2}{5}log_{2}\frac{2}{5}--\frac{3}{5}log_{2}\frac{3}{5}=0.971

Overcast(阴天)【4 天】的熵:

-\frac{5}{5}log_{2}\frac{5}{5}=0

雨天【5 天】的熵: 

-\frac{3}{5}log_{2}\frac{3}{5}-\frac{2}{5}log_{2}\frac{2}{5}=0.971 

整体天气都熵值:

\frac{5}{14}*0.971+\frac{4}{14}*0+\frac{5}{14}*0.971=0.693

 则信息增益为0.940-0.693=0.247

3. 基于温度的划分

Hot【4 天】的熵:

-\frac{2}{4}log_{2}\frac{2}{4}-\frac{2}{4}log_{2}\frac{2}{4}=1.000

Mild【6 天】的熵: 

-\frac{4}{6}log_{2}\frac{4}{6}-\frac{2}{6}log_{2}\frac{2}{6}=0.918 

Cool【4 天】的熵: 

-\frac{3}{4}log_{2}\frac{3}{4}-\frac{1}{4}log_{2}\frac{1}{4}=0.811 

 熵值计算:

4/14*1 + 6/14*0.918 + 4/14*0.811 = 0.911

信息增益为:0.940 - 0.911 = 0.029

 4. 基于湿度的划分

5. 基于风的划分

 以上两个按照步骤就可以算出来

最终:天气:0.247
温度:0.029
湿度:0.151
有风:0.048
显然,信息增益最大的是:天气 > 湿度 > 有风 >温度

所有就以天气为根节点 ,然后天气下面有三种情况sunny、rainy、overcast,然后再对剩下的温度、湿度、有风三种情况求熵值然后求信息增益,找出各自的根节点

 例如在sunny相同下求根节点,以此类推

 最终得到的结果如下图:


 2.C4.5

2.1 信息增益率

息增益率:特征 A 对训练数据集 D 的信息增益比g_R(D,A),为其信息增益g(D,A)与训练数据集 D 的经验熵H(D)之比:

g_R(D,A)=\frac{g(D,A)}{H(D)}

 C4.5 算法是一种决策树生成算法,它使用信息增益比(gain ratio)来选择最优分裂属性,具体步骤如下:
1、计算所有样本的类别熵(H)。
2、对于每一个属性,计算该属性的熵【也为自身熵】(Hi)。
3、对于每一个属性,计算该属性对于分类所能够带来的信息增益(Gi = H - Hi)。
4、计算每个属性的信息增益比(gain ratio = Gi / Hi),即信息增益与类别自身熵的比值。
选择具有最大信息增益比的属性作为分裂属性。

 2.2.案例分析

在前面我们算出信息增益:

 第一遍计算:【找首要节点】

天气的信息增益为: 0.247,

天气的自身熵值:
\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad-\frac{5}{14}log_{2}\frac{5}{14}-\frac{4}{14}log_{2}\frac{4}{14}-\frac{5}{14}log_{2}\frac{5}{14}=1.577

5 天晴天、4 天多云、5 天有雨。
信息增益率: 0.247/1.577 = 0.1566

2. 温度的自身熵值:

\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad-\frac{4}{14}log_{2}\frac{4}{14}-\frac{6}{14}log_{2}\frac{6}{14}-\frac{4}{14}log_{2}\frac{4}{14}=1.557

信息增益率: 0.029/1.557 = 0.0186

3. 湿度的自身熵值:

\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad-\frac{7}{14}log_{2}\frac{7}{14}-\frac{7}{14}log_{2}\frac{7}{14}=1.0

信息增益率: 0.151/1.0 = 0.151

4. 有风的自身熵值:

\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad-\frac{8}{14}log_{2}\frac{8}{14}-\frac{6}{14}log_{2}\frac{6}{14}=0.985

信息增益率: 0.048/0.985 = 0.049

 信息增益率排序:天气(0.1566)湿度(0.151)有风(0.049)温度(0.0186)天气 > 湿度 > 有风 > 温度,然后后面以此类推

 3.CART决策树

3.1基尼值和基尼指数

        CART 是 Classification and Regression Tree 的简称,这是⼀种著名的决策树学习算法 , 分类和回归任务都可⽤。
        基尼值Gini D ): 从数据集 D 中随机抽取两个样本,其类别标记不⼀致的概率。 故, Gini D )值越⼩,数据集 D 的纯 度越⾼。
数据集 D 的纯度可⽤基尼值来度量 :

 

        基尼指数Gini_index D ): ⼀般,选择使划分后基尼系数最⼩的属性作为最优化分属性。

 

 一看概念介绍就懵,我们自己上案例

3.2案例分析

现有一张贷款申请表如下:

青年(5 人,2 人贷款)的基尼系数:

\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\frac{2}{5}*(1-\frac{2}{5})+\frac{3}{5}*(1-\frac{3}{5})=0.48

\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad2*\frac{2}{5}*(1-\frac{2}{5})=0.48

如果是类别是二分类,则基尼系数:


\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad p(1-p)+(1-p)p=2p(1-p)

非青年(10 人,7 人贷款)的基尼系数:


\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad\quad2*\frac{7}{10}*(1-\frac{7}{10})=0.42

总公式:


\quad\quad\quad\quad\quad\quad\quad\frac{5}{15}*[2*\frac{2}{5}*(1-\frac{2}{5})]+\frac{10}{15}*[2*\frac{7}{10}*(1-\frac{7}{10})]=0.44

A1=2(中年)条件下,D 的基尼指数:


\quad\quad\quad\quad\quad\quad\quad\quad\quad\frac{5}{15}*[2*\frac{3}{5}*(1-\frac{3}{5})]+\frac{10}{15}*[2*\frac{6}{10}*(1-\frac{6}{10})]=0.48


A1=3条件下,D 的基尼:


\quad\quad\quad\quad\quad\quad\quad\frac{5}{15}*[2*\frac{4}{5}*(1-\frac{4}{5})]+\frac{10}{15}*[2*\frac{5}{10}*(1-\frac{5}{10})]=0.44

 由于\text{Gini}(D,A_1=1\text{Gini}(D,A_1=3)相等,都可以选作A_1的最优切分点 

 求特征A_2A_3的基尼指数:

\quad\quad\quad\quad\quad\text{Gini}(D,A_2=1)=0.32

\quad\quad\quad\quad\quad\quad\text{Gini}(D,A_3=1)=0.27


由于A_2A_3只有一个切分点,所以它们就是最优切分点。
求特征A_4的基尼指数:


\quad\quad\quad\quad\quad\quad\text{Gini}(D,A_4=1)=0.36

\quad\quad\quad\quad\quad\quad\quad\quad\text{Gini}(D,A_4=2)=0.47
\quad\quad\quad\quad\quad\quad\quad\quad\text{Gini}(D,A_4=3)=0.32


\text{Gini}(D,A_4=3)最小,所以\text{Gini}(D,A_4=3)A_4的最优切分点。

         在A_1,A_2,A_3,A_4几个特征中,\text{Gini}(D,A_3=1)=0.27最小,所以选择特征A_3为最优特征,A_3=1为其最优切分点。于是根结点生成两个子结点,一个是叶结点。对另一个结点继续使用以上方法在A_1,A_2,A_4中选择最优特征及其最优切分点,结果是A_2=1。依此计算得知,所得结点都是叶结点。

三、决策树剪枝

为什么要剪枝?
        防止过拟合。
如何剪枝?
        预剪枝和后剪枝。
预剪枝策略:

  • 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.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5]


【原因:实际上考虑两个变量间任意一个位置为切分点均可】一个一个切分点计算
<1> 切分点 1.5 的计算 

第一部分:(1,5.56)
第二部分:(2,5.7)、(3,5.91)、(4,6.4)...(10,9.05)

(2) 计算损失

\quad\quad\quad L(j,s)=\sum_{x_i\in R_1(j,s)}(y_i-\hat{c}_1)^2+\sum_{x_i\in R_2(j,s)}(y_i-\hat{c}_2)^2

C_1=5.56

\) \(C_2=\frac{1}{9}(5.7+5.91+6.4+6.8+7.05+8.9+8.7+9+9.05)=7.5\) \(Loss=(5.56-5.56)^2+(5.7-7.5)^2+(5.91-7.5)^2+...+(9.05-7.5)^2\) \(\quad\quad=0+15.72\) \(\quad\quad=15.72 

(3) 同理计算其他分割点的损失

容易看出,当s=6.5时,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)


C_1=5.56
C_2=\frac{1}{5}(5.7+5.91+6.4+6.8+7.05)=6.37
Loss=0+(5.7-6.37)^2+(5.91-6.37)^2+...+(7.05-6.37)^2\) \(\quad\quad=0+1.3087=1.3087

 (5) 因此得到:

容易看出:

        <1> 当s=3.5时,loss=0.2771最小,所以第一个划分点s=3.5。
        <2> 当s=8.5时,loss=0.021最小,所以第一个划分点s=8.5。 

(6) 假设只分裂我们计算的这几次:


那么分段函数为:
        <1> 当x\leq3.5 时 ,\frac{1}{3}(5.56+5.7+5.91)=5.72
        <2> 当3.5<x\leq6.5 时, \frac{1}{3}(6.4+6.8+7.05)=6.75
        <3> 当6.5<x\leq8.5 时,  \frac{1}{2}(8.9+8.7)=8.8
        <4> 当8.5<x时,     \frac{1}{2}(9+9.05)=9.025

(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]
  1. criterion:节点分裂依据。默认:mse

    • 可选择:mae(平均绝对误差) -> 使用绝对值\(\sum|y_i - c_1| + \sum|y_i - c_2|\)

    • 【按默认选择mse即可】

  2. splitter:默认best,表示以最优的方式切分节点。决定了树构建过程中的节点分裂策略。值为best,意味着在每个节点上,算法会找出最好的分割点来尽量降低信息熵或者减少均方误差。如果设置为random,则算法会随机选择一个特征进行分裂。

    • 【按默认选择best即可】

  3. max_depth:树的最大深度。过深的树可能导致过拟合。

    • 【通过交叉验证来进行选择】

  4. min_samples_split:默认值是 2. 分裂一个内部节点需要的最小样本数,

    • 【含义与分类相同】

  5. min_samples_leaf:默认值是 1,叶子节点最少样本数

    • 【含义与分类相同】

  6. max_leaf_nodes:设置最多的叶子节点个数,达到要求就停止分裂【控制过拟合】

    • 【设置此参数之后max_depth失效】★重要

        回归树的代码部分比较简单,重点理解一下它的逻辑过程就行,知道是这样计算的,不需要实际硬背。

代码部分

reg = tree.DecisionTreeRegressor()
reg = reg.fit(x,y)


网站公告

今日签到

点亮在社区的每一天
去签到