决策树与随机森林

七月在线机器学习笔记

决策树

决策树是一种树型结构,其中每个内部结点 表示在一个属性上的测试,每个分支代表一 个测试输出,每个叶结点代表一种类别。

决策树学习是以实例为基础的归纳学习。

决策树学习采用的是自顶向下的递归方法, 其基本思想是以信息熵为度量构造一棵熵值 下降最快的树,到叶子节点处的熵值为零, 此时每个叶节点中的实例都属于同一类。

优点是:

     它可以自学习。在学习的过程中,不需要使用者了解过 多背景知识,只需要对训练实例进行较好的标注,就能够进行学习。

          显然,属于有监督学习。 

          从一类无序、无规则的事物(概念)中推理出决策 树表示的分类规则

建立决策树的关键,即在当前状态下选择哪 个属性作为分类依据。根据不同的目标函数,建立决策树主要有一下三种算法。

        ID3

        C4.5

        CART

信息增益(即互信息,在前面最大熵模型的博客中已经提到)

            概念:当熵和条件熵中的概率由数据估计(特别是 极大似然估计)得到时,所对应的熵和条件熵分别称为经验熵和经验条件熵。

            信息增益表示得知特征A的信息而使得类X的信息的不确定性减少的程度。---互信息

                            g(D,A)=H(D) – H(D|A)<==>I(X,Y)=H(Y)– H(Y|X)=H(X)– H(X|Y)

基本记号

       设训练数据集为D,|D|表示其容量,即样本个数。

       设有K个类C_k,k=1,2,…,K,|C_k|为属于类C_k的样本个数。\sum_k|C_k|=|D|

       设特征A有n个不同的取值\left \{ a_1,a_2,...,a_n \right \},根据特征A的取值将D划分为n个子集D_1,D_2,...,D_n,|D_i|为D_i的样本个数,\sum_i|D_i|

                      比如,特征A为年龄,不同的取值表示{0-10岁,10-20岁,20-30岁,...},可以根据这些取值将D划分为n部分

                                           p(A_i)=\frac{|D_i|}{|D|}                  

       记子集D_i中属于类C_k 的样本的集合为D_{ik},|D_{ik}|为D_{ik}的样本个数。即|D_{ik}|= A_iC_k同时出现的样本个数

则,                       p(D_i|A_k)=\frac{p(D_i,A_k)}{p(A_k)}=\frac{|D_{ik}|}{|D_i|}

计算信息增益-------ID3

 经验熵

       H(D)=-\sum_{k=1}^{K}p(C_k)logp(C_k)=-\sum_{k=1}^{K}\frac{C_k}{D}log\frac{C_k}{D}------------------实践中,当数据量很大时,用频率估计概率【大数定理】

经验条件熵 

      \begin{align*} H(D|A) &= -\sum_{i,k}p(D_k,A_i)logp(D_k|A_i) \\ &= -\sum_{i,k}p(A_i)p(D_k|A_i)logp(D_k|A_i)\\ &= -\sum_{i=1}^{n}\sum_{k=1}^{K}p(A_i)p(D_k|A_i)logp(D_k|A_i) \\ &= -\sum_{i=1}^{n}p(A_i)\sum_{k=1}^{K}p(D_k|A_i)logp(D_k|A_i)\\ &= -\sum_{i=1}^{n}\frac{|D_i|}{|D|}\sum_{k=1}^{K}\frac{|D_{ik}|}{|D_i|}log\frac{|D_{ik}|}{|D_i|} \end{align*}

其他目标:

       信息增益率:g_r(D,A)=g(D,A)/H(A)

       基尼指数:

       \begin{align*} Gini(p)&=\sum_{k=1}^{K}p_k(1-p_k)=1-\sum_{k=1}^{K}p_k^2&\\ &= 1-\sum_{k=1}^{K}\left ( \frac{|C_k|}{|D|} \right )^2 \end{align*},    pk表示样本按某一特征划分后属于C_k类的概率。

ID3决策:适应信息增益来进行特征选择的决策树学习过程。
所以如果是取值更多的属性,更容易使得数据更“纯” ,其信息增益更大,决策树会首先挑选这个属 性作为树的顶点。结果训练出来的形状是一棵庞大 且深度很浅的树,这样的划分是极为不合理的。
因此有学者提出 C4.5以降低A的影响,即信息增益率g_r(D,A)=g(D,A)/H(A)

