人工智能之决策树算法

EduCoder:人工智能之决策树算法

第1关:决策树算法求解分类预测问题

编程要求:

本关的编程任务是补全右侧代码片段 build、predict、parse_datacalc_all_gaincalc_attr_gaincalc_bool_gainget_targis_leaf 中 Begin 至 End 中间的代码,具体要求如下:

  • 在build中,创建一棵决策树,输入参数为根结点;

  • 在predict中,根据归纳好的决策树预测输入样例x的谓词 WillWait 状态(Yes 或者 No);

  • 在_parse_data_中,解析输入矩阵数据(在 Python 里以二维列表数据存储),各参数详见代码中函数注解,然后返回信息增益最大的属性名称及其属性值列表;

  • 在_calc_all_gain_中,计算所有样本的信息熵并返回,各参数详见代码中函数注解;

  • calc_attr_gain 中,计算某一特征属性的信息熵并返回,各参数详见代码中函数注解;

  • 在_calc_bool_gain_中,计算二值随机变量的信息熵并返回,各参数详见代码中函数注解;

  • 在_get_targ_中,计算叶子结点的决策分类标签并返回,各参数详见代码中函数注解;

  • 在_is_leaf_中,判断该结点是否为叶子结点,若是则返回 True,否则返回 False。

测试说明:

平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。

以下是平台的测试样例:

测试输入:

[[example, Alt, Bar, Fri, Hun, Pat, Price, Rain, Res, Type, Est],[x1,
Yes, No, No, Yes, Some, $$$, No, Yes, French, 0-10]]

预期输出:

Yes

代码如下:


# -*- coding: UTF-8 -*-
import math
class TreeNode:
    '''决策树结点数据结构
    成员变量:
    row - int 列表数据的行数,初始13
    col - int 列表数据的列数,初始12
    data - list[[]] 二维列表数据,初始数据形式在testDecisionTree.py里
                    第0行:[第0列:example(样本名字) 中间各列(1-10):各个特征属性名称 第11列:WillW ait(目标分类) ]
                    第1-12行:[样本名字,具体属性值,分类目标]
        data = [
        ['example', 'Alt', 'Bar', 'Fri', 'Hun', 'Pat',  'Price', 'Rain', 'Res', 'Type',   'Est',   'WillW ait'],
        ['x1',      'Yes', 'No',  'No',  'Yes', 'Some', '$$$',   'No',   'Yes', 'French', '0-10',  'y1=Yes'   ],
        ['x2',      'Yes', 'No',  'No',  'Yes', 'Full', '$',     'No',   'No',  'Thai',   '30-60', 'y2=No'    ],
            ........            .....       .....       .........           ............
        ['x12',     'Yes', 'Yes', 'Yes', 'Yes', 'Full', '$',     'No',   'No',  'Burger', '30-60', 'y12=Yes'  ] ]
    targ - string 分类结果 Yes No
    name - string 结点名字:特征属性名称
    attr - list[string] 该特征属性下的各个属性值
    children - list[GameNode] 该特征属性下的各个决策树子结点,与 attr 一一对应
    '''
    def __init__(self, row, col, data):
        self.row = row
        self.col = col
        self.data = data
        self.targ = ''          # target result
        self.name = ''          # attribute name
        self.attr = []          # attribute value list
        self.child = []         # attribute - TreeNode List
