博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Decision Trees 笔记
阅读量:6921 次
发布时间:2019-06-27

本文共 5443 字,大约阅读时间需要 18 分钟。

hot3.png

Decision Tree(决策树)属于监督学习算法, 训练过程根据训练集各个属性的不同取值进行逐层划分, 每一层都是对一个属性的划分, 直至节点中的所有样本都为同一个标签(label), 从而构建出决策树; 决策时从树的根节点往下遍历, 直至叶子节点, 当前节点的标签(labe)属性就是测试样本的标签(label).

优点 计算复杂度不高, 输出结果易于理解, 对中间值缺失不敏感; 建树过程无需参数设置及领域知识
缺点 可能会产生过度匹配的问题
适用数据类型 数值型, 标称型

基础概念

1. : 信息的期望值, 用于描述系统的混乱程度(越大越混乱) 
2. 每次划分应当取得最大信息增益, 即使系统以最快速度趋于稳定
3. 熵的计算公式: (对于一个集合, 它的第i个可能取值为 Xi, 取第i个值的概率为 p(i))

算法描述(本文使用  )

1. 计算当前集合的熵
2. 对集合的每一个属性(列), 计算按此属性划分后的新集合的熵
3. 对信息增益最大的划分作为当前划分(信息增益越大, 表示新集合越趋向有序)
4. 对一个属性(列)进行划分: 这个属性的每一个取值都是一个新的节点
5. 至一个节点中的所有样本都为同一个分类, 或只剩下一个属性(列)时停止划分.
6. 可以看到 Decision Trees 是有训练阶段的, 所以需要能把建好的树保存到磁盘, 并能重新载入, 以避免每次都要重新计算
7. (补充, 书本 P160) ID3每次选取当前最佳的特征来分割数据, 并按照该特征的所有可能取值来切分. 也就是说, 如果一个特征有 4 种取值, 那么数据被切成 4 份. 一旦按某特征切分后, 该特征之后的算法执行过程中将不会再起作用.

算法流程图(建树)

代码

# -*- coding: utf-8 -* from math import logimport operatorimport copy# 数据集# 第一列表示"不浮出水面是否可以生存"# 第二列表示"是否有脚蹼"# 第三列表示"是否属于鱼类"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]      # -1 表示最后一列        if currentLabel not in labelCounts.keys():             labelCounts[currentLabel] = 0        labelCounts[currentLabel] += 1    # 出现次数/总行数 即为频率 prob    shannonEnt = 0.0    for key in labelCounts:        prob = float(labelCounts[key])/numEntries        shannonEnt -= prob * log(prob,2) # 以 2 为底计算对数    return shannonEnt # 按照给定特征划分数据集, 即把 dataSet 中第 axis 列值为 value 的行取出来作为一个新集合# 新取出的行不包括第 axis 列 def splitDataSet(dataSet, axis, value):    retDataSet = []    for featVec in dataSet:        if featVec[axis] == value:            reducedFeatVec = featVec[:axis]   # reducedFeatVec 不包含第 alis 列            reducedFeatVec.extend(featVec[axis+1:])            retDataSet.append(reducedFeatVec)    return retDataSet    # 选择最好的数据集划分方式, 即划分后的信息增益值最大, 即熵最大; 返回符合划分的列下标def chooseBestFeatureToSplit(dataSet):    # numFeatures 为 dataSet 总列数-1, 最后一列是标签(label), 不参与划分    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        # uniqueVals 为第 i 列的所有可能取值        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                     # 将 list 中的每个元素及出现次数映射成 map# 返回出现次数最多的元素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(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]    # 如果数据集只有一列, 则这一列中出现最多的标签(label) 就是此节点的 label    if len(dataSet[0]) == 1:         return majorityCnt(classList)    # 找出最适合拆分的属性下标    bestFeat = chooseBestFeatureToSplit(dataSet)    bestFeatLabel = labels[bestFeat]    myTree = {bestFeatLabel:{}}    del(labels[bestFeat])       # 每个 label 只会被用来划分一次    # 根据此属性的不同值, 划分成不同节点, 再对每个节点递归建树    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                                # 判断某个 testVec 属于哪个 label, 递归遍历树def classify(inputTree,featLabels,testVec):    firstStr = inputTree.keys()[0]    secondDict = inputTree[firstStr]    featIndex = featLabels.index(firstStr)    key = testVec[featIndex]    valueOfFeat = secondDict[key]    # 如果是字典, 表示可以递归, 否则就是到叶子节点了, 返回当前节点的 label 即可    if isinstance(valueOfFeat, dict):         classLabel = classify(valueOfFeat, featLabels, testVec)    else: classLabel = valueOfFeat    return classLabel# 保存决策树到文件中def storeTree(inputTree,filename):    import pickle    fw = open(filename,'w')    pickle.dump(inputTree,fw)    fw.close()    # 从文件读取决策树def grabTree(filename):    import pickle    fr = open(filename)    return pickle.load(fr)        if __name__ == "__main__":    myDat, labels = createDataSet()    tree =  createTree(myDat, copy.copy(labels))  # 复制 labels 后才作为参数    print tree    print classify(tree, labels, [1, 1])#    print calcShannonEnt(myDat)

说明

本文为《Machine Leaning in Action》第三章(Splitting datasets one feature at a time: decision trees)读书笔记, 代码稍作修改及注释.

好文参考

1.《

转载于:https://my.oschina.net/zenglingfan/blog/177252

你可能感兴趣的文章
Java 编译解释
查看>>
push 与 concat的区别
查看>>
如何使用跨平台工具创建 NuGet 包(转)
查看>>
curl入门
查看>>
Laravel 使用阿里云 oss 存储对象
查看>>
Python中@property和@classmethod和@staticmethod
查看>>
http协议小结
查看>>
「docker」问题集
查看>>
判断联网wifi
查看>>
python基础-02-while格式化逻辑运算
查看>>
MySQL简介
查看>>
js如何判断客户端是iOS还是Android等移动终端
查看>>
《微分方程_附应用及历史注记》Page 4.
查看>>
Analysis by Its History_exercise 2.1
查看>>
Python中的正则表达式
查看>>
SQLServer2005的维护计划无法删除的解决方法
查看>>
网络编程5_进程
查看>>
20172304 2017-2018《程序设计与数据结构》实验1报告
查看>>
windows双机调试
查看>>
J实现时间格式的转换(附加对象的转换)
查看>>