机器学习(三):决策树

1、决策树

1.1什么是决策树

有监督机器学习算法中的一类经典算法,是最经常使用的数据挖掘算法
它是一种典型的分类方法,首先对数据进行处理,利用归纳算法生成可读的规则和决策树,然后使用决策对新数据进行分析。本质上决策树是通过一系列规则对数据进行分类的过程。 典型算法有ID3,C4.5,CART等>

1.2和knn算法的区别

k-近邻算法的缺点是无法给出数据的内在含义

决策树:

  • 优点:计算复杂度不高,输出结果易于理解,对中间值的确实不敏感,可以处理并不相关特征数据
  • 缺点:可能会产生过度匹配问题
  • 使用数据类型:数值型,标称型

1.3伪代码

检测数据集中每个子项是否属于同一分类
IF so return 类标签
Else
	寻找划分数据集的最好特征
	划分数据集
	创建分支节点
		for 每个划分的子集
			调用函数createBranch并增加返回结果到分支节点中
		return 分支节点

2 决策树的构造流程

  • 收集数据
  • 准备数据
  • 分析数据
  • 训练数据
  • 测试算法
  • 使用算法
    在这里插入图片描述

决策树学习的目的是为了产生一棵泛化能力强,
即处理未见示例能力强的决策树

3 属性划分方法

3.1 信息增益(ID3)

信息增益: 划分数据集之前之后信息发生的变化
划分数据集的原则:将无序的数据变得更加有序
ID3(Iterative Dichotomiser,迭代二分器)决策树学习算法[Quinlan, 1986]以信息增益为准则来选择划分属性。

3.1.1 信息熵

“信息熵”是度量样本集合纯度最常用的一种指标

假定,当前样本集合D中第k类样本所占的比例为 pk (K=1, 2, …, |y|);则D的信息熵增益为:
在这里插入图片描述

  • Ent(D)的值越小,则D的纯度越高
  • 计算信息熵时约定:若p = 0,则plog2p=0
  • Ent(D)的最小值为0,最大值为log2|y|

python代码计算给定数据集的信息熵

def createDataSet():
    dataSet = [[1, 1, 'yes'],
               [1, 1, 'yes'],
               [1, 0, 'no'],
               [0, 1, 'no'],
               [0, 1, 'no']]
    labels = ['no surfacing','flippers']
    return dataSet,labels

def calcShannonEnt(dataSet):
    numEntris = len(dataSet)
    labelCounts = {}
    for featVec in dataSet:
        currentLabel = featVec[-1]
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
            labelCounts[currentLabel] +=1
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntris
        shannonEnt -= prob * log(prob,2)
    return shannonEnt

测试代码:

>>> d,l=trees.createDataSet()
>d
 [[1, 1, 'yes'], [1, 1, 'yes'],
  [1, 0, 'no'], [0, 1, 'no'], 
  [0, 1, 'no']]
>>> trees.calcShannonEnt(d)
0.9709505944546686

3.1.2 信息增益

在这里插入图片描述
样本数越多的分支结点的影响越大
信息增益越大,则意味着使用属性a来进行划分所获得的“纯度提升”越大
信息增益对可取值数目较多的属性有所偏好

代码实现:

在这里插入代码片

3.2 信息增益率(C4.5)

缺点: 对取值数目较少的属性有所偏好

代码实现:

def create_tree_C45(self,dataset,feat_labels):
    #print("本节点特征",labels)
    classList=list(dataset['label'])#获得类别列表

    #若是所有样本属于同一类别,则返回这一类别做节点标记,停止划分
    if classList.count(classList[0])==len(classList):
        #print("该节点上所有样本为同一类")
        return classList[0]

    #若特征集为空,则返回数据集中样本数最多的类作为节点标记,停止划分
    if len(dataset.iloc[0])==1:
        #print("特征集为空")
        return self.majorityCnt(classList)

    bestFeatLabel,bestFeatIndex=self.choose_best_feature_C45(dataset,feat_labels) #选择最优特征

    print("最佳划分特征(createtree)",bestFeatLabel,bestFeatIndex)
    myTree={bestFeatLabel:{}} #分类结果以字典形式保存        
    #如果最佳划分特征不为空则继续划分
    if(bestFeatLabel!=''):
        del(feat_labels[bestFeatIndex])
        featValues=dataset[bestFeatLabel] #最好划分特征的所有取值
        #print(featValues)
        uniqueVals=set(featValues)
        for value in uniqueVals:
            subLabels=feat_labels[:]
            #print(bestFeatLabel,"取值为:",value)
            values_head=subLabels
            newdataset=self.split_dataset(dataset,bestFeatLabel,bestFeatIndex,value,values_head)
            myTree[bestFeatLabel][value]=self.create_tree_C45(newdataset,subLabels)
        return myTree

3.3 基尼指数(CART)

3.3.1 求解基尼指数

import numpy as np

def calcGini(data_y):  #根据基尼指数的定义,根据当前数据集中不同标签类出现次数,获取当前数据集D的基尼指数
    m = data_y.size #获取全部数据数量
    labels = np.unique(data_y)  #获取所有标签值类别(去重后)
    gini = 1.0  #初始基尼系数

    for i in labels:    #遍历每一个标签值种类
        y_cnt = data_y[np.where(data_y==i)].size / m    #出现概率
        gini -= y_cnt**2    #基尼指数

    return gini

3.3.2 实现数据集切分

