基于Python实现的决策树模型

决策树模型
目录
人工智能第五次实验报告 1
决策树模型 1
一 、问题背景 1
1.1 监督学习简介 1
1.2 决策树简介 1
二 、程序说明 3
2.1 数据载入 3
2.2 功能函数 3
2.3 决策树模型 4
三 、程序测试 5
3.1 数据集说明 5
3.2 决策树生成和测试 6
3.3 学习曲线评估算法精度 7
四 、实验总结 8
附 录 – 程序代码 8
一 、问题背景
1.1监督学习简介
机器学习的形式包括无监督学习,强化学习,监督学习和半监督学习;学习任务有分类、聚类和回 归等。
监督学习通过观察”输入—输出”对,学习从输入到输出的映射函数。分类监督学习的训练集为标记 数据,本文转载自http://www.biyezuopin.vip/onews.asp?id=16720每一条数据有对应的”标签”,根据标签可以将数据集分为若干个类别。分类监督学习经训练集生 成一个学习模型,可以用来预测一条新数据的标签。
常见的监督学习模型有决策树、KNN算法、朴素贝叶斯和随机森林等。
1.2决策树简介
决策树归纳是一类简单的机器学习形式,它表示为一个函数,以属性值向量作为输入,返回一个决策。
决策树的组成
决策树由内节点上的属性值测试、分支上的属性值和叶子节点上的输出值组成。

import numpy as np
from matplotlib import pyplot as plt
from math import log
import pandas as pd
import pydotplus as pdp

"""
19335286 郑有为
人工智能作业 - 实现ID3决策树
"""

nonce = 0  # 用来给节点一个全局ID
color_i = 0
绘图时节点可选的颜色, 非叶子节点是蓝色的, 叶子节点根据分类被赋予不同的颜色
color_set = ["#AAFFDD", "#DDAAFF", "#DDFFAA", "#FFAADD", "#FFDDAA"]

载入汽车数据, 判断顾客要不要买
class load_car:
    # 在表格中,最后一列是分类结果
    # feature_names: 属性名列表
    # target_names: 标签(分类)名
    # data: 属性数据矩阵, 每行是一个数据, 每个数据是每个属性的对应值的列表
    # target: 目标分类值列表
    def __init__(self):
        df = pd.read_csv('../dataset/car/car_train.csv')
        labels = df.columns.values
        data_array = np.array(df[1:])
        self.feature_names = labels[0:-1]
        self.target_names = labels[-1]
        self.data = data_array[0:,0:-1]
        self.target = data_array[0:,-1]

载入蘑菇数据, 鉴别蘑菇是否有毒
class load_mushroom:
    # 在表格中, 第一列是分类结果: e 可食用; p 有毒.

    # feature_names: 属性名列表
    # target_names: 标签(分类)名
    # data: 属性数据矩阵, 每行是一个数据, 每个数据是每个属性的对应值的列表
    # target: 目标分类值列表
    def __init__(self):
        df = pd.read_csv('../dataset/mushroom/agaricus-lepiota.data')
        data_array = np.array(df)
        labels = ["edible/poisonous", "cap-shape", "cap-surface", "cap-color", "bruises", "odor", "gill-attachment",
                  "gill-spacing", "gill-size", "gill-color", "stalk-shape", "stalk-root", "stalk-surface-above-ring",
                  "stalk-surface-below-ring", "stalk-color-above-ring", "stalk-color-below-ring",
                  "veil-type", "veil-color", "ring-number", "ring-type", "spore-print-color", "population", "habitat"]
        self.feature_names = labels[1:]
        self.target_names = labels[0]
        self.data = data_array[0:,1:]
        self.target = data_array[0:,0]

创建一个临时的子数据集, 在划分测试集和训练集时使用
class new_dataset:
    # feature_names: 属性名列表
    # target_names: 标签(分类)名
    # data: 属性数据矩阵, 每行是一个数据, 每个数据是每个属性的对应值的列表
    # target: 目标分类值列表
    def __init__(self, f_n, t_n, d, t):
        self.feature_names = f_n
        self.target_names = t_n
        self.data = d
        self.target = t

计算熵, 熵的数学公式为: $H(V) = - \sum_{k} P(v_k) \log_2 P(v_k)$
       其中 P(v_k) 是随机变量 V 具有值 V_k 的概率
