机器学习实战之决策树


学习《机器学习实战》

1、决策树的构造

1、决策树理解

决策树是一种分类器,根据已知的特征,做一个最纯净的划分。
例子:现在想构建一个邮件分类系统,第一步:先检测发送邮件的域名的地址,若地址是myEmployer.com,就把邮件放在无聊时需要阅读的邮件,若域名不是。第二步:就检测该邮件是否 含有曲棍球的关键字,若含有曲棍球的关键字,则放在需要及时处理的朋友邮件分类里,没有曲棍球的关键字,就放在无需阅读的垃圾邮件里面。整个分类的步骤如下图所示。
在这里插入图片描述

2、决策树优缺点

优点:计算得复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相干的特征数据
缺点:可能产生匹配过度的问题

3、构建决策树的一般流程

  1. 收集数据
  2. 准备数据:适用于标称型数据,数值型数据需要离散化。(标称型:一般在有限的数据中取,而且只存在是和否两种结果(一般用于分类))
  3. 分析数据:用于检测画出的图和自己理解的是否一致
  4. 训练算法:构建决策树,主要是剪枝和构造树
  5. 测试数据:用于计算错误率
  6. 使用算法

2、ID3

1、信息增益和熵

在划分数据集之前之后信息发生的变化称为信息增益,在ID3算法中,使用信息增益作为决策点。
集合信息的度量方式称为香农熵或者简称为熵。
熵的定义为信息的期望值。
信息定义:若待分类的事物可能存在于多个分类之中,则符号Xi的信息定义为:
在这里插入图片描述
其中p(xi)是选择该分类的概率。
熵的计算方式,所有类别所有可能值包含的信息期望值。公式如下:
在这里插入图片描述

2、实例

计算香农熵
下面有五个样本。特征是不浮出水面可以生存和是否有脚蹼,label是属于鱼类,如下表

不浮出水面可以生存是否有脚蹼属于鱼类
1
2
3
4
5

第一步:创建数据特征矩阵:

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):
    """
    :param dataSet: 特征列表,dataSet
    :return:
    """
    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: # 遍历存储所有的特征列表,也就是yes:3 no :2
        prob = float(labelCounts[key]) / numEntries # 计算每个特征出现的概率
        shannonEnt -= prob * log(prob, 2)  # 计算总的信息熵
    return shannonEnt

划分数据集
分类算法处理计算香农熵,还需要划分数据集,度量划分数据集的熵,以确定是否正确的划分数据。对每个特征划分数据集的结果计算一次信息熵,判断按照那种方式是最好的划分数据集的方式,比如在二维坐标轴上,按照x轴划分好还是y轴划分好。
根据跟定的数据集的划分数据集的代码

def splitDataSet(dataSet, axis, value):
    """
    :param dataSet: 数据集列表 dataSet
    :param axis: 划分的特征 0-->不浮出水面可以生存和1-->是否有脚蹼
    :param value: 需要返回特征的值
    :return:
    if里面三行,特征抽取的代码。
    随着axis的增大,特征featVec也在移动,reducedFeatVec也随着移动。这两个分别占领的一行数据的两部分
    """
    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):
    """
    :param classList: 分类名称的列表
    :return: 返回出现次数最多的分类名称
    """
    classCount={}
    for vote in classList:
        if vote not in classCount.keys(): 
            classCount[vote] = 0
        classCount[vote] += 1
    sortedClassCount = sorted(classCount.iteritems(), 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)
    
    # 每个数据集上递归调用,将返回的值插入myTree
    for value in uniqueVals:
        subLabels = labels[:]     # 复制类别标签   
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree

结果打印

if __name__ == '__main__':
    myDat,labels = createDataSet()
    myTree = createTree(myDat,labels)
    print(myTree)

结果:

{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值