算法一:决策树(Decision Tree)

1. 什么是决策树/判定树(decision tree)?

   判定树是一个类似于流程图的树结构:其中,每个内部结点表示在一个属性上的测试,每个分支代表一个属性输出,而每个树叶结点代表类或类分布。树的最顶层是根结点。

    决策树模型本质上就是一个IF-Then规则的集合

算法一:决策树(Decision Tree)_第1张图片算法一:决策树(Decision Tree)_第2张图片

2.如何构建决策树?

决策树学习的第一步就是“特征选择”

特征选择的两个步骤:

          1)首先,选择 对训练数据具有最大分类能力的特征进行树的叶子节点的分类;

          2)其次,选择该特征最合适的分裂点进行分裂;

熵与信息增益

熵(Entropy) 是表示随机变量不确定性的度量(一条信息的信息量大小和它的不确定性有直接的关系,要搞清楚一件非常非常不确定的事情,或者  是我们一无所知的事情,需要了解大量信息==>信息量的度量就等于不确定性的多少)。假设 X 是一个具有有限个值的离散型随机变量,服从如下的概率分布:

算法一:决策树(Decision Tree)_第3张图片

 

 例子:假设:是否买电脑受年龄,收入,是否是学生以及信用四个因素影响,算一下买电脑的不确定性即“熵”(数据见下图,比特(bit)来衡量信息的多少  变量的不确定性越大,熵也就越大)

算法一:决策树(Decision Tree)_第4张图片

根据公式:H(x)=-{[(9/14)*log(9/14)]+[(5/14)*log(5/14])}=0.940bits

算法一:决策树(Decision Tree)_第5张图片

通俗的讲,信息增益是针对特征而言的,信息增益越大,特征对最终的分类结果影响也就越大,我们就应该选择对最终分类结果影响最大的那个特征作为我们的分类特征。(信息增益=熵-条件熵)

条件熵:设特征A有n个不同的取值{a1,a2,···,an},根据特征A的取值将D划分为n个子集{D1,D2,···,Dn},|Di|为Di的样本个数。记子集Di中属于Ck的样本的集合为Dik,即Dik = Di ∩ Ck,|Dik|为Dik的样本个数。于是经验条件熵的公式可以些为:

机器学习实战教程(二):决策树基础篇之让我们从相亲说起

例:以是否买电脑样本数据表为例进行说明。看下年龄这一列的数据,也就是特征A1,一共有三个类别,分别是:青年、中年和老年。我们只看年龄是青年的数据,年龄是青年的数据一共有5个,所以年龄是青年的数据在训练数据集出现的概率是5/14。同理,年龄是中年和老年的数据在训练数据集出现的概率分别为:4/14 和 5/14。现在我们只看年龄是青年的数据的最终得到贷款的概率为1/4,同理,年龄是中年和老年的数据最终得到贷款的概率分别为4/4 和 3/5。所以计算年龄的信息增益,过程如下:

g(D,A1)=H(D)-[(5/14)*H(D1)+(4/14)*H(D2)+(5/14)*H(D3)]

           =0.940-{-(5/14)*[(1/4)*log(1/4)+(3/4)*log(3/4)] -  (4/14)*[(4/4)*log(4/4)]  - (5/14)*[(3/5)*log(3/5)+(2/5)*log(2/5)]}

          =0.940-0.694

        =0.240bits

同理,可得:g(D,A2)=0.029bits      g(D,A3)=0.151      g(D,A4)=0.048

通过比较可得,年龄A1的信息增益值最大,所以选择年龄作为根节点

代码如下:

# -*- coding: UTF-8 -*-
from math import log
import csv

"""
函数说明:计算给定数据集的经验熵(香农熵)

Parameters:
    dataSet - 数据集
Returns:
    shannonEnt - 经验熵(香农熵)
Author:
    Aaron Gao
Modify:
    2018-12-01
"""
#创建数据
def createData():
    featureList = []
    labelList = []
    # 读取数据
    csv_reader = csv.reader(open("AllElectronics.csv"))
    #获取列名
    headers = next(csv_reader)
    for row in csv_reader:
        labelList.append(row[len(row) - 1])
        rowDict = {}
        for i in range(1, len(row) - 1):
            rowDict[headers[i]] = row[i]
        featureList.append(rowDict)
    return featureList,labelList

#计算香浓熵
def calcShannonEnt(labelList):
    numEntires = len(labelList)                        #返回数据集的行数
    labelCounts = {}                                #保存每个标签(Label)出现次数的字典
    for currentLabel in labelList:                            #对每组特征向量进行统计,提取标签(Label)信息
        if currentLabel not in labelCounts.keys():    #如果标签(Label)没有放入统计次数的字典,添加进去
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1                #Label计数
    shannonEnt = 0.0                                #经验熵(香农熵)
    for key in labelCounts:                            #计算香农熵
        prob = float(labelCounts[key]) / numEntires    #选择该标签(Label)的概率
        shannonEnt -= prob * log(prob, 2)            #利用公式计算
    return shannonEnt                                #返回经验熵(香农熵)