target: 分类结果的列表, return: 信息熵
def get_h(target):
    target_count = {}
    for i in range(len(target)):
        label = target[i]
        if label not in target_count.keys():
            target_count[label] = 1.0
        else:
            target_count[label] += 1.0
    h = 0.0
    for k in target_count:
        p = target_count[k] / len(target)
        h -= p * log(p, 2)
    return h

取数据子集, 选择条件是原数据集中的属性 feature_name 值是否等于 feature_value
注: 选择后会从数据子集中删去 feature_name 属性对应的一列
def get_subset(dataset, feature_name, feature_value):
    sub_data = []
    sub_target = []
    f_index = -1
    for i in range(len(dataset.feature_names)):
        if dataset.feature_names[i] == feature_name:
            f_index = i
            break

    for i in range(len(dataset.data)):
        if dataset.data[i][f_index] == feature_value:
            l = list(dataset.data[i][:f_index])
            l.extend(dataset.data[i][f_index+1:])
            sub_data.append(l)
            sub_target.append(dataset.target[i])

    sub_feature_names = list(dataset.feature_names[:f_index])
    sub_feature_names.extend(dataset.feature_names[f_index+1:])
    return new_dataset(sub_feature_names, dataset.target_names, sub_data, sub_target)

寻找并返回信息收益最大的属性划分
信息收益值划分该数据集前后的熵减
计算公式为: Gain(A) = get_h(ori_target) - sum(|sub_target| / |ori_target| * get_h(sub_target))$
def best_spilt(dataset):

    base_h = get_h(dataset.target)
    best_gain = 0.0
    best_feature = None
    for i in range(len(dataset.feature_names)):
        feature_range = []
        for j in range(len(dataset.data)):
            if dataset.data[j][i] not in feature_range:
                feature_range.append(dataset.data[j][i])

        spilt_h = 0.0
        for feature_value in feature_range:
            subset = get_subset(dataset, dataset.feature_names[i], feature_value)
            spilt_h += len(subset.target) / len(dataset.target) * get_h(subset.target)

        if best_gain <= base_h - spilt_h: best_gain="base_h" spilt_h best_feature="dataset.feature_names[i]" return # 返回数据集中一个数据最可能的标签 def vote_most(dataset): target_range="{}" best_target="None" best_vote="0" for t in dataset.target: if not target_range.keys(): target_range[t]="1" else: +="1"> best_vote:
            best_vote = target_range[t]
            best_target = t

    return best_target

&#x8FD4;&#x56DE;&#x6D4B;&#x8BD5;&#x7684;&#x6B63;&#x786E;&#x7387;
predict_result: &#x9884;&#x6D4B;&#x6807;&#x7B7E;&#x5217;&#x8868;, target_result: &#x5B9E;&#x9645;&#x6807;&#x7B7E;&#x5217;&#x8868;
def accuracy_rate(predict_result, target_result):
    # print("Predict Result: ", predict_result)
    # print("Target Result:  ", target_result)
    accuracy_score = 0
    for i in range(len(predict_result)):
        if predict_result[i] == target_result[i]:
            accuracy_score += 1
    return accuracy_score / len(predict_result)

&#x51B3;&#x7B56;&#x6811;&#x7684;&#x8282;&#x70B9;&#x7ED3;&#x6784;
class dt_node:

    def __init__(self, content, is_leaf=False, parent=None):
        global nonce
        self.id = nonce # &#x4E3A;&#x8282;&#x70B9;&#x8D4B;&#x4E88;&#x4E00;&#x4E2A;&#x5168;&#x5C40;ID, &#x76EE;&#x7684;&#x662F;&#x65B9;&#x4FBF;&#x753B;&#x56FE;
        nonce += 1
        self.feature_name = None
        self.target_value = None
        self.vote_most = None # &#x8BB0;&#x5F55;&#x5F53;&#x524D;&#x8282;&#x70B9;&#x6700;&#x53EF;&#x80FD;&#x7684;&#x6807;&#x7B7E;
        if not is_leaf:
            self.feature_name = content # &#x975E;&#x53F6;&#x5B50;&#x8282;&#x70B9;&#x7684;&#x5C5E;&#x6027;&#x540D;
        else:
            self.target_value = content # &#x53F6;&#x5B50;&#x8282;&#x70B9;&#x7684;&#x6807;&#x7B7E;

        self.parent = parent
        self.child = {} # &#x4EE5;&#x5F53;&#x524D;&#x8282;&#x70B9;&#x7684;&#x5C5E;&#x6027;&#x5BF9;&#x5E94;&#x7684;&#x5C5E;&#x6027;&#x503C;&#x4F5C;&#x4E3A;&#x952E;&#x503C;

