机器学习实战--决策树

  • 决策树概述
    决策树利用分层的概念将一个复杂的决策问题分解为多个简单的判断问题,最后逐级得到最大支持度的决策结果。

    决策树
    优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据
    缺点:可能产生过度匹配问题
    适用数据类型:数值型和标称型
  • 决策树算法原理
    决策树概念比较简单,用一个男女相亲的例子来描述决策树原理如下:

    机器学习实战--决策树_第1张图片

  • 示例:使用决策树实现分类器
    1. 决策树的构造
    在构造决策树时,我们需要解决的第一个问题就是,当前数据集上哪个特征在划分数据分类时起决定性作用。
    (1)信息增益
        划分数据集的最大原则是:将无序数据变得更加有序。组织杂乱无章数据的一种方法就是使用信息论度量信息,信息论是量化处理信息的分支科学。在划分数据之前和之后信息发生的变化称为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。
        如果待分类的事物可能划分在多个分类之中,则符号ci的信息定义为:

    这里写图片描述

    其中p(ci)是选择该分类的概率。
        熵定义为信息的期望值,通过下面的公式得到:
    这里写图片描述

    熵越高,则数据集混合的数据越多即数据集无序程度越高,计算香农熵的python代码如下:

    
    # 计算给定数据集的香农熵
    
    def calc_entropy(self,data_set):
        # 获取数据集大小
        entries_num = len(data_set)
        label_counts = {}
        for feat_vec in data_set:
            # 每个数据示例的最后一列存储的是类别信息
            current_label = feat_vec[-1]
            # 判断当前类别是否在类库中,如果不存在则初始化为1,如果存在则将数量增1
            if current_label not in label_counts.keys():
                label_counts[current_label] = 0
            label_counts[current_label] += 1
        entrop = 0.0
        for key in label_counts:
            # 计算当前类别出现的概率
            prob = float(label_counts[key])/entries_num
            entrop -= prob * log(prob, 2)
        return entrop

    (2)划分数据集
    循环计算所有特征划分数据集后的香农熵,并利用原始数据集的熵和划分后的熵相减得到该特征划分的信息增益。取信息增益最大的划分,作为最好的划分方式。

    
    # 按照给定特征划分数据集
    
    
    # dataset-待划分数据集 axis-划分数据集的特征 value-需要返回的特征的值
    
    def split_dataset(self,dataset, axis, value):
        # 创建新的数据集,避免破坏原数据集结构
        new_dataset = []
        for feat_vec in dataset:
            # 如果某个实例的指定特征值符合要求,则剔除此特征后,将本实例加入划分后的数据集
            if feat_vec[axis] == value:
                reduce_feat_vec = feat_vec[:axis]
                reduce_feat_vec.extend(feat_vec[axis+1:])
                new_dataset.append(reduce_feat_vec)
        return new_dataset
    
    
    # 选择最好的数据集划分方式
    
    def choose_best_feature_to_split(self, dataset):
        # 获取特征向量中特征的个数
        feature_nums = len(dataset[0]) - 1
        # 计算原数据集香农熵
        base_entropy = self.calc_entropy(dataset)
        best_info_gain = 0.0
        best_feature = -1
        for i in range(feature_nums):
            # 获取所有实例中,指定特征的特征值列表
            feat_list = [example[i] for example in dataset]
            # 去除特征值列表中重复值
            unique_vals = set(feat_list)
            new_entropy = 0.0
            for value in unique_vals:
                # 为指定特征的所有不同的值划分数据集
                sub_dataset = self.split_dataset(dataset, i ,value)
                prob = len(sub_dataset)/float(len(dataset))
                # 计算利用此特征划分数据集后的香农熵
                new_entropy += prob * self.calc_entropy(sub_dataset)
            # 计算利用此特征划分数据集后的信息增益
            info_gain = base_entropy - new_entropy
            # 取信息增益最大的特征划分数据集
            if info_gain > best_info_gain:
                best_info_gain = info_gain
                best_feature = i
        return best_feature

    (3)递归构建决策树
        递归构建决策树工作原理如下:得到原始数据集,然后基于最好的特征值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。
        递归结束的条件是:程序遍历完所有划分数据集的特征,或者每个分支下的所有实例都具有相同的分类。如果所有实例具有相同的分类,则得到一个叶子节点或者终止块,任何到达叶子节点的数据必然属于叶子节点的分类。
        如果数据集已经处理了所有属性,但是类标签依然不是唯一的,此时我们需要决定如何定义该叶子节点,在这种情况下,我们通常会采用多数表决的方法决定该叶子节点的分类。

    
    # 利用投票表决的方式来决定叶子节点的分类
    
    def majority_cnt(self, class_list):
        class_count = {}
        for vote in class_list:
            if vote not in class_count.keys():
                class_count[vote] = 0
            class_count[vote] += 1
        sorted_class_count = sorted(class_count.iteritems(),key=operator.itemgetter(1),reverse=True)
        return sorted_class_count[0][0]

    最终,创建决策树的代码如下:

    
    # 创建决策树
    
    
    # dataset-数据集 labels-标签列表,包含数据集中所有特征的名称
    
    def creat_tree(self, dataset, labels):
        # 创建包含数据集所有类标签的列表
        class_list = [example[-1] for example in dataset]
        # 如果类标签列表中的所有类标签相同则返回此类标签
        if class_list.count(class_list[0]) == len(class_list):
            return class_list[0]
        # 如果所有特征都被使用完,则利用投票方法选举出类标签返回
        if len(dataset[0]) == 1:
            return self.majority_cnt(class_list)
        # 获取最好的数据集划分方式
        best_feat = self.choose_best_feature_to_split(dataset)
        best_feat_label = labels[best_feat]
        my_tree = {best_feat_label:{}}
        del(labels[best_feat])
        feat_values = [example[best_feat] for example in dataset]
        unique_vals = set(feat_values)
        for value in unique_vals:
            sub_labels = labels[:]
            my_tree[best_feat_label][value] = self.creat_tree(self.split_dataset(dataset,best_feat,value), sub_labels)
        return my_tree
    

    2. 测试和存储分类器
    我们以上已经完成了决策树的所有创建工作,加入以下代码进行测试即可:

    
    # 创建训练数据集
    
    
    # dataset-训练数据集 labels-特征名称
    
    def creat_dataset(self):
        dataset = [[1, 1, 'yes'],
                   [1, 0, 'no'],
                   [0, 1, 'no'],
                   [0, 1, 'no']]
        labels = ['不浮出水面是否可以生存', '是否有脚蹼']
        return dataset,labels
    
    
    # 决策树分类器
    
    def classify(self, input_tree, feat_labels, test_vec):
        first_str = input_tree.keys()[0]
        second_dict = input_tree[first_str]
        feat_index = feat_labels.index(first_str)
        for key in second_dict.keys():
            if test_vec[feat_index] == key:
                if type(second_dict[key]).__name__ == 'dict':
                    class_label = self.classify(second_dict[key], feat_labels, test_vec)
                else:
                    class_label = second_dict[key]
        return class_label
    
    def test_classify(self):
        dataset,labels = self.creat_dataset()
        feat_labels = []
        feat_labels.extend(labels)
        decision_tree = self.creat_tree(dataset,feat_labels)
        test_vec = [1,0]
        print self.classify(decision_tree,labels,test_vec)
    

    我们还存在一个问题,每次训练一颗决策树耗费大量的时间与物理资源,所以我们需要将训练好的决策树储存到物理磁盘上,以便进行分类预测时进行调用:

    
    # 将训练好的决策树存入磁盘    
    
    def store_tree(self, input_tree, file_name):
        import pickle
        fw = open(file_name, 'w')
        pickle.dump(input_tree, fw)
        fw.close()
    
    
    # 从磁盘读取预存的决策树
    
    def grab_tree(self,file_name):
        import pickle
        fr = open(file_name)
        return pickle.load(fr)

你可能感兴趣的:(Android,BigData)