ID3, C4.5, CART

决策树学习基本算法

叶节点对应决策结果,其他节点对应一个属性测试,通过属性测试的值将样本划分的子节点中


    TreeGenerate(D,A){
        生成节点Node;
        if D只有一个类别C thenNode标记为C类叶节点;
            return;
        end if
        if A是空集 或 DA上取值相同 thenNode标记为类别最多的叶节点;
            return;
        end ifA中选择最优的划分属性a*;
        for a*的每一个取值v doNode生成一个分支;
            Dv表示在a*上取值为v的样本子集;
            if Dv是空集 then
                将分支节点标记为叶节点,其父节点类别为样本最多的类别;
                return;
            elseTreeGenerate(Dv,A\{a*})为分支节点;
            end if
        end for
    }

2.划分选择

一般而言,随着划分过程的不断进行,我们希望决策树的分支节点所包含的样本尽可能地属于同一类别,即节点的“纯度”越来越高。

1.信息增益

信息熵: Ent(D)=nk=1pklog2pk ,其中n为特征维数, pk 为第k类样本所占比例,Ent(D)越小,D的纯度越高。

信息增益: Gain(D,a)=End(D)Vi=1|Dv||D|Ent(Dv) ,其中V为D中属性a的取值数, || 表示样本集中样本的个数

一般信息增益越大,所获得的纯度越高。

2.增益率

信息增益存在对取值较多的属性有所偏好的问题,使用增益率可减轻这种偏好的不利影响。

Gain_ratio(D,a)=Gain(D,a)Iv(a),Iv(a)=Vi=1|Dv|Dlog2|Dv|D

增益率对取值数目少的属性有所偏好

3.基尼指数

Gini(D)=1nk=11p2k,Gini_index(D,a)=Vi=1|Dv||D|Gini(Dv)

基尼指数越小,纯度越高。

剪枝处理

原因:针对决策树过拟合问题,在决策树学习中,为了尽可能正确的分类训练样本,节点划分过程将不断重复,有时会造成分支过多,决策树太复杂导致过拟合。

预剪枝

在决策树的生成过程中,对每个节点在划分前进行评估,若当前结点的划分不能带来的决策树泛化性能的提升,则停止划分并将当前结点标记为叶节点。

后剪枝

先从训练集中生成一棵完整的决策树,然后自底向上地对非叶节点进行考察,若将该节点对应的子树替换为叶节点能提升泛化性能,则将其标记为叶节点。

一般情况下,后剪枝决策树的欠拟合风险很小,泛化性能往往由于预剪枝决策树。但后剪枝过程是在生成完全决策树后进行的,并且要自底向上地对树中的所有非叶节点进行逐一考察,因此其训练时间开销要大得多。

连续值与缺失值处理

连续值处理

连续属性离散化技术,比如最简单的二分法

给定样本集D和连续属性a,假定a在D上出现了n个不同的取值,将这些值排序为 {a1,a2,,an} .基于划分点t可将D分为子 D+t Dt 其中 D+t 包含那些在属性a上取值大于t的样本, Dt 则包含那些在属性a上取值小于t的样本。显然,对相邻属性取值 ai ai+1 来说,t在区间 [ai,ai+1) 中取任意值产生的划分结果相同。所以n-1个候选划分点集合为: Ta={ai+ai+12|1in1} 。可以选取最优的划分点作为样本集合的划分,比如 Gain(D,a)=maxtTaGain(D,a,t)=maxtTaEnt(D)λ{+,}|Dλt||D|Ent(Dλt)

注意:与离散属性不同,若当前节点划分属性是连续属性,该属性还可作为后代节点的划分属性。

缺失值处理

两个问题:如何在属性缺失值的情况下选择划分属性?给定划分属性,若样本在该属性上的值缺失如何对样本划分?

给定训练集D和属性a,令 D~ 表示D中在属性a上没有缺失值的样本子集。假定属性a有V个取值 {a1,a2,,aV} ,令$tilde{D}_v \tilde{D} a a^v \tilde{D}^k \tilde{D} kx w_x ρ=xD~wxxDwx,p~k=xD~kwxxD~wx,r~v=xD~vwxxD~wx aρp~kk \tilde{r}_v$表示无缺失样本中在属性a上取值为a^v的样本所占比例。

信息增益的公式可推广为:

Gain(D,a)=ρ×Gain(D~,a)=ρ×(Ent(D~)v=1Vr~vEnt(D~v))Ent(D~)=k=1np~klog2p~k

样本划分:若样本在属性a上的取值已知,则根据取值将其划入相应子节点中,且样本权重不变。若样本在划分属性      a上的取值缺失,则将其同时划入所有的子节点中,且样本权重在与属性值a^v对应的子节点中调整为\tilde{r}_v\cdot w_x。

