机器学习之决策树算法

决策树原理介绍

决策树是用样本的属性作为结点,用属性的取值作为分支的树结构,是通过一系列规则对数据进行分类的过程,它提供一种在什么条件下会得到什么值的类似规则的方法。决策树分为分类树和回归树两种,分类树对离散变量做决策树,回归树对连续变量做决策树。下图是一个相亲时候的决策图,女方根据男方的条件来决定是否见面(仅为解释)。

相亲图.png

决策树的构造

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

  1. 特征选择
    特征选择是指从训练数据中众多的特征中选择一个特征作为当前节点的分裂标准,比如相亲时要根据男方的一系列特征[年龄,长相,收入,是否公务员]来决定见不见面,特征选择就是每次选择哪个特征才能得到一个最好的决策结果。如何选择特征有着很多不同量化评估标准标准,从而衍生出不同的决策树算法。
  2. 决策树生成
    根据选择的特征评估标准,从上至下递归地生成子节点,直到数据集不可分则停止决策树停止生长。
  3. 剪枝
    决策树容易过拟合,一般来需要剪枝,缩小树结构规模、缓解过拟合。剪枝技术有预剪枝和后剪枝两种。

在构造决策树时,需要解决的第一个问题是,当前数据集上那个特征在划分数据分类时起决定作用。为找到决定性的特征,划分出最好的结果,需要评估每个特征。当根据第一个特征进行数据划分后,原始数据集会分布在第一个决策点的所有分支上。如果某个分支下的数据属于同一类型,则该分支无需继续划分。如果数据子集内的数据不属于同一类型则需要根据新的特征继续划分。
创建分支的伪代码如下:

def create_branch():
    根据数据集中的每个子项是否属于同一分类:
        if so:
            return 类标签
        else:
            寻找划分数据集的最好特征
            划分数据集
            创建分支节点
            for 每个划分的子集:
                create_branch()
            return 分支节点
            

信息熵

每次划分数据集时如何选择合适的特征才能产生最优的划分结果,可以用信息熵来评估。在概率论中,信息熵给了我们一种度量不确定性的方式,是用来衡量随机变量不确定性的,熵就是信息的期望值。若待分类的事物可能划分在N类中,分别是x1,x2,……,xn,每一种取到的概率分别是P1,P2,……,Pn,那么X的熵就定义为:

深度截图20170504224633.png

熵越高就表明数据越混乱,不确定性越高。

举个例子,掷一枚筛子的结果的信息熵

深度截图20170504224659.png

划分数据集的最大原则是:将无序的数据变得更加有序。划分数据集前后信息熵发生的变化叫做信息增益,信息增益是熵的减少或数据无序度的减少,获得信息增益最高的特征就是最好的选择。
Gain(A) = Info(D) - Info(D-a)

使用特征a进行划分后的信息增益 =(原始数据的信息熵)-(使用特征a进行划分后得到的新数据集的信息熵)。

划分数据集

有了信息熵的概念,我们就可以依此来选择特征划分数据集了。具体过程是遍历数据集的每个特征,用每个特征的不同值去划分数据集,计算信息增益。最后返回信息增益最大的那个特征。

递归构建决策树

得到原始数据集,基于最好特征划分一次数据集。第一次划分后,数据将被向下传递到树分支的下一节点,在这个节点熵再次划分数据。递归结束的条件是程序遍历完所有属性或者每个分之下的所有实力都属于同一分类。

代码实现

生成如下数据集

|id| 年龄 | 长相 | 收入 | 公务员 | 是否见面 |
| ------------- |:-------------:| -----:|
|1| <30 | 帅 | 低 |是| no |
|2| <30 | 丑 | 高 |否| no |
|3| <30 | 帅 | 中等 |是| yes |
|4| >30 | 帅 | 高 |否| no |
|5| <30 | 丑 | 低 |是| no |
|6| <30 | 帅 | 低 |否| no |
|7| <30 | 帅 | 高 |否| yes |
|8| <30 | 帅 | 中等 |否| no |
|9| >30 | 帅 | 中等 |是| no |
|10| <30 | 丑 | 中等 |是| no |

from math import log

# 构造数据集
def createDataSet():
    dataSet = [
        [1, 1, 0, 1, 'no'],
        [1, 0, 2, 0, 'no'],
        [1, 1, 1, 1, 'yes'],
        [0, 1, 2, 0, 'no'],
        [1, 0, 0, 1, 'no'],
        [1, 1, 0, 0, 'no'],
        [1, 1, 2, 0, 'yes'],
        [1, 1, 1, 0, 'no'],
        [0, 1, 1, 1, 'no'],
        [1, 0, 1, 1, 'no'],
    ]
    labels = ['年龄','长相','收入','公务员']
    return dataSet, labels
