机器学习 - [源码实现决策树小专题]决策树中,信息增益、信息增益率计算以及最佳特征挑选的Python实现

简介: 机器学习 - [源码实现决策树小专题]决策树中,信息增益、信息增益率计算以及最佳特征挑选的Python实现

信息增益、信息增益率计算 以及 最佳特征挑选 的Python实现

导读:决策树是一种基于信息的学习算法。在决策树算法中需要不断地挑选出最佳特征,而挑选最佳特征地依据就是信息增益率

增益本身就具有相对地特性,表征某事物从一个状态到另一个状态后,某个指标的变化量。

在决策树算法中,信息增益指的是依据某个特征的取值划分数据集时数据集划分后相对于划分前,所能导致减少的信息不确定度

这也就是说信息增益即不确定度的降低值。当我们以信息熵(香浓熵,简称)作为不确定性的度量时,以数据集划分前的原始熵减去数据集划分后的剩余熵得到的值就是信息增益


1. 求解信息增益

1.1 已经准备好的接口

(1)划分数据集函数(仅展示接口,具体内容请参阅【博文1】)

def dividing_data_set(date_set,node_feature,node_feature_value):
    """
    划分数据集
    整个划分方法的思想是"记录索引-重索引"。简而言之就是先记住特征取值为指定取值的索引号,然
    后依据记录索引号保对其它特征下同索引号的元素进行保留。最终实现留下当前划分数据条的目的。
    Parameters
    ----------
    date_set: "dict"结构的数据集,其中键为”labels“的键值对对应为标签集(源于x_train),其余
               的对应为特征取值键值对(源于y_train)。
    node_feature:可以是num、str等类型,但是必须和date_set中的键的类型保持一致。表示需要划分
               数据集的节点处对应的特征名。
    node_feature_value:是对应与 node_feature 的一个特定取值。
    Returns
    -------
    result : dict
        返回子数据集字典,其形式与date_set保持一致。其中键`labels`对应的值类似是子标签集数组。
    """

(2)混杂度求取函数(仅展示接口,具体内容请参阅【博文2】)

def impurity(anArray, impurity_t="entropy"):
    """
    计算混杂度
    Parameters
    ----------
    impurity_t:  str,表示混杂度的度量方式,只能是{"entropy","gini"}中的一个。
    anArray:     an Array like object,由某特征依次对应每条数据下的取值构成。
    Return
    result: float
        为计算得到的impurity的数值。
    """

1.2 使用实例讲解

这里采用【博文1】中的例子:

import numpy as np
# 定义模拟数据
x_train = np.array([[1, 4, 2, 0, 3, 1, 1, 0, 1, 4, 2, 4, 4, 2, 4, 2, 0, 2, 2, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 3, 1, 3, 1, 3, 1, 1, 0, 1, 4, 3, 4, 4, 2],
       [0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 3, 0, 3, 1, 1, 0, 0, 4, 2, 2, 4, 1, 1, 0, -1, 0, 4, 0, -1, -1, 0, 0, 0, 0, 0, 0, 2],
       [4, 2, 2, 3, 1, 2, 1, 1, 0, 2, 1, 1, 1, 0, 3, 0, 3, 2, 2, 0, 0, 0, 0, 3, 1, 1, 2, 3, 4, 3, 1, 1, 3, 1, 2, 1, 1, 0, 1, 2, 2, 1, 0],
       [1, 4, 2, 2, 3, 1, 1, 0, 0, 2, 1, 1, 1, 0, 3, 4, 2, 2, 4, 1, 0, 1, 0, 3, 2, 2, 4, 3, 1, 2, -1, 2, 2, 1, 0, 1, -1, 0, 1, 1, 1, 0, 0],
       [1, 2, 2, 1, 3, 1, 1, 0, 0, 2, 2, 1, 1, 0, 0, 4, 1, 2, 1, 0, 0, 0, 0, 2, 1, 1, 2, 3, 3, 0, -1, 2, 1, 3, 1, 1, 0, 0, 2, 3, 2, 1, 0],
       [1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 3, 3, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 3, 2, 4, 2, 2, -1, 2, 2, 3, 0, 0, 0, 0, 2, 2, 2, 2, 0],
       [1, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 3, 2, 1, 1, 0, 2, 2, 1, 0, 3, 3, 2, 1, 1, 3, 0, -2, -1, -1, 0, 1, 0, 2, 2, 1],
       [0, 0, 3, 3, 2, 0, 0, 0, 0, 3, 3, 3, 0, 2, 1, 3, 3, 3, 2, 1, 1, 0, 0, 3, 4, 4, 1, 2, 1, 0, 1, 2, 2, 1, -1, -1, 0, 0, 2, 1, 2, 1, 2],
       [2, 4, 2, 0, 2, 1, 0, 1, 0, 2, 2, 3, 4, 2, 2, 3, 0, 2, 0, 1, 1, 0, 0, 0, 3, 3, 0, 4, 2, 2, 1, 3, 1, 4, 0, -1, 1, 0, 3, 1, 2, 4, 0],
       [1, 4, 1, 0, 1, 0, 0, 0, 0, 3, 2, 3, 3, 4, 4, 1, 0, 1, 0, 1, 0, 1, 0, 0, 2, 1, 4, 2, 0, 4, 1, 3, 1, 3, -1, 0, -1, 0, 3, 2, 3, 2, 3]],)
