机器学习实战之决策树算法学习心得(上)

       上一篇文章机器学习实战之K近邻方法心得体会我详细的讲解了K近邻方法,短短几天就很受大家的热捧,这更加坚定了我写下去的决心(其实那篇文章也就100+的浏览量,哈哈哈哈,我才不会逼着你们去看那篇文章的) 打算写这篇决策树的文章时,本以为会写的很快,因为它内容也就那么多,而且涉及的知识点也不深。可哪想被我这个爱钻研得人(其实是爱钻牛角啦,哈哈,我是个厚颜无耻的人,大家请自动忽略)一点一点剖析,最后竟发现历时三天还是无法动笔。虽然这其中有上课没时间的缘故,但自己还是最这个进度不满意。所以觉得无论无何还是先把自己的心得写出来,虽然还有一个小part没弄懂,我将会在最后写出来(毕竟不完美的才是最完美的,嘿嘿),也希望各位大佬能给我些指导。好了,废话比比完了,现在进入正文了(严肃脸).这一篇我将介绍ID3算法,C4.5算法,CART算法这三个主要的决策树算法

1 决策树的相关概念

       决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方法,是直观运用概率分析的一种图解法。由于这种决策分支画成图形很像一棵树的枝干,故称决策树。在机器学习中,决策树是一个预测模型,他代表的是对象属性与对象值之间的一种映射关系

决策树学习的损失函数:通常是正则化的极大似然函数

决策树学习的策略:是以损失函数为目标函数的最小化

决策树学习的算法通常是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得对各个子数据集有一个最好的分类的过程。

剪枝:决策树可能对训练数据有很好的分类能力,但可能发生过拟合现象.。所以需要对已生成的树自下而上进行剪枝,将树变得更简单,从而使它具有更好的泛化能力。具体地,就是去掉过于细分的叶结点,使其回退到父结点,甚至更高的结点,然后将父结点或更高的结点改为新的叶结点.

特征选择:如果特征数量很多,在决策树学习开始时对特征进行选择,只留下对训练数据有足够分类能力的特征。

信息增益:特征A对训练数据集D的信息增益g(D,A),定义为集合D的经验嫡H(D)与特征A给定条件下D的经验条件嫡H(D|A)之差,即

                                                                                       5deb664f-c5e6-4ddb-bb95-50d2ba1bbf8c

决策树学习中的信息增益等价于训练数据集中类与特征的互信息

根据信息增益准则的特征选择方法是:对训练数据集(或子集)D,计算其每个特征的信息增益,并比较它们的大小,选择信息增益最大的特征。

                                                         3b9f3190-7b4b-46ee-9a57-8e4a52967ec9

                                                         48bd1836-9c3c-45c1-a1a9-47eb2abde99a

|D|表示其样本容量,即样本个数。设有K个类Ck,k=1,2,...,K,|Ck|为属于类Ck的样本个数。根据特征A的取值将D划分为n个子集D1,D2,...,Dn,|Di|为Di的样本个数。记子集Di中属于类Ck的样本的集合为Dik。

信息增益值的大小是相对于训练数据集而言的,并没有绝对意义。在分类问题困难时,也就是说在训练数据集的经验嫡大的时候,信息增益值会偏大,反之,信息增益值会偏小。

信息增益比:特征A对训练数据集D的信息增益比gR(D,A)定义为其信息增益g(D,A)与训练数据集D的经验H(D)之比:

                                                                                       13510f77-2f2b-4cf1-ae39-0f7a4f5e3fe8

好了,大致的概念是介绍完了(其实这一大段都是借鉴别人的,原谅一个文笔不好的理工男) 下面就开始介绍具体的决策树了

2. ID3算法

ID3算法的核心是在决策树各个结点上应用信息增益准则选择特征。

                                                a190b189-d8bf-400e-ab82-cb553052b86d

                                                865248f4-913b-4179-9461-1a932a01d710

ID3算法简单的说就是每次选择信息增益最大的特征进行分类,然后根据选择的特征构建整个树。

ID3算法只有分类树算法没有回归树算法

ID3算法只有树的生成,所以该算法生成的树容易产生过拟合