&#x51B3;&#x7B56;&#x6811;&#x6A21;&#x578B;
class dt_tree:

    def __init__(self):
        self.tree = None # &#x51B3;&#x7B56;&#x6811;&#x7684;&#x6839;&#x8282;&#x70B9;
        self.map_str = """
            digraph demo{
            node [shape=box, style="rounded", color="black", fontname="Microsoft YaHei"];
            edge [fontname="Microsoft YaHei"];
            """ # &#x7528;&#x4E8E;&#x4F5C;&#x56FE;: pydotplus &#x683C;&#x5F0F;&#x7684;&#x6811;&#x56FE;&#x751F;&#x6210;&#x4EE3;&#x7801;&#x7ED3;&#x6784;
        self.color_dir = {} # &#x7528;&#x4E8E;&#x4F5C;&#x56FE;: &#x53F6;&#x5B50;&#x8282;&#x70B9;&#x53EF;&#x9009;&#x989C;&#x8272;, &#x4EE5;&#x6807;&#x7B7E;&#x503C;&#x4E3A;&#x952E;&#x503C;

    # &#x8BAD;&#x7EC3;&#x6A21;&#x578B;, train_set: &#x8BAD;&#x7EC3;&#x96C6;
    def fit(self, train_set):

        if len(train_set.target) <= 0: # 如果测试集数据为空, 则返回空节点, 结束递归 return none target_all_same="True" for i in train_set.target: if !="train_set.target[0]:" break target_all_same: 如果测试集数据中所有数据的标签相同, 则构造叶子节点, node="dt_node(train_set.target[0]," is_leaf="True)" self.tree="=" none: 如果根节点为空,则让该节点成为根节点 用于作图, 更新 map_str 内容, 为树图增加一个内容为标签值的叶子节点 node_content="&#x6807;&#x7B7E;&#xFF1A;" + str(node.target_value) self.map_str str(node.id) "[label="\""" "\", fillcolor="\""" self.color_dir[node.target_value] style="filled]\n"" elif len(train_set.feature_names)="=" 如果测试集待考虑属性为空, 这里让叶子结点的标签为概率上最可能的标签 self.color_dir[vote_most(train_set)]="color_set[0]" else: 普通情况, 构建一个内容为属性的非叶子节点 best_feature="best_spilt(train_set)" 寻找最优划分属性, 作为该结点的值 best_feature_index="-1" range(len(train_set.feature_names)): train_set.feature_names[i]="=" best_feature: node.vote_most="vote_most(train_set)" 初始化叶子节点可选颜色 range(len(train_set.target)): train_set.target[i] not self.color_dir: global color_i self.color_dir[train_set.target[i]]="color_set[color_i]" %="len(color_set)" feature_range="[]" 获取该属性出现在数据集中的可选属性值 t train_set.data: t[best_feature_index] feature_range: feature_range.append(t[best_feature_index]) 用于做图, 创建一个内容为属性的非叶子节点 node.feature_name feature_value subset="get_subset(train_set," best_feature, feature_value) 获取每一个子集 node.child[feature_value]="self.fit(subset)" 递归调用 fit 函数生成子节点 如果创建的子节点为空, 则创建一个叶子节点作为其子节点, 其中标签值为概率上最可能的标签 node.child[feature_value].parent="node" 创建当前节点到所有子节点的连线 " -> " + "id" + str(node.child[feature_value].id) + "[label=\"" + str(feature_value) + "\"]\n"

            # print("Rest Festure: ", train_set.feature_names)
            # print("Best Feature: ", best_feature_index, best_feature, "Feature Range: ", feature_range)
            # for feature_value in feature_range:
            #     print("Child[", feature_value, "]: ", node.child[feature_value].feature_name, node.child[feature_value].target_value)
            return node

    # &#x6D4B;&#x8BD5;&#x6A21;&#x578B;, &#x5BF9;&#x6D4B;&#x8BD5;&#x96C6; test_set &#x8FDB;&#x884C;&#x9884;&#x6D4B;
    def predict(self, test_set):
        test_result = []
        for test in test_set.data:
            node = self.tree # &#x4ECE;&#x6839;&#x8282;&#x70B9;&#x4E00;&#x53EA;&#x5F80;&#x4E0B;&#x627E;, &#x77E5;&#x9053;&#x5230;&#x8FBE;&#x53F6;&#x5B50;&#x8282;&#x70B9;
            while node.target_value == None:
                feature_name_index = -1
                for i in range(len(test_set.feature_names)):
                    if test_set.feature_names[i] == node.feature_name:
                        feature_name_index = i
                        break
                if test[feature_name_index] not in node.child.keys():
                    break
                else:
                    node = node.child[test[feature_name_index]]

            if node.target_value == None:
                test_result.append(node.vote_most)
            else: # &#x5982;&#x679C;&#x6CA1;&#x6709;&#x5230;&#x8FBE;&#x53F6;&#x5B50;&#x8282;&#x70B9;, &#x5219;&#x53D6;&#x6700;&#x540E;&#x5230;&#x8FBE;&#x8282;&#x70B9;&#x6982;&#x7387;&#x4E0A;&#x6700;&#x53EF;&#x80FD;&#x7684;&#x6807;&#x7B7E;&#x4E3A;&#x76EE;&#x6807;&#x503C;
                test_result.append(node.target_value)

        return test_result

    # &#x8F93;&#x51FA;&#x6811;, &#x751F;&#x6210;&#x56FE;&#x7247;, path: &#x56FE;&#x7247;&#x7684;&#x4F4D;&#x7F6E;
    def show_tree(self, path="demo.png"):
        map = self.map_str + "}"
        print(map)
        graph = pdp.graph_from_dot_data(map)
        graph.write_png(path)