#计算信息增益
def getinfoGain (shannonEnt,key,featureList, labelList):#传值为香农熵,当前的特征,数据,标签
    labelCounts = {}
    numEntires = len(featureList)
    infoGain=0.0
    for currKeyLabel in featureList:
        if currKeyLabel[key] not in labelCounts:
            labelCounts[currKeyLabel[key]]=0
        labelCounts[currKeyLabel[key]]+=1  #统计在该特征下 各个属性的数量(例:统计年龄特征下,老年、中年和青年的数量)

    newEntropy=0.0
    for currtKey in labelCounts.keys():
        # 计算条件熵
        currLabelList = [];
        for i in range(len(featureList)):
            if featureList[i][key]==currtKey:
                currLabelList.append(labelList[i])

        prob = float(labelCounts[currtKey]) / float(numEntires)  # 计算子集的概率
        newEntropy += prob * calcShannonEnt(currLabelList) # 根据公式计算经验条件熵
    infoGain = shannonEnt - newEntropy  # 信息增益
    return infoGain


if __name__=='__main__':
    featureList, labelList=createData()
    currKeys=featureList[0].keys()
    shannonEnt = calcShannonEnt(labelList)
    infoGainDic={}
    bestInfoGain=0.0
    for key in currKeys:
        infoGain=getinfoGain(shannonEnt,key,featureList, labelList)
        infoGainDic[key]=infoGain
        if infoGain>bestInfoGain:
            bestInfoGain=infoGain
    print(bestInfoGain)
    print("条件熵如下:"+str(infoGainDic))

Sklearn 实现决策树算法:

from sklearn import tree
from sklearn import datasets
from sklearn.model_selection import train_test_split

#加载iris数据集
iris=datasets.load_iris()
featureList=iris.data
labelList=iris.target

print('样本容量:',len(labelList))

featureList_train, featureList_test,labelList_train, labelList_test=train_test_split(featureList,labelList,test_size=0.3,random_state=42)

#模型初始化,并训练
clf=tree.DecisionTreeClassifier()
clf.fit(featureList_train,labelList_train)

#预测结果
ans=clf.predict(featureList_test)

#计算准确率
cnt=0
for i in range(len(labelList_test)):
    if ans[i] -labelList_test[i]<1e-1:
        cnt+=1
print('准确率:',cnt*100.0/len(labelList_test),'%')

其他算法:

               C4.5:  Quinlan

               Classification and Regression Trees (CART): (L. Breiman, J. Friedman, R. Olshen, C. Stone)

               共同点:都是贪心算法,自上而下(Top-down approach)

               区别:属性选择度量方法不同: C4.5 (gain ratio), CART(gini index), ID3 (Information Gain)

决策树的剪枝策略

决策树的生成算法递归的构建决策树,直到不能继续构建为止。因此,这样产生的决策树很容易过分的学习训练样本,使得它对未知的测试数据的分类效果比较差,即这样产生的决策树很容易过拟合。过拟合的原因在于我们构建的决策树过分复杂,导致其过多地学习训练样本的分布,而对未知样本的学习泛化能力较弱。解决这个问题的方法就是简化已生成的决策树,使其变得更简单。

         在决策树学习的过程中将已经生成的决策树进行简化的过程称作“剪枝”。也就是说,我们可以将决策树中划分的过细的叶子节点剪去,退回到其父节点成为新的叶子节点,使得新的决策树变得简单,并且能够在未知的测试数据的预测上取得更好的分类结果。

        决策树的剪枝策略主要分为两大类:预剪枝策略后剪枝策略

Ø预剪枝策略

1.每一个结点所包含的最小样本数目,例如10,则该结点总样本数小于10时,则不再分;

2. 指定树的高度或者深度,例如树的最大深度为4

3. 指定结点的熵小于某个值,不再划分。

Ø预剪枝策略优缺点

     优点:预剪枝策略能够减少不必要的分裂操作,节省时间;

     缺点:将来的某个时刻可能够获取更高的增益,也就是说预剪枝不能得到最优的树;

Ø后剪枝策略

1.将决策树增长到它的最大深度,递归的进行剪枝,剪去那些使得增益值为负值的叶子节点;

Ø后剪枝策略优缺点

     优点:能够获得最优的剪枝策略决策树;

     缺点:那些被后剪枝的树节点的分裂过程浪费时间;

你可能感兴趣的:())