决策树之要不要见相亲对象

  在介绍决策树模型(Decision Tree Model)之前,先讲一个易于理解决策树的例子。女孩的母亲见女儿一直单着,就给女儿介绍了一个男朋友,于是有了下面的这段对话:
  女儿:多大年纪了?
  母亲:28岁
  女儿:长的帅不帅?
  母亲:长相挺一般的
  女儿:收入怎么样?
  母亲:也就中等情况
  女儿:是公务员吗?
  母亲:是,在民政局上班
  女儿:那好,我去见见
  对于母亲介绍的男朋友,女孩先打听对方的每一项情况,然后逐一判断是否符合自己的标准,如果各方面条件都满足,就会见对方一面,如果有一项不符合要求,那么这个男孩就没有任何机会了。下图是基于女孩相亲标准创建的树模型,通过判定多个条件决定是否与相亲对象见面。
在这里插入图片描述
  决策树模型是一个模拟人类决策过程思想的模型,一颗决策树包含多个内部节点(internal node)和若干个叶节点(leaf node)。叶结点对应于结果,比如上例中的决策结果只有两个:见与不见;内部结点则对应于属性或特征,比如年龄、长相等。决策树遵循简单且直观的“分而治之”(divide-and-conquer)策略,首先从根结点开始,对实例的某一特征进行测试,基于测试结果分配到其子结点;然后递归的进行测试和分配,直至到达叶节点。决策树生成的伪代码如下:


输入: 训练集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x n , y n ) } D = \{(x_1,y_1),(x_2,y_2),...,(x_n,y_n)\} D={(x1,y1),(x2,y2),...,(xn,yn)}
       属性集 A = { a 1 , a , . . . , a d } A= \{a_1,a_,...,a_d \} A={a1,a,...,ad}
过程: 函数TreeGenerate(D,A)

  1. 生成节点node
  2. if D中样本全属于同一类 C k C_k Ck then
  3.   将node标记为 C k C_k Ck类叶结点 return
  4. end if
  5. if A = ∅ A=\emptyset A= OR D中样本在A上取值相同 then
  6.   将node标记为叶结点,其类别标记为D中样本数最多的类 return
  7. end if
  8. 从A中选择最优划分属性 a ∗ a_* a
  9. for a ∗ a_* a的每一个值 a ∗ v a_*^v av do
  10.   为node生成一个分支;令 D v D_v Dv表示 D D D中在 a ∗ a_* a上取值为 a ∗ v a_*^v av的样本子集
  11.   if D v D_v Dv为空 then
  12.     将分支结点标记为叶结点,其类别标记为D中样本最多的类 return
  13.   else
  14.     以TreeGenerate( D v D_v Dv,A\ { a ∗ a_* a})为分支结点
  15.   end if
  16. end for
    输出: 以node为根结点的一颗决策树

  决策树学习本质上是从训练数据集中归纳出一组分类规则,常用的算法有ID3C4.5。两种算法的区别在于选择特征的准则不同,ID3算法是基于信息增益选择特征的,而C4.5基于信息增益比选择特征。在介绍生成树算法之前,先介绍几个基本概念:熵、信息增益、信息增益比
  熵(entropy)是度量样本集合纯度最常用的一种指标。假定当前样本集合D中第k类样本所占的比例为 p k ( k = 1 , 2 , . . . , K ) p_k(k=1,2,..., K) pk(k=1,2,...,K),则D的熵定义为:
H ( D ) = − ∑ k = 1 K p k l o g 2 p k H(D)=-\sum_{k=1}^{K} p_k {log}_2 p_k H(D)=k=1Kpklog2pk
H ( D ) H(D) H(D)的值越小,则D的纯度越高。
  假定离散属性 a a a V V V个可能的取值{ a 1 , a 2 , . . . , a V a^1,a^2,...,a^V a1,a2,...,aV},若使用a来对样本集 D D D进行划分,则会产生 V V V个分支结点,其中第 v v v个分支结结点包含了 D D D中所有在属性 a a a上的取值为 a v a^v av的样本,记为 D v D^v Dv。考虑到不同的分支结点包含的样本数不同,给分支结点赋予权重 ∣ D v ∣ / ∣ D ∣ |D^v|/|D| Dv/D,即样本数越多的分支结点的影响越大。用属性 a a a对样本集 D D D进行划分所获得的信息增益(information gain)为:
G a i n ( D , a ) = H ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ H ( D v ) Gain(D,a)=H(D)-\sum_{v=1}^V\frac{|D^v|}{|D|}H(D^v) Gain(D,a)=H(D)v=1VDDvH(Dv)
一般而言,信息增益越大,则意味着使用属性a来进行划分所获得的“纯度提升”越大。
  信息增益准则对可取值数目较多的属性有所偏好,为减少这种偏好可能带来的不利影响,C4.5决策树算法不直接使用信息增益,而是使用信息增益率(information gain ratio)来选择最优划分属性。
G a i n _ r a t i o ( D , a ) = G a i n ( D , a ) I V ( a ) Gain\_ratio(D,a)=\frac{Gain(D,a)}{IV(a)} Gain_ratio(D,a)=IV(a)Gain(D,a)
其中 I V ( a ) = − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ l o g 2 ∣ D v ∣ ∣ D ∣ IV(a)=-\sum_{v=1}^{V}\frac{|D^v|}{|D|}log_2\frac{|D^v|}{|D|} IV(a)=v=1VDDvlog2DDv称为属性 a a a的“固有值”(intrinsic value)。属性 a a a的可能取值数目越多(即 V V V越大),则 I V ( a ) IV(a) IV(a)的值通常会越大。
  信息增益率准则对可取值数目较少的属性有所偏好,因此,C4.5算法并不是直接选择增益率最大的候选划分属性,而是使用了一个启发式:先从候选划分属性中找出信息增益高于平均水平的属性,再从中选择增益率最高的。
  如果我们采集到一些相亲数据,如何生成一颗相亲见面与否的决策树呢?下面是基于ID3算法实现的决策树,基于信息增益的准则选择最优的特征,训练集只包含了10个样本,让我们一起看下训练的效果如何。

import numpy as np
import operator
#创建包含10个样本的数据集
def createDataSet():
    #特征:年龄、长相、收入、是否公务员,类标记:是否见面
    dataSet = \
    [\
        ['leq30','handsome','high','no','see'],\
        ['leq30','handsome','high','yes','see'],\
        ['leq30','handsome','middle','yes','see'],\
        ['g30','handsome','high','no','no see'],\
        ['leq30','ugly','high','no','no see'],\
        ['leq30','handsome','middle','no','no see'],\
        ['leq30','handsome','low','yes','no see'],\
        ['g30','handsome','low','no','no see'],\
        ['leq30','ugly','middle','yes','no see'],\
        ['g30','ugly','high','yes','no see']\
    ]
    labels = ['age','looks','income','civil-service']
    return dataSet, labels

#计算数据集的熵
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  #类数+1
    shannonEnt = 0.0
    for key in labelCounts:
        prob = float(labelCounts[key])/numEntries  #类占比
        shannonEnt -= prob * np.log2(prob)
    return shannonEnt

#基于特征分配得到子数据集
def splitDataSet(dataSet,axis,value):
    subDataSet = []  #创建新list对象
    for featVec in dataSet:
        if featVec[axis] == value:  #抽取符合特征的非该特征列
            reducedFeatVec = featVec[:axis]
            reducedFeatVec.extend(featVec[axis+1:])
            subDataSet.append(reducedFeatVec)
    return subDataSet

#基于信息增益准则选择最优的特征
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):
    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]

#生成决策树
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[:]
        myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels) #递归生成决策树
    return myTree

#创建数据集和特征标签
myDat,labels = createDataSet()
#基于信息增益创建决策树
myTree = createTree(myDat,labels)

  最终训练得到的决策树如下图所示,决策树以JSON的形式表示,key是特征标签或特征值,value是see或no see。可以看出,训练得到的决策树就是上例中女孩相亲的决策树。
在这里插入图片描述
  决策树是基于特征对实例进行分类的树状结构,从根结点开始,递归的选择特征并分配产生新的子结点,直至叶结点。特征选择的目的在于选择对训练数据能够分类的特征,常用的准则包括信息增益和信息增益比。一般来讲,机器学习项目并不直接使用决策树分类,而是基于决策树等弱学习器,通过集成学习方法提升为强学习器,比如Boosting、Bagging和Random Forest。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值