机器学习实战-决策树

算法概述

        决策树和K近邻一样,都是解决分类问题的算法。决策树从名字解读,目标是建立一棵树,数据看作树的根或者叶,选取数据的特征作为树的一个个分支(决策点),每个分支把数据集分为了不同的数据子集,这里体现了分类的决策,最后直到数据集无法再进行划分。决策树作为预测模型,代表了对象属性和对象标签之间的映射关系,我们可以发现数据内部蕴含的知识(k近邻无法获取数据的内在含义)

         一般一棵决策树包含一个根节点、若干内部节点、若干叶节点,内部节点代表了特征或者属性,叶节点代表了一个分类(决策结果)。根节点包含样本全集,每个节点包含的样本集合根据属性测试的结果划分到子节点中,从根节点到每个叶节点的路径对应一个判定测试序列,从而建立一棵泛化能力强的决策树。

算法原理

        在构建决策树时,首先需要选取某个特征将原始数据划分为n个子数据集,那么第一个问题就是确认哪个特征在划分数据集时起决定性作用,即如何选择最优划分特征,一般而言,随着划分过程的不断进行,我们希望决策树的分支节点所包含的样本尽可能属于同一类别,使节点的纯度越来越高。这里我们采用信息增益来进行划分

        信息熵:用来度量样本集合纯度最常用的一种指标,假设当前样本集合D中第k类(西瓜的好坏)样本所占的比例为,则信息熵的定义为:                                           

                                                                                 

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

        信息增益:假设离散属性(特征)a有n个不同的取值{a1,a2,...an},使用属性a来对样本集进行划分,则会产生n个不同的分支节点,其中第i个分支节点包含了D中所有在属性i上取值为ai的样本,我们根据公式计算当前样本集中第i类的信息熵Di,再   考虑到不同的分支节点所包含的样本数不同,给不同的分支节点赋予权重,即样本数越多的分支节点的影响越大,最后计算属性a对样本集D进行划分所获得的信息增益

                                                                     

        一般而言:信息增益越大,则意味着使用属性a来进行划分获得的纯度提升越大,ID3决策树学习算法就是采用信息增益为准则来选择划分属性。

        ID3决策树算法思路:采用信息增益大小来判断当前节点应该用什么特征来构建决策树,并且用计算出的信息增益最大的特征来建立决策树的当前节点。

举例计算信息增益

         西瓜数据集(机器学习),该数据集中包含17个训练数据,用于学习一棵预测是否好瓜的决策树。在决策树学习开始,根节点包含数据集D所有样本,这里的初始k等于2(好瓜/坏瓜),其中正例p=8/17,反例q=9/17,通过公式计算根节点的信息熵为:

                                                         

                                      

        然后我们需要计算出每个属性(特征)的信息增益(色泽,根蒂,敲声,纹理,脐带,触感),这里我们以色泽为例,色泽取值为{青绿,乌黑,浅白},使用该属性对D进行划分为3个子集,记为D1,D2,D3,然后分别计算各子集的信息熵,D1中有6个样例,正例p=3/6,反例q=3/6,D2中有6个样例,正例p=4/6,反例q=2/6,D3中有5个样例,正例p=1/5,反例q=4/5。

分别计算各子集的信息熵:

                                                                     

                                                                     

                                                                     

最后计算根据属性色泽划分样本的信息增益为:通过计算不同属性的信息增益,选择最大信息增益的属性作为划分依据。

                            

ID3不足:1)没考虑到连续特征,比如长度、密度等;2)信息增益准则对取值数目较多的属性有所偏好;3)没考虑过拟合情况;4)没有考虑缺失值处理。

示例1:

from math import log
import operator


# 创建训练数据集
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


