决策树模型
目录
人工智能第五次实验报告 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 npfrom matplotlib import pyplot as pltfrom math import logimport pandas as pdimport pydotplus as pdp"""19335286 郑有为人工智能作业 - 实现ID3决策树"""nonce = 0  # 用来给节点一个全局IDcolor_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  best_vote:            best_vote = target_range[t]            best_target = t    return best_target# 返回测试的正确率# predict_result: 预测标签列表, target_result: 实际标签列表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)# 决策树的节点结构class dt_node:    def __init__(self, content, is_leaf=False, parent=None):        global nonce        self.id = nonce # 为节点赋予一个全局ID, 目的是方便画图        nonce += 1        self.feature_name = None        self.target_value = None        self.vote_most = None # 记录当前节点最可能的标签        if not is_leaf:            self.feature_name = content # 非叶子节点的属性名        else:            self.target_value = content # 叶子节点的标签        self.parent = parent        self.child = {} # 以当前节点的属性对应的属性值作为键值# 决策树模型class dt_tree:    def __init__(self):        self.tree = None # 决策树的根节点        self.map_str = """            digraph demo{            node [shape=box,, color="black", fontname="Microsoft YaHei"];            edge [fontname="Microsoft YaHei"];            """ # 用于作图: pydotplus 格式的树图生成代码结构        self.color_dir = {} # 用于作图: 叶子节点可选颜色, 以标签值为键值    # 训练模型, train_set: 训练集    def fit(self, train_set):        if len(train_set.target)  " + "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    # 测试模型, 对测试集 test_set 进行预测    def predict(self, test_set):        test_result = []        for test in test_set.data:            node = self.tree # 从根节点一只往下找, 知道到达叶子节点            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: # 如果没有到达叶子节点, 则取最后到达节点概率上最可能的标签为目标值                test_result.append(node.target_value)        return test_result    # 输出树, 生成图片, path: 图片的位置    def show_tree(self, path="demo.png"):        map = self.map_str + "}"        print(map)        graph = pdp.graph_from_dot_data(map)        graph.write_png(path)# 学习曲线评估算法精度 dataset: 数据练集, label: 纵轴的标签, interval: 测试规模递增的间隔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()  # 载入汽车数据集    # c = load_mushroom()  # 载入蘑菇数据集    train_num = 1000 # 训练集规模(剩下的数据就放到测试集)    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)  # 训练    dt.show_tree("../image/demo.png") # 输出决策树图片    print(accuracy_rate(dt.predict(test_set), list(test_set.target))) # 进行测试, 并计算准确率吧    # incremental_train_scale_test(load_car(), "car")    # incremental_train_scale_test(load_mushroom(), "mushroom", interval=20)