决策树
1、引言
小屌丝:鱼哥,我被你骗了。
 小鱼:… 别闹,我怎么可能骗你呢。
 小屌丝:你上次说的去泡澡
 小鱼:嗯,然后呢。
 小屌丝:然后咱俩去的是啥地方
 小鱼:…嗯… 这个… 是泡澡的地方啊
 小屌丝:你可拉倒吧。
 小鱼:那我问你,咱俩去没去泡澡。
 小屌丝:去了啊。
 小鱼:那咱俩泡澡的地方有没有汗蒸房
 小屌丝: 有啊。
 小鱼:那咱俩泡澡的地方,有没有提供饮料。
 小屌丝:有啊
 小鱼:那咱俩泡澡的地方,有没有敲背。
 小屌丝:有啊…
 小鱼: 那咱俩泡完澡,有没有吃夜宵。
 小屌丝:… 也有啊
 小鱼: 你看,你都说了,这个流程咱都有,你咋说我骗你呢。
 小屌丝:…但不,咱去的泡澡的地方跟原来的不一样
 小鱼: …哪不一样了。

 小屌丝:你看,这就是你说你请我去的泡澡的地方。
 小鱼:那你说,这次咱俩洗完澡,是不是比平时更舒服了
 小屌丝:这么一说,确实是这样的。
 小鱼:而且冬天了,咱也得感受一下东北的搓澡文化。
 小屌丝:…这么一说,那下次,咱还去这搓澡。
 小鱼:你这还喜欢上搓澡了。
2、决策树
2.1 定义
决策树算法是一种常用的机器学习算法,它主要用于分类和回归问题。
该算法通过构建一棵树状结构来对输入数据进行分类或预测。
决策树算法的核心思想是基于一系列的特征对数据集进行分割,使得每个分割后的子集能够更好地满足某种条件,从而逐步逼近最终的分类或预测结果。
2.2 原理
是通过对数据集进行递归地划分,使得每个划分子集中的数据尽可能地相似。
通过计算不同特征的信息增益或基尼不纯度等指标,选择最佳的划分特征。
在划分的过程中,会逐渐减小不确定性,使得每个子集内的数据更加纯净。
2.3 实现方式
决策树的实现通常包括以下几个步骤:
- 特征选择:选择最优特征进行划分,常用的特征选择方法有信息增益、增益率、基尼不纯度等。
 决策树生成:递归地划分数据集,生成决策树。常用的划分准则是完全划分,即每个子集只包含同一类别的数据。
- 剪枝:对生成的决策树进行剪枝,以提高模型的泛化能力。常见的剪枝策略有预剪枝和后剪枝。
- 决策树评估:使用测试数据对决策树进行评估,常用的评估指标有准确率、精确率、召回率和F1分数等。
2.4 算法公式
决策树算法的实现方式可以有多种,包括:
- ID3算法:ID3算法使用信息增益来选择最佳划分特征
- C4.5算法:C4.5算法使用信息增益率来选择最佳划分特征
- CART算法:CART算法则使用基尼不纯度来选择最佳划分特征
- …
2.4.1 信息增益公式
信息增益公式如下:
  
      
       
        
        
          G 
         
        
          a 
         
        
          i 
         
        
          n 
         
        
          ( 
         
        
          D 
         
        
          , 
         
        
          A 
         
        
          ) 
         
        
          = 
         
        
          E 
         
        
          n 
         
        
          t 
         
        
          r 
         
        
          o 
         
        
          p 
         
        
          y 
         
        
          ( 
         
        
          D 
         
        
          ) 
         
        
          − 
         
         
         
           ∑ 
          
          
          
            v 
           
          
            = 
           
          
            1 
           
          
         
           V 
          
         
         
          
          
            ∣ 
           
           
           
             D 
            
           
             v 
            
           
          
            ∣ 
           
          
          
          
            ∣ 
           
          
            D 
           
          
            ∣ 
           
          
         
        
          E 
         
        
          n 
         
        
          t 
         
        
          r 
         
        
          o 
         
        
          p 
         
        
          y 
         
        
          ( 
         
         
         
           D 
          
         
           v 
          
         
        
          ) 
         
        
       
         Gain(D, A) = Entropy(D) - \sum_{v=1}^{V}\frac{|D_v|}{|D|}Entropy(D_v) 
        
       
     Gain(D,A)=Entropy(D)−v=1∑V∣D∣∣Dv∣Entropy(Dv)
  
     
      
       
       
         D 
        
       
      
        D 
       
      
    D表示原始数据集, 
     
      
       
       
         A 
        
       
      
        A 
       
      
    A表示待选择的划分特征, 
     
      
       
       
         V 
        
       
      
        V 
       
      
    V表示特征 
     
      
       
       
         A 
        
       
      
        A 
       
      
    A的取值集合, 
     
      
       
        
        
          D 
         
        
          v 
         
        
       
      
        D_v 
       
      
    Dv表示特征 
     
      
       
       
         A 
        
       
      
        A 
       
      
    A取值为 
     
      
       
       
         v 
        
       
      
        v 
       
      
    v的子集, 
     
      
       
       
         ∣ 
        
       
         D 
        
       
         ∣ 
        
       
      
        |D| 
       
      
    ∣D∣表示数据集的样本个数, 
     
      
       
       
         ∣ 
        
        
        
          D 
         
        
          v 
         
        
       
         ∣ 
        
       
      
        |D_v| 
       
      
    ∣Dv∣表示子集 
     
      
       
        
        
          D 
         
        
          v 
         
        
       
      
        D_v 
       
      
    Dv的样本个数。
