[python函数学习]机器学习实战 - ID3决策树(1)

ID3决策树实现代码

from math import log
import operator
# 计算香农熵
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):
    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)  # set() 函数创建一个无序不重复元素集
        newEntropy = 0.0
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value)
            prob = len(subDataSet)/float(len(dataSet))  # 计算|D^v|/|D|,子样本在全集中出现概率
            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.items, key = operator.itemgetter(1), reverse = Ture)   # 字典拆分为列表 按字典的值排序
    return sortedClassCount[0][0]
# 递归创建树    数据集   标签列表
def createTree(dataSet, labels):
    classList = [example[-1] for example in dataSet]  # 提取标签
    # 类别完全相同则停止划分
    if(classList.count(classList[0]) == len(classList)):  # .count() 统计在列表中出现的次数
        return classList[0]
    
    # 遍历完所有特征时返回出现次数最多的
    if(len(dataSet[0]) == 1):  # 数据集列数1,只有一个特征
        return majorityCnt(classList)
    
    bestFeat = chooseBestFeatureToSplit(dataSet)  # 找出分割特征
    bestFeatLabel = labels[bestFeat]
    myTree = {bestFeatLabel:{}} # 创建新子树
    del(labels[bestFeat]) # 删掉labels中分割出去的特征
    featValues = [example[bestFeat] for example in dataSet] # bestFeat下的数据
    uniqueVals = set(featValues)
    for value in uniqueVals:
        subLabels = labels[:]
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
    return myTree
#使用决策树的分类函数
def classify(inputTree, featLabels, testVec):
    firstStr = next(iter(inputTree))  # 取第一个key
    secondDict = inputTree[firstStr]  # 取第一个key对应的第一个value 即下方的节点
    featIndex = featLabels.index(firstStr)  # 将标签字符串转换为标签数组的索引
    for key in secondDict:  
        if testVec[featIndex] == key:
            if type(secondDict[key]).__name__ == 'dict':
                classLabel = classify(secondDict[key], featLabels, testVec)
            else: classLabel = secondDict[key]
    return classLabel

ID3决策树

[python函数学习]机器学习实战 - ID3决策树(1)_第1张图片
决策树是一种监督学习,在已知数据集上训练。通过对各特征划分,构建树形结构。使用满足划分准则的特征不断的将数据集划分为纯度更高,不确定性更小的子集的过程。
创建ID3决策树的步骤:
(1) 计算子树下各特征的信息增益
(2)选出信息增益最大的特征
(3)创建信息增益最大特征下的新子树,递归返回第一步继续划分子树
递归终止条件:
(4)如果子树中划分类别相同,停止划分子树
(5)如果划分子树下只有一个特征则选取出现最多的类别作为分类结果,停止划分子树

信息增益

信息熵

“信息熵”是度量样本集合纯度最常用的一种指标。
信息熵公式:
Ent(D)
p k p_ k pk是样本集合D中第k类样本所占比例
信息熵越小,则D的纯度越高

信息增益

“信息增益”是划分前样本信息熵 - 划分后子集样本信息熵
划分后子集样本纯度越高,其信息熵越小,则信息增益越大。
选取信息增益最大的特征划分子集,便可以贪心的使划分达到最大纯度。
信息增益公式:
Gain(D,a)
使用a特征对样本集D进行划分,则会产生V个分支结点,其中第v个分支结点包含了D中所有在属性a上取值为 a v a^v av的样本,记为 D v D^v Dv
|D^v|/|D| 即子样本在全集中出现概率

# pickle存储数据
def storeTree(inputTree, filename):
    import pickle
    fw = open(filename, 'wb')
    pickle.dump(inputTree, fw)
    fw.close()
    
def grabTree(filename):
    import pickle
    fr = open(filename, 'rb')
    return pickle.load(fr)

决策树只需一次训练便可生成,将其保存在磁盘,每次调用节省时间。
python中使用pickle模块可实现基本的数据序列化和反序列化。

那么为什么需要序列化和反序列化这一操作呢?
  1.便于存储。序列化过程将文本信息转变为二进制数据流。这样就信息就容易存储在硬盘之中,当需要读取文件的时候,从硬盘中读取数据,然后再将其反序列化便可以得到原始的数据。在Python程序运行中得到了一些字符串、列表、字典等数据,想要长久的保存下来,方便以后使用,而不是简单的放入内存中关机断电就丢失数据。python模块大全中的Pickle模块就派上用场了,它可以将对象转换为一种可以传输或存储的格式。
  2.便于传输。当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把這个对象转换为字节序列,在能在网络上传输;接收方则需要把字节序列在恢复为对象。

文中使用的是基本序列化、反序列化方法,注意读写都要使用二进制方式。

你可能感兴趣的:(机器学习实战笔记)