机器学习之决策树

一颗决策树通常包括一个根节点,若干内部节点,若干叶节点。叶节点对应决策结果,其他节点则对应一个属性测试,每个节点包含的样本集合根据属性测试划分到子节点当中;根节点包含全部样本集。从根节点到每个叶节点的路劲对应了一个判定序列。其策略为“分而治之“。

1 划分选择

1.1 信息增益

信息熵:度量样本集合纯度的一种常用指标,假定当前样本集合D中第k类样本所占的比列为p_{k}(k=1,2,....,\left | Y \right |),则D的信息熵定义为

Ent(D) = -\sum_{k=1}^{m}p_{k}log_{2}p_{k}

Ent(D)的值越小,则表示D的纯度越高。

信息增益:假定离散属性a有V个可能的取值\left \{ a^{1} ,a^{2} ,....,a^{V} \right \},若使用a来对样本集D进行划分,则会产生V个分支,其中第v个分支节点包含了D中所有在属性a上取值为a^{V}的样本,记为D^{v}.可以根据上式计算出D^{v}的信息熵,考虑到不同节点所包含的样本数不同,给分支点赋予权重\left | D^{v} \right |/\left | D \right |,样本数越多分支结点影响越大,于是可计算出用属性a对样本集D进行划分所获得的”信息增益“

Gain(D,a) = Ent(D) - \sum_{v=1}^{V}\frac{\left | D^{v} \right |}{\left | D \right |}Ent(D^{v})

一般而言,信息增益越大,则意味着使用属性a来划分所获得的”纯度提升“越大,因此选择属性a_{*} =\underset{a\in A}{arg\: max}\; Gain(D,a).

ID3决策树学习算法就是基于信心增益准则来划分属性的。

代码如下:

计算每个属性中取值的个数,比如色泽中青绿、乌黑、浅白的数量:

def calculateLable(label_arr):
    label_count={}
    for label in label_arr:
        if label in label_count:
            label_count[label] += 1
        else:
            label_count[label] = 1
    return label_count

计算信息熵

'''
InfoEnt(label_arr)计算信息熵
'''

def InfoEnt(label_arr):
    n = len(label_arr)
    ent = 0
    label_count = calculateLable(label_arr)
    for key in label_count:
        ent -= (label_count[key]/n) * (np.log2(label_count[key]/n))
    return ent

计算信息增益

#计算信息增益

def InfoGain(df,index):
    info_gain = InfoEnt(df.values[:,-1])
    divide_value = 0
    n = len(df[index]) #获取总数 eg:17
    if df[index].dtype == "float64":
        #初熵�化一熵字典用来存放二分之后的值所对应的信熵增益
        sub_info_ent = {}
        #指定熵 按找值进行排熵
        df = df.sort_values([index],ascending=True)
        df = df.reset_index(drop=True)
        data_arr = df[index]
        labele_arr = df[df.columns[-1]]
        for i in range(n-1):
            div = (data_arr[i] + data_arr[i + 1]) / 2
            sub_info_ent[div] = ((i+1) / n * InfoEnt(labele_arr[0:i+1])) + ((n-i-1) / n * InfoEnt(labele_arr[i+1:-1]))
        divide_value , sub_info_min = min(sub_info_ent.items(),key = lambda x: x[1])
        info_gain -= sub_info_min
    else:
        #离散性变量的信息增益计算方式
        atrribute_value = calculateLable(df[index]) #计算集合在属性上各个取值结果的熵熵 eg:{"青绿"熵6,"乌黑"熵6熵""浅白"熵5}
        data_arr = df[index]  #获得属性上的所有熵
        classlabel = df[df.columns[-1]] #获取最后一列类型熵        
        for key in atrribute_value:
            value = classlabel[data_arr == key]  #获取数据集在某一属性上的取值的类别标熵?
            info_gain -= (atrribute_value[key]/n)*InfoEnt(value)  # 计算信息增益    
    return info_gain, divide_value

比较各个属性的信息增益,选择信息增益值最大的属熵

def optimalAtrribute(df):
    info_gain = 0
    for index in df.columns[1:-1]:
        info_gain_temp, div_value_tmp = InfoGain(df,index)
        if info_gain_temp > info_gain:
            optAtrribute = index
            info_gain = info_gain_temp
            div_value = div_value_tmp
    return optAtrribute, div_value

1.2增益率

信息增益准则对可取值数目比较多的属性有所偏好,C4.5决策树算法使用增益率来选择最优划分属性。增益率表示为

Gain-ratio(D,a) = \frac{Gain(D,a)}{IV(a)}

其中IV(a) = -\sum_{v=1}^{V}\frac{\left | D^{v} \right |}{\left | D \right |}log_{2}\frac{\left | D^{v} \right |}{\left | D \right |}成为属性a的固有值。属性a的可能取值数目越多,则IV(a)的值通常会越大,增益率准则对取值数目比较少的属性有所偏好,C4.5算法不是直接选择增益率最大的候选划分属性,而是使用一个启发式:先从候选划分属性中找出信息增益高于平均水平的属性,在从中选择增益率最高的。