y_train = np.array([1, 0, 0, 1, 0, 0, 1, 0, 0, 1]) 
features = ["feature_"+str(i) for i in range(43)]  # 产生43个不同的特征名字
# 转换为数据集字典
date_set = dict(zip(features,x_train.T))
date_set.update({"labels":y_train})      # 将标签集(labels,也就是输出y们)也加入数据集

在【博文1】中,我们是假设了"feature_13"最为node_feature,也就是第一个”最佳特征“。但是当时只是假设的,并不是计算得出。到了本文,我的的任务就是要算出所有节点划分数据集前后的信息增益,取其最大者为真实的最佳特征。

不过我们仍然可以以"feature_13"为例,计算"feature_13"在划分前后的信息增益。

1.3 信息增益计算的实现

计算数据集划分前labels的熵作为划分前的熵,数据集划分后各个子数据集labels熵的和作为数据集划分后的熵。以此直接求取信息增益。

def gain(impurity_t, impurity_before_divide, data_set, probable_feature):
    """
    计算信息增益
    需要传入数据集划分前的不纯度、划分数据集所依赖特征对应的取值数组。考虑到在同一个节点测试不同子特征增益时都有用
    到划分前的不纯度,为了提升运行效率故在gain()外计算好该节点分裂前的不纯度后再传入gain()函数。其中数据集划分前的
    熵就是划分前的标签集labels的熵。其中按某特征划分后的不确定度,为依该特征各个取值划分的子数据集的中的标签集(即
    该特征划分完后所有的子标签集)的不确定度总和。
    Parameters
    ----------
    impurity_t:              str,不纯度的度量方式,只能是{"entropy","gini"}中的一个。
    impurity_before_divide:  float,表示数据集划分前的不纯度。            
    data_set:               dict,划分前的数据集。
    probable_feature:        str,用于划分数据集的特征。
    Return
    ------
    result:      float,表征信息增益值。
    """
    impurity_after_divide = 0                             # 初始化数据集划分后的不存度为0
    for value in set(date_set[probable_feature]):         # 获取该特征所有的取值并使用集合去重,遍历之
        one_sublabel_array = dividing_data_set(           # 获取该子数据集中的标签集数组
            date_set = date_set, 
            node_feature = probable_feature,
            node_feature_value = value
        )['labels']
        impurity_after_divide = impurity(one_sublabel_array,impurity_t) # 累加每个子数据标签集的不存度
    return impurity_before_divide - impurity_after_divide               # 做差得到这个特征的增益并返回
impurity_t = "entropy"                                               # 使用信息熵度量混杂度              
impurity_before_divide = impurity(date_set["labels"],impurity_t)     # 数据集划分前labels的混杂度
probable_feature = "feature_13"        # 假设当前划分数据集用该特征
gain(impurity_t, impurity_before_divide, date_set, probable_feature)

Out[i]: 0.9709505944546686

2. 使用信息增益存在的问题与信息增益率

由于取值越连续则不确定度越大,直接使用信息增益往往容易导致取值数量越多的特征则越容易被挑选出来。为了平衡特征取值趋于连续带来的影响,我们使用信息增益率作为信息增益的替代方案以选取最佳特征。

改为求解信息增益率的算法如下:

