决策树算法一:hunt算法,信息增益(ID3)

决策树入门

决策树是分类算法中最重要的算法,重点

决策树算法在电信营业中怎么工作?

 

这个工人也是流失的,在外网转移比处虽然没有特征来判断,但是在此节点处流失率有三个分支概率更大

为什么叫决策树?
  因为树的叶子节点是我们最终预判的结果。
决策树如何来?
  根据训练样本建立。
问题1:为什么费用变换率放第一个?
  根据特征建决策树,会有n棵树,找出最优树。
问题2:当我们特征是连续值的时候,到底从哪里开始切分?
  连续值要改为离散的。
问题3:决策树能不能做回归

决策树例子:

不同的决策树对我们判定的效率,速度有影响。

总结:

树的深度:深度的基数是1,上图深度是4。一棵树所有层次的最大值称为深度。

决策树进阶

 

**决策树一共有五种算法。**前面四个都是有关联的。

Hunt算法:

其中所说的类别是输出的情况

属性即每一个样本的多种特征(如年龄、性别等)

以一个例子一一解说hunt算法过程

 2、3、4行的解释,分两种情况

情况一(决策树刚从根节点出发):假设上面图买电脑这一列全是是,则是这一个类是叶节点也是根节点

情景二(决策树正在内部节点分支过程中):上面图买电脑这一列不全是是,是否混杂,在从根节点,子节点,子节点的子节点的分支过程中,某一分支处的特征(年龄、收入以及学生特征都以作为节点,假设学生开始分支,信用特征作为分支节点时),不论特征的情况如何(即不论信用的好坏)买电脑的情况都是是,则是这一个类作为学生的分支节点

5、6行的解释

情况一(A = 空):在从根节点,子节点,子节点的子节点的分支过程中,某一分支处的特征(年龄、收入、学生特征以及信用特征都已经作为划分时(假如分到信用特征这一节点处继续向下划分))没有时,类的情况还是混杂的(买电脑的情况还是买与不买的都有),则此分支作为叶节点,且类别为这些混杂中类别最多的那一类。如下图sal<25的分支处(这里<25买房情况相同分支的叶节点可以是0也可以是1,最终选择为1是应为>25的另外一个分支上是0)

 情景二(D中的样本在A上的取值相同):在从根节点,子节点,子节点的子节点的分支过程中,某一分支处的特征(年龄、收入、学生特征以及信用特征都已经作为划分时(假如分到信用特征这一节点处继续向下划分)),但是信用这一节点的取值都相同(就像上图中假设30、32、33、sal的值都是3000时,右侧继续划分),则此分支作为叶节点,且类别为这些混杂中类别最多的那一类

8-12行的解释

a*为属性,a*v为属性分支时的划分点(离散型的属性如性别可以划分男女,连续性的属性例如年龄可以以范围划分),例如下图的例子,(对应11、12行解释)年龄以80划分时大于80的分支为空,则此分支作为叶节点,且类别时这些样本集类别最多的那一类。线面18-34样本集中最多的类是0。

14行解释:

循环递归重复1-14行

总结:

决策树是一个递归的过程。
2,3解释:当所有的样本点都属于同一个类别的时候,不需要划分(递归结束的一个条件);
5,6解释:属性不能再划分的时候,其类别标记取决于该样本中数据最多的类。如果类别数量相同,注意看一下另一个叶子节点,不能与上一个叶子节点的类别相同,否则,无需划分。
8,解释:如何构建最优决策树。
hunt算法有一个bug:不好选最优划分属性。D是样本集。
9~14解释:对于某一个特征(属性),的每一个值,设置为node并生成一个分支;形成两个样本子集。为空,分支节点为叶子节点,否则,样本子集中数量多的类为返回值。

信息增益–information gain(ID3)

在这里插入图片描述在这里插入图片描述

(b)方法更好。
ID3算法主要针对属性选择问题
使用信息增益度选择测试属性。

决策树属于递归树。

信息熵:信息的度量,量化信息的作用。信息熵越小则已知的信息就越多

不确定性的多少就是未知的信息量多少

信息熵衡量的公式:

  数据集D中m个不同的分类,表示以输出结果的分类(例如上面电信运营案例中的流失情况就是输出结果,流失与不流失是数据集分类;下面买电脑的情况就是输出结果,买与不买是数据集分类情况);

  Ci,D表示数据集D中Ci类的集合(例如下面买电脑情况,买这一类有5个,没买这一类有9个)

