机器学习--决策树

一、决策树概述

1.1概述

决策树是一种非参数的监督学习方法,通过对训练集数据学习,挖掘一定规则用于对新的数据集进行预测,通俗来说,是if-then决策集合。目的是使样本尽可能属于同一类别,分类更准确,通过递归选择最优特征对数据集进行分割,使每个子集都有一个最优分类过程。通过特征选择,选择最佳特征,将数据集分割成正确分类的子集。

1.2组成

决策树的主要组成包括:

根节点(Root Node):决策树的起始节点,代表整个数据集。

内部节点(Internal Nodes):非叶子节点,表示一个特征属性及其对应的判断条件。

叶节点(Leaf Nodes):最终的输出节点,表示一个类别或者数值。

分裂条件(Splitting Criteria):决定在每个内部节点如何分裂数据集的条件,常见的包括基尼系数、信息增益、信息增益率等。

决策规则(Decision Rules):由决策树的结构和节点组成,用于对新样本进行分类或回归预测。

剪枝(Pruning):一种防止过拟合的技术,通过删除一些不必要的节点来简化树结构。

特征重要性(Feature Importance):衡量每个特征对模型预测的贡献程度。

深度(Depth):决策树的层数,表示树的复杂度和泛化能力。

1.3流程

决策树的一般流程

(1)收集数据:可以使用任何方法

(2)准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化

(3)分析数据:可以使用任何方法,构造树完成之后,我们应该检查图形是否符合预期

(4)训练算法:构造树的数据结构

(5)测试算法:使用经验树计算错误率

(6)使用算法:此步骤可以适用于任何监督学习算法,而使用决策树可以更好地理解数据的内在含义

二、决策树的构造算法

2.1 ID3(Iterative Dichotomiser 3)

信息增益的计算基于信息熵(Entropy)的概念,信息熵定义为信息的期望值,信息熵越大,则表明数据集的混乱程度越大。

信息熵: H(D)=−∑ni−1p(xi)log2p(xi)

信息增益 = 信息熵 - 条件熵:G(D,A)=H(D)−H(D|A)

条件熵:H(D|A)=∑ni=1|Di||D|H(Di)

2.2  C4.5(信息增益率)

C4.5算法最大的特点是克服了ID3对特征数目的偏重这一缺点,引入信息增益率来作为分类标准。

信息增益率=信息增益/特征本身的熵:

信息增益率对可取值较少的特征有所偏好(分母越小,整体越大),因此C4.5并不是直接用增益率最大的特征进行划分,而是使用一个启发式方法:先从候选划分特征中找到信息增益高于平均值的特征,再从中选择增益率最高的。

2.3基尼指数(CART)

决策树中的基尼指数是用来衡量节点的纯度或不纯度的指标之一。在构建决策树时,基尼指数通常用于选择最佳的划分特征,以便将数据集划分为纯度更高的子集。

基尼指数计算公式:Gini(D)=1−∑nk=1(pk)^2

基尼指数的取值范围在0到1之间,数值越低表示节点的纯度越高,即样本的类别分布越趋于一致;而数值越高则表示节点的不纯度越高,即样本的类别分布越杂乱。

三、决策树代码实战

对下面给出的数据构造出决策树

对下面给出的数据构造出决策树

编号  色泽  根蒂  敲声  纹理  脐部  触感 好瓜

0       青绿  蜷缩  浊响  清晰  凹陷  硬滑  是

1       乌黑  蜷缩  沉闷  清晰  凹陷  硬滑  是

2       乌黑  蜷缩  浊响  清晰  凹陷  硬滑  是

3       青绿  蜷缩  沉闷  清晰  凹陷  硬滑  是

4       浅白  蜷缩  浊响  清晰  凹陷  硬滑  是

5       青绿  稍蜷  浊响  清晰  稍凹  软粘  是

6       乌黑  稍蜷  浊响  稍糊  稍凹  软粘  是

7       乌黑  稍蜷  浊响  清晰  稍凹  硬滑  是

8       乌黑  稍蜷  沉闷  稍糊  稍凹  硬滑  否

9       青绿  硬挺  清脆  清晰  平坦  软粘  否

10     浅白  硬挺  清脆  模糊  平坦  硬滑  否

11     浅白  蜷缩  浊响  模糊  平坦  软粘  否

12     青绿  稍蜷  浊响  稍糊  凹陷  硬滑  否