# 计算信息熵(Ent(D)=sum(pi*log(pi,2))
def calcShannonEnt(dataSet):
    # 训练数据集长度
    numEntries = len(dataSet)
    # 生成训练数据集标签字典
    labelCounts = {}
    for featVec in dataSet:
        # 获得训练数据集标签
        currentLabel = featVec[-1]
        # 若该标签不在标签字典中,则加一
        labelCounts[currentLabel] = labelCounts.get(currentLabel, 0) + 1
    # 初始化信息熵
    shannonEnt = 0.0
    for key in labelCounts.keys():
        # 计算不同类标签概率
        prob = float(labelCounts[key]) / numEntries
        shannonEnt -= prob * log(prob, 2)
    return shannonEnt


# 按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):
    """

    :param dataSet:  待划分数据集
    :param axis:     划分数据集的特征
    :param value:    需要返回的特征值
    :return:
    """
    # 为了保证不修改原始列表(列表为可变对象),创建新列表保存数据
    retDataSet = []
    # 删除返回的特征值保存到retDataSet列表中
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis + 1:])
            retDataSet.append(reducedFeatVec)
    return retDataSet


# 选择最优的分类特征
def chooseBestFeatureToSplit(dataSet):
    # 获取训练数据的特征个数
    numFeatures = len(dataSet[0]) - 1
    # 计算信息熵初始值
    baseEntropy = calcShannonEnt(dataSet)
    # 设置信息增益初始值
    bestInfoGain = 0
    # 设置初始划分特征
    bestFeature = -1
    for i in range(numFeatures):
        # 创建唯一的分类标签属性值
        featureList = [example[i] for example in dataSet]
        uniqueVals = set(featureList)
        newEntropy = 0.0
        # 计算每种划分方式的信息熵(不同权重)
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value)
            # 计算属性分支的权重
            prob = len(subDataSet) / float(len(dataSet))
            newEntropy += prob * calcShannonEnt(subDataSet)
        infoGain = baseEntropy - newEntropy
        # 返回最大信息增益
        if infoGain > bestInfoGain:
            bestInfoGain = infoGain
            bestFeature = i
    return bestFeature


def majorityCnt(classList):
    classCount = {}
    for vote in classList:
        classCount[vote] = classCount.get(vote, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]


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


if __name__ == '__main__':
    dataSet, labels = createDataSet()
    # print(dataSet[0], len(dataSet[0]))
    # labelCounts = calcShannonEnt(dataSet)
    # retDataSet = splitDataSet(dataSet, 1, 1)
    # feature = chooseBestFeatureToSplit(dataSet)
    myTree = createTree(dataSet, labels)
    print(myTree)

C4.5决策树算法思路:采用信息增益率来选择最优划分属性。

       增益率定义:

                                                                        

       其中:                                                            

                                                                         

       增益率准则对取值数目较少的属性有所偏好,因此,C4.5算法不是直接选择增益率最大的来作为划分属性,而是先从候选划分属性中找出信息增益高于平均水平的属性,然后选择增益率最高的作为划分属性。

示例2:使用决策树对web站点的用户在线浏览行为及最终购买行为(选择的服务类型或者用户类型)进行预测。

              特征/属性:每个用户的来源网站,用户的ip位置,是否阅读FAQ,浏览网页数目

              标签:游客,基本用户,高级用户

数据集如下:

data=[['slashdot','USA','yes',18,'None'],
         ['google','France','yes',23,'Premium'],
         ['digg','USA','yes',24,'Basic'],
         ['kiwitobes','France','yes',23,'Basic'],
         ['google','UK','no',21,'Premium'],
         ['(direct)','New Zealand','no',12,'None'],
         ['(direct)','UK','no',21,'Basic'],
         ['google','USA','no',24,'Premium'],
         ['slashdot','France','yes',19,'None'],
         ['digg','USA','no',18,'None'],
         ['google','UK','no',18,'None'],
         ['kiwitobes','UK','no',19,'None'],
         ['digg','New Zealand','yes',12,'Basic'],
         ['slashdot','UK','no',21,'None'],
         ['google','UK','yes',18,'Basic'],
         ['kiwitobes','France','yes',19,'Basic']]

 

 

              

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值