机器学习算法之三——分类(三)

1 决策树概念

    决策树是通过一系列的规则进行分类的过程,决策树分为分类树和回归树,分类树是对离散变量进行决策,回归树是对连续变量进行决策。决策树的构造过程实际上是找到具有决定性作用的特征,决定性作用最大的那个作为根节点,然后递归找到次大的,以此类推。    

一棵决策树的生成过程主要分为以下3个部分:

  • 特征选择:特征选择是从特征集合中找到当前节点的分裂标准,如何选择特征有不同的量化评估标准。

  • 决策树生成: 根据选择的特征评估标准,从上至下递归地生成子节点,直到数据集不可分则停止决策树停止生长。 

  • 剪枝:决策树容易过拟合,一般来需要剪枝,缩小树结构规模、缓解过拟合。剪枝技术有预剪枝和后剪枝两种。

2 决策树典型算法

  我们上面说到了决策树算法首先要进行特征选择,那么如何选择最佳的特征作为划分依据?量化的方法有很多,其中有一项就是信息论度量信息分类。基于信息论的决策树算法有ID3、CART和C4.5等算法,C4.5和CART是从ID3算法中衍生而来。

2.1 ID3算法

    ID3算法根据信息论的信息增益评估和选择特征,每次选择信息增益最大的特征作为判断模块。ID3算法可以用来划分数据集,没有剪枝的过程,为了去除过度数据匹配的问题,可通过裁剪合并相邻的无法产生大量信息增益的叶子节点(例如设置信息增益阀值)。但是使用信息增益来选择特征有一个缺点:如果某个属性的属性值越多,那么就越有可能拿它来作为分裂标准,但这样有时候是没有意义的,另外ID3不能处理连续分布的数据特征。

2.2 C4.5算法

    为了解决上文提到的ID3算法的两个问题,C4.5算法应运而生。C4.5算法用信息增益率来选择分裂的属性,克服了偏向选择属性取值多的问题;能够对连续属性进行离散化处理,以及对不完整的数据进行处理。C4.5算法离散化连续属性是通过使用二元切分,即求一个特定的分裂值,如果特征值大于分裂值就走左子树,反之右子树。分裂值的选择标准是使得划分后的子树“混乱程度降低”。C4.5算法产生的分类规则易于理解、准确率较高。但是C4.5有个缺点:效率低,在构造的过程中需要对数据集进行多次顺序扫描和排序,故只适合能够驻留于内存的数据集。

    对于连续分布的特征,C4.5处理方法是:

  •    对特征的取值进行升序排序
  •    两个特征取值之间的中点作为可能的分裂点,将数据集分成两部分,计算每个可能的分裂点的信息增益(InforGain)。优化算法就是只计算分类属性发生改变的那些特征取值。
  •    选择修正后信息增益(InforGain)最大的分裂点作为该特征的最佳分裂点
  •    计算最佳分裂点的信息增益率(Gain Ratio)作为特征的Gain Ratio。注意,此处需对最佳分裂点的信息增益进行修正:减去log2(N-1)/|D|(N是连续特征的取值个数,D是训练数据数目,此修正的原因在于:当离散属性和连续属性并存时,C4.5算法倾向于选择连续特征做最佳树分裂点)

2.3 CART算法

    CART算法的全称是Classification And Regression Tree,采用的是Gini指数(选Gini指数最小的特征s)作为分裂标准,同时它也是包含后剪枝操作。ID3算法和C4.5算法虽然在对训练样本集的学习中可以尽可能多地挖掘信息,但其生成的决策树分支较大,规模较大。为了简化决策树的规模,提高生成决策树的效率,就出现了根据GINI系数来选择测试属性的决策树算法CART。

3 数学原理

    假设一个分类系统的样本空间(D,C),D表示样本,样本有m个特征;C表示有n个类别,样本属于类别Ci的概率表示为P(Ci)。

该分类系统的熵为:H(C) = \sum_{i=1}^{n} P(C_i)logP(C_i)

样本特征X取值为xi的概率为Pi,该特征被固定为xi的条件信息熵为H(C|X=xi)

H(C|X) = \sum_{i=1}^{m} P_iH(C|X=x_i)

信息增益Gain(D,X) = H(C ) - H(C|X)

信息增益比率GainRatio(D,X) = \frac{Gain(D,X)}{SplitInformation(D,X)} = \frac{H(C)-H(C|X)}{-\sum_{i=1}^{m}P_ilogP_i}

4 运行实例

#coding=utf-8
import operator
from math import log
import time

def createDataSet():
    dataSet=[[1,1,'a'],
            [1,1,'a'],
            [1,0,'b'],
            [0,1,'b'],
            [0,1,'b']]
    labels = ['a','b']
    return dataSet, labels

#计算香农熵
def calcShannonEnt(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}
    for feaVec in dataSet:
        currentLabel = feaVec[-1]
        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):
    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.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
    return max(classCount)         
    
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
    
def main():
    data,label = createDataSet()
    t1 = time.clock()
    myTree = createTree(data,label)
    t2 = time.clock()
    print myTree
    print 'execute for ',t2-t1
if __name__=='__main__':
    main()
发布了30 篇原创文章 · 获赞 17 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/selinaqqqq/article/details/89466327