def splitDataSet(data_X,data_Y,fea_axis,fea_val): #根据特征、和该特征下的特征值种类,实现切分数据集和标签
    #根据伪算法可以知道,我们要将数据集划分为2部分:特征值=a和特征值不等于a
    eqIdx = np.where(data_X[:,fea_axis]==fea_val)
    neqIdx = np.where(data_X[:,fea_axis]!=fea_val)

    return data_X[eqIdx],data_Y[eqIdx],data_X[neqIdx],data_Y[neqIdx]

3.3.3 选取最优特征和特征值划分

def chooseBestFeature(data_X,data_Y):   #遍历所有特征和特征值,选取最优划分
    m,n = data_X.shape
    bestFeature = -1
    bestFeaVal = -1
    minFeaGini = np.inf

    for i in range(n):  #遍历所有特征
        fea_cls = np.unique(data_X[:,i])   #获取该特征下的所有特征值
        # print("{}---".format(fea_cls))
        for j in fea_cls:   #遍历所有特征值
            newEqDataX,newEqDataY,newNeqDataX,newNeqDataY=splitDataSet(data_X,data_Y,i,j)  #进行数据集切分

            feaGini = 0 #计算基尼指数
            feaGini += newEqDataY.size/m*calcGini(newEqDataY) + newNeqDataY.size/m*calcGini(newNeqDataY)
            if feaGini < minFeaGini:
                bestFeature = i
                bestFeaVal = j
                minFeaGini = feaGini
    return bestFeature,bestFeaVal   #返回最优划分方式

3.3.4 创建树

代码实现:

def createTree(data_X,data_Y,fea_idx):   #创建决策树
    y_labels = np.unique(data_Y)
    #1.如果数据集中,所有实例都属于同一类,则返回
    if y_labels.size == 1:
        return data_Y[0]

    #2.如果特征集为空,表示遍历了所有特征,使用多数投票进行决定
    if data_X.shape[1] == 0:
        bestFea,bestCnt = 0,0
        for i in y_labels:
            cnt = data_Y[np.where(data_Y==i)].size
            if cnt > bestCnt:
                bestFea = i
                bestCnt = cnt
        return bestFea

    #按照基尼指数,选择特征,进行继续递归创建树
    bestFeature, bestFeaVal = chooseBestFeature(data_X,data_Y)
    # print(bestFeature,bestFeaVal)
    feaBestIdx = fea_idx[bestFeature]
    my_tree = {feaBestIdx:{}}
    #获取划分结果
    newEqDataX,newEqDataY,newNeqDataX,newNeqDataY = splitDataSet(data_X,data_Y,bestFeature,bestFeaVal)
    #删除我们选择的最优特征
    newEqDataX = np.delete(newEqDataX,bestFeature,1)
    newNeqDataX = np.delete(newNeqDataX,bestFeature,1)

    fea_idx = np.delete(fea_idx,bestFeature,0)

    my_tree[feaBestIdx]["{}_{}".format(1,bestFeaVal)] = createTree(newEqDataX,newEqDataY,fea_idx)
    my_tree[feaBestIdx]["{}_{}".format(0,bestFeaVal)] = createTree(newNeqDataX,newNeqDataY,fea_idx)

    return my_tree

3.4决策树

3.4.1 创建树的代码

def createTree(dataSet,labels):
    classList = [example[-1] for example in dataSet]
    if classList.count(classList[0]) == len(classList): 
        return classList[0]
    if len(dataSet[0]) == 1: 
        return majorityCnt(classList)
    bestFeat = chooseBestFeatureToSplit(dataSet)
    bestFeatLabel = labels[bestFeat]
    myTree = {bestFeatLabel:{}}
    del(labels[bestFeat])
    featValues = [example[bestFeat] for example in dataSet]
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:] 
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree     

3.4.2 使用决策树的分类函数

def classify(inputTree,featLabels,testVec):
    firstStr = inputTree.keys()[0]
    secondDict = inputTree[firstStr]
    featIndex = featLabels.index(firstStr)
    key = testVec[featIndex]
    valueOfFeat = secondDict[key]
    if isinstance(valueOfFeat, dict): 
        classLabel = classify(valueOfFeat, featLabels, testVec)
    else: classLabel = valueOfFeat
    return classLabel

3.4.4 存储决策树

def storeTree(inputTree,filename):
    import pickle
    fw = open(filename,'w')
    pickle.dump(inputTree,fw)
    fw.close()

def grabTree(filename):
    import pickle
    fr = open(filename)
    return pickle.load(fr)

4. 实验部分

4.1 预测隐形眼镜类型

>>> fr = open('lenses.txt')
>>> lensens = [inst.strip().split('\t') for inst in fr.readlines()]
>>> lensensLabels = ['age', 'prescript', 'astigmatic', 'tearRate']
>>> lensesTree = trees.createTree(lensens,lensensLabels)
>>> lensensTree

结果:

{'tearRate': {'normal': {'astigmatic': {'yes': {'prescript': {'hyper': {'age': {'pre': 'no lenses', 'presbyopic': 'no lenses', 'young': 'hard'}}, 'myope': 'hard'}}, 'no': {'age': {'pre': 'soft', 'presbyopic': {'prescript': {'hyper': 'soft', 'myope': 'no lenses'}}, 'young': 'soft'}}}}, 'reduced': 'no lenses'}}
在这里插入图片描述

5 总结

鸣谢:
1
2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值