1.3 基尼指数

CART决策树采用”基尼指数“来选择划分属性,数据集D的纯度可以用基尼值来度量:

Gini(D) = \sum_{k=1}^{\left | Y \right |}\sum_{k \neq {k}' }p_{k}p_{​{k}'} = 1- \sum_{k=1}^{\left | Y \right |}p_{k}^{2}

直观来说,基尼指数反映了从数据集D中随机抽取两个样本,其类别不一致的概率,因此,基尼指数越小,则数据集D的纯度越高。

属性a的基尼指数定义为

Gini-index(D,a) = \sum_{v=1}^{V}\frac{\left |D^v \right |}{\left |D \right |}Gini(D^v)

代码如下:

def Gini(arr):
    gini = 1
    n = len(arr)
    label_count = calculate(arr)
    for key in label_count:
        gini -= (label_count[key]/n) * (label_count[key]/n)
    return gini


def Gini_index(df, attr_id):
    gini_index = 0
    div_value = 0
    data_arr = df[attr_id]
    n = len(data_arr)
    if df[attr_id].dtype == float:
        sub_gini = {}  # store the div_value (div) and it's subset gini value
        
        df = df.sort([attr_id], ascending=1)  # sorting via column
        df = df.reset_index(drop=True)
        
        data_arr = df[attr_id]
        label_arr = df[df.columns[-1]]
        
        for i in range(n-1):
            div = (data_arr[i] + data_arr[i+1]) / 2
            sub_gini[div] = ( (i+1) * Gini(label_arr[0:i+1]) / n ) \
                              + ( (n-i-1) * Gini(label_arr[i+1:-1]) / n )
        # our goal is to get the min subset entropy sum and it's divide value
        div_value, gini_index = min(sub_gini.items(), key=lambda x: x[1])
    else:
        
        label_arr = df[df.columns[-1]]
        data_count = calculate(data_arr)        
        for key in data_count:
            key_label_arr = label_arr[data_arr == key]
            gini_index = data_count[key]/n * Gini(key_label_arr)
    return gini_index, div_value

决策树算法

class Node(object): 
    def __init__(self, attr_init=None, label_init=None, attr_down_init={} ):  
        self.attr = attr_init  
        self.label = label_init 
        self.attr_down = attr_down_init 

# 生成决策树
def generateTree(df):
    #生成父节点
    new_node = Node(None,None,{})
    # 类别
    label_arr = df[df.columns[-1]]
    #计算各个类别的数量,用字典存 
    label_count = calculateLable(label_arr)

    if label_count:  # assert the label_count isn's empty
        new_node.label= max(label_count, key=label_count.get) 
            
        # end if there is only 1 class in current node data
        # end if attribution array is empty
        if len(label_count) == 1 or len(label_arr) == 0:
            return new_node
        
        # get the optimal attribution for a new branching
        new_node.attr, div_value = optimalAtrribute(df)
        
        # recursion
        if div_value == 0:  # categoric variable
            value_count = calculateLable(df[new_node.attr]) 
            for value in value_count:
                df_v = df[ df[new_node.attr].isin([value]) ]  # get sub set
                # delete current attribution
                df_v = df_v.drop(new_node.attr, 1)  
                new_node.attr_down[value] = generateTree(df_v)
                
        else:  # continuous variable # left and right child
            value_l = "<=%.3f" % div_value
            value_r = ">%.3f" % div_value
            df_v_l = df[ df[new_node.attr] <= div_value ]  # get sub set
            df_v_r = df[ df[new_node.attr] > div_value ]
 
            new_node.attr_down[value_l] = generateTree(df_v_l)
            new_node.attr_down[value_r] = generateTree(df_v_r)
    return new_node

2 剪枝处理

剪枝处理是决策树算法对付过拟合的主要手段。基本策略有”预剪枝“和”后剪枝“。

预剪枝是值在决策树生成过程中,对每个结点在划分的前进行估计,若当前结点的划分不能带来决策树泛化性能提升,则停止划分并将当前结点标记为叶节点。不仅降低了过拟合的风险,还显著减少了训练时间和测时间,但是预剪枝基于贪心本质禁止这些分支展开,给预剪枝带来欠拟合的风险。

后剪枝则是先从训练集生成一颗完成的决策树,然后自底向上地对非叶结点进行考察,若该结点对应的子树替换为叶节点能带来决策树泛化能力的提升,则将该子树替换为叶结点。后剪枝欠拟合风险较小,泛化性能往往优于预剪枝决策树,但是后剪枝决策树实在完全决策树之后进行的,并且要自底向上对所有非叶节点注意考察,训练时间要比预剪枝大得多。

