ID3决策树学习算法——python实现

本周“人工智能理论与实践”课程老师要求复现决策树算法,本人复现思路参考周志华老师的《机器学习》书中的决策树学习基本算法,数据集使用的是书中“西瓜数据集2.0”。

这是本人的第一篇博客,如有问题还请大家多多指正。完整代码及训练数据已上传至Github。GitHub - MYuan0209/Decision-Tree----ID3

目录

问题描述

决策树

基本概念

划分选择

核心代码

计算当前样本集合中每一类所占的样本比例

计算样本集合的信息熵

计算信息增益

产生决策树

预测样本

运行结果


问题描述

给定一个西瓜特征与是否为好瓜对应的表格,利用决策树判断是否为好瓜。

编号色泽根蒂敲声纹理脐部触感好瓜
1青绿蜷缩浊响清晰凹陷硬滑
2乌黑蜷缩沉闷清晰凹陷硬滑
3乌黑蜷缩浊响清晰凹陷硬滑
4青绿蜷缩沉闷清晰凹陷硬滑
5浅白蜷缩浊响清晰凹陷硬滑
6青绿稍蜷浊响清晰稍凹软粘
7乌黑稍蜷浊响稍糊稍凹软粘
8乌黑稍蜷浊响清晰稍凹硬滑
9乌黑稍蜷沉闷稍糊稍凹硬滑
10青绿硬挺清脆清晰平坦软粘
11浅白硬挺清脆模糊平坦硬滑
12浅白蜷缩浊响模糊平坦软粘
13青绿稍蜷浊响稍糊凹陷硬滑
14浅白稍蜷沉闷稍糊凹陷硬滑
15乌黑稍蜷浊响清晰稍凹软粘
16浅白蜷缩浊响模糊平坦硬滑
17青绿蜷缩沉闷稍糊稍凹硬滑

决策树

基本概念

决策树是一类常见的机器学习方法,其本质是通过树的结构来完成二分类任务。

决策树一般包含一个根节点,若干个内部节点和若干个叶子节点。其中叶子节点对应的是决策结果,其它每个节点对应的是一个属性测试。每个节点包含的样本集合根据属性测试的结果被划分到其子节点中,从根节点到每个叶子节点的路径对应着一个判定序列。

划分选择

我们如何选择属性测试的顺序从而达到最优划分?一般而言,我们希望每次划分后能够让尽可能多的样本属于同一类别。ID3决策树学习算法是以信息增益为准则来选择划分属性。

假定当前样本集合D中第k类样本所占的比例为p_{k}(k = 1, 2, ..., \left | y \right |),则D的信息熵定义为

Ent(D) = -\sum_{k=1}^{\left | y \right |}p_{k}log_{2}p_{k} .

假定离散属性a有V个可能的取值{a^{1}, a^{2}, ..., a^{V}},若使用a来对样本集合D进行划分,则会产生V个分支节点,其中第v个分支节点包含了D中所有在属性a上取值为a^{v}的样本,记为D^{v}。属性a对样本集合D进行划分所获得的信息增益为

Gain(D, a) = Ent(D) - \sum_{v=1}^{n}\frac{\left | D^{v} \right |}{\left | D \right |}Ent(D^{v}).

一般而言,信息增益越大,则代表着使用属性a来进行划分后样本尽可能多的属于同一个类别。ID3决策树学习算法使用的选择属性公式为

a_{*} = argmax Gain(D, a), a\in A.

核心代码

计算当前样本集合中每一类所占的样本比例

def probability(x):
    """
    求当前样本集合D中每一类样本所占的比例
    :param x:
    :return:
    """
    return [x[val == x].size / x.size for val in numpy.unique(x)]

计算样本集合的信息熵

​
def information_entropy(attribute):
    """
    求样本集合x的信息熵
    :param attribute: 结果 -> array
    :return: 结果的信息熵 -> float
    """
    p = probability(attribute)
    return -numpy.sum(p * numpy.log2(p))

​

计算信息增益

def conditional_information_entropy(data, attribute):
    """
    求条件信息熵
    :param data: 输入 -> array
    :param attribute: 结果 -> array
    :return: 条件信息熵 -> array
    """
    return [numpy.sum([data[val == data].size / data.size * information_entropy(attribute[val == data])
                       for val in numpy.unique(data)]) for data in data.T]


def information_gain(data, attribute):
    """
    求信息增益
    :param data: 输入 -> array
    :param attribute: 结果 -> array
    :return: 每一列的信息增益 -> array
    """
    ent = information_entropy(attribute)
    ce = numpy.array(conditional_information_entropy(data, attribute))
    return ent - ce

产生决策树

def tree_generate(data, attribute, label):
    """
    产生决策树
    :param data: 输入 -> array
    :param attribute: 结果 -> array
    :param label: 列标签 -> Index
    :return: 决策树 -> dict
    """
    # 如果输入全属于同一类别或所有输入的结果取值相同,直接返回结果
    p = probability(attribute)
    if data.shape[1] == 1 or p[0] == 1:
        return attribute[numpy.argmax(p)]
    # 获得最大信息增益所对应的序号
    nodes = information_gain(data, attribute)
    max_index = nodes.argmax()
    # 建立树的根节点
    node = label[max_index]
    edge = {}
    tree = {node: edge}
    # 递归建立树的有向边
    cross_flag = numpy.ones(label.size, dtype=bool)
    cross_flag[max_index] = False
    for value in numpy.unique(data.T[max_index]):
        row_flag = value == data.T[max_index]
        edge[value] = tree_generate(data[row_flag][:, cross_flag], attribute[row_flag], label[cross_flag])
    return tree

预测样本

def predict(tree, character, label):
    """
    预测函数
    :param tree: 决策树 -> dict
    :param character: 特征向量 -> array
    :param label: 列标签 -> Index
    :return: 预测结果
    """
    root = tree
    # 从根节点搜索到叶子节点
    while isinstance(root, dict):
        # 获得节点值对应的列标签序号
        node = [k for k in root.keys()][0]
        index = numpy.where(label == node)[0][0]
        # 将根节点改为对应序号的子节点
        edge = root[node]
        root = edge[character[index]]
    return root

运行结果

  • 12
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值