def gain_rate(impurity_t, impurity_before_divide, data_set, probable_feature):
    """
    计算信息增益率
    相对于信息增益的计算,信息增益率还要求解出由于该特征的不同取值带来的不确度。
     - 若由于特征取值带来的不确定度为0,说明无特征取值连续化影响,直接返回信息增益;
     - 若特征取值带来的不确定度不是0,则使用信息增益除以特征取证带来的不确定度。
    Parameters
    ----------
    impurity_t:              str,不纯度的度量方式,只能是{"entropy","gini"}中的一个。
    impurity_before_divide:  float,表示数据集划分前的不纯度。            
    data_set:               dict,划分前的数据集。
    probable_feature:        str,用于划分数据集的特征。
    Return
    ------
    result:      float,表征信息增益值。
    """
    impurity_after_divide = 0                             # 初始化数据集划分后的不存度为0
    for value in set(date_set[probable_feature]):         # 获取该特征所有的取值并使用集合去重,遍历之
        one_sublabel_array = dividing_data_set(           # 获取该子数据集中的标签集数组
            date_set = date_set, 
            node_feature = probable_feature,
            node_feature_value = value
        )['labels']
    impurity_after_divide = impurity(one_sublabel_array,impurity_t)     # 累加每个子数据标签集的不存度
    gain = impurity_before_divide - impurity_after_divide               # 做差得到这个特征的增益
    feature_impurity = impurity(data_set[probable_feature],impurity_t)
    gain_rate = gain/feature_impurity if feature_impurity > 0 else gain
    return gain_rate
impurity_t = "entropy"                                               # 使用信息熵度量混杂度              
impurity_before_divide = impurity(date_set["labels"],impurity_t)     # 数据集划分前labels的混杂度
probable_feature = "feature_13"        # 假设当前划分数据集用该特征
gain_rate(impurity_t, impurity_before_divide, date_set, probable_feature)

Out[i]:0.7134285408041596


3. 基于信息增益率的最佳特征挑选

这是本节最为简单的部分了,需要完成的工作包括:

  • 获取当前节点处所有的特征;
  • 依次假设每一个特征就是当前节点处分裂的最佳特征,划分数据集从而计算出这些特征各自再划分前后的信息增益率;
  • 比较:选取上一步中,实际划分前后信息增益率最大者作为当前节点处的最佳特征返回之。

实现代码如下:

def best_feature(impurity_t,date_set):
    """
    求取节点处的最佳特征
    Parameters
    ----------
    date_set:    dict,与某个节点处的对应的数据集
    Return
    ------
    result:     str,数据集date_set所属节点处可用于分裂的最佳特征
    """
    features = [i for i in date_set if i != "labels"]                   # 获取数据集中当前节点处所有特征
    impurity_before_divide = impurity(date_set["labels"],impurity_t)    # 数据集划分前labels的混杂度
    max_gain_rate = -1          # 不会小于0,因此随便给个负数初始值
    the_best_feature = ""
    for probable_feature in features:
        rate = gain_rate(impurity_t, impurity_before_divide, date_set, probable_feature)
        if rate > max_gain_rate:
            max_gain_rate = rate
            the_best_feature = probable_feature
    return the_best_feature
impurity_t = "entropy"   
best_feature(impurity_t,date_set)

Out[i]:‘feature_8’