13     浅白  稍蜷  沉闷  稍糊  凹陷  硬滑  否

14     乌黑  稍蜷  浊响  清晰  稍凹  软粘  否

15     浅白  蜷缩  浊响  模糊  平坦  硬滑  否

16     青绿  蜷缩  沉闷  稍糊  稍凹  硬滑  否

该数据集包含17个样本,每个样本有6个特征:

色泽:表示西瓜的外皮颜色,包括青绿、乌黑、浅白。
根蒂:表示西瓜的根部状况,有蜷缩、稍蜷、硬挺三种。
敲声:敲击西瓜时发出的声音,有浊响和沉闷两种。
纹理:西瓜表面的纹理,分为清晰和模糊两种。
脐部:西瓜脐部的形状,有凹陷和稍凹两种。
触感:摸到西瓜表面的感觉,有软粘和硬滑两种。

数据集如下: 

  1. 色泽:0表示青绿,1表示乌黑,2表示浅白。
  2. 根蒂:0表示蜷缩,1表示稍蜷,2表示硬挺。
  3. 敲声:0表示浊响,1表示沉闷。
  4. 纹理:0表示清晰,1表示模糊。
  5. 脐部:0表示凹陷,1表示稍凹,2表示平坦。
  6. 触感:0表示软粘,1表示硬滑。

data = [[0, 0, 0, 0, 0, 0],
        [1, 0, 1, 0, 0, 0],
        [1, 0, 0, 0, 0, 0],
        [0, 0, 1, 0, 0, 0],
        [2, 0, 0, 0, 0, 0],
        [0, 1, 0, 0, 1, 1],
        [1, 1, 0, 1, 1, 1],
        [1, 1, 0, 0, 1, 0],
        [1, 1, 1, 1, 1, 0],
        [0, 2, 2, 0, 2, 1],
        [2, 2, 2, 2, 2, 0],
        [2, 0, 0, 2, 2, 1],
        [0, 1, 0, 1, 0, 0],
        [2, 1, 1, 1, 0, 0],
        [1, 1, 0, 0, 1, 1],
        [2, 0, 0, 2, 2, 0],
        [0, 0, 1, 1, 1, 0]]

 3.1 使用ID3构造决策树

def entropy(data):
        # 计算数据集的总体熵
        labels = [row[-1] for row in data]
        label_counts = {}
        for label in labels:
                if label not in label_counts:
                        label_counts[label] = 0
                label_counts[label] += 1
        entropy = 0.0
        for count in label_counts.values():
                prob = count / len(data)
                entropy -= prob * math.log2(prob)
        return entropy

def split_data(data, column, value):
        # 根据特征列的值将数据集分割成子集
        sub_data = []
        for row in data:
                if row[column] == value:
                        sub_row = row[:column] + row[column +1:]
                        sub_data.append(sub_row)
        return sub_data

def information_gain(data, base_entropy, column):
        # 计算特征列的信息增益
        unique_values = set([row[column] for row in data])
        new_entropy = 0.0
        for value in unique_values:
                sub_data = split_data(data, column, value)
                prob = len(sub_data) / len(data)
                new_entropy += prob * entropy(sub_data)
        information_gain = base_entropy - new_entropy
        return information_gain

def choose_best_feature(data):
        # 选择信息增益最大的特征作为划分特征
        num_features = len(data[0]) - 1
        base_entropy = entropy(data)
        best_information_gain = 0.0
        best_feature = -1
        for i in range(num_features):
                info_gain = information_gain(data, base_entropy, i)
                if info_gain > best_information_gain:
                        best_information_gain = info_gain
                        best_feature = i
        return best_feature

def create_tree(data, labels):
        # 递归构造决策树
        class_list = [row[-1] for row in data]
        if class_list.count(class_list[0]) == len(class_list):
                # 如果所有样本属于同一类别,则返回该类别
                return class_list[0]
        if len(data[0]) == 1:
                # 如果所有特征已经使用完毕,则返回样本数最多的类别
                label_counts = {}
                for label in class_list:
                        if label not in label_counts:
                                label_counts[label] = 0
                        label_counts[label] += 1
                sorted_label_counts = sorted(label_counts.items(), key=lambda x: x[1], reverse=True)
                return sorted_label_counts[0][0]
        best_feature = choose_best_feature(data)
        best_feature_label = labels[best_feature]
        tree = {best_feature_label: {}}
        del(labels[best_feature])
        feature_values = [row[best_feature] for row in data]
        unique_values = set(feature_values)
        for value in unique_values:
                sub_labels = labels[:]
                tree[best_feature_label][value] = create_tree(split_data(data, best_feature, value), sub_labels)
        return tree