class DecisionTree:
    '''决策树
    成员变量:
    root - TreeNode 博弈树根结点
    成员函数:
    buildTree - 创建决策树
    predict - 预测样本分类标签
    _parse_data_ - 解析数据中最大信息增益的特性属性
    _calc_all_gain_ - 计算整个样本的信息熵
    _calc_attr_gain_ - 计算某一特征属性的信息熵
    _calc_bool_gain_ - 通用计算函数:计算二值随机变量的信息熵
    _get_targ_ - 获取叶子结点的决策分类标签
    _is_leaf_ - 判断该结点是否为叶子结点
    '''
    def __init__(self, row, col, data):
        self.root = TreeNode(row, col, data)
    def build(self, root):
        '''递归法创建博弈树
        参数:
        root - TreeNode 初始为决策树根结点
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        if self._is_leaf_(root):
            root.targ = self._get_targ_(root)
            return
        root.name, root.attr = self._parse_data_(root.row, root.col, root.data)
        idj = [j for j in range(root.col) if root.data[0][j] == root.name][0]
        for attr in root.attr:
            row = 0
            col = root.col - 1
            data = []
            for i in range(root.row):
                if i!=0 and root.data[i][idj] != attr:
                    continue
                tmp = []
                for j in range(root.col):
                    if j == idj:
                        continue
                    tmp.append(root.data[i][j])
                data.append(tmp)
                row += 1
            node = TreeNode(row, col, data)
            root.child.append(node)
        for node in root.child:
            self.build(node)
        #********** End **********#
    def predict(self, root, x):
        '''分类预测
        参数:
        root - TreeNode 决策树根结点
        x - [[]] 测试数据,形如:
           [ ['example', 'Alt', 'Bar', 'Fri', 'Hun', 'Pat', 'Price', 'Rain', 'Res', 'Type',  'Est'],
             ['x1',      'Yes', 'No',  'No',  'Yes', 'Some', '$$$',  'No',   'Yes', 'French','0-10'] ]
        返回值:
        clf - string 分类标签 Yes No
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        if self._is_leaf_(root):
            return root.targ
        id_name = x[0].index(root.name)
        for id_attr, attr in enumerate(root.attr):
            if attr == x[1][id_name]:
                return self.predict(root.child[id_attr], x)
        #********** End **********#
    def _parse_data_(self, row, col, data):
        '''解析数据:计算数据中最大信息增益的特性属性
        参数:
        row - int 列表数据的行数
        col - int 列表数据的列数
        data - list[[]] 二维列表数据,形如:
                第0行:[第0列:example(样本名字) 中间各列(1-10):各个特征属性名称 第11列:WillW ait(目标分类) ]
                第1-12行:[样本名字,具体属性值,分类目标]
        data = [
        ['example', 'Alt', 'Bar', 'Fri', 'Hun', 'Pat',  'Price', 'Rain', 'Res', 'Type',   'Est',   'WillW ait'],
        ['x1',      'Yes', 'No',  'No',  'Yes', 'Some', '$$$',   'No',   'Yes', 'French', '0-10',  'y1=Yes'   ],
        ['x2',      'Yes', 'No',  'No',  'Yes', 'Full', '$',     'No',   'No',  'Thai',   '30-60', 'y2=No'    ],
            ........            .....       .....       .........           ............
        ['x12',     'Yes', 'Yes', 'Yes', 'Yes', 'Full', '$',     'No',   'No',  'Burger', '30-60', 'y12=Yes'  ] ]
        返回值:
        clf - string, list[] 信息增益最大的属性名称 及其 属性值列表
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        max_gain = -float('inf')
        max_name = ''
        max_attr = []
        max_idj = -1
        all_gain = self._calc_all_gain_(row-1, [x[-1] for x in data[1:]])      # col = 1
        #print('all_gain: ', all_gain)
        for j in range(1, col-1, 1):
            tmp_data = []
            for i in range(1, row, 1):
                tmp_data.append([data[i][j], data[i][-1]])
            tmp_gain = self._calc_attr_gain_(row-1, tmp_data)       # col = 2
            if (all_gain - tmp_gain) > max_gain:
                max_gain = all_gain - tmp_gain
                max_name = data[0][j]
                max_idj = j
            #print(max_gain, max_name, max_idj, tmp_gain, data[0][j], all_gain - tmp_gain)
        for i in range(1, row, 1):
            if data[i][max_idj] not in max_attr:
                max_attr.append(data[i][max_idj])
        return max_name, max_attr
        #********** End **********#
    def _calc_all_gain_(self, row, data):
        '''计算整个样本的信息熵
        参数:
        row - int 列表数据的行数
        data - list[] 一维列表数据,形如:[分类目标]
                data = ['y1=Yes', 'y2=No', ........, 'y12=Yes']
        返回值:
        clf - float 信息熵
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        dict_ = {'yes':0.0, 'no':0.0}
        for i in range(row):
            if data[i][-1] == 's':  # 'Yes'
                dict_['yes'] += 1.0
            else:               # 'No'
                dict_['no'] += 1.0
        sum = 0.0
        for key_ in dict_:
            sum += (1.0 * dict_[key_] / float(row)) * math.log(1.0 * dict_[key_] / float(row), 2)
        return -sum
        #********** End **********#
    def _calc_attr_gain_(self, row, data):
        '''计算某一特征属性的信息熵
        参数:
        row - int 列表数据的行数
        data - list[[]] 二维列表数据(2列),形如:[[某一属性值,分类目标]]
                  [ ['0-10',  'y1=Yes'   ],
                    ['30-60', 'y2=No'    ],
                      ........
                    ['30-60', 'y12=Yes'  ] ]
        返回值:
        clf - float 信息熵
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        # attributes
        dict_ = {}
        for i in range(row):
            if data[i][0] not in dict_:
                dict_[data[i][0]] = [0.0, 0.0]  # [yes, no]
            # attribute : yes or no
            if data[i][1][-1] == 's':   # yes
                dict_[data[i][0]][0] += 1.0
            else:                       # no
                dict_[data[i][0]][1] += 1.0
        sum = 0.0
        for key_ in dict_:
            p = 1.0 * dict_[key_][0] / (dict_[key_][0] + dict_[key_][1])
            sum += (1.0 * (dict_[key_][0] + dict_[key_][1]) / float(row)) * self._calc_bool_gain_(p)
        return sum
        #********** End **********#
    def _calc_bool_gain_(self, p):
        '''通用计算函数:计算二值随机变量的信息熵
        参数:
        p - float 二值随机变量的概率 在[0, 1]之间
        返回值:
        clf - float 信息熵
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        if p == 1 or p == 0:
            return 0.0
        return -(p * math.log(p, 2) + (1-p) * math.log((1-p), 2))
        #********** End **********#
    def _get_targ_(self, node):
        '''计算叶子结点的决策分类标签
        参数:
        node - TreeNode 决策树结点
        返回值:
        clf - string 分类标签 Yes No
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        yes = 0
        no = 0
        for i in range(1, node.row, 1):
            if node.data[i][-1][-1] == 's':     # 'Yes'
                yes += 1
            else:                               # 'No'
                no += 1
        if yes > no:
            return 'Yes'
        else:
            return 'No'
        #********** End **********#
    def _is_leaf_(self, node):
        '''判断该结点是否为叶子结点
        参数:
        node - TreeNode 决策树结点
        返回值:
        clf - bool 叶子结点True 非叶子结点False
        '''
        #请在这里补充代码,完成本关任务
        #********** Begin **********#
        if node.col == 2:               # [ x* , y* ] without any attributes
            return True
        targ = node.data[-1][-1][-1]    # [ x* , attr , y* ] attributes
        for i in range(node.row):
            if i == 0:
                continue
            if node.data[i][-1][-1] != targ:
                return False
        return True                     # all y* are Yes or No

        #********** End **********#
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pretend ^^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值