目录
相关文章
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
241 4
|
11月前
|
算法 Java Python
使用Python来绘制樱花树
本文以林徽因的《你是人间的四月天》为引,将春日意象与现代职场编程艺术结合,通过Python的Turtle模块绘制分形树和花瓣图案。文章详细解析了Turtle模块的使用方法、递归算法及随机性在图形生成中的应用,展示了如何用代码创造自然美感。核心代码包含tree函数(绘制分形树)和petal函数(绘制花瓣),最终生成一幅生动的春日画卷。项目不仅帮助读者掌握Turtle绘图技巧,更激发对编程艺术的兴趣,鼓励探索数字世界的无限可能。
332 5
|
机器学习/深度学习 自然语言处理 JavaScript
信息论、机器学习的核心概念:熵、KL散度、JS散度和Renyi散度的深度解析及应用
在信息论、机器学习和统计学领域中,KL散度(Kullback-Leibler散度)是量化概率分布差异的关键概念。本文深入探讨了KL散度及其相关概念,包括Jensen-Shannon散度和Renyi散度。KL散度用于衡量两个概率分布之间的差异,而Jensen-Shannon散度则提供了一种对称的度量方式。Renyi散度通过可调参数α,提供了更灵活的散度度量。这些概念不仅在理论研究中至关重要,在实际应用中也广泛用于数据压缩、变分自编码器、强化学习等领域。通过分析电子商务中的数据漂移实例,展示了这些散度指标在捕捉数据分布变化方面的独特优势,为企业提供了数据驱动的决策支持。
1873 2
信息论、机器学习的核心概念:熵、KL散度、JS散度和Renyi散度的深度解析及应用
|
存储 大数据 索引
解锁Python隐藏技能:构建高效后缀树Suffix Tree,处理大数据游刃有余!
通过构建高效的后缀树,Python程序在处理大规模字符串数据时能够游刃有余,显著提升性能和效率。无论是学术研究还是工业应用,Suffix Tree都是不可或缺的强大工具。
214 6
|
存储 算法 数据挖掘
高效文本处理新纪元:Python后缀树Suffix Tree,让数据分析更智能!
在大数据时代,高效处理和分析文本信息成为关键挑战。后缀树作为一种高性能的数据结构,通过压缩存储字符串的所有后缀,实现了高效的字符串搜索、最长公共前缀查询等功能,成为文本处理的强大工具。本文探讨Python中后缀树的应用,展示其在文本搜索、重复内容检测、最长公共子串查找、文本压缩及智能推荐系统的潜力,引领数据分析迈入新纪元。虽然Python标准库未直接提供后缀树,但通过第三方库或自定义实现,可轻松利用其强大功能。掌握后缀树,即掌握开启文本数据宝藏的钥匙。
231 5
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
327 2
|
存储 算法 搜索推荐
Python进阶必备:字典树Trie与后缀树Suffix Array,效率提升的神器!
在Python编程中,掌握高效的数据结构对于提升程序性能至关重要。本文将深入探讨两种强大的字符串处理数据结构——字典树(Trie)与后缀数组(Suffix Array)。字典树,又称前缀树,适用于自动补全和拼写检查等功能。例如,在文本编辑器中实现自动补全时,字典树能够即时提供单词补全选项。后缀数组则用于存储字符串的所有后缀并按字典序排序,结合最长公共前缀(LCP)数组,可以高效解决许多字符串问题,如查找最长重复子串等。通过实际案例,我们将展示这两种数据结构的强大功能,帮助你在Python编程中更进一步。
334 2
|
存储 开发者 Python
从理论到实践:Python中Trie树与Suffix Tree的完美结合,开启编程新篇章!
在编程领域,高效的数据结构对于解决问题至关重要。本文通过一个案例分析,介绍如何在Python中结合使用Trie树(前缀树)和Suffix Tree(后缀树)。案例聚焦于开发具备高效拼写检查和文本相似度检测功能的文本编辑器。首先,通过构建Trie树快速检查单词是否存在;接着,利用Suffix Tree检测文本相似度。尽管Python标准库未直接提供Suffix Tree,但可通过第三方库或自定义实现。本文展示了高级数据结构在实际应用中的强大功能,并强调了理论与实践相结合的重要性。
213 1
|
存储 算法 Python
逆袭之路:掌握Python字典树Trie与后缀树,成为技术圈的耀眼新星!
在编程的征途上,每个人都渴望成为那个能够独当一面、解决复杂问题的技术高手。而掌握高级数据结构,如字典树(Trie)与后缀树(Suffix Tree),无疑是你逆袭路上的重要一步。这些数据结构不仅能够提升你的编码技能,还能让你在解决特定问题时游刃有余,从而在技术圈中脱颖而出,成为那颗耀眼的新星。
218 1
|
存储 算法 索引
从菜鸟到大神:一文带你彻底搞懂Python中的后缀树Suffix Tree奥秘!
在Python编程中,后缀树是一种高效的数据结构,特别适用于处理复杂的字符串问题,如搜索、最长公共前缀查询及最长重复子串查找等。本文通过问答形式介绍后缀树的基本概念、重要性及其实现方法。后缀树能显著提高字符串处理效率,将传统方法的时间复杂度从O(nm)降至接近O(m)。尽管其构建过程较复杂,但通过手动编写代码或使用第三方库,我们可以在Python中实现这一强大工具。后缀树的应用广泛,涵盖字符串搜索、压缩、生物信息学等多个领域,学习它不仅能帮助解决实际问题,更能提升算法思维和数据结构设计能力。
485 1

推荐镜像

更多