# 构造决策树
labels = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
tree = create_tree(data=Data, labels=labels)
print(tree)

运行结果为:{'脐部': {0: 0, 1: {'敲声': {0: {'色泽': {0: 1, 1: {'纹理': {0: {'触感': {0: 0, 1: 1}}, 1: 1}}}}, 1: 0}}, 2: {'色泽': {0: 1, 2: {'根蒂': {0: {'触感': {0: 0, 1: 1}}, 2: 0}}}}}}

3.2使用C4.5构造决策树

def entropy(data):
    # 计算数据集的总体熵
    labels = [row[-1] for row in data]
    label_counts = {}
    for label in labels:
        if label not in label_counts:
            label_counts[label] = 0
        label_counts[label] += 1
    entropy = 0.0
    for count in label_counts.values():
        prob = count / len(data)
        entropy -= prob * math.log2(prob)
    return entropy
 
 
def split_data(data, column, value):
    # 根据特征列的值将数据集分割成子集
    sub_data = []
    for row in data:
        if row[column] == value:
            sub_row = row[:column] + row[column + 1:]
            sub_data.append(sub_row)
    return sub_data
 
 
def information_gain_ratio(data, base_entropy, column):
    # 计算特征列的信息增益比
    unique_values = set([row[column] for row in data])
    new_entropy = 0.0
    split_info = 0.0
    for value in unique_values:
        sub_data = split_data(data, column, value)
        prob = len(sub_data) / len(data)
        new_entropy += prob * entropy(sub_data)
        split_info -= prob * math.log2(prob)
    if split_info == 0:
        return 0  # 避免出现除以零的情况
    information_gain_ratio = (base_entropy - new_entropy) / split_info
    return information_gain_ratio
 
 
def choose_best_feature(data):
    # 选择信息增益比最大的特征作为划分特征
    num_features = len(data[0]) - 1
    base_entropy = entropy(data)
    best_information_gain_ratio = 0.0
    best_feature = -1
    for i in range(num_features):
        info_gain_ratio = information_gain_ratio(data, base_entropy, i)
        if info_gain_ratio > best_information_gain_ratio:
            best_information_gain_ratio = info_gain_ratio
            best_feature = i
    return best_feature
 
def create_tree(data, labels):
    # 递归构造决策树
    class_list = [row[-1] for row in data]
    if class_list.count(class_list[0]) == len(class_list):
        # 如果所有样本属于同一类别,则返回该类别
        return class_list[0]
    if len(data[0]) == 1:
        # 如果所有特征已经使用完毕,则返回样本数最多的类别
        label_counts = {}
        for label in class_list:
            if label not in label_counts:
                label_counts[label] = 0
            label_counts[label] += 1
        sorted_label_counts = sorted(label_counts.items(), key=lambda x: x[1], reverse=True)
        return sorted_label_counts[0][0]
    best_feature_index = choose_best_feature(data)
    best_feature_label = labels[best_feature_index]
    decision_tree = {best_feature_label: {}}
    del (labels[best_feature_index])
    feature_values = [row[best_feature_index] for row in data]
    unique_values = set(feature_values)
    for value in unique_values:
        sub_labels = labels[:]
        decision_tree[best_feature_label][value] = create_tree(split_data(data, best_feature_index, value), sub_labels)
    return decision_tree
 
# 测试
labels = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感']
tree = create_tree(Data, labels)
print(tree)

 运行结果:{'脐部': {0: 0, 1: {'敲声': {0: {'色泽': {0: 1, 1: {'纹理': {0: {'触感': {0: 0, 1: 1}}, 1: 1}}}}, 1: 0}}, 2: {'色泽': {0: 1, 2: {'根蒂': {0: {'触感': {0: 0, 1: 1}}, 2: 0}}}}}}

四、总结

4.1 优点

速度快
准确度高
可处理连续字段和种类字段
无需领域知识和参数假设
适合高维数据

4.2 缺点

容易过拟合
忽略相关性
各类别样本数量不一致
特征选择偏向于取值较多的特征

综上所述,决策树算法是一种简单而有效的机器学习算法,但在实际应用中需要注意过拟合、数据准备和特征选择等问题。 

  • 16
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值