ID3

特征选择:信息增益

特性:ID3不能保证得到最优解,有可能陷入局部最优;ID3容易过拟合;ID3很难处理连续属性。

    #ID3实现
    '''
    优点:计算复杂度不高,输出结果易于理解,对中间值缺失不敏感,可处理不相关特征数据
    缺点:易过拟合;ID3不能处理连续型特征数据

    from math import log

    def create_dataset():
        dataset = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
        features = ['no surfacing', 'flippers']#特征标签
        return dataset, features

    #计算熵
    def cal_Ent(dataset):
        labels = []
        for d in dataset:
            labels.append(d[-1])
        total = len(set(labels))
        label = list(set(labels))
        str_labels = "".join(labels)
        p = []
        for l in label:
            p.append(str_labels.count(str(l)))
        sum = 0
        for k in p:
            pk = float(k) / len(dataset)
            sum -= pk * log(pk, 2)
        return sum

    #以特征axis的值value划分数据集
    def split_dataset(dataset, axis, value):
        sub_dataset = []
        for x in dataset:
            if x[axis] == value:
                t = x[0:axis]
                t.extend(x[axis + 1:])
                sub_dataset.append(t)
        return sub_dataset

    #根据信息增益选择最好的划分属性
    def choose_best_feature(dataset):
        EntD = cal_Ent(dataset)
        best_gain = 0.0
        n = len(dataset[0]) - 1  # feature num
        for i in range(n):
            gain_Dv = 0.0
            features = [x[i] for x in dataset]
            unique_features = list(set(features))
            for v in unique_features:
                sub_data = split_dataset(dataset, i, v)
                pv = len(sub_data) / len(dataset)
                gain_Dv += pv * cal_Ent(sub_data)
            gain_Dv = EntD-gain_Dv
            if gain_Dv > best_gain:
                best_gain = gain_Dv
                best_fea = i
        return best_fea

    #投票选择大多数的类别
    from collections import Counter
    def vote_label(labels):
        return Counter(labels).most_common(1)[0][0]

    #生成树(递归过程)
    def generate_tree(dataset,features):
        labels = [x[-1] for x in dataset]
        if len(set(labels))==1:
            return labels[0]
        feature_num =  len(dataset[0])-1
        if feature_num==0:
            return vote_label(labels)
        best_fea_axis = choose_best_feature(dataset)
        best_fea = features[best_fea_axis]
        features.remove(best_fea)
        Tree = {best_fea:{}}
        best_fea_values = list(set([x[best_fea_axis] for x in dataset]))
        for v in best_fea_values:
            sub_dataset = split_dataset(dataset,best_fea_axis,v)
            sub_features = features[:]
            Tree[best_fea][v] = generate_tree(sub_dataset,sub_features)
        return Tree

    #使用决策树分类
    def classify(tree,feature_labels,test_data):
        first_key = list(tree.keys())[0]
        next_dict = tree[first_key]
        key_index = feature_labels.index[first_key]
        for key in next_dict.keys():
            if test_data[key_index] ==key:
                if type(next_dict[key]).__name__ == "dict":
                    class_label = classify(next_dict[key],feature_labels,test_data)
                else:
                    class_label = next_dict[key]
        return class_label

    import pickle
    #存储树
    def store_tree(tree,filename):
        fw = open(filename,'wb')
        pickle.dump(tree,fw)
        fw.close()
    #获取树
    def get_tree(filename):
        fr = open(filename,'rb')
        return pickle.load(fr)

    myData, features = create_dataset()
    myTree = generate_tree(myData,features)
    print(myTree)
    store_tree(myTree,'tree_storge.txt')
    print(get_tree('tree_storge.txt'))

C4.5

特征选择:增益率(先从候选划分属性中选择信息增益高于平均水平的属性,再从中选择增益率最高的属性)

连续值处理:二分法

确实值处理:如上介绍。

相对于ID3的改进:用信息增益率来选择属性,克服了用信息增益选择属性时偏向选择取值多的属性的不足;可处理连续属性和离散属性;可处理缺失值属性;后剪枝优化。

优点:产生的分类规则易于理解,准确率较高。其缺点是:在构造树的过程中,需要对数据集进行多次的顺序扫描和排序,因而导致算法的低效。此外,C4.5只适合于能够驻留于内存的数据集,当训练集大得无法在内存容纳时程序无法运行。

CART

既可用于分类也可用于回归;CART是二叉树

特征选择:基尼指数(分类),均方误差(回归)

分类树生成

选择最优特征和最优切分点:对每一个特征 a(xj) ,对其每一个取值 av ,根据样本点对 xj=av 的测试为“是”或“否”将样本集划分为两个子集 D1,D2 ,计算基尼指数 (|D1||D|Gini(D1)+|D2||D|Gini(D2)) 。选择最小的基尼指数对应的特征及取值作为切分特征和切分点。

回归树生成

启发式方法对输入空间进行划分:选择第j个变量x^j和它的取值s作为切分变量和切分点,并定义两个区域: R1(j,s)={x|xjs},R2(j,s)={x|xj>s} 然后求解 minj,s[minc1xiR1(j,s)(yic1)2+minc2xiR2(j,s)(yic2)2] 寻找最优切分变量和最优切分点,(j,s)划分区域对应的输出值为: c^j=average(yi|xiRj(j,s)),j=1,2

在训练数据集所在的输入空间中,递归地将每个区域划分为两个子区域并决定每个子区域的输出值,构建二叉决策树。将输入空间划分为M个单元 R1,R2,,RM ,回归树模型为: f(x)=Mm=1cmI(xRm) ( cm 为单元 Rm 对应的输出值,利用均方误差 xiRm(yif(xi))2 优化可得最优值 c^m=average(yi|xiRm) )。

    #CART回归树生成代码实现
    #coding= utf-8
    from numpy import *

    def load_data(filename):
        dataset = genfromtxt(filename,delimiter='\t')
        return mat(dataset)

    #二元切分法
    def bin_split_data(dataset,feature,value):
        left_dataset =[]
        right_dataset = []
        #print(dataset.shape,feature)
        for x in dataset.tolist():
            if x[feature]>value: right_dataset.append(x)
            else: left_dataset.append(x)
        return mat(left_dataset), mat(right_dataset)
    #选择最优切分变量和最有切分点
    def choose_best_split(dataset,leaf_type,error_type,ops):
        tolS = ops[0]#容许的误差下降值
        tolN = ops[1]#切分的最小样本数
        #只有一类,无需再划分
        if len(set(dataset[:,-1].T.tolist()[0])) ==1:
            return None,leaf_type(dataset)
        m,n = shape(dataset)
        S = error_type(dataset)
        bestS= inf
        best_fea = 0
        best_fea_val = 0
        for fea in range(n-1):
            vals = set(dataset[:,fea].reshape(1,m).tolist()[0])
            for v in vals:
                l_mat,r_mat = bin_split_data(dataset,fea,v)
                if ((shape(l_mat)[0]<tolN) or (shape(l_mat)[1]<=0)) or ((shape(r_mat)[0]<tolN) or (shape(r_mat)[1]<=0)): continue
                newS = error_type(l_mat)+error_type(r_mat)
                if newS <bestS:
                    bestS = newS
                    best_fea = fea
                    best_fea_val = v
        #若切分数据集后提升效果不够大,则不进行切分而直接创建叶节点
        if (S-bestS)<tolS:
            return None,leaf_type(dataset)
        l_mat,r_mat = bin_split_data(dataset,best_fea,best_fea_val)
        if (shape(l_mat)[0] < tolN) or (shape(r_mat)[0] < tolN): return None,leaf_type(dataset)
        return best_fea,best_fea_val

    def reg_leaf(dataset):
        return mean(dataset[:,-1])

    def reg_error(dataset):
        return var(dataset[:,-1])*shape(dataset)[0]

    def create_tree(dataset,leaf_type=reg_leaf,error_type=reg_error,ops=(1,20)):
        '''
        params: leaf_type给出建立叶节点的函数;error_type代表误差计算函数;
                ops是一个包含树构建所需其他参数的元组
        '''
        fea,val = choose_best_split(dataset,leaf_type,error_type,ops)
        if fea ==None:
            return val
        tree = {}
        tree['split_axis'] = fea
        tree['split_val'] =val
        left_data,right_data = bin_split_data(dataset,fea,val)
        tree['left'] = create_tree(left_data,leaf_type,error_type,ops)
        tree['right'] = create_tree(right_data, leaf_type, error_type, ops)
        return tree

    def is_tree(obj):
        return (type(obj).__name__=='dict')

    def get_mean(tree):
        if is_tree(tree['right']): tree['right'] = get_mean(tree['right'])
        if is_tree(tree['left']): tree['left'] = get_mean(tree['left'])
        return (tree['left']+tree['right'])/2.0
    #剪枝
    def prune(tree,testData):
        if shape(testData)[0]==0: return get_mean(tree)
        if is_tree(tree['right']) or is_tree(tree['left']):
            left_data,right_data = bin_split_data(testData,tree['split_axis'],tree['split_val'])
        if is_tree(tree['left']): prune(tree['left'],left_data)
        if is_tree(tree['right']): prune(tree['right'],right_data)
        if not is_tree(tree['right']) and not is_tree(tree['left']):
            left_data, right_data = bin_split_data(testData, tree['split_axis'], tree['split_val'])
            print(left_data.shape,right_data.shape)
            if (shape(left_data)[1] > 0) and (shape(right_data)[1]> 0):
                #print(tree,left_data,right_data)
                error_no_merge = sum(power(left_data[:,-1]-tree['left'],2))+sum(power(right_data[:,-1]-tree['right'],2))
                tree_mean = (tree['left']+tree['right'])/2
                error_merge = sum(power(testData[:,-1]-tree_mean,2))
                if error_merge<error_no_merge:
                    print("merging")
                    return tree_mean
                else:
                    return tree
        else:
            return tree

    def tree_eval(model):
        return float(model)
    #预测函数
    def tree_forecast(tree,in_data,model_eval=tree_eval):
        if not is_tree(tree): return model_eval(tree)
        if in_data[tree['split_axis']]>tree['split_val']:
            if is_tree(tree['left']):
                return tree_forecast(tree['left'],in_data,model_eval)
            else:
                return tree_eval(tree['left'])
        else:
            if is_tree(tree['right']):
                return tree_forecast(tree['right'],in_data,model_eval)
            else:
                return tree_eval(tree['right'])

    def create_forecast(tree,test_data,model_eval=tree_eval):
        m = len(test_data)
        pre = mat(zeros((m,1)))
        for i in range(m):
            pre[i] = tree_forecast(tree,test_data[i],model_eval)
        return pre

    myTrainData = load_data('bikeSpeedVsIq_train.txt')
    myTree = create_tree(myTrainData)
    myTestData = load_data('bikeSpeedVsIq_test.txt')
    pre = create_forecast(myTree,myTestData[:,0])
    print(corrcoef(pre,myTestData[:,1],rowvar=1)[0,1])

ID3,C4.5,CART区别

样本数据上的差异:ID3只能对分类变量进行处理,C4.5和CART可以处理连续和分类两种自变量;ID3对缺失值敏感,C4.5和CART对缺失值可以进行多种方式的处理;只从样本量考虑,小样本建议C4.5,大样本CART。

目标因变量的差异:ID3和C4.5只能做分类,CART不仅可以做分类也可以做回归;ID3和C4.5节点上可以产出多叉,而CART节点永远是二叉。

样本特征上的差异:特征变量的使用中,多分的分类变量ID3和C4.5层级之间只能单次使用,CART可多次重复使用。

多变量决策树

决策树所形成的分类边界是由若干个与轴平行的分段组成。

多变量决策树:不是为每个非叶节点寻找一个最优划分属性,而是试图建立一个合适的线性分类器。

决策树优缺点:

优点:

1:决策树易于理解和实现. 人们在通过解释后都有能力去理解决策树所表达的意义

2:对于决策树,数据的准备往往是简单或者是不必要的 其他的技术往往要求先把数据一般化,比如去掉多余的或者空白的属性。

3:决策树算法的时间复杂度(即预测数据)是用于训练决策树的数据点的对数

4:能够同时处理数据型和常规型属性。 其他的技术往往要求数据属性的单一

5:是一个白盒模型如果给定一个观察的模型,那么根据所产生的决策树很容易推出相应的逻辑表达式。相比之下,在一个黑盒子模型(例如人工神经网络),结果可能更难以解释

6:易于通过静态测试来对模型进行评测。

7:在相对短的时间内能够对大型数据源做出可行且效果良好的结果

8:效率高,决策树只需要一次构建,反复使用,每一次预测的最大计算次数不超过决策树的深度。

缺点:

1:决策树算法学习者易创建复杂的树,导致过拟合。为了避免这种问题,出现了剪枝的概念,即设置一个叶子结点所需要的最小数目或者设置树的最大深度。过拟合的原因有以下几点: a).噪音数据:训练数据中存在噪音数据,决策树的某些节点有噪音数据作为分割标准,导致决策树无法代表真实数据;b).缺少代表性数据:训练数据没有包含所有具有代表性的数据,导致某一类数据无法很好的匹配,这一点可以通过观察混淆矩阵(Confusion Matrix)分析得出;c).多重比较(Mulitple Comparition),这一情况和决策树选取分割点类似,需要在每个变量的每一个值中选取一个作为分割的代表,所以选出一个噪音分割标准的概率是很大的。

2:决策树的结果可能是不稳定的,在数据中一个很小的变化可能导致生成一个完全不同的树,这个问题可以通过使用集成决策树来解决。

3:学习一棵最优决策树是NP难的问题。实际决策树学习算法是基于启发式算法,如贪婪算法,寻求在每个节点上的局部最优决策。这样的算法不能保证返回全局最优决策树。

4:有一些概念是决策树不能很轻易的表达它们,比如说异或校验或复用的问题。

5:忽略数据集中属性之间的相关性,在处理特征关联性比较强的数据时表现得不是太好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>