在学习了jack cui机器学习博客后,为了给自己留下一个理解的笔记,本人比较笨,以后方便查看。他的博客地址在下方,写得很好。
点击打开链接
决策树机器学习的一种分类方法,
拿相亲来说,决策树模型就是上面这一个,长方形为这个人的某个特征,决策树有内部节点和叶节点。内部节点就是这个长方形,是其特征。叶节点是椭圆,就是其得到的结果,是备胎还是值得考虑。而特征也是一个分类的规则条件。是将它继续划分到哪一边。每一个特征都会影响这个决策树分类的结果。
决策树的构建这一过程要分为3个步骤:
特征选择,决策树的生成和决策树的修剪。
使用决策树做预测需要一下过程:收集数据-->准备数据-->分析数据-->训练算法-->测试算法-->使用算法
1.特征选择:
特征选择就是选取具有分类能力的特征,如果某个特征对本次分类不会产生影响或则说产生的影响很低,那么我们就可以选择抛弃这个特征。这个特征就是没有分类能力的,无用特征。而在选取特征上,有一个方法就是计算某个特征的信息增益,然后看其信息增益的大小,越大的说明它对本次分类结果影响越大,反之亦然。
这里有一个实例,就是贷款申请的样本数据:
这次我们通过说给的数据学习一个贷款申请的决策树,用于对未来某个客户提出申请贷款时,我们可以根据这个人的一些特征数据考虑是否给这个人贷款。
那么我们要构建一个决策树模型,就应该选择分类能力大的特征,现在看上面那个表单,不会看到什么不同。这是就需要我们计算信息增益了,在计算信息增益之前,我们需要计算一个香农熵。
(1)香农熵(经验熵)
根据此公式计算经验熵H(D),分析贷款申请样本数据表中的数据。最终分类结果只有两类,即放贷和不放贷。根据表中的数据统计可知,在15个数据中,9个数据的结果为放贷,6个数据的结果为不放贷。所以数据集D的经验熵H(D)为
所以H(D)经验熵为0.971
那么现在需要的一个就是条件熵,也就是某个特征的信息增益:
在上面我给的那个博客地址上已经讲得很好了,很容易理解
以贷款申请样本数据表为例进行说明。看下年龄这一列的数据,也就是特征A1,一共有三个类别,分别是:青年、中年和老年。我们只看年龄是青年的数据,年龄是青年的数据一共有5个,所以年龄是青年的数据在训练数据集出现的概率是十五分之五,也就是三分之一。同理,年龄是中年和老年的数据在训练数据集出现的概率也都是三分之一。现在我们只看年龄是青年的数据的最终得到贷款的概率为五分之二,因为在五个数据中,只有两个数据显示拿到了最终的贷款,同理,年龄是中年和老年的数据最终得到贷款的概率分别为五分之三、五分之四。所以计算年龄的信息增益,过程如下:
好了,现在就来解读整个代码,这也是我这个小白写笔记的原因,方便以后复习
在编写代码之前,我们先对数据集进行属性标注。
- 年龄:0代表青年,1代表中年,2代表老年;
- 有工作:0代表否,1代表是;
- 有自己的房子:0代表否,1代表是;
- 信贷情况:0代表一般,1代表好,2代表非常好;
- 类别(是否给贷款):no代表否,yes代表是。
- from math import log
-
- '''''
- 函数说明:
- 创建测试数据集
- Parameters(参数):
- 无
- returns:
- dataSet:数据集
- labels 分类属性
- '''
- def createDataSet():
- dataSet = [[0, 0, 0, 0, 'no'], #数据集
- [0, 0, 0, 1, 'no'],
- [0, 1, 0, 1, 'yes'],
- [0, 1, 1, 0, 'yes'],
- [0, 0, 0, 0, 'no'],
- [1, 0, 0, 0, 'no'],
- [1, 0, 0, 1, 'no'],
- [1, 1, 1, 1, 'yes'],
- [1, 0, 1, 2, 'yes'],
- [1, 0, 1, 2, 'yes'],
- [2, 0, 1, 2, 'yes'],
- [2, 0, 1, 1, 'yes'],
- [2, 1, 0, 1, 'yes'],
- [2, 1, 0, 2, 'yes'],
- [2, 0, 0, 0, 'no']]
- labels=['不放贷','放贷']
- return dataSet,labels
- '''''
- 函数说明:
- 计算给定数据集的经验熵(香农熵)
- Parameters:
- dataSet
- Returns:
- shannonEnt-经验熵(香农熵)
- '''
- def calcShannonEnt(dataSet):
- #这个函数对计算经验熵和条件熵都是相同的,因为公式其实并无区别。只是条件熵加上了一个条件,数据集变小了而已
- numEntires=len(dataSet)
- #计算这个数据集有多少行
- labelCounts={}
-
- #定义一个空字典
- for featVec in dataSet:
- #循环遍历数据集中的每行数据
- currentLabel=featVec[-1]
- #得到每行数据中的最后一个标签
- if currentLabel not in labelCounts.keys():
- #判断这个标签是否在这个字典里面
- labelCounts[currentLabel]=0
- #如果没有先初始化这个标签名(label)
- labelCounts[currentLabel]+=1
- #记录标签名出现的次数,记录字典里的键值
- shannonEnt=0.0
- #定义一个浮点数,是用来保存香农熵的
- for key in labelCounts:
- #循环遍历字典里的键名
- prob=float(labelCounts[key])/numEntires
- #得到yes或no 这个标签的概率
- shannonEnt -=prob * log(prob,2)
- #公式计算香农熵
- return shannonEnt
- """
- 函数说明:按照给定特征划分数据集
-
- Parameters:
- dataSet - 待划分的数据集
- axis - 划分数据集的特征
- value - 需要返回的特征的值
- Returns:
- 无
- Author:
- Jack Cui
- Modify:
- 2017-03-30
- """
- def splitDataSet(dataSet, axis, value):
- retDataSet = [] #定义一个列表来保存我们需要的子集
- for featVec in dataSet: #遍历每行
- if featVec[axis] == value:#如果这一行的这一列(axis)有一个元素是我们要找的,比如在第一列有我们要找的青年,就把这一行数据保存下来
- '''''
- reducedFeatVec = featVec[:axis]
- reducedFeatVec.extend(featVec[axis+1:])
- retDataSet.append(reducedFeatVec)
- '''
- retDataSet.append(featVec)#我觉得博主写的这个好像有点多余,因为我们将数据带入calShannonEnt()函数中就是看它后面的标签是否放贷,所以将某一行的这个元素去掉,没必要
- return retDataSet
-
- '''''
- 函数说明:计算每个特征的信息增益,然后选择最优特征
- Parameters:
- dataSet-数据集
- Returns:
- bestFeature-信息增益最大的特征的索引值
- '''
- def chooseBestFeatureToSplit(dataSet):
- numFeatures=len(dataSet[0])-1#得到特征数
- baseEntropy=calcShannonEnt(dataSet)#得到总数据的香农熵
- bestInfoGain=0.0#定义一个浮点数,来接受经验熵的值
- bestFeature=-1#定义一个数接受最优特征是第几列
- for i in range(numFeatures):
- #循环遍历4个特征
- featList = [example[i] for example in dataSet]#将每列的特征的数据存入这个列表,这个列表就保存每列特征的数据,下一列的数据会覆盖掉上一列数据
- uniqueVals = set(featList)#创建set集合{},保存的元素不可重复
- newEntropy = 0.0
- for value in uniqueVals:
- #遍历集合中的每个元素,计算信息增益,例如:年龄中的青年,中年,老年。因为字典中的每个不同元素就代表了青年,中年,老年
- subDataSet = splitDataSet(dataSet, i, value)#划分包含了value的某些子集,就是某些包含了value的行,将它变成一个数据集,然后计算他的信息增益(条件熵),然后我们转到这个函数
- prob = len(subDataSet) / float(len(dataSet))#下面的就是计算步骤了,没什么区别
- newEntropy += prob * calcShannonEnt(subDataSet) #这是累加,我们计算的是这一列的条件熵,例如为年龄的条件熵,所以就将青年,中年,老年的加起来
- infoGain = baseEntropy - newEntropy #这就是真正的信息增益了,经验熵减去条件熵
- print("第%d个特征的增益为%.3f" % (i, infoGain))#输出每列特征的增益
- if (infoGain > bestInfoGain): #比较每个增益,输出最大增益
- bestInfoGain = infoGain
- bestFeature = i#保留它是第几列,后面方便输出
- return bestFeature
-
-
-
- if __name__=="__main__":
- dataSet, features = createDataSet()
- print("最优特征索引值:" + str(chooseBestFeatureToSplit(dataSet)))