机器学习——决策树

一、算法简介

1.1算法概述

决策树是一种基本的机器学习算法,用于分类和回归任务。它通过从数据中学习一系列简单的规则来建立模型,这些规则形成了树状结构,其中每个内部节点表示一个特征/属性测试,每个叶节点表示一个类别(在分类问题中)或一个数值(在回归问题中)。由于这种决策分支画成图形很像一棵树的枝干,故称决策树。下图为决策树的基本形状:
在这里插入图片描述
决策树只有一个根节点,所以要选择“纯度”最高的哪一个特征来作为根节点。

1.2决策树的生成

决策树的构造是一个递归的过程,主要包括以下步骤:

  1. 选择划分特征:
    通过某种准则选择最佳的划分特征,常用的准则包括信息增益、基尼不纯度等。
    对于每个可能的特征,计算其划分后的不纯度或信息增益,选择最佳的特征进行划分。
  2. 划分数据集:
    根据选择的特征对数据集进行划分,形成子集。
    每个子集中的样本都具有相同的特征值,这些子集构成了树中的一个节点。
  3. 递归构建子树:
    对每个子集,递归地重复上述过程,直到满足终止条件。
    终止条件可以是达到最大深度、节点中样本个数小于阈值等。
  4. 树的生长过程:
    通过不断地选择最佳的划分特征和划分数据集,构建整棵树。
    树的生长过程可以在预剪枝和后剪枝的基础上进行,以防止过拟合。
  5. 处理连续特征:
    对于连续型特征,需要确定划分点。
    可以通过遍历所有可能的划分点或使用启发式方法来选择划分点。
  6. 处理缺失值:
    对于包含缺失值的样本,可以选择一个默认的子集或者根据缺失值的概率进行权重分配。
  7. 树的剪枝:
    在树的构建过程中或构建完成后,可以对树进行剪枝,以提高泛化能力并防止过拟合。
    预剪枝和后剪枝是常用的剪枝策略。
  8. 生成决策树:
    构造完成后,得到一棵完整的决策树,树的节点包括特征值、划分条件和子树等信息。

二、特征的选择

2.1特征选择方法

选择决策树的特征是构建决策树时非常重要的一步,它直接影响了树的性能和泛化能力。通常可以使用以下方法来选择决策树的特征:

  1. 信息增益(ID3 算法):
    信息增益是一种常用的特征选择准则,用于选择能够最大程度降低数据集不确定性的特征。在每次划分时,选择能够使得信息熵或基尼不纯度减少最多的特征作为划分特征。信息增益越大说明样本纯度越高。
    在这里插入图片描述示例:假设有一个二分类任务的数据集,目标变量为 𝑌,特征值为𝑋,取值为
    {x1,x2,…xv},根据特征 𝑋进行划分后得到 𝑉个子集 𝐷1,𝐷2,…,𝐷𝑉。
    1.计算整个数据集的信息熵 :信息熵(𝐷)。
    2.对于每个特征取值 𝑣,计算相应的子集 𝐷𝑣 的信息熵 (𝐷𝑣)
    3.计算每个特征取值对应的权重,即 ∣𝐷𝑣∣/∣𝐷∣。
    4.根据公式计算信息增益(X)。
    5.选择信息增益最大的特征作为划分特征。

  2. 基尼指数(CART 算法):
    基尼不纯度是衡量数据集不纯度的一种指标,它表示从数据集中随机选取两个样本,其类别标签不一致的概率。选择能够使得基尼不纯度减少最多的特征作为划分特征。信息增益越小说明样本纯度越高。
    在这里插入图片描述

  3. 信息增益率(C4.5 算法):
    信息增益比是信息增益的一种变体,它对特征取值数目较多时的偏好性进行了调整。信息增益比考虑了特征取值的多少,避免了对取值较多的特征过分偏好。信息增益越大说明样本纯度越高。
    在这里插入图片描述
    其中在这里插入图片描述

    但是要计算这三个算法还要先计算一个东西,叫做信息熵。

2.2信息熵

信息熵是信息论中衡量信息不确定性的概念。在机器学习中,信息熵常被用于衡量数据集的纯度或不确定性,特别是在决策树等算法中作为特征选择的指标之一。信息熵越高,数据集的不确定性也就越大。

2.2信息熵计算方法

假设当前样本集合D中第k个样本所占的比例为pk(k=1,2,…,|y|),则D的信息熵定义为
在这里插入图片描述Ent(D)的值越小,则D的纯度越高。

三、决策树剪枝

剪枝分类

3.1预剪枝

预剪枝是在决策树生成过程中,对每个结点在划分前先进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶结点。

3.2后剪枝处理

后剪枝是从训练集生成一棵完整的决策树,然后自底向上地对非叶结点进行考察,若将该结点对应的子树替换为叶结点能带来决策树泛化性能提升,则将该子树替换为叶结点。

四、信息增益算法实现

4.1准备数据集

