决策树原理和Python实现

决策树原理和Python实现

一个实现了ID3, C4.5, CART三种算法的完整样例的github源码地址:https://github.com/HenryLiu0/decision-tree-sample

1.算法原理

决策树算法是机器学习中常用的分类和回归算法,而决策树学习属于有监督学习。经典的决策树模型有 ID3,C4.5 和 CART 三种,它们的区别在于选择最佳特征的原则不同。

决策树如同它的名字,是一个树形的结构,它既可以是二叉树,也可以是非二叉树。决策树学习通过学习大量带有标签的样本,构建一个树模型。根据这个树模型,可以预测某条不含标签样本的结果。

在这里插入图片描述

构建决策树的过程可以分为几个步骤:

  1. 创建根结点;
  2. 判断当前的结点是否可以成为叶节点,这需要特定的边界条件。若是叶节点,根据多数原则判定此叶结点类别,然后此递归的分支结束,若不是,去往下一步骤;
  3. 遍历当前结点特征,根据特定原则(ID3,C4.5,CART)选择一个最佳的特征,根据这个特征将数据集划分成若干个子数据集,为每个子数据集创建一个子结点,每个子结点的特征删去选出的最佳特征;
  4. 递归建树,对每个子结点,去往步骤 2。

假设当前结点的数据集为D,特征集为A,那么构建决策树过程中递归的边界条件如下:

  1. D 中的所有样本标签属于同一类别C,则当前结点标记为C类叶结点;
  2. A 为空集,此时没有可用于选择的特征,这种情况出现在决策树的最后一层;D 中所有样本在 A 中所有特征上取值相同,此时所有特征都可称为最佳特征。以上两种情况都无法划分D,将当前结点标记为叶结点,类别为D中出现最多的类。
  3. D 为空集,发生这种情况是在当前结点的父结点选出的最佳特征的取值缺少某个类别的时候,比如下图中,attr1 结点中缺少 attr1 取值为 low 的样本,导致没有 low 路径。训练集样本不会有这条路径不代表测试集样本没有这条路径,不增加这条路径会引发测试错误。解决方案是每个结点需要依据所选特征的所有取值创建子结点。

在这里插入图片描述

选择最佳特征的经典方法有三种:

  1. 使用信息增益的方法(ID3),以 A 中信息增益最大的特征为最佳特征。首先计算数据集 D 的经验熵 H ( D ) = − ∑ d ∈ D p ( d ) log ⁡ p ( d ) H(D)=-\sum_{d\in D}p(d)\log p(d) H(D)=dDp(d)logp(d),再计算某个特征 a 对数据集 D 的条件熵 H ( D ∣ a ) = ∑ a ∈ A p ( a ) H ( D ∣ A = a ) H(D|a)=\sum_{a\in A}p(a)H(D|A=a) H(Da)=aAp(a)H(DA=a),最后计算信息增益 g ( D , a ) = H ( D ) = H ( D ∣ a ) g(D,a)=H(D)=H(D|a) g(D,a)=H(D)=H(Da)
  2. 用信息增益率的方法(C4.5),以 A 中信息增益率最大的特征为最佳特征,改善了 ID3 偏向多数样本的缺点。首先计算 a 对数据集 D 的信息增益 g ( D , a ) = H ( D ) − H ( D │ a ) g(D,a)=H(D)-H(D│a) g(D,a)=H(D)H(Da),然后计算数据集 D 关于特征 a 的值的熵 S p l i t I n f o ( D , a ) = − ∑ j = 1 v ∣ D j ∣ ∣ D ∣ × log ⁡ ⁡ ∣ D j ∣ ∣ D ∣ SplitInfo(D,a)=-\sum_{j=1}^v \frac{|D_j |}{|D|} ×\log⁡{\frac{|D_j |}{|D|}} SplitInfo(D,a)=j=1vDDj×logDDj,最后计算信息增益率 g R a t i o ( D , a ) = g ( D , a ) S p l i t I n f o ( D , a ) gRatio(D,a)=\frac{g(D,a)}{SplitInfo(D,a)} gRatio(D,a)=SplitInfo(D,a)g(D,a)
  3. 使用 Gini 系数的方法(CART),值越小表示不确定性越小,所以 A 中 Gini 系数最小的特征为最佳特征。数据集 D 的 Gini 系数就是 g i n i ( D , a ) = ∑ j = 1 v p ( a j ) × g i n i ( D j │ A = a j ) gini(D,a)=\sum_{j=1}^v p(a_j )\times gini(D_j│A=a_j ) gini(D,a)=j=1vp(aj)×gini(DjA=aj),其中 g i n i ( D j │ A = a j ) = ∑ ∑ i = 1 n p i ( 1 − p i ) = 1 − ∑ i = 1 n p i 2 gini(D_j│A=a_j )=∑\sum_{i=1}^n p_i (1-p_i ) =1-\sum_{i=1}^n p_i^2 gini(DjA=aj)=i=1npi(1pi)=1i=1npi2