所以综合以上几点,都觉得ID3算法训练出来的分类器就是一弱分类器,适用范围很小,不推荐大家使用(写了这么多大家还是要看下去的啊) 下面就是老规矩贴源码了 我的编程环境是Python2.7 可能在Python3.6中出现一些语法问题,大家注意了

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


# 计算给定数据集的香农熵
def calcShannonEnt(dataSet):
    numEntries = 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]) / numEntries
        shannonEnt -= prob * log(prob, 2)
    return shannonEnt


# 按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):
    reDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            reDataSet.append(reducedFeatVec)
    return reDataSet


# 选择最好的数据集划分方式
def chooseBestFeatureToDplit(dataSet):
    numFeatures = len(dataSet[0]) - 1
    baseEntropy = calcShannonEnt(dataSet)
    bestInfoGain = 0.0
    bestFeature = -1
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)
        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:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter, 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 = chooseBestFeatureToDplit(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__':
    myDat, labels = createDataSet()
    reDat = splitDataSet(myDat, 0, 1)
    myTree = createTree(myDat, labels)
    print(myTree)

下面这个是相应的图,大家看完之后会有个更直接的形象

好了,ID3算法就将这么多了,因为也确实也没什么好讲的啦,下面将讲解C4.5算法

2. C4.5算法

与ID3算法相似,不同是用信息增益比来选择特征。

                                               10900fa5-d2e5-4238-92ef-dd5a04e6f207

以信息增益作为训练数据集的特征,存在偏向于选择取值较多的特征的问题,使用信息增益比可以对这一问题进行校正

但在我看来:不能进行剪枝处理的决策树都不是最好的决策树,而且C4.5决策树目前应用最多的也是分类树,所以尽管它相较于ID3分类树有了一些改进,也不推荐大家使用。下面贴源码参考

import operator
from math import log


# 创建本地数据集
import treePlotter


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 calcShannonEntOfFeature(dataSet, feat):
    numEntries = len(dataSet)
    labelCounts = {}
    for feaVec in dataSet:
        currentLabel = feaVec[feat]
        if currentLabel not in labelCounts:
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries
        shannonEnt -= prob * log(prob, 2)
    return shannonEnt


# 按照给定特征划分数据集
def splitDataSet(dataSet, axis, value):
    reDataSet = []
    for featVec in dataSet:
        if featVec[axis] == value:
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            reDataSet.append(reducedFeatVec)
    return reDataSet


def chooseBestFeatureToSplit(dataSet):
    # last col is label
    numFeatures = len(dataSet[0]) - 1
    baseEntropy = calcShannonEntOfFeature(dataSet, -1)
    bestInfoGainRate = 0.0
    bestFeature = -1
    for i in range(numFeatures):
        featList = [example[i] for example in dataSet]
        uniqueVals = set(featList)
        newEntropy = 0.0
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value)
            prob = len(subDataSet) / float(len(dataSet))
            newEntropy += prob * calcShannonEntOfFeature(subDataSet, -1)
        infoGain = baseEntropy - newEntropy
        iv = calcShannonEntOfFeature(dataSet, i)
        # value of the feature is all same,infoGain and iv all equal 0, skip the feature
        if iv == 0:
            continue
        infoGainRate = infoGain / iv
        if infoGainRate > bestInfoGainRate:
            bestInfoGainRate = infoGainRate
            bestFeature = i
    return bestFeature


# 返回出现次数最多的分类名称
def majorityCnt(classList):
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter, 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__':
    fr = open('lenses.txt')
    lenses = [inst.strip().split('\t') for inst in fr.readlines()]
    lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate']
    lensesTree = createTree(lenses, lensesLabels)
    print(lensesTree)
    treePlotter.createPlot(lensesTree)

下面是相应的图

                                    

好了,这两个Low比的决策树终于介绍完了(两个决策树的作者此时会不会跑出来打我,哈哈哈哈,开个玩笑哈)。下面我将会着重介绍CART分类和回归树,以及它们相应的剪枝算法。机器学习实战这本书上只给了CART回归树这个算法,可能是觉得分类树比较简单吧。我这个老实娃还是把分类树这个算法也一并给大家供大家参考(不要感动的一塌糊涂啊!!!)

 

展开阅读全文

没有更多推荐了,返回首页