计算信息熵例子:

0~100%,先变大,后变小。50%的时候最大。熵为1的时候,不确性最大。熵为0,数据最纯。

条件信息熵

如何理解下面图片的第一句话

  属性A表示每个样本中的特征(年龄、性别、收入等),v个不同的取值就是如何去划分属性A中的每一个特征(以上面买电脑情况来说明,年龄以<30,30-40,>40划分三类,性别以男女划分两类。。。);

  根据每一个特征求得的信息熵越小就是最优的划分属性(即hunt中没有说明如何找出最优属性的方法)。即某条件下的信息熵越小,信息量越小,以此条件对于数据集的划分越容易得到最终结果。

Info(Dj)样本子集的信息熵;假设Dj为年龄划分的样本子集,Info(Dj)表示年龄以<30 / 30-40 / >40的信息熵

信息增益=样本集的信息熵-某属性的信息熵
选择信息增益最大的属性。

(按条件进行划分的信息熵)例子

按年龄来划分:(年龄的信息增益最大)

 

实现ID3算法:

import numpy as np
import operator

def creatDataSet():
    """
    outlook-> 0:sunny | 1:overcast | 2:rain
    temperature-> 0:hot | 1:mild | 2:cool
    humidity-> 0:high | 1:normal
    windy-> 0:false | 1:true   输出表示玩不玩,N/Y
    """
    dataSet = np.array([[0, 0, 0, 0, 'N'],
               [0, 0, 0, 1, 'N'],
               [1, 0, 0, 0, 'Y'],
               [2, 1, 0, 0, 'Y'],
               [2, 2, 1, 0, 'Y'],
               [2, 2, 1, 1, 'N'],
               [1, 2, 1, 1, 'Y']])
    labels = np.array(['outlook', 'temperature', 'humidity', 'windy'])
    return dataSet, labels


def createTestSet():
    """
    outlook-> 0:sunny | 1:overcast | 2:rain
    temperature-> 0:hot | 1:mild | 2:cool
    humidity-> 0:high | 1:normal
    windy-> 0:false | 1:true
    """
    testSet = np.array([[0, 1, 0, 0],
               [0, 2, 1, 0],
               [2, 1, 1, 0],
               [0, 1, 1, 1],
               [1, 1, 0, 1],
               [1, 0, 1, 0],
               [2, 1, 0, 1]])
    return testSet

def dataset_entropy(dataset):
    """
    计算数据集的信息熵
    """
    classLabel=dataset[:,-1]
    labelCount={}
    for i in range(classLabel.size):
        label=classLabel[i]
        labelCount[label]=labelCount.get(label,0)+1     #将所有的类别都计算出来了
    #熵值(第一步)
    cnt=0
    for k,v in labelCount.items():
        cnt += -v/classLabel.size*np.log2(v/classLabel.size)
    
    return cnt

    #接下来切分,然后算最优属性
def splitDataSet(dataset, featureIndex):
    #划分后的子集
    subdataset=[]
    featureValues = dataset[:,featureIndex]
    featureSet = list(set(featureValues))
    for i in range(len(featureSet)):
        newset=[]
        for j in range(dataset.shape[0]):
            if featureSet[i] == featureValues[j]:
                newset.append(dataset[j,:])
        newset=np.delete(newset,featureIndex,axis=1)
        # newset = newset[:,0:featureIndex].extend(newset[:,featureIndex+1:]) #不包括当前的列,得到新的数据集(数组的处理方式)       
        subdataset.append(np.array(newset))
    return subdataset  #划分得到三个子集

def splitDataSetByValue(dataset,featureIndex,value):
    subdataset=[]
    #迭代所有的样本
    for example in dataset:
        if example[featureIndex]==value:
            subdataset.append(example)
    return np.delete(subdataset,featureIndex,axis=1)


def chooseBestFeature(dataset,labels):
    """
    选择最优特征,但是特征是不包括名称的。
    如何选择最优特征:每一个特征计算,信息增益最大==条件熵最小就可以。
    """
    #特征的个数
    featureNum=labels.size
    #设置最小熵值
    minEntropy,bestFeatureIndex=1,None
    #样本总数
    n=dataset.shape[0]  
    for i in range(featureNum):
        #指定特征的条件熵
        featureEntropy=0
        #返回第i个特征的所有子集
        allSubDataSet=splitDataSet(dataset,i)
        for subDataSet in allSubDataSet:
            featureEntropy += subDataSet.shape[0]/n*dataset_entropy(subDataSet) #表示第i个特征的条件熵
        if minEntropy > featureEntropy: #将最小的条件上赋给minEntropy,特征索引值i给bestFeatureIndex
            minEntropy=featureEntropy
            bestFeatureIndex=i
    return bestFeatureIndex #最佳增益

