机器学习之决策树算法

决策树综述

        决策树是一种基于树状结构的分类和回归算法。决策树由结点(node)有向边(directed edge)组成。结点有两种类型:内部结点(internal node)叶结点(leaf node)。内部结点表示一个特征或属性(features),叶结点表示一个类(labels)。它通过对数据进行特征选择,将数据集分成更小的子集,并在每个自己上递归地应用相同的过程来构建一个树形模型。每个内部节点表示一个特征或属性,每个分支代表该特征的一个可能取值,而每个叶子节点表示一个类别或者回归结果。决策树算法的优点包括易于理解和解释,能够处理离散和连续特征,不需要预处理数据,具有较好的可扩张性等。

        决策树算法有多种实现方式,如ID3、C4.5、CART等。他们它们的主要区别在于用于选择最佳特征的度量方法、树的剪枝策略和处理连续特征的方法等方面。ID3算法使用信息增益作为特征选择的标准,C4.5算法使用信息增益比,而CART算法则使用基尼指数。剪枝策略也有不同的方法,如预剪枝和后剪枝等。处理连续特征的方法包括二元切分和多元切分等方式。

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

一、特征选择

        选择一个合适的特征作为判断节点,可以快速地分类,减少决策树地深度。决策树地目标就是把数据集按对应地类标签进行分类。特征选择地目标使得分类后地数据集比较纯。如何衡量一个数据集地纯度,需要引入数据纯度函数。

1.信息增益

        信息增益是一个统计量,它表示在得知某一特征的取值后,对原始集合进行划分所获得的信息量。

信息量可以用熵来度量,熵越大表示不确定性越大,信息量越大,反之则越小。

一个数据集的熵可以用如下公式来计算:

其中pi表示该数据集中第i个类别的样本所占比例。

在特征选择过程中,信息增益即为某个特征引入后,整个数据集的熵减少的大小。假设某个特征为A,它可以将数据集D划分成k个子集D1​,D2​,...,Dk​,则A的信息增益可以用如下公式来计算:

其中,H(D)表示数据集D的熵,∣D∣/∣Di​∣​表示第i个子集中样本所占比例。

2.基尼指数

在决策树的特征选择过程中,基尼指数是另一种常用的评估指标。它衡量了样本的不纯度,用于选择最佳的特征来进行划分。

对于一个数据集D,假设有K个类别,令Pi表示第i个样本在D中的比例。则基尼指数可以用下面的公式计算:

基尼指数越小,表示数据集的不纯度越低,样本越容易被分类。对于某个特征A,它可以将数据集D划分成k个子集D1,D2,...,Dk,则A的基尼指数可以用下面公式计算:

其中,∣Di​∣表示第i个子集的样本数量。

二、决策树的构建

  1. 数据准备:收集和整理用于构建决策树的数据集。确保数据集包含特征(属性)和对应的类别(标签)。
  2. 特征选择:选择最佳的特征来进行划分。常用的特征选择方法有信息增益、基尼指数等。
  3. 决策树构建:根据选定的特征,将数据集划分为不同的子集。每个子集对应于特征的一个取值。重复这个过程,递归地构建决策树,直到满足一定的停止条件。
  4. 停止条件判断:在构建决策树的过程中,需要判断是否满足停止条件。
  5.  递归构建子树:对于每个划分得到的子集,重复上述步骤,递归地构建子树。
  6.  剪枝:为了防止决策树过度拟合训练数据,需要采取剪枝策略。
  7. 决策树输出:完成决策树的构建和剪枝之后,可以通过遍历决策树,根据待预测样本的特征值进行分类预测。

三、决策树的修剪

决策树剪枝是为了防止决策树过度拟合训练数据,提高模型的泛化能力而进行的操作。剪枝可以分为预剪枝(pre-pruning)和后剪枝(post-pruning)两种方式。

1.预先剪枝策略

预剪枝是在构建决策树的过程中,在每个节点进行划分之前,通过一定的条件预先停止划分。常用的预剪枝条件包括:

  • 最大深度限制:限制决策树的最大深度,防止过度生长。
  • 叶子节点样本数阈值:当叶子节点的样本数量小于一定阈值时,停止划分。
  • 信息增益阈值:如果划分后的信息增益低于某个阈值,则停止划分。

优点:

  1. 效率高:预剪枝在构建决策树的过程中,每次在分裂一个节点之前就判断是否进行分裂,避免了无效的分裂操作,减少了计算量,提高了构建速度。
  2. 防止过拟合:预剪枝通过提前停止树的生长,减少了决策树的复杂度,防止了过拟合现象的发生,提高了模型的泛化能力。

缺点:

  1. 受限的分类能力:预剪枝在构建过程中可能会过早地终止树的生长,导致不能充分利用训练数据的特征信息,从而降低了决策树的分类能力。
  2. 依赖于启发式规则:预剪枝需要选择合适的判断条件来决定是否进行分裂,这些判断条件通常是基于经验或启发式规则得到的,可能不一定适用于所有情况,存在一定的主观性和局限性。

2.后剪枝策略