3 连续值和缺失值

3.1连续值

可以把连续属性离散化,例如二分法对连续值进行处理。

给定样本集D和连续属性a,假定a在D上出现了n个不同的取值,将这些值从小到大进行排序,记为\left \{ a^1,a^2,...,a^n \right \},基于划分点t可将样本集D分为子集D_{t}^{+}D_{t}^{-},分别表示那些在属性a上大于和小于t的样本,对于连续属性a可考察包含 n-1 个元素的候选划分点集合

T_{a} = \left \{ \frac{a^i+a^{i+1}}{2}|1\leq i\leq n-1 \right \}      即把区间的中位点作为候选点。

Giant(D,a) = \underset{t\in T_a}{max} \: Gain(D,a,t)= \underset{t\in T_a}{max} \: Ent(D) - \sum_{\lambda \in \left \{ -,+ \right \}}\frac{\left |D_t^\lambda \right | }{\left |D \right |}Ent(D_t^\lambda )

 

3.2 缺失值

对于缺失值我们需要解决两个问题

(1)如何在属性值缺失的情况下进行划分属性

(2)给定划分属性,若该样本在属性上的值缺失,如何对样本进行划分?

对于问题(1),给定训练集D和属性a,令\tilde{D }表示D上没有缺失值的样本子集,假定属性aV个可取值\left \{ a^1,a^2,....,a^V \right \},令\tilde{D}^v表示\tilde{D }中属性a上取值为a^v的样本子集,\tilde{D}_{k}表示\tilde{D }中属于第k类的样本子集。假定我们为每个样本\mathbf{x}赋予一个权重w_\mathbf{x},并且定义:

\rho = \frac{\sum_{\mathbf{x} \in \tilde{D}}w_\mathbf{x}}{\sum_{\mathbf{x} \in D}w_\mathbf{x}}    对于属性a,\rho表示无缺失值样本所占的比列

\tilde{p_k} =\frac{\sum_{\mathbf{x} \in \tilde{D}_k}w_\mathbf{x}}{\sum_{\mathbf{x} \in D}w_\mathbf{x}} 对于属性a,\tilde{p_k}表示无缺失值样本中第k类所占的比列

\tilde{r}_v =\frac{\sum_{\mathbf{x} \in \tilde{D}^v}w_\mathbf{x}}{\sum_{\mathbf{x} \in D}w_\mathbf{x}}  对于属性a,\tilde{r}_v表示无缺失值样本中属性a上取值为a^v的样本所占的比例。

基于上述定义,将信息增益计算式推广为Gain(D,a) = \rho \times Gain(\tilde{D},a) = \rho \times \left ( Ent(\tilde{D}) - \sum_{v=1}^{V}Ent(\tilde{D}^v) \right ),其中Ent(\tilde{D}) = - \sum_{k =1}^{\left |Y \right |}\tilde{p_k}log_{2}\tilde{p_k}

对于问题(2)若样本\mathbf{x}在划分属性a上的取值未知,则将\mathbf{x}同时划分进所有子结点,且样本权值在与属性值a^v对应的子节点中调整为\tilde{r_v}\cdot w_\mathbf{x},直观来看这就是让用一个样本以不同的概率划入不同的子结点中。

4 多变量决策树

多变量决策树不再是仅对某个属性,而是对属性的线性组合进行测试,换言之,每个非叶节点都是一个线性分类器。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
决策树是常用的机器学习算法之一,通过对数据的分类和特征值计算来完成对未知数据的预测。本文将介绍使用Python实现决策树算法的相关步骤。 首先,需要导入决策树算法工具包,使用以下代码: ```python from sklearn import tree ``` 然后,导入训练数据和测试数据,并进行预处理。为了方便起见,在本文中采用生成随机数的方式来生成样本数据,使用以下代码: ```python from sklearn.datasets import make_classification X, y = make_classification(n_samples=100, n_features=4, n_classes=2, n_informative=2, n_redundant=0, random_state=0, shuffle=False) ``` 接下来,使用生成的样本数据进行模型训练。这里使用scikit-learn中的DecisionTreeClassifier()函数。 ```python clf = tree.DecisionTreeClassifier() clf = clf.fit(X, y) ``` 训练后,调用predict()方法进行对测试数据的预测,使用以下代码: ```python y_pred = clf.predict(X) ``` 最后,评估模型的准确率,使用以下代码: ```python from sklearn.metrics import accuracy_score print(accuracy_score(y, y_pred)) ``` 这就是使用Python实现决策树算法的基本过程。决策树可以根据数据中的不同特征进行分类,是一个简单且常用的分类算法。决策树算法也可用于回归问题,例如预测一个数的大小。与其他机器学习算法相比,决策树具有易于理解和可解释的优点,同时还可以处理非线性的分类问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值