2.4.2 信息增益率公式
信息增益率公式如下:
  
      
       
        
        
          G 
         
        
          a 
         
        
          i 
         
        
          n 
         
        
          _ 
         
        
          R 
         
        
          a 
         
        
          t 
         
        
          i 
         
        
          o 
         
        
          ( 
         
        
          D 
         
        
          , 
         
        
          A 
         
        
          ) 
         
        
          = 
         
         
          
          
            G 
           
          
            a 
           
          
            i 
           
          
            n 
           
          
            ( 
           
          
            D 
           
          
            , 
           
          
            A 
           
          
            ) 
           
          
          
          
            S 
           
          
            p 
           
          
            l 
           
          
            i 
           
          
            t 
           
          
            _ 
           
          
            I 
           
          
            n 
           
          
            f 
           
          
            o 
           
          
            ( 
           
          
            D 
           
          
            , 
           
          
            A 
           
          
            ) 
           
          
         
        
       
         Gain\_Ratio(D, A) = \frac{Gain(D, A)}{Split\_Info(D, A)} 
        
       
     Gain_Ratio(D,A)=Split_Info(D,A)Gain(D,A)
其中, S p l i t _ I n f o ( D , A ) Split\_Info(D, A) Split_Info(D,A)表示特征 A A A的分裂信息,用于避免对具有较多取值的特征偏好。
基尼不纯度公式如下:
  
      
       
        
        
          G 
         
        
          i 
         
        
          n 
         
        
          i 
         
        
          ( 
         
        
          D 
         
        
          ) 
         
        
          = 
         
        
          1 
         
        
          − 
         
         
         
           ∑ 
          
          
          
            k 
           
          
            = 
           
          
            1 
           
          
         
           K 
          
         
        
          ( 
         
         
         
           p 
          
         
           k 
          
         
         
         
           ) 
          
         
           2 
          
         
        
       
         Gini(D) = 1 - \sum_{k=1}^{K}(p_k)^2 
        
       
     Gini(D)=1−k=1∑K(pk)2
  
     
      
       
       
         K 
        
       
      
        K 
       
      
    K表示类别的个数, 
     
      
       
        
        
          p 
         
        
          k 
         
        
       
      
        p_k 
       
      
    pk表示属于类别 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k的样本在数据集 
     
      
       
       
         D 
        
       
      
        D 
       
      
    D中的比例。
2.5 代码示例
# -*- coding:utf-8 -*-
# @Time   : 2024-01-21
# @Author : Carl_DJ
'''
实现功能:
    实现了一个简单的决策树分类器,其中包含了
      信息熵和基尼指数的计算函数,
      构建决策树、预测的方法
'''
import numpy as np
# 定义熵函数,计算给定标签集合的熵(信息熵)
def entropy(y):
    # 获取y中所有唯一值及其出现次数
    _, counts = np.unique(y, return_counts=True)
    # 计算每个类别的概率
    probabilities = counts / len(y)
    # 根据香农熵公式计算熵值
    return -np.sum(probabilities * np.log2(probabilities))
# 定义基尼不纯度指数函数,计算给定标签集合的基尼指数
def gini(y):
    # 同样获取y中所有唯一值及其出现次数
    _, counts = np.unique(y, return_counts=True)
    # 计算每个类别的概率
    probabilities = counts / len(y)
    # 根据基尼指数公式计算基尼不纯度
    return 1 - np.sum(probabilities**2)