在这里插入图片描述
为方便,将其进行进行数字改写:
年龄:0代表青年,1代表中年,2代表老年;
有工作:0代表否,1代表是;
有自己的房子:0代表否,1代表是;
信贷情况:0代表一般,1代表好,2代表非常好;
类别(是否给贷:no代表否,yes代表是。

def createDataSet():
    dataSet = [
        [0, 0, 0, 0, 'no'],
        [0, 0, 0, 1, 'no'],
        [0, 1, 0, 1, 'yes'],
        [0, 1, 1, 0, 'yes'],
        [0, 0, 0, 0, 'no'],
        [1, 0, 0, 0, 'no'],
        [1, 0, 0, 1, 'no'],
        [1, 1, 1, 1, 'yes'],
        [1, 0, 1, 2, 'yes'],
        [1, 0, 1, 2, 'yes'],
        [2, 0, 1, 2, 'yes'],
        [2, 0, 1, 1, 'yes'],
        [2, 1, 0, 1, 'yes'],
        [2, 1, 0, 2, 'yes'],
        [2, 0, 0, 0, 'no'],
    ]
    # 列名
    labels = ['年龄','有工作','有自己的房子','信贷情况']
    return dataSet, labels

4.2信息熵计算

def calcEntropy(dataSet):
    # 1. 获取所有样本数
    exampleNum = len(dataSet)
    # 2. 计算每个标签值的出现数量
    labelCount = {}
    for featVec in dataSet:
        curLabel = featVec[-1]
        if curLabel in labelCount.keys():
            labelCount[curLabel] += 1
        else:
            labelCount[curLabel] = 1
    # 3. 计算熵值(对每个类别求熵值求和)
    entropy = 0
    for key, value in labelCount.items():
        # 概率值
        p = labelCount[key] / exampleNum
        # 当前标签的熵值计算并追加
        curEntropy = -p * math.log(p, 2)
        entropy += curEntropy
    # 4. 返回
    return entropy

4.3划分数据集

def splitDataSet(dataSet, featureIndex, value):
    returnDataSet = []
    for featVec in dataSet:
        if featVec[featureIndex] == value:
            # 将featureIndex那一列删除
            deleteFeatVec = featVec[:featureIndex]
            deleteFeatVec.extend(featVec[featureIndex + 1:])
            # 将删除后的样本追加到新的dataset中
            returnDataSet.append(deleteFeatVec)
    return returnDataSet

4.4选择最好的特征

def chooseBestFeatureToSplit(dataSet):
    # 1. 计算特征个数 -1 是减去最后一列标签列
    featureNum = len(dataSet[0]) - 1
    # 2. 计算当前(未特征划分时)熵值
    curEntropy = calcEntropy(dataSet)
    # 3. 找最好特征划分
    bestInfoGain = 0  # 最大信息增益
    bestFeatureIndex = -1  # 最好特征索引
    for i in range(featureNum):
        # 拿到当前列特征
        featList = [example[i] for example in dataSet]
        # 获取唯一值
        uniqueVals = set(featList)
        # 新熵值
        newEntropy = 0
        # 计算分支(不同特征划分)的熵值
        for val in uniqueVals:
            # 根据当前特征划分dataSet
            subDataSet = splitDataSet(dataSet, i, val)
            # 加权概率值
            weight = len(subDataSet) / len(dataSet)
            # 计算熵值,追加到新熵值中
            newEntropy += (calcEntropy(subDataSet) * weight)
        # 计算信息增益
        infoGain = curEntropy - newEntropy
        # 更新最大信息增益
        print("第%d个特征的增益为%.3f" % (i, infoGain))
        if bestInfoGain < infoGain:
            bestInfoGain = infoGain
            bestFeatureIndex = i
    # 4. 返回
    return bestFeatureIndex

4.5构建决策树

def createTreeNode(dataSet, labels, featLabels):
    # 取出当前节点的样本的标签 -1 表示在最后一位
    curLabelList = [example[-1] for example in dataSet]
    
    # -------------------- 停止条件 --------------------
    # 1. 判断当前节点的样本的标签是不是已经全为1个值了,如果是则直接返回其唯一类别
    if len(curLabelList) == curLabelList.count(curLabelList[0]):
        return curLabelList[0]
    # 2. 判断当前可划分的特征数是否为1,如果为1则直接返回当前样本里最多的标签
    if len(labels) == 1:
        return getMaxLabelByDataSet(curLabelList)
    
    # -------------------- 下面是正常选择特征划分的步骤 --------------------
    # 1. 选择最好的特征进行划分(返回值为索引)
    bestFeatIndex = chooseBestFeatureToSplit(dataSet)
    # 2. 利用索引获取真实值
    bestFeatLabel = labels[bestFeatIndex]
    # 3. 将特征划分加入当前决策树
    featLabels.append(bestFeatLabel)
    # 4. 构造当前节点
    myTree = {bestFeatLabel: {}}
    # 5. 删除被选择的特征
    del labels[bestFeatIndex]
    # 6. 获取当前最佳特征的那一列
    featValues = [example[bestFeatIndex] for example in dataSet]
    # 7. 去重(获取唯一值)
    uniqueFeaValues = set(featValues)
    # 8. 对每个唯一值进行分支
    for value in uniqueFeaValues:
        # 递归创建树
        myTree[bestFeatLabel][value] = createTreeNode(
            splitDataSet(dataSet, bestFeatIndex, value), labels.copy(),
            featLabels.copy())
    # 9. 返回
    return myTree

4.6运行结果

在这里插入图片描述

五、信息增益率算法实现

5.1改动计算信息增益的函数为信息增益率的函数。

def calcEntropy(dataSet):
  # 返回数据集的行数
    numEntires = len(dataSet) 
    labelCounts = {} 
    for featVec in dataSet:
        currentLabel = featVec[-1] 
         # 如果标签(Label)没有放入统计次数的字典,添加进去
        if currentLabel not in labelCounts.keys(): 
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1 
    Entropy= 0.0 
    for key in labelCounts:  
        prob = float(labelCounts[key]) / numEntires
      Entropy-= prob * log(prob, 2) 
    return Entropy

5.2运行结果

第0个特征的增益为0.052
第1个特征的增益为0.352
第2个特征的增益为0.433
第3个特征的增益为0.232
第0个特征的增益为0.164
第1个特征的增益为1.000
第2个特征的增益为0.340
{'有自己的房子': {0: {'有工作': {0: 'no', 1: 'yes'}}, 1: 'yes'}}
  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值