机器学习实战 第3章 决策树

1.决策树

k-近邻算法可以完成很多分类任务,但是它最大的缺点就是无法给出数据的内在含义,决策树的主要优势就在于数据形式非常容易理解。

优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据。

缺点:可能会产生过度匹配问题。

适用数据类型:数值型和标称型。

 

2.信息增益

划分数据集的大原则是:将无序的数据变得更加有序。

在划分数据集之前之后信息发生的变化称为信息增益,知道如何计算信息增益,我们就可以计算每个特征值划分数据集获得的信息增益,获得信息增益最高的特征就是最好的选择。

集合信息的度量方式称为香农熵或者熵。

 

4.熵

熵定义为信息的期望值。

如果待分类的事务可能划分在多个分类之中,则符号xi的信息定义为:

                     (其中p(xi)是选择该分类的概率)

为了计算熵,我们需要计算所有类别所有可能值包含的信息期望值,

          (其中n为分类的数目)

 

5.划分数据集

分类算法除了需要测量信息熵,还需要划分数据集,度量划分数据集的熵,以便判断当前是否正确地划分了数据集。

我们将对每个特征划分数据集的结果计算一次信息熵,然后判断按照哪个特征划分数据集是最好的划分方式。

 

6.代码示例

# coding:utf-8

from math import log

# 计算数据集的熵,数据集的格式为[[],[]],其中dataSet[i][-1]为类型
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 = labelCounts[key]/numEntries*1.0
        # print(-log(prob, 2), prob)
        shannonEnt -= prob * log(prob, 2)      # 这里是信息期望值(熵)的计算公式
    return shannonEnt

# 生成数据集
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

dataSet, labels = createDataSet()
# shannonEnt = calcShannonEnt(dataSet)
# print(shannonEnt)

# 将原数据集按索引和类别返回划分数据集
def splitDataSet(dataset, axis, value):
    retdataset = []
    for featvet in dataset:
        if featvet[axis] == value:
            reducedfeatvet = featvet[:axis]
            reducedfeatvet.extend(featvet[axis+1:])
            retdataset.append(reducedfeatvet)
    # print(retdataset)
    return retdataset

# splitDataSet(dataSet, 0, 1)

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)
            print(subdataset, uniquevals, value, i)
        infogain = baseEntropy - newEntropy
        if infogain > bestinfogain:
            bestinfogain = infogain
            bestfeature = i                  # 获取熵最小的类型,作为此级划分的首选项
        # print(i, newEntropy, uniquevals)   # 因为可以找到类型最纯的一个划分数据集
    # print(bestfeature)
    return bestfeature

chooseBestfeatureTosplit(dataSet)

# 代码运行的结果告诉我们,第0个特征是最好的用于划分数据集的特征。

 

7.递归构建决策树

上面的内容为从数据集构造决策树算法所需的子功能的模块,其工作原理是:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分。第一次划分之后,数据将被向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据。因此我们可以采用递归的原则处理数据集。

代码示例,

# coding:utf-8

from math import log

# 计算数据集的熵,数据集的格式为[[],[]],其中dataSet[i][-1]为类型
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 = labelCounts[key]/numEntries*1.0
        # print(-log(prob, 2), prob)
        shannonEnt -= prob * log(prob, 2)      # 这里是信息期望值(熵)的计算公式
    return shannonEnt

# 生成数据集、特征
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

dataSet, labels = createDataSet()
# shannonEnt = calcShannonEnt(dataSet)
# print(shannonEnt)

# 将原数据集按索引和结果返回划分数据集,并将相应索引的源数据删除
def splitDataSet(dataset, axis, value):
    retdataset = []
    for featvet in dataset:
        if featvet[axis] == value:
            reducedfeatvet = featvet[:axis]
            reducedfeatvet.extend(featvet[axis+1:])
            retdataset.append(reducedfeatvet)
    # print(retdataset)
    return retdataset

# splitDataSet(dataSet, 0, 1)

# 获取当前最好的划分数据集的索引
def chooseBestfeatureTosplit(dataset):
    numfeatures = len(dataset[0]) - 1
    baseEntropy = calcShannonEnt(dataset)
    bestinfogain = 0.0
    bestfeature = -1
    feature_list = []
    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)
            # print(subdataset, uniquevals, value, i)
        infogain = baseEntropy - newEntropy
        if infogain > bestinfogain:
            bestinfogain = infogain
            bestfeature = i                  # 获取熵最小的类型,作为此级划分的首选项,因为可以找出类型最纯的一个划分数据集
        # print(i, newEntropy, uniquevals)
        feature_list.append(newEntropy)
    # print(bestfeature)
    # print(feature_list)
    return bestfeature

# chooseBestfeatureTosplit(dataSet)

import operator
# 划分数据集的结果的个数排序
def majorityCnt(classList):
    classCount = {}
    for vote in classList:
        if vote not in classCount.keys():
            classCount[vote] = 0
        classCount[vote] += 1
    sortedclasscount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedclasscount[0][0]

classcount = majorityCnt(labels)

# 创建决策树
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]                                 # 清理已使用的特征
    featvals = [example[bestFeat] for example in dataset]      # 根据索引取得相应的数据集的值
    uniquevals = set(featvals)                                 # 列表转为集合,实现数据去重
    for vals in uniquevals:
        sublabels = labels[:]
        mytree[bestfeatlabel][vals] = createtree(splitDataSet(dataset, bestFeat, vals), sublabels)
    return mytree

mytree = createtree(dataSet, 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、付费专栏及课程。

余额充值