后剪枝是在构建完整的决策树之后,通过剪枝操作减少节点或子树来降低过拟合的风险。常用的后剪枝方法有:

  • 悲观剪枝:计算剪枝前后的错误率,若剪枝后的错误率下降,则进行剪枝。
  • 损失函数剪枝:通过引入正则化项来衡量模型的复杂度,将损失函数与模型复杂度相结合,选择最优子树进行剪枝。
  • 代价复杂度剪枝:计算每个内部节点的错误率,并比较剪枝前后的错误率,若剪枝后的错误率下降,则进行剪枝。

优点:

  1. 提高分类能力:后剪枝在构建完整的决策树后,再通过剪枝操作来减少过拟合现象,保留了更多的特征信息,提高了决策树的分类能力。
  2. 一定程度上减小了过拟合风险:后剪枝通过剪除一些无关紧要的分支节点,减小了决策树的复杂度,有效地减小了过拟合的风险。

缺点:

  1. 计算开销大:后剪枝需要先构建一个完整的决策树,然后再进行剪枝操作,因此计算开销较大,特别是在处理大规模数据集时可能会耗费较多时间和资源。
  2. 受限的泛化能力:后剪枝是在构建完整的决策树后进行剪枝,可能会出现过拟合的情况,导致决策树泛化能力下降。

综上所述,预剪枝策略适用于大规模数据集和时间敏感的场景,可提高构建速度和防止过拟合;后剪枝策略适用于需要提高分类能力和减小过拟合风险的场景,但计算开销较大。

四、决策树的代码实现

实验通过一系列特征来预测一个人是否会购买产品的结果。

首先是数据集的准备,定义一个数据集X,包括三个特征,以及对应的标签y

import numpy as np
# 定义数据集
X = np.array([
    ['青年', '高', '私人公司'],
    ['中年', '低', '政府机构'],
    ['老年', '中', '自由职业'],
    ['中年', '低', '自由职业'],
    ['老年', '高', '政府机构'],
    ['青年', '中', '私人公司'],
    ['青年', '高', '自由职业'],
    ['老年', '低', '政府机构']
])

y = np.array(['是', '是', '否', '是', '否', '是', '是', '否'])

将数据集按特征和取值划分,并递归构建子树。

计算熵和信息增益是决策树算法中的重要步骤,用于衡量数据的不确定性,进而选择最优的特征进行划分。

在计算信息增益时,对于每个特征的每个取值,将数据集划分成左右两部分,分别计算划分后的熵,然后加权平均得到信息增益。

# 定义决策树算法
def build_decision_tree(X, y):
    # 创建根节点
    root = Node()
    
    # 如果所有样本属于同一类别,则直接返回结果
    if len(np.unique(y)) == 1:
        root.result = y[0]
        return root
    
    # 如果没有特征可用,则选择出现次数最多的类别作为结果
    if X.shape[1] == 0:
        root.result = np.argmax(np.bincount(y))
        return root
    
    # 选择最佳特征和切分点
    best_feature, best_value = None, None
    best_info_gain = -1
    for feature in range(X.shape[1]):
        values = np.unique(X[:, feature])
        for value in values:
            mask = X[:, feature] == value
            info_gain = calculate_info_gain(y, mask)
            if info_gain > best_info_gain:
                best_feature = feature
                best_value = value
                best_info_gain = info_gain
    
    # 设置当前节点的特征和取值
    root.feature = best_feature
    root.value = best_value
    
    # 根据最佳特征和切分点划分数据集,并递归构建子树
    mask = X[:, best_feature] == best_value
    left_X, left_y = X[mask], y[mask]
    right_X, right_y = X[~mask], y[~mask]
    root.children['left'] = build_decision_tree(left_X, left_y)
    root.children['right'] = build_decision_tree(right_X, right_y)
    
    return root

# 计算熵
def calculate_entropy(y):
    classes = np.unique(y)
    entropy = 0
    for cls in classes:
        p = np.sum(y == cls) / len(y)
        entropy -= p * np.log2(p)
    return entropy

# 计算信息增益
def calculate_info_gain(y, mask):
    p = np.sum(mask) / len(y)
    entropy = calculate_entropy(y)
    left_entropy = calculate_entropy(y[mask])
    right_entropy = calculate_entropy(y[~mask])
    info_gain = entropy - p * left_entropy - (1 - p) * right_entropy
    print("Left entropy:", left_entropy)
    print("Right entropy:", right_entropy)
    return info_gain



打印出信息增益

# 可视化决策树
def visualize_decision_tree(node, indent=''):
    if node.result is not None:
        print(indent + '预测结果:', node.result)
    else:
        print('{}{} = {}?'.format(indent, node.feature, node.value))
        for child in node.children.values():
            visualize_decision_tree(child, indent + '  ')

# 构建决策树并进行可视化
tree = build_decision_tree(X, y)
visualize_decision_tree(tree)

最终输出结果:

实验小结:

        这次关于决策树的实验,我对此收获很大,首先是对于决策树的构建方式,以及通过信息增益来对决策树的特征选择进行修改,然后是剪枝策略来防止决策树的过拟合等操作。在进行实际的代码构建决策树的过程中遇到了很多问题,比如数据集的大小太小导致决策树的输出结果有很大出入,以及一些代码问题导致了很多的错误出现,通过查阅以及参考课本,解决了一系列的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值