您的当前位置:首页正文

【数据挖掘】监督学习---决策树

2024-11-11 来源:个人技术集锦

决策树定义

决策树及对某个数据集选择某个特征进行分裂,由此对数据进行分类

决策树构建过程

特征选择:从训练数据的特征中选择一个特征作为当前节点的分裂标准
决策树生成:根据所选特征评估标准,从上之下递归地生成子节点,直到数据集不可分则停止决策树生长
剪枝:如果决策树层次过于深,就会造成过拟合,需要剪枝来缩小树的结构和规模(包括预剪枝和后剪枝)
注意:决策树的生成是一个递归地过程,在决策树的基本算法中,有三种情况会导致递归返回:
1.当前节点包含的样本权属于同一类别,不需要划分了
2.当前属性集为空,或是所有样本在所有属性上取值相同,无法划分
3.当前节点包含的样本集为空不能划分

决策树算法

ID3通过信息增益
C4.5通过信息增益率
CART通过基尼系数
  • ID3
    以信息论为基础,以信息熵和信息增益为衡量标准,从而实现对数据的归纳分类;
    核心思想:以信息增益度量属性选择,选择分裂后信息增益最大的属性进行分裂
信息熵
信息增益

实例:

按年龄
按收入
按学生
按信用
最后比较信息增益结果第一次按照年龄进行分裂

第三步确定第二次分裂的属性:
分裂过程与第一次相同,基于年龄的数据集之上


直到最后节点只有同类的数据就停止分裂

决策树优点概念简单,计算复杂度不高,可解释性强,输出结果易于理解;数据的准备工作简单;对中间值的缺失不敏感;应用范围广
决策树的缺点可能会产生过度匹配的问题;信息缺失时处理起来比较困难;信息增益来度量会偏向于取值较多的属性作为分类属性
  • C4.5
    ID3算法,属性只能是离散的,C4.5是对ID3算法的优化
改进1用信息增益率代替信息增益来选择属性,克服了用信息增益选择属性时偏向选择取值多的属性不足:
改进2能够完成对连续值属性的离散化处理
改进3能处理属性值缺失的情况
改进4在决策树构造完成之后进行剪枝:预剪枝:事先设定需要生成几个层次的树,在完全正确分类训练集之后就停止树的生长;后剪枝:先让整棵树生成,然后再剪枝

实例:

  • CART
    ID3只适用于分类,通过每一列的信息增益
    C4.5通过每一列的信息增益率
    而CART通过分类之后的纯度-----基尼系数
    分裂的目的是为了能够让数据变纯,使决策树输出的结果更接近真实值,如果是分类树,CART采用GINI值衡量节点纯度,如果是回归树,采用样本方差衡量节点纯度,节点越不纯,节点分类或者预测的效果就越差;
    CART既可以做分类又可以做回归,是一颗二叉树

    实例:
按有房情况
按婚姻状况

连续值的分裂:

ID3python实现

# -*- coding: utf-8 -*-
"""
Created on Sat Mar  7 11:58:40 2020

@author: DELL
"""
#定义函数,构造函数数据集
import operator
from math import log
import subprocess
from sklearn import tree
def createDataSet():
    labels =["头发","声音"]#两个特征
    dataSet=[['长','粗','男'],
             ['短','粗','男'],
             ['短','粗','男'],
             ['长','粗','女'],
             ['长','细','女'],
             ['短','粗','女'],
             ['短','细','女'],
             ['长','粗','女']]
    
 
    return dataSet,labels
    
    
#获取函数类型的类别
def createTree(dataSet,lables):
    #获取每个类别男生数量,女生数量
    classlist=[example[-1] for example in dataSet]
    #print(classlist)
    #统计每个类别的人数classlist.count(classlist)
    #判断 是否停止树的计算
    #第一种情况,如果类别人数和总人数相等,不进行下述计算
    if classlist.count(classlist[0])==len(classlist):
        return classlist[0]
    #第二种情况,如果数据集中只有一个人  也不进行计算
    if len(dataSet[0]) == 1:
        return majorityCnt(classlist)
    #选择分裂类型
    bestFeat = chooseBestFeatureTosplit(dataSet)
    bestFeatlable = lables[bestFeat]
    myTree = {bestFeatlable:{}}
    del(lables[bestFeat])
    featValues=[example[bestFeat]  for example in dataSet]
    uniqueVals=set(featValues)
    for value in uniqueVals:
        subLabels=lables[:]
        myTree[bestFeatlable][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels)
    return myTree
    
    
def chooseBestFeatureTosplit(dataSet):
    #获取特征列的个数("长",“粗”)两列
    numFeature = len (dataSet[0])-1
    #对整个数据集 计算 信息熵
    baseEntropy = calcShannonEnt(dataSet)
    #对每一列计算条件信息熵
    bestInfoGain = 0
    bestFeature = -1
    #把两列属性获取出来
    for i in range (numFeature):
        featlist = [example [i] for example in dataSet]
        #print(featlist)
        '''['长', '短', '短', '长', '长', '短', '短', '长']
           ['粗', '粗', '粗', '粗', '细', '粗', '细', '粗']'''
        #获取每个特征里面 有的属性值  长的一列  粗的一列
        uniqueVals = set(featlist)
        #print(uniqueVals)
        '''{'长', '短'}
{'粗', '细'}'''
        newEntroy = 0
        #统计每个类别的  样本的 数量--->得出信息熵
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet,i,value)#对哪个数据集,哪个类别,哪个属性
        
        #计算 条件信息熵
            prob = len(subDataSet)/float(len(dataSet))
            newEntroy += prob*calcShannonEnt(subDataSet)
        #信息增益  信息熵-条件信息熵
        infoGain= baseEntropy - newEntroy
        #比较两列属性的信息增益
        #比较出最大的信息增益
        if(infoGain > bestInfoGain):
            bestInfoGain=infoGain
            bestFeature = i
        return bestFeature
    
#计算信息熵   
#按照某个特征值  对数据集进行划分,希望获取某个类的总个数
#返回和某个类别一样的所有数据“长”, 去除掉 当前的属性列   所有数据集
def splitDataSet(dataSet,i,value):
    retDataSet=[]
    for vote in dataSet:
        if vote[i]==value:
            reduceFeatVec = vote[:i]
            reduceFeatVec.extend(vote[i+1:])
            retDataSet.append(reduceFeatVec)
    return retDataSet
                  
def calcShannonEnt(dataSet):
    classCount = {}
    #遍历整个 数据集   各个类别的人数统计出来
    for vote in dataSet:
        currentLabel = vote [-1]
    if currentLabel not in classCount.keys():
        classCount[currentLabel]=0
    classCount[currentLabel] += 1
#得到 (男,3) (女,4)这种类别
#根据  信息熵的计算方式 得到信息熵
    shannonEnt = 0
    for key in classCount:
        #获取整个数据集的大小-----14
        numEntries = len(dataSet)
        prob = float(classCount[key])/numEntries
        shannonEnt -= prob*log(prob,2)#基础的信息熵
    return shannonEnt
        

def majorityCnt(classlist):
    classcount ={}
#在classlist男和女进行遍历
    for vote in classlist:
        if vote not in classcount.keys():
            #如果没有男或女这个标签就添加并加一
            classcount[vote] = 0
        classcount[vote] += 1
        sortedClasscount=sorted(classcount.items(),key=operator.itemgetter(1),reverse=True)
    #print (sortedClasscount[0][0])
    return sortedClasscount[0][0]
    
    

if __name__=='__main__':
    dataSet,lables = createDataSet()
    
    #print(dataSet)
    print(createTree(dataSet,lables))
   
     

            
显示全文