《统计学习方法》笔记——决策树(二):CART

CART算法

  CART(分类与回归树)是在给定输入随机变量X条件下输出随机变量Y的条件概率分布的学习方法。
  CART假设决策树是二叉树,内部结点特征的取值是“是”和“否”,左分支是取值为“是”的分支,右分支是取值为“否”的分支。
  这样的决策树等价于递归地二分每个特征,将输入空间即特征空间划分为有限个单元,并在这些单元上确定预测的概率分布,也就是在输入给定的条件下输出条件概率分布。
  CART算法由以下两步组成:
(1)决策树生成:基于训练数据集生成决策树,生成的决策树要尽量大;
(2)决策树剪枝:用验证数据集对一生成的树进行剪枝并选择最优子树,这时用损失函数最小作为剪枝的标准。

CART生成

决策树的生成就是递归地构建二叉决策树的过程。

  • 对回归树用平方误差最小化准则,进行特征选择,生成二叉树
  • 对分类树用基尼指数最小化准则,进行特征选择,生成二叉树

回归树的生成

最小二乘回归树生成算法
输入:训练数据集D;
输出:回归树 f ( x ) f(x) f(x)
  在训练数据集所在的输入空间中,递归地将每个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树:
(1)选择最优切分变量j与切分点s,求解
min ⁡ j , s [ min ⁡ c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min ⁡ c 2 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] \min\limits_{j,s}[\min\limits_{c_1}\sum\limits_{x_i \in R_1(j,s)}{(y_i-c_1)^2} + \min\limits_{c_2}\sum\limits_{x_i \in R_2(j,s)}{(y_i-c_2)^2}] j,smin[c1minxiR1(j,s)(yic1)2+c2minxiR2(j,s)(yic2)2]
遍历变量j,对固定的切分变量j扫描切分点s,选择使上式达到最小值的对 ( j , s ) (j,s) (j,s)
(2)用选定的对 ( j , s ) (j,s) (j,s)划分区域并决定相应的输出值:
R 1 ( j , s ) = { x ∣ x j ≤ s } , R 2 ( j , s ) = { x ∣ x j > s } R_1(j,s) = \{x|x^{j} \leq s\}, R_2(j,s) = \{x|x^{j} > s\} R1(j,s)={xxjs},R2(j,s)={xxj>s}
c ^ m = 1 N m ∑ x i ∈ R m ( j , s ) y i , x ∈ R m , m = 1 , 2 \hat{c}_m = \frac{1}{N_m}\sum\limits_{x_i \in R_m(j,s)}{y_i}, \quad x \in R_m, \quad m=1,2 c^m=Nm1xiRm(j,s)yi,xRm,m=1,2
(3)继续对两个子区域调用步骤(1),(2),直至满足停止条件。
(4)将输入空间划分为M个区域 R 1 , R 2 , . . . , R M R_1,R_2,...,R_M R1,R2,...,RM,生成决策树:
f ( x ) = ∑ m = 1 M c ^ m I ( x ∈ R m ) f(x) = \sum\limits_{m=1}^M{\hat{c}_m{I(x \in R_m)}} f(x)=m=1Mc^mI(xRm)

分类树的生成

基尼指数
分类问题中,假设有K个类,样本点属于第k类的概率为 p k p_k pk,这概率分布的基尼指数定义为
G i n i ( p ) = ∑ k = 1 K p k ( 1 − p k ) = 1 − ∑ k = 1 K p k 2 Gini(p) = \sum\limits_{k=1}^K{p_k(1-p_k)} = 1 - \sum\limits_{k=1}^K{p_k^2} Gini(p)=k=1Kpk(1pk)=1k=1Kpk2
对于二分类问题,若样本点属于第1个类的概率是 p p p,这概率分布的基尼指数为
G i n i ( p ) = 2 p ( 1 − p ) Gini(p) = 2p(1-p) Gini(p)=2p(1p)
对于给定的样本集合D,其基尼指数为
G i n i ( D ) = 1 − ∑ k = 1 K ( ∣ C k ∣ ∣ D ∣ ) 2 Gini(D) = 1- \sum\limits_{k=1}^K{(\frac{|C_k|}{|D|})^2} Gini(D)=1k=1K(DCk)2
这里, C k C_k Ck是D中属于第k类的样本子集,K是类的个数。