CART:基尼指数

总结:一个属性的信息增益越大,表明属性对样本的熵减少的能力更强,这个属性使得数据由不确定性变成确定性的能力越强

决策树的过拟合

   决策树对训练属于有很好的分类能力,但对未知的测试数据未必有好的分类能力,泛化能力弱,即可能发生过拟合现象。

    剪枝

随机森林

    Bootstraping---------pull up by your own bootstraps(不需要外界帮助,仅依靠自身力量让自己变得更好)

    bagging策略

         bootstrap aggregation

             1. 从样本集中重采样(有重复的)选出n个样本

             2.在所有属性上,对这n个样本建立分类器 (ID3、C4.5、CART、SVM、Logistic回归等)
             3.重复以上两步m次,即获得了m个分类器

             4.将数据放在这m个分类器上,最后根据这m个分类器的投票结果,决定数据属于哪一类

        bagging的另一种描述

         Given a standard training set \large D of size \LARGE n,bagging generates m new training sets \large D_i,each of size \LARGE n',by sampling examples from \large Duniformly and with replacement.By sampling with replacement,it is likely that some examples will be repeated in each \large D_i.if \LARGE n'=n,then for large \LARGE n the set \large D_i is expected to have the fraction (1-1/e)(≈63.2%) of the unique examples of \large D,the rest being duplicates. this kind of samples is known as a bootstrap sample.the \LARGE m models are fitted using the above \LARGE m bootstrap samples and combined by averaging the output(for regression) or voting(for classification).

      随机森林在bagging的基础上做了修改

              从样本集中用Bootstrap采样选出n个样本;

              从所有属性中随机选择k个属性,选择最佳分割属性作为节点建立CART决策树(Gini系数);

              重复以上步骤m次,即建立了m棵CART决策树;

              这m棵CART决策树形成随机森林,通过投票表决结果,决定数据属于哪一类

      投票机制

                   简单投票机制 : 一票否决(一致表决) ;少数服从多数/有效多数(加权);阈值表决

                   贝叶斯投票机制 : 简单投票法假设每个分类器都是平等的。在实际生活中,我们听取一个人的意见,会考虑这个人过去的意见是否有用,从而加大或者减少权值。贝叶斯投票机制基于每个基本分类器在过去的分类表现设定一个权值,然后按照这个权值进行投票。

        

      

      示例:

              下图是实际B超拍摄的胎儿影像。设计算法 完成头骨的自动检测,从而进一步估算胎儿头骨直径。

                                           

              假定现在有已经标记的几千张的不同胎儿的图像,对于新的一张图像,如何做自动检测和计算?

       一种可行的解决方案:

                       1.通过Haar特征提取等对每幅图片分别处理, 得到M个特征。N个图片形成N*M的矩阵。

                       2.随机选择若干特征和样本,得到a*b的小矩 阵,建立决策树;

                       3.重复K次得到随机森林。

                       4.投票方法选择少数服从多数。

注意:随机森林当然可以使用决策树作为基本分类器;但也可以使用SVM、Logistic回归等其他分类器,习惯上,这些分类器组成的“总分类器”,仍然叫做随机森林。

举例--回归问题

       离散点是样本集合,描述了臭氧(横轴)和温度(纵轴)的关系;试拟合二者的变化曲线

   

          局部加权线性回归:

           1. Fit \theta to minimize \sum_i\omega _i(y_i-\theta^Tx_i)^2                     \dpi{120} \omega_i=e^{-\frac{||x_i-x||^2}{2\sigma^2}},     x_i,x表示列向量,  高斯距离:距离越近(相似度越大),权值越大

           2. Output  \theta^Tx                                                              

 #下面代码或许还有一些问题,仅作为我个人记录之用,请忽略

from sklearn.datasets import load_iris
from numpy import *
import numpy as np
import operator,copy