2.伪代码

以下代码为递归创建决策树的伪代码。

def createTree(dataSet, Attribute, AttrUniValSet, algorithm = 'ID3'):
    """
    输入:当前结点数据集D,特征集A,特征集A每个特征a能取到的值的集合,最佳收益算法
    输出:当前结点后部分的决策树
    描述:递归创建决策树函数
    """
    labelList = [entry[-1] for entry in dataSet]
    # 1. 所有样本标签相同,那么该结点为记为该类的叶子结点
    if len(labelList) == labelList.count(labelList[0]):
        return labelList[0]
    # 2 数据集D没有可以分类的属性,那么,该结点标记为出现最多类的叶子结点
    if len(attr) == 0:
        # 返回出现次数最多的标签
        return Counter(labelList).most_common(1)[0][0]
    # bestAttrIndex是收益最大的属性的下标
    bestAttrIndex = selectBestAttrIndex(dataSet, algorithm)
    # bestAttr是收益最大属性
    bestAttr = attr[bestAttrIndex]
    # 构建字典树
    resTree = {bestAttr : {}}
    # 从A中删除收益最大属性,与split后的dataSet相同长度
    del(attr[bestAttrIndex])
    # valueSet 是 bestAttr 所有可能的取值
    valueSet = attrUniValSet[bestAttrIndex]
    # 删除
    del(attrUniValSet[bestAttrIndex])
    # 为每个value创建分支
    for value in valueSet:
        subDataSet = splitDataSet(dataSet, bestAttrIndex, value)
        # 3. 数据集为空,预测标签为父节点出现最多的标签
        if len(subDataSet) == 0:
            resTree[bestAttr][value] = Counter(labelList).most_common(1)[0][0]
        else:
            # 递归创建子树
            resTree[bestAttr][value] = createTree(subDataSet, cpyAttr, attrUniValSet, algorithm)
    return resTree

根据以上伪代码,可以画出如下流程图

在这里插入图片描述

3.关键代码

1)伪代码中出现过的选择最佳特征函数 selectBestAttrIndex,根据评价最佳(区分度最大)特征的标准不同而选择不同的函数。

def selectBestAttrIndex(dataSet, algorithm):  
    """ 
    输入:二维数据集D 
    输出:特定标准下A中最佳特征 
    """  
    if algorithm == 'ID3':  
        return selectBestAttrIndex_ID3(dataSet)  
    elif algorithm == 'C4.5':  
        return selectBestAttrIndex_C45(dataSet)  
    elif algorithm == 'CART':  
        return selectBestAttrIndex_CART(dataSet)  

2)ID3 方法函数,输入数据集D,返回最佳特征下标