def mayorClass(classList):
    labelCount={} #建立字典类型用于标签熟练存储
    for i in range(classList.size):
        label=classList[i]
        labelCount[label]=labelCount.get(label,0)+1 #计算每个标签的数量
    sortedLabel=sorted(labelCount.items(),key=operator.itemgetter(1),reverse=True)
    return sortedLabel[0][0]

def createTree(dataset,labels):
    """
    参考hunt算法那张图片
    """
    classList=dataset[:,-1]
    if len(set(dataset[:,-1]))==1:
        return dataset[:,-1][0] #返回类别
    if labels.size==0 or len(dataset[0])==1:  #条件熵最少的一定是类别最多的
        #条件熵算不下去的时候,
        return mayorClass(classList)
    bestFeatureIndex=chooseBestFeature(dataset,labels)
    bestFeature=labels[bestFeatureIndex]
    dtree={bestFeature:{}}  #用代码表示这棵树
    featureList=dataset[:,bestFeatureIndex]
    featureValues=set(featureList) #得到最优节点的划分情况
    for value in featureValues:
        subdataset=splitDataSetByValue(dataset,bestFeatureIndex,value)
        sublabels=np.delete(labels,bestFeatureIndex) #将最优节点的标签删去后的标签组
        dtree[bestFeature][value]=createTree(subdataset,sublabels) #将原始的labels干掉一列后的
    return dtree                                                   #数据集和标签,开始递归

if __name__ == "__main__":
    dataset,labels=creatDataSet()
    # print(dataset_entropy(dataset)
    # s=splitDataSet(dataset,0)
    # for item in s:
    #     print(item)
    print(createTree(dataset,labels))
·····················································
输出:
{'outlook': {'2': {'windy': {'0': 'Y', '1': 'N'}}, '0': 'N', '1': 'Y'}}

 

if labels.size==0 or len(dataset[0])==1:  #条件熵最少的一定是类别最多的
   #条件熵算不下去的时候,
   return mayorClass(classList)
"""
这两种情况分别是
['N','N','Y']
['N']
"""

 

labels.size == 0的情况

len(dateset[0]) == 1的情况

 

代码解释:
切分子集是为了,计算每一个特征的条件熵,然后选出最优特征。

总结:
决策树里面的重要的公式:
1,数据集的信息熵公式:Info(D)
2,指定条件下的信息熵:Info_A(D)

将算法优化:
将splitDataSet()和splitDataSetByValue()合二为一。

import numpy as np
import operator

def creatDataSet():
    """
    outlook-> 0:sunny | 1:overcast | 2:rain
    temperature-> 0:hot | 1:mild | 2:cool
    humidity-> 0:high | 1:normal
    windy-> 0:false | 1:true
    """