&#x5B66;&#x4E60;&#x66F2;&#x7EBF;&#x8BC4;&#x4F30;&#x7B97;&#x6CD5;&#x7CBE;&#x5EA6; dataset: &#x6570;&#x636E;&#x7EC3;&#x96C6;, label: &#x7EB5;&#x8F74;&#x7684;&#x6807;&#x7B7E;, interval: &#x6D4B;&#x8BD5;&#x89C4;&#x6A21;&#x9012;&#x589E;&#x7684;&#x95F4;&#x9694;
def incremental_train_scale_test(dataset, label, interval=1):
    c = dataset
    r = range(5, len(c.data) - 1, interval)
    rates = []
    for train_num in r:
        print(train_num)
        train_set = new_dataset(c.feature_names, c.target_names, c.data[:train_num], c.target[:train_num])
        test_set = new_dataset(c.feature_names, c.target_names, c.data[train_num:], c.target[train_num:])
        dt = dt_tree()
        dt.fit(train_set)
        rates.append(accuracy_rate(dt.predict(test_set), list(test_set.target)))

    print(rates)
    plt.plot(r, rates)
    plt.ylabel(label)
    plt.show()

if __name__ == '__main__':

    c = load_car()  # &#x8F7D;&#x5165;&#x6C7D;&#x8F66;&#x6570;&#x636E;&#x96C6;
    # c = load_mushroom()  # &#x8F7D;&#x5165;&#x8611;&#x83C7;&#x6570;&#x636E;&#x96C6;
    train_num = 1000 # &#x8BAD;&#x7EC3;&#x96C6;&#x89C4;&#x6A21;(&#x5269;&#x4E0B;&#x7684;&#x6570;&#x636E;&#x5C31;&#x653E;&#x5230;&#x6D4B;&#x8BD5;&#x96C6;)
    train_set = new_dataset(c.feature_names, c.target_names, c.data[:train_num], c.target[:train_num])
    test_set = new_dataset(c.feature_names, c.target_names, c.data[train_num:], c.target[train_num:])

    dt = dt_tree()  # &#x521D;&#x59CB;&#x5316;&#x51B3;&#x7B56;&#x6811;&#x6A21;&#x578B;
    dt.fit(train_set)  # &#x8BAD;&#x7EC3;
    dt.show_tree("../image/demo.png") # &#x8F93;&#x51FA;&#x51B3;&#x7B56;&#x6811;&#x56FE;&#x7247;
    print(accuracy_rate(dt.predict(test_set), list(test_set.target))) # &#x8FDB;&#x884C;&#x6D4B;&#x8BD5;, &#x5E76;&#x8BA1;&#x7B97;&#x51C6;&#x786E;&#x7387;&#x5427;

    # incremental_train_scale_test(load_car(), "car")
    # incremental_train_scale_test(load_mushroom(), "mushroom", interval=20)

</=></=>

基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型
基于Python实现的决策树模型

Original: https://blog.csdn.net/sheziqiong/article/details/126803242
Author: biyezuopin
Title: 基于Python实现的决策树模型

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/623535/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球