class CART(object):
    def __init__(self):

        iris = load_iris()
        self.train_data = iris.data
        self.train_label = iris.target
        self.feature_name = iris.feature_names
        self.copy_fea_name=copy.copy(self.feature_name)
        self.target_names = iris.target_names

    #calculate the entropy
    def cal_entropy(self,label):#熵
        entropy = 0
        n=label.size
        count = {}
        for i in range(len(self.target_names)):
            x = np.where(label == i)
            count[self.target_names[i]] = len(x[0])
        # print(self.target_names)
        for k in count:
            p_xi = count[k]/n
            entropy -= p_xi*np.log2(p_xi)
        print(entropy)
        return entropy

    def gini(self,label):
        '''
        :param label: 样本对应的标签(样本的类别)
        :return: 按照某一特征划分数据后的基尼系数
        '''
        n=label.size
        count = {}
        for i in range(len(self.target_names)):
            x = np.where(label == i)#得到对应类的索引列表,这里是iris数据的label为[0,1,2]
            count[self.target_names[i]] = len(x[0])
        # print(count)
        p_k =np.array(list(count.values()))/n
        g = 1-np.sum(np.square(p_k))
        # print(g)
        return g

    def split_data(self,data,axis):#按照特征(索引)划分数据
        # print(axis)
        value=np.median(data[:,axis])#特征的中值

        # print(value)
        left=[]
        right=[]
        for i in range(len(data)):
            if data[i,axis]<value:
                left.append(i)
            else:
                right.append(i)
        return left,right

    def idx2data(self,data, label, left,right, fea_idx):
        datal=np.delete(data[left],fea_idx,axis=1)#抽取出不含fea_idx的数据
        datar=np.delete(data[right],fea_idx,axis=1)
        labell = label[left]
        labelr= label[right]
        return datal, datar, labell, labelr

    def choose_best_split_node(self,data,label):#遍历未作为节点的特征,并计算对应的最大Gini系数/信息增益,划分数据
        num_feature=len(self.copy_fea_name)
        n=len(label)
        base_gini=self.gini(label)
        best_gain=-1
        for i in range(num_feature):
            gini_t=0
            left,right=self.split_data(data,i)
            left_prob=len(left)/n
            right_prob=len(right)/n
            gini_t+=left_prob*self.gini(label[left])
            gini_t+=right_prob*self.gini(label[right])
            info_gain=base_gini-gini_t
            if info_gain>best_gain:
                best_gain=info_gain
                best_idx=i
        return best_idx

    def majority_category(self,label):#当没有特征可以划分时,返回数据最多的那个类
        count = {}
        for i in range(len(self.target_names)):
            x = np.where(label == i)
            count[self.target_names[i]] = len(x[0])
        # print(count)
        sorted_count=sorted(count.items(),key=operator.itemgetter(1),reverse=True)#将字典转换为数组并降序排列
        # print('--------sc--',sorted_count)
        return sorted_count[0][0]

    def buildtree(self,data,label):
        labels=list(label)
        if labels.count(label[0])==label.size:#为同一类元素,不再划分
            return self.target_names[label[0]]
        if len(self.copy_fea_name)==0:#当没有特征可以划分时,返回数据最多的那个类
            return self.majority_category(label)
        best_split_fea_axis=self.choose_best_split_node(data,label)#最佳划分特征的索引
        # print(best_split_fea_axis)
        best_split_fea=self.copy_fea_name[best_split_fea_axis]
        mytree={best_split_fea:{}}#保存树
        self.copy_fea_name.pop(best_split_fea_axis)#剔除已做为节点的特征
        left,right=self.split_data(data,best_split_fea_axis)#划分数据集
        datal, datar, labell, labelr=self.idx2data(data,label,left,right,best_split_fea_axis)#抽取数据和标签(类别)
        # print(len(left),len(right))
        mytree[best_split_fea]["<"]=self.buildtree(datal,labell)
        mytree[best_split_fea][">"]=self.buildtree(datar,labelr)
        return mytree

    def classify(self,mytree,testdata):
        if type(mytree).__name__ != 'dict':
            return mytree
        fea_name = list(mytree.keys())[0]  # get the name of first feature
        fea_idx = self.feature_name.index(fea_name)  # the index of feature 'fea_name'
        val = testdata[fea_idx]
        nextbranch = mytree[fea_name]

        # judge the current value > or < the pivot (average)
        if val > np.median(self.train_data[:,fea_idx]):
            nextbranch = nextbranch[">"]
        else:
            nextbranch = nextbranch["<"]
        return self.classify(nextbranch, testdata)

    def start(self):
        mytree=self.buildtree(self.train_data,self.train_label)
        print(mytree)
        tt=self.train_data[111]
        x=self.classify(mytree,tt)
        print(x)

cart=CART()
cart.start()


         

      

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值