dataSet = np.array([[0, 0, 0, 0, 'N'], [0, 0, 0, 1, 'N'], [1, 0, 0, 0, 'Y'], [2, 1, 0, 0, 'Y'], [2, 2, 1, 0, 'Y'], [2, 2, 1, 1, 'N'], [1, 2, 1, 1, 'Y']]) labels = np.array(['outlook', 'temperature', 'humidity', 'windy']) return dataSet, labels
def createTestSet(): """ outlook-> 0:sunny | 1:overcast | 2:rain temperature-> 0:hot | 1:mild | 2:cool humidity-> 0:high | 1:normal windy-> 0:false | 1:true """ testSet = np.array([[0, 1, 0, 0], [0, 2, 1, 0], [2, 1, 1, 0], [0, 1, 1, 1], [1, 1, 0, 1], [1, 0, 1, 0], [2, 1, 0, 1]]) return testSet def dataset_entropy(dataset): """ 计算数据集的信息熵 """ classLabel=dataset[:,-1] labelCount={} for i in range(classLabel.size): label=classLabel[i] labelCount[label]=labelCount.get(label,0)+1 #将所有的类别都计算出来了 #熵值(第一步) cnt=0 for k,v in labelCount.items(): cnt += -v/classLabel.size*np.log2(v/classLabel.size) return cnt #接下来切分,然后算最优属性 def splitDataSet(dataset,featureIndex,value): subdataset=[] #迭代所有的样本 for example in dataset: if example[featureIndex]==value: subdataset.append(example) return np.delete(subdataset,featureIndex,axis=1) def chooseBestFeature(dataset,labels): """ 选择最优特征,但是特征是不包括名称的。 如何选择最优特征:每一个特征计算,信息增益最大==条件熵最小就可以。 """ #特征的个数 featureNum=labels.size #设置最小熵值 minEntropy,bestFeatureIndex=1,None #样本总数 n=dataset.shape[0] for i in range(featureNum): #指定特征的条件熵 featureEntropy=0 #返回所有子集 featureList=dataset[:,i] featureValues=set(featureList) for value in featureValues: subDataSet=splitDataSet(dataset,i,value) featureEntropy += subDataSet.shape[0]/n*dataset_entropy(subDataSet) #一个的条件熵 if minEntropy > featureEntropy: minEntropy=featureEntropy bestFeatureIndex=i return bestFeatureIndex #最佳增益 def mayorClass(classList): labelCount={} for i in range(classList.size): label=classList[i] labelCount[label]=labelCount.get(label,0)+1 sortedLabel=sorted(labelCount.items(),key=operator.itemgetter(1),reverse=True) return sortedLabel[0][0] def createTree(dataset,labels): """ 参考hunt算法那张图片 """ classList=dataset[:,-1] if len(set(dataset[:,-1]))==1: return dataset[:,-1][0] #返回类别 if labels.size==0 or len(dataset[0])==1: #条件熵最少的一定是类别最多的 #条件熵算不下去的时候, return mayorClass(classList) bestFeatureIndex=chooseBestFeature(dataset,labels) bestFeature=labels[bestFeatureIndex] dtree={bestFeature:{}} #用代码表示这棵树 featureList=dataset[:,bestFeatureIndex] featureValues=set(featureList) for value in featureValues: subdataset=splitDataSet(dataset,bestFeatureIndex,value) sublabels=np.delete(labels,bestFeatureIndex) dtree[bestFeature][value]=createTree(subdataset,sublabels) #将原始的labels干掉一列 return dtree if __name__ == "__main__": dataset,labels=creatDataSet() # print(dataset_entropy(dataset) # s=splitDataSet(dataset,0) # for item in s: # print(item) print(createTree(dataset,labels)) ······················································· 输出结果: {'outlook': {'1': 'Y', '0': 'N', '2': {'windy': {'1': 'N', '0': 'Y'}}}}

 

import numpy as np
import operator

def creatDataSet():
    """
    outlook-> 0:sunny | 1:overcast | 2:rain
    temperature-> 0:hot | 1:mild | 2:cool
    humidity-> 0:high | 1:normal
    windy-> 0:false | 1:true   输出表示玩不玩,N/Y
    """
    dataSet = np.array([[0, 0, 0, 0, 'N'],
               [0, 0, 0, 1, 'N'],
               [1, 0, 0, 0, 'Y'],
               [2, 1, 0, 0, 'Y'],
               [2, 2, 1, 0, 'Y'],
               [2, 2, 1, 1, 'N'],
               [1, 2, 1, 1, 'Y']])
    labels = np.array(['outlook', 'temperature', 'humidity', 'windy'])
    return dataSet, labels


def createTestSet():
    """
    outlook-> 0:sunny | 1:overcast | 2:rain
    temperature-> 0:hot | 1:mild | 2:cool
    humidity-> 0:high | 1:normal
    windy-> 0:false | 1:true
    """
    testSet = np.array([[0, 1, 0, 0],
               [0, 2, 1, 0],
               [2, 1, 1, 0],
               [0, 1, 1, 1],
               [1, 1, 0, 1],
               [1, 0, 1, 0],
               [2, 1, 0, 1]])
    return testSet

def dataset_entropy(dataset):
    """
    计算数据集的信息熵
    """
    classLabel=dataset[:,-1]
    labelCount={}
    for i in range(classLabel.size):
        label=classLabel[i]
        labelCount[label]=labelCount.get(label,0)+1     #将所有的类别都计算出来了
    #熵值(第一步)
    cnt=0
    for k,v in labelCount.items():
        cnt += -v/classLabel.size*np.log2(v/classLabel.size)
    
    return cnt

    #接下来切分,然后算最优属性