# 计算香农熵
def calcShannonEnt(dataSet):
    numEntries = len(dataSet)
    labelCounts = {}
    # 统计数据集中每个分类的数量
    for featVec in dataSet: 
        currentLabel = featVec[-1]
        labelCounts[currentLabel] = labelCounts.get(currentLabel, 0) + 1
    shannonEnt = 0.0
    # 计算每个分类的熵,然后就和得到整个数据集的熵
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries # 每个分类的数量/总数据集的数量=每个分类的概率
        shannonEnt -= prob * log(prob,2) #log base 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

# 用第i个特征的一个value划分一次数据集得到一个数据子集,计算该数据子集的熵
# 对每个value划分得到的数据子集的熵就和得到用该特征划分数据集后的熵
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] # 所有数据的第i个特征
        uniqueVals = set(featList)       # 对特征值去重
        newEntropy = 0.0
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value) # 用第i个特征的一个value来划分数据集
            prob = len(subDataSet)/float(len(dataSet)) # 第i个特征等于value的个数/整个数据集的数量
            newEntropy += prob * calcShannonEnt(subDataSet)     
        infoGain = baseEntropy - newEntropy     #用第i个特征进行数据划分后的信息增益
        if (infoGain > bestInfoGain):       #compare this to the best gain so far
            bestInfoGain = infoGain         #if better than current best, set to best
            bestFeature = i
    return bestFeature   
    
# 返回类别中数量最多的那个分类
def majorityCnt(classList):
    classCount={}
    for vote in classList:
        classCount[vote] = classCount.get(vote, 0) + 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)
    for value in uniqueVals:
        subLabels = labels[:]       #copy all of labels, so trees don't mess up existing labels
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels)
    return myTree  

if __name__ == '__main__':
    dataSet, labels = createDataSet()
    tree = createTree(dataSet, labels)
    print(tree)
{'收入': {0: 'no', 1: {'年龄': {0: 'no', 1: {'长相': {0: 'no', 1: {'公务员': {0: 'no', 1: 'yes'}}}}}}, 2: {'年龄': {0: 'no', 1: {'长相': {0: 'no', 1: 'yes'}}}}}}
# 根据决策树来构建分类器
def classify(inputTree, featLabels, testVec):
    firstStr = list(inputTree.keys())[0]
    secondDict = inputTree[firstStr]
    featIndex = featLabels.index(firstStr)
    for key in secondDict.keys():
        if testVec[featIndex] == key:
            if type(secondDict[key]).__name__ == 'dict':
                classLabel = classify(secondDict[key], featLabels, testVec)
            else:
                classLabel = secondDict[key]
    return classLabel
# 测试
labels = ['年龄','长相','收入','公务员']
label = classify(tree, labels, [1, 0, 1, 1])
print(label)
no
# 用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)

storeTree(tree, '决策树.txt')
dt = grabTree('决策树.txt')
print(dt)

{'收入': {0: 'no', 1: {'年龄': {0: 'no', 1: {'长相': {0: 'no', 1: {'公务员': {0: 'no', 1: 'yes'}}}}}}, 2: {'年龄': {0: 'no', 1: {'长相': {0: 'no', 1: 'yes'}}}}}}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 决策树理论在决策树理论中,有这样一句话,“用较少的东西,照样可以做很好的事情。越是小的决策树,越优于大的决策树”。...
    制杖灶灶阅读 5,757评论 0 25
  • 转自算法杂货铺--决策树决策树和随机森林学习笔记-欢迎补充 http://www.cnblogs.com/fion...
    明翼阅读 10,602评论 1 6
  • 一.朴素贝叶斯 1.分类理论 朴素贝叶斯是一种基于贝叶斯定理和特征条件独立性假设的多分类的机器学习方法,所...
    wlj1107阅读 2,995评论 0 5
  • 这里开始机器学习的笔记记录。今天的这篇是一个分类方法--决策树。 决策树优点:计算复杂度不高,输出结果易于理解,对...
    七号萝卜阅读 6,371评论 0 18
  • 侬为君痴君不知。 在医院养病的日子,可谓平淡似水。 每天晚上孩子们下班之后,都会过来陪令熊吃晚饭,再赔她聊一会儿。...
    籽盐阅读 722评论 0 0