决策树是监督学习算法之一,而且是一种基本的分类与回归方法;决策树也分为回归树和分类树,本文讨论的是分类树。若是了解或者学过数据结构,确定对"树"这个概念是不陌生的,在此基础上学习掌握决策树也会更加容易,下面经过一个小例子帮助理解何为决策树。python
下图所示流程图即为一个决策树,矩形表明判断模块、椭圆形则表明终止模块,表示已经得出结论能够终止程序的运行;左右箭头表示分支,能够经过它到达另外一判断模块或终止模块。
这个流程图主要是假想一个择偶系统,以前网上不流行这样一句话嘛,"阿姨我不想努力了",该树就以是否想继续努力为判断依据,若是你不想继续努力了,你能够选择找一个"富婆";反之,你想找一个女友一块儿奋斗,这里又以女孩的性格为判断依据,若是喜欢性格温柔的,即选择"温柔女孩",若喜欢性格高冷的,则选择"酷女孩"。算法
整个决策树能够当作一个if—then规则,即"若是判断条件,则……",而且须要注意如下三点:数据结构
构造决策树的数据必需要充足,特征较少的数据集可能会致使决策树的正确率偏低。若数据特征过多,不会选择特征也会影响决策树的正确率。构建一个比较理想的决策树,大体可分为如下三步:特征选择、决策树的生成与决策树的修剪。ide
特征选择即决定用数据集中哪个特征划分特征空间,主要在于选取对训练数据具备分类能力的特征。这样作的目的是提升决策树的效率,若是一个用特征的分类结果和随机分类的结果差异不大,那么这个特征是没有什么分类能力的,通常地讲这类特征去除对决策树的精度影响不大。函数
下面表格是一份关于海洋生物的数据,数据的两个特征分别为no surfacing(不浮出水面是否能生存)、flippers(是否有脚蹼),一个类标签fish(是否为鱼类)。学习
no surfacing | flippers | fish |
---|---|---|
是 | 是 | 是 |
是 | 是 | 是 |
是 | 否 | 否 |
否 | 是 | 否 |
否 | 是 | 否 |
不论一个数据集有多少特征,每次划分数据集时只能选一个特征,那么第一次选择哪一个特征做为划分的参考属性才能将数据更快的分类呢?答案必定是必定是分类能力最好的那个特征,但问题来了,如何判断哪个特征分类能力最好呢?这时就要引入一个新的概念——信息增益。测试
什么是信息增益呢?在划分数据集以前以后信息发生的变化成为信息增益,知道如何计算信息增益,咱们就能够计算每一个特征值划分数据集得到的信息增益,得到信息增益最高的特征就是最好的选择。code
在计算信息增益以前,须要先知道"熵"这个概念,"熵"到底是什么东西,没必要去深究它,咱们只需了解熵定义为信息的指望值,在信息论与几率统计中,熵是表示随机变量不肯定性的度量,用一句通俗的话讲就是这个体系的混乱程度是如何的。blog
假设有一个样本为n的数据集,第i类样本为Xi,那么符号Xi的信息可定义:
其中其中p(Xi)是选择该分类的几率。经过改变i的值便可得到数据集中全部类别的信息。递归
为了计算熵,咱们须要计算全部类别全部可能值包含的信息指望值(数学指望),经过下面的公式获得:
熵越高,变量的不肯定性越大,也就是数据的混合程度越高。
在建立数据集以前,对数据进行一下自定义简化,用1表明"是",0表明"否"。
计算熵代码以下:
#建立数据集 def createDataSet(): DataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] #矩阵转化Dataframe DataSet = pd.DataFrame(DataSet,columns=['no surfacing','flippers','fish']) return DataSet #计算信息熵 def calculatent(DataSet): #统计类标签的分类 fish_type = DataSet['fish'].value_counts() ''' no 3 yes 2 ''' #获取样本个数 m = DataSet.shape[0] #每一个分类的几率,即p(Xi) p = fish_type/m #计算熵值 ent = (-p*np.log2(p)).sum() return ent ''' 0.9709505944546686 '''
最后返回熵为0.9709505944546686
这份数据中,最后的类标签共有两类"是鱼类"、"不是鱼类",前者占总标签个数的2/5,后者占3/5。
因此这部分代码的核心部分即套用求熵公式:
在获得熵以后,就能够按照最大信息增益的方法划分数据集,下一部分开始计算信息增益。
计算过熵后,怎么计算信息增益呢?信息增益的计算就是将父节点的熵减去其下全部子节点的熵之和,而且在求和时,因为类别比重不一样,须要对其实现加权平均。
以"no surfacing"这一列举例,5个样本中,"1"有3个,"0"有2个,因此两者的权重一个为3/5,另外一个为2/5;
其中对于"1"这三个样本,样本标签fish中的"是"有两个,"否"有一个,因此分类的几率分别为2/3和1/3,同理对于"0"这两个样本,样本标签fish都为"否",因此分类几率为1。
这一列的信息增益计算公式以下:
两个特征的信息增益计算结果以下:
计算每一个特征信息增益的目的就是要选择出每次分类时当前的最优特征,因此必定会有一个比较过程,便于获得最大的信息增益,而且返回该特征的索引,而后就能够利用这个最优特征对数据集进行切割。
因此这里构造两个函数,一个选择最优特征函数,另外一个是分割数据集函数,具体代码以下:
#最优特征函数 def ChooseBF(DataSet): #获取基础信息熵 all_ent = calculatent(DataSet) BestGain = 0 #初始化一个信息增益 col = -1 #初始化一个最优特征索引 for i in range(DataSet.shape[1]-1): #遍历全部特征 #获取第i列的分类 index_ = DataSet.iloc[:,i].value_counts().index child_ent = 0 #初始化某列的信息熵 for j in index_: #将第i列按照类别分类 child_DataSet = DataSet[DataSet.iloc[:,i]==j] #计算某一类别的信息熵 ent = calculatent(child_DataSet) #将全部类别信息熵加权求和 child_ent += (child_DataSet.shape[0]/DataSet.shape[0])*ent print("第%s列的熵为%s"%(i,child_ent)) TheGain = all_ent-child_ent #获取信息增益 print("第%s列的信息增益为%s"%(i,TheGain)) #得出最优特征索引 if(TheGain>BestGain): BestGain = TheGain col = i print("最优特征索引为:%s"%col) return col
这个函数两次调用计算信息熵函数calculatent,一次是为了获取基础信息熵,即上面公式中ent('总');另外一次则是计算特征中不一样类别的信息熵,即上面公式中ent('是')、ent('否')。
代码运行截图以下:
返回的最优特征索引为0,也就是"no surfacing"这一列;而且两列的信息增益都与上文手写计算的结果一致,因此代码是彻底没有问题的。
下一步,利用最优特征对数据集进行划分,代码以下:
#切分数据集函数 def splitSet(DataSet,col,value): #找到最优特征索引标签 index = (DataSet.iloc[:,col]).name #切分数据集,并把当前最优特征删去 resetdata = DataSet[DataSet.iloc[:,col]==value].drop(index,axis = 1) return resetdata
这个函数须要传入三个参数,分别是须要切分的数据集、最优特征索引、特征中需保留的分类,可能有的人尚未理解这个value的做用,对比一下运行结果就懂了。
''' col = 0,value = 1 flippers fish 0 1 yes 1 1 yes 2 0 no col = 0,value = 0 flippers fish 3 1 no 4 1 no '''
上面两个DataFrame,是分割数据集函数返回的结果,当value = 1时,保留下来的这三个样本,都是"no surfacing"这一特征中值为1的;而当value = 0时,保留下来的两个样本,就是"no surfacing"这一特征中值为0的,这样一对比就很容易理解value的做用了。
至此熵与信息增益的计算方法大体上已经介绍完毕,文中所取数据集特征数不多,因此致使数据集分类次数也会不多,当数据特征比较多时,通过第一次划分以后,数据集向下传递到决策树的分支的下一个结点,在这个结点上,咱们能够再次划分数据,所以须要采用递归的原则处理数据集。
下文将会介绍如何用递归构建决策树以及决策树的可视化,毕竟决策树一个很大的优势就是经过可视化更好的理解数据是如何一步一步划分的。
公众号【奶糖猫】后台回复“信息增益”便可获取源码供参考,感谢阅读。