def selectBestAttrIndex_ID3(dataSet):
    # label是特征attribute数目
    labelNum = len(dataSet[0])-1
    # 计算数据集D的经验熵
    oldEntropy = calEntropy(dataSet)
    # bestIndex是最佳特征在A中的下标
    bestIndex = -1
    # maxInfoGain用于保存最大信息增益
    maxInfoGain = 0.0
    for index in range(labelNum):
        newEntropy = 0.0
        # 获得dataSet中每个特征的所有value的列表
        attrValueList = [entry[index] for entry in dataSet]
        # 获得value列表的不重复set,遍历计算每个value的熵
        attrValueSet = set(attrValueList)
        for uniqueValue in attrValueSet:
            # 分离出col=index, value = uniqueValue 的数据集
            subDataSet = splitDataSet(dataSet, index, uniqueValue)
            # 计算子数据集占总数据比例
            p = float(len(subDataSet)) / len(dataSet)
            newEntropy += p * calEntropy(subDataSet)
        infoGain = oldEntropy - newEntropy
        if infoGain > maxInfoGain:
            maxInfoGain = infoGain
            bestIndex = index
    return bestIndex

3)C4.5方法函数,仅仅在 ID3 方法增加计算特征熵的语句

def selectBestAttrIndex_C45(dataSet):  
    labelNum = len(dataSet[0])-1  
    oldEntropy = calEntropy(dataSet)  
    bestIndex = -1  
    maxInfoGainRotio = 0.0  
    for index in range(labelNum):  
        newEntropy = 0.0  
        splitInfo = 0.0  
        attrValueList = [entry[index] for entry in dataSet]  
        attrValueSet = set(attrValueList)  
        for uniqueValue in attrValueSet:  
            subDataSet = splitDataSet(dataSet, index, uniqueValue)  
            p = float(len(subDataSet)) / len(dataSet)  
            newEntropy += p * calEntropy(subDataSet)  
            # 计算特征的熵  
            splitInfo -= p * log(p, 2)  
        infoGain = oldEntropy - newEntropy  
        if splitInfo == 0.0:  
            continue  
        # 计算信息增益率  
        infoGainRatio = infoGain / splitInfo  
        if infoGainRatio > maxInfoGainRotio:  
            maxInfoGainRotio = infoGainRatio  
            bestIndex = index  
    return bestIndex  

4)CART 方法函数

def selectBestAttrIndex_CART(dataSet):
    labelNum = len(dataSet[0])-1
    # 保存最佳特征下标
    bestIndex = -1
    # 保存最小Gini系数
    minGini = float("inf")
    for index in range(labelNum):
        attrValueList = [entry[index] for entry in dataSet]
        attrValueSet = set(attrValueList)
        newGini = 0.0
        for uniqueValue in attrValueSet:
            subDataSet = splitDataSet(dataSet, index, uniqueValue)
            p = float(len(subDataSet)) / len(dataSet)
            # 对每个子数据集计算Gini系数
            newGini += p * calGini(subDataSet)
        if newGini < minGini:
            minGini = newGini
            bestIndex = index
    return bestIndex

最后

您的点赞是对我最大的激励!

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
决策树是一种常用的机器学习算法,用于分类和回归问题。它基于一系列的决策规则来构建一个树状模型,通过对数据集的划分来进行预测。 决策树原理如下: 1. 特征选择:根据某个度量标准(如信息增益、基尼指数等),选择最优的特征作为根节点。 2. 决策节点划分:利用选定的特征进行数据划分,生成子节点。 3. 递归构建:对每个子节点递归地进行特征选择和划分,直到满足终止条件(如叶节点纯度达到一定阈值或无法再划分)。 4. 树剪枝:为了防止过拟合,可以通过后剪枝或预剪枝技术对决策树进行修剪。 下面是一个基于Python决策树示例实现: ```python from sklearn import datasets from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split # 加载数据集 iris = datasets.load_iris() X = iris.data y = iris.target # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # 创建决策树分类器 clf = DecisionTreeClassifier() # 拟合训练集 clf.fit(X_train, y_train) # 预测测试集 y_pred = clf.predict(X_test) # 计算准确率 accuracy = sum(y_pred == y_test) / len(y_test) print("准确率:", accuracy) ``` 以上代码使用`sklearn`库中的`DecisionTreeClassifier`类实现决策树分类器,通过加载Iris数据集进行训练和预测,并计算出准确率。你可以根据自己的需求进行修改和调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值