如果样本集合D根据特征A是否取某一可能值a被分割成 D 1 D_1 D1 D 2 D_2 D2两部分,即
D 1 = { ( x , y ) ∈ D ∣ A ( x ) = a } , D 2 = D − D 1 D_1 = \{(x,y) \in D|A(x)=a\}, \quad D_2 = D - D_1 D1={(x,y)DA(x)=a},D2=DD1
则在特征A的条件下,集合D的基尼指数定义为
G i n i ( D , A ) = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) Gini(D,A) = \frac{|D_1|}{|D|}Gini(D_1) + \frac{|D_2|}{|D|}Gini(D_2) Gini(D,A)=DD1Gini(D1)+DD2Gini(D2)

  • 基尼指数 G i n i ( D ) Gini(D) Gini(D)表示集合D的不确定性
  • 基尼指数 G i n i ( D , A ) Gini(D,A) Gini(D,A)表示经 A = a A=a A=a分割后集合D的不确定性。
  • 基尼指数值越大,样本集合的不确定性也就越大,这一点与熵相似。

CART生成算法
输入:训练数据集D,停止计算的条件;
输出:CART决策树。
根据训练数据集,从根结点开始,递归地对每个结点进行以下操作,构建二叉决策树:
(1)设结点的训练数据集为D,计算现有特征对该数据集的基尼指数。
  对每一个特征A,对其可能取的每个值a。根据样本点对 A = a A=a A=a的测试为“是”或“否”将D分割成 D 1 D_1 D1 D 2 D_2 D2两部分,计算 A = a A=a A=a时的基尼指数。
(2)在所有可能的特征A以及它们所有可能的切分点a中,选择基尼指数最小的特征及其对应的切分点,将训练数据集依特征分配到两个子结点中去。
(3)对两个子结点递归地调用(1),(2),直至满足停止条件。
(4)生成CART决策树。
  算法停止计算的条件是结点中的样本个数小于预定阈值,或样本集的基尼指数小于预定阈值(样本基本属于同一类),或者没有更多特征。

CART剪枝

CART剪枝算法由两步组成:
(1)从生成算法产生的决策树 T 0 T_0 T0底端开始不断剪枝,直到 T 0 T_0 T0的根结点,形成一个子树序列 { T 0 , T 1 , . . . , T n } \{T_0,T_1,...,T_n\} {T0,T1,...,Tn}
(2)通过交叉验证法在独立的验证集上对子树序列进行测试,从中选择最优子树。
CART剪枝算法
输入:CART算法生成的决策树 T 0 T_0 T0
输出:最优决策树 T α T_{\alpha} Tα
(1)设 k = 0 , T = T 0 k = 0, \quad T = T_0 k=0,T=T0
(2)设 α = + ∞ \alpha = +\infty α=+
(3)自下而上地对各内部结点t计算 C ( T t ) C(T_t) C(Tt) ∣ T t ∣ |T_t| Tt以及
g ( t ) = C ( t ) − C ( T t ) ∣ T t ∣ − 1 g(t) = \frac{C(t) - C(T_t)}{|T_t| - 1} g(t)=Tt1C(t)C(Tt)
α = min ⁡ ( α , g ( t ) ) \alpha = \min(\alpha, g(t)) α=min(α,g(t))
这里, T t T_t Tt表示以t为根结点的子树, C ( T t ) C(T_t) C(Tt)是对训练数据的预测误差, ∣ T t ∣ |T_t| Tt T t T_t Tt的叶结点个数。
(4)对 g ( t ) = α g(t) = \alpha g(t)=α的内部结点t进行剪枝,并对叶结点t以多数表决法决定其类,得到树T。
(5)设 k = k + 1 , α k = α , T k = T k = k+1, \quad \alpha_k = \alpha, \quad T_k = T k=k+1,αk=α,Tk=T
(6)如果 T k T_k Tk不是由根结点及两个叶结点构成的树,则回到步骤(2);否则另 T k = T n T_k = T_n Tk=Tn
(7)采用交叉验证法在子树序列 T 0 , T 1 , . . . , T n T_0,T_1,...,T_n T0,T1,...,Tn中选取最优子树 T α T_{\alpha} Tα

Python自编程实现

def gini(dataset):
    """计算样本集合的基尼指数"""
    prob = dataset.iloc[:,-1].value_counts()/dataset.shape[0]
    return 1-prob.apply(lambda p: p*p).sum()