def splitDataSet(dataset, featureIndex):
    #划分后的子集
    subdataset=[]
    featureValues = dataset[:,featureIndex]
    featureSet = list(set(featureValues))
    for i in range(len(featureSet)):
        newset=[]
        for j in range(dataset.shape[0]):
            if featureSet[i] == featureValues[j]:
                newset.append(dataset[j,:])
        newset=np.delete(newset,featureIndex,axis=1)
        # newset = newset[:,0:featureIndex].extend(newset[:,featureIndex+1:]) #不包括当前的列,得到新的数据集(数组的处理方式)       
        subdataset.append(np.array(newset))
    return subdataset  #划分得到三个子集

def splitDataSetByValue(dataset,featureIndex,value):
    subdataset=[]
    #迭代所有的样本
    for example in dataset:
        if example[featureIndex]==value:
            subdataset.append(example)
    return np.delete(subdataset,featureIndex,axis=1)


def chooseBestFeature(dataset,labels):
    """
    选择最优特征,但是特征是不包括名称的。
    如何选择最优特征:每一个特征计算,信息增益最大==条件熵最小就可以。
    """
    #特征的个数
    featureNum=labels.size
    #设置最小熵值
    minEntropy,bestFeatureIndex=1,None
    #样本总数
    n=dataset.shape[0]  
    for i in range(featureNum):
        #指定特征的条件熵
        featureEntropy=0
        #返回第i个特征的所有子集
        allSubDataSet=splitDataSet(dataset,i)
        for subDataSet in allSubDataSet:
            featureEntropy += subDataSet.shape[0]/n*dataset_entropy(subDataSet) #表示第i个特征的条件熵
        if minEntropy > featureEntropy: #将最小的条件上赋给minEntropy,特征索引值i给bestFeatureIndex
            minEntropy=featureEntropy
            bestFeatureIndex=i
    return bestFeatureIndex #最佳增益

def mayorClass(classList):
    labelCount={} #建立字典类型用于标签熟练存储
    for i in range(classList.size):
        label=classList[i]
        labelCount[label]=labelCount.get(label,0)+1 #计算每个标签的数量
    sortedLabel=sorted(labelCount.items(),key=operator.itemgetter(1),reverse=True)
    return sortedLabel[0][0]

##添加
def predict(tree,labels,testDate):  
    rootName =list(tree.keys())[0]
    rootValue = tree[rootName]
    featureIndex = list(labels).index(rootName)
    classLabel = None
    for key in rootValue.keys():
        #print(key)
        if testDate[featureIndex] == int(key):
            if type(rootValue[key]).__name__ == "dict":
                classLabel = predict(rootValue[key], labels, testDate)
            else:
                classLabel = rootValue[key]
                pass
            pass
        pass
    return classLabel

##添加
def predictAll(teree,labels,treeSet):
    classLabels = []
    for i in testSet:
        classLabels.append(predict(tree,labels,i))
        pass
    return classLabels

def createTree(dataset,labels):
    """
    参考hunt算法那张图片
    """
    classList=dataset[:,-1]
    if len(set(dataset[:,-1]))==1:
        return dataset[:,-1][0] #返回类别
    if labels.size==0 or len(dataset[0])==1:  #条件熵最少的一定是类别最多的
        #条件熵算不下去的时候,
        return mayorClass(classList)
    bestFeatureIndex=chooseBestFeature(dataset,labels)
    bestFeature=labels[bestFeatureIndex]
    dtree={bestFeature:{}}  #用代码表示这棵树
    featureList=dataset[:,bestFeatureIndex]
    featureValues=set(featureList) #得到最优节点的划分情况
    for value in featureValues:
        subdataset=splitDataSetByValue(dataset,bestFeatureIndex,value)
        sublabels=np.delete(labels,bestFeatureIndex) #将最优节点的标签删去后的标签组
        dtree[bestFeature][value]=createTree(subdataset,sublabels) #将原始的labels干掉一列后的
    return dtree                                                   #数据集和标签,开始递归

if __name__ == "__main__":
    dataset,labels=creatDataSet()
    # print(dataset_entropy(dataset)
    # s=splitDataSet(dataset,0)
    # for item in s:
    #     print(item)
    tree = createTree(dataset,labels)
    testSet = createTestSet()
    print(predictAll(tree, labels, testSet))#测试

"""
结果
['N', 'N', 'Y', 'N', 'Y', 'Y', 'N']
"""

 

posted @ 2021-04-22 09:28  北极星!  阅读(1034)  评论(1编辑  收藏  举报