# 定义信息增益计算函数,基于某个特征列计算划分数据集的信息增益
def information_gain(X, y, feature_index):
    # 计算原始数据集的熵(或基尼指数)作为总熵
    total_entropy = entropy(y)
    # 获取特征列feature_index上所有唯一值及其出现次数
    values, counts = np.unique(X[:, feature_index], return_counts=True)
    # 计算每个特征值对应的子集熵,并加权求和得到信息增益
    weighted_entropies = []
    for value, count in zip(values, counts):
        subset_y = y[X[:, feature_index] == value]
        # 子集的熵
        subset_entropy = entropy(subset_y)
        # 加权后的熵
        weighted_entropies.append(subset_entropy * count / len(y))
    # 总熵减去加权子集熵之和即为信息增益
    return total_entropy - np.sum(weighted_entropies)
# 定义基尼指数增益计算函数,与信息增益类似,但使用基尼指数代替熵
def gini_index(X, y, feature_index):
    # 计算原始数据集的基尼指数作为总基尼指数
    total_gini = gini(y)
    # 获取特征列feature_index上所有唯一值及其出现次数
    values, counts = np.unique(X[:, feature_index], return_counts=True)
    # 计算每个特征值对应的子集基尼指数,并加权求和得到基尼指数增益
    weighted_ginis = []
    for value, count in zip(values, counts):
        subset_y = y[X[:, feature_index] == value]
        # 子集的基尼指数
        subset_gini = gini(subset_y)
        # 加权后的基尼指数
        weighted_ginis.append(subset_gini * count / len(y))
    # 总基尼指数减去加权子集基尼指数之和即为基尼指数增益
    return total_gini - np.sum(weighted_ginis)
# 定义决策树类
class DecisionTree:
    # 初始化方法,设置最大深度和分裂标准(默认为基尼指数)
    def __init__(self, max_depth=None, criterion='gini'):
        self.max_depth = max_depth
        self.criterion = criterion
    # 训练方法,构建决策树模型
    def fit(self, X, y):
        # 调用私有方法构建决策树
        self.tree = self._build_tree(X, y)
    # 预测方法,根据输入样本X预测类别
    def predict(self, X):
        # 对于每个样本x,遍历决策树进行预测,并将结果存放在数组中
        return np.array([self._traverse_tree(x, self.tree) for x in X])
    # 私有方法,递归构建决策树
    def _build_tree(self, X, y, depth=0):
        # 如果达到最大深度或者所有样本标签相同,则返回该标签作为叶子节点
        if depth == self.max_depth or len(np.unique(y)) == 1:
            return np.bincount(y).argmax()
        
        # 选择最优特征并根据最优特征划分数据集
        best_feature = self._select_best_feature(X, y)
        tree = {best_feature: {}}
        
        # 获取当前最佳特征的所有可能取值
        values = np.unique(X[:, best_feature])
        for value in values:
            # 根据特征值筛选出子集,并对子集继续构建子树
            subset_X = X[X[:, best_feature] == value]
            subset_y = y[X[:, best_feature] == value]
            tree[best_feature][value] = self._build_tree(subset_X, subset_y, depth + 1)
        
        # 返回当前结点及其包含的子树
        return tree
    # 私有方法,选择最优特征(根据信息增益或基尼指数)
    def _select_best_feature(self, X, y):
        # 根据设定的标准选取相应的评价函数
        if self.criterion == 'gini':
            criterion_func = gini_index
        elif self.criterion == 'entropy':
            criterion_func = information_gain
        else:
            raise ValueError("Invalid criterion.")
        
        # 遍历所有特征,找到具有最高信息增益/基尼指数增益的特征
        best_feature = None
        best_score = -np.inf
        for feature_index in range(X.shape[1]):
            score = criterion_func(X, y, feature_index)
            if score > best_score:
                best_score = score
                best_feature = feature_index
        
        # 返回最优特征索引
        return best_feature
    # 私有方法,遍历决策树进行预测
    def _traverse_tree(self, x, tree):
        # 如果当前结点是字典类型(非叶节点),则继续深入决策树
        if isinstance(tree, dict):
            feature_index = list(tree.keys())[0]
            value = x[feature_index]
            subtree = tree[feature_index][value]
            return self._traverse_tree(x, subtree)
        # 如果当前结点不是字典类型(叶节点),则返回预测类别
        else:
            return tree
3、总结
看到这里,监督爱学习的决策树就讲到这里了。
 我们不仅要理解决策树的定义,还需要通过实例来掌握其原理,
 因为只要在实践中,才能掌握其技能。
我是小鱼:
- CSDN 博客专家;
- 阿里云 专家博主;
- 51CTO博客专家;
- 51认证讲师等;
- 认证金牌面试官;
- 职场面试培训、职场规划师;
- 多个国内主流技术社区的认证专家博主;
- 多款主流产品(阿里云等)测评一、二等奖获得者;
关注小鱼,学习机器学习领域的知识。