def condition_gini(dataset,feature, feature_value):
    """计算特征条件下,样本集合的基尼指数"""
    sub_dataset1 = dataset[dataset[feature]==feature_value]
    sub_dataset2 = dataset[dataset[feature]!=feature_value]
    sub_len1 = sub_dataset1.shape[0]
    sub_len2 = sub_dataset2.shape[0]
    data_len = dataset.shape[0]
    return sub_len1/data_len*gini(sub_dataset1) + sub_len2/data_len*gini(sub_dataset2)
def choose_feature_pair(dataset,features):
    res = None
    min_gini = 1
    for feature in features:
        for value in dataset[feature].unique():
            gini_value = condition_gini(dataset, feature, value)
            print("feature:{0}, value:{1}, gini:{2}".format(feature, value, gini_value))
            if min_gini >= gini_value:
                min_gini = gini_value
                res = (feature, value, gini_value)
    print("基尼指数最小的特征:{0},及最优切分点为:{1}".format(res[0], res[1]))
    return res
import numpy as np
import pandas as pd
features=["年龄","有工作","有自己的房子","信贷情况"]
X_train=np.array([
                  ["青年", "否", "否", "一般"],
                  ["青年", "否", "否", "好"],
                  ["青年", "是", "否", "好"],
                  ["青年", "是", "是", "一般"],
                  ["青年", "否", "否", "一般"],
                  ["中年", "否", "否", "一般"],
                  ["中年", "否", "否", "好"],
                  ["中年", "是", "是", "好"],
                  ["中年", "否", "是", "非常好"],
                  ["中年", "否", "是", "非常好"],
                  ["老年", "否", "是", "非常好"],
                  ["老年", "否", "是", "好"],
                  ["老年", "是", "否", "好"],
                  ["老年", "是", "否", "非常好"],
                  ["老年", "否", "否", "一般"]
                  ])
y_train=np.array(["否","否","是", "是", "否", "否", "否", "是", "是", "是", "是", "是", "是", "是", "否"])
X = pd.DataFrame(X_train)
y = pd.DataFrame(y_train)
dataset = pd.concat((X,y),axis=1)
dataset.columns = ["年龄","有工作","有自己的房子","信贷情况","类别"]

在这里插入图片描述
下面是构建CART树

class CartTree(object):
    def __init__(self, labels):
        self.node_id = 0
        self.lables = labels
        
    def get_value(self,dataset):
        value = []
        for item in self.lables:
            value.append(dataset[dataset.iloc[:,-1]==item].shape[0])
        return value
        
    def build_tree(self, dataset, features, attribute=None, ep=0.05):
        """这里停止条件是,样本集gini指数小于某个阈值ep"""
        value=self.get_value(dataset)
        dataset_gini = gini(dataset)
        # 1.如果数据集中所有实例属于同一类 
        if dataset.iloc[:,-1].value_counts().shape[0] == 1:
            label = dataset.iloc[0,-1]
            return Node(self.node_id,None,None,attribute,label,value,dataset_gini)
        # 2如果特征为空,或数据集在属性上都相同,
        label = dataset.iloc[:,-1].value_counts().idxmax()
        if len(features)==0 or dataset.iloc[:,0:-1].value_counts().shape[0]==1:      
            return Node(self.node_id, None,None,attribute,label,value,dataset_gini)  
        # 4.如果小于阈值
        if dataset_gini < ep:
            return Node(self.node_id,None,None,attribute,label,value,dataset_gini)
        # 3.选择特征
        choosed_feature, feature_type, feature_gini = choose_feature_pair(dataset, features)  
        
        # 6.分割子集,递归调用
        children = []
        new_features = features.copy()
        new_features.remove(choosed_feature)
        # 复制当前结点名称
        this_node = self.node_id
        # 数据集满足A=a被分配到左结点
        left_dataset = dataset[dataset[choosed_feature]==feature_type]
        self.node_id += 1
        left_node = self.build_tree(left_dataset, new_features,"是", ep)
        # 数据集满足A!=a被分配到左结点
        right_dataset = dataset[dataset[choosed_feature]!=feature_type]
        self.node_id += 1
        right_node = self.build_tree(right_dataset, new_features, "否", ep)
        children.append(left_node)
        children.append(right_node)
        # 根结点或内部结点    
        return Node(this_node,children, "{0}={1}".format(choosed_feature,feature_type),attribute,label, value,dataset_gini)

在这里插入图片描述
代码中的Node及画决策树的代码见上篇(https://blog.csdn.net/jasmine0244/article/details/113704872)。
在这里插入图片描述
关于CART树剪枝部分的代码,后面再补充。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值