
决策树算法是一个强大的预测方法,它非常流行。因为它们的模型能够让新手轻而易举地理解得和专家一样好,所以它们比较流行。同时,最终生成的决策树能够解释做出特定预测的确切原因,这使它们在实际运用中倍受亲睐。
同时,决策树算法也为更高级的集成模型(如 bagging、随机森林及 gradient boosting )提供了基础。
在这篇教程中,你将会从零开始,学习如何用python实现《Classification And Regression Tree algorithm》中所说的内容。在学完该教程之后,你将会知道:如何计算并评价数据集中地候选分割点(Candidate Split Point)
如何在决策树结构中排分配这些分割点
如何在实际问题中应用这些分类和回归算法
让我们开始吧。
一、概要
本节简要介绍了关于分类及回归树(Classification and Regression Trees)算法的一些内容,并给出了将在本教程中使用的钞票数据集(Banknote Dataset)。
1.1 分类及回归树
分类及回归树(CART)是由 Leo Breiman 提出的一个术语,用来描述一种能被用于分类或者回归预测模型问题的回归树算法。
我们将在本教程中主要讨论 CART 在分类问题上的应用。
二叉树(Binary Tree)是 CART 模型的代表之一。这里所说的二叉树,与数据结构和算法里面所说的二叉树别无二致,没有什么特别之处(每个节点可以有0、1或2个子节点)。
每个节点代表在节点处有一个输入变量被传入,并根据某些变量被分类(我们假定该变量是数值型的)。树的叶节点(又叫做终端节点,Terminal Node)由输出变量构成,它被用于进行预测。
在树被创建完成之后,每个新的数据样本都将按照每个节点的分割条件,沿着该树从顶部往下,直到输出一个最终决策。
创建一个二元分类树实际上是一个分割输入空间的过程。递归二元分类(Recursive Binary Splitting)是一个被用于分割空间的贪心算法。这实际上是一个数值过程:当一系列的输入值被排列好后,它将尝试一系列的分割点,测试它们分类完后成本函数(Cost Function)的值。
有最优成本函数(通常是最小的成本函数,因为我们往往希望该值最小)的分割点将会被选择。根据贪心法(greedy approach)原则,所有的输入变量和所有可能的分割点都将被测试,并会基于它们成本函数的表现被评估。(译者注:下面简述对回归问题和分类问题常用的成本函数。)
回归问题:对落在分割点确定区域内所有的样本取误差平方和(Sum Squared Error)。
分类问题:一般采用基尼成本函数(Gini Cost Function),它能够表明被分割之后每个节点的纯净度(Node Purity)如何。其中,节点纯净度是一种表明每个节点分类后训练数据混杂程度的指标。
分割将一直进行,直到每个节点(分类后)都只含有最小数量的训练样本或者树的深度达到了最大值。1.2 Banknote 数据集
Banknote 数据集,需要我们根据对纸币照片某些性质的分析,来预测该钞票的真伪。
该数据集中含有1372个样本,每个样本由5个数值型变量构成。这是一个二元分类问题。如下列举5个变量的含义及数据性质:
图像经小波变换后的方差(Variance)(连续值)
图像经小波变换后的偏度(Skewness)(连续值)
图像经小波变换后的峰度(Kurtosis )(连续值)
图像的熵(Entropy)(连续值)
钞票所属类别(整数,离散值)
如下是数据集前五行数据的样本。

使用零规则算法(Zero Rule Algorithm)来预测最常出现类别的情况(译者注:也就是找到最常出现的一类样本,然后预测所有的样本都是这个类别),对该问的基准准确大概是50%。
你可以在这里下载并了解更多关于这个数据集的内容:UCI Machine Learning Repository。
请下载该数据集,放到你当前的工作目录,并重命名该文件为data_banknote_authentication.csv。
二、教程 本教程分为五大部分:
对基尼系数(Gini Index)的介绍
(如何)创建分割点
(如何)生成树模型
(如何)利用模型进行预测
对钞票数据集的案例研究
这些步骤能帮你打好基础,让你能够从零实现 CART 算法,并能将它应用到你子集的预测模型问题中。2.1 基尼系数
基尼系数是一种评估数据集分割点优劣的成本函数。
数据集的分割点是关于输入中某个属性的分割。对数据集中某个样本而言,分割点会根据某阈值对该样本对应属性的值进行分类。他能根据训练集中出现的模式将数据分为两类。
基尼系数通过计算分割点创建的两个类别中数据类别的混杂程度,来表现分割点的好坏。一个完美的分割点对应的基尼系数为0(译者注:即在一类中不会出现另一类的数据,每个类都是「纯」的),而最差的分割点的基尼系数则为1.0(对于二分问题,每一类中出现另一类数据的比例都为50%,也就是数据完全没能被根据类别不同区分开)。
下面我们通过一个具体的例子来说明如何计算基尼系数。
我们有两组数据,每组有两行。第一组数据中所有行都属于类别0(Class 0),第二组数据中所有的行都属于类别1(Class 1)。这是一个完美的分割点。
首先我们要按照下式计算每组数据中各类别数据的比例:
proportion=count(class_value)/count(rows)
那么,对本例而言,相应的比例为:

基尼系数按照如下公式计算:
gini_index=sum(proportion*(1.0-proportion))
将本例中所有组、所有类数据的比例带入到上述公式:

化简,得:
gini_index=0+0+0+0=0
如下是一个叫做 gini_index() 的函数,它能够计算给定数据的基尼系数(组、类别都以列表(list)的形式给出)。其中有些算法鲁棒性检测,能够避免对空组除以0的情况。# Calculate the Gini index for a split dataset
def gini_index ( groups , class_values ) :
gini = 0.0
for class_value in class_values :
for group in groups :
size = len ( group )
if size == 0 :
continue
proportion = [ row [ - 1 ] for row in group ] . count ( class_value ) / float ( size )
gini += ( proportion * ( 1.0 - proportion ) )
return gini
我们可以根据上例来测试该函数的运行情况,也可以测试最差分割点的情况。完整的代码如下:
# Calculate the Gini index for a split datasetdef gini_index ( groups , class_values ) :
gini = 0.0
for class_value in class_values :
for group in groups :
size = len ( group )
if size == 0 :
continue
proportion = [ row [ - 1 ] for row in group ] . count ( class_value ) / float ( size )
gini += ( proportion * ( 1.0 - proportion ) )
return gini
# test Gini values
print ( gini_index ( [ [ [ 1 , 1 ] , [ 1 , 0 ] ] , [ [ 1 , 1