机器学习总结(4)—分类中的树模型(决策树及RF.GBDT集成模型)
前言
过去几个月,一直在学习机器学习模型,输入只是学习的一部分,输出可以帮助自己更熟练地掌握概念和知识。把一个复杂的事物简单的讲述出来,才能表示真正弄懂了这个知识。所以我将在博客中尽量简单地把这些模型讲述出来,以加深自己的掌握,也为他人提供一点点参考。感谢大神刘建平Pinard的博客,如有任何疑惑可参考该神博客,此作仅为狗尾续貂之作。
每个模型介绍都将用基本思想,输入,输出,损失函数,优化方法,伪代码来进行介绍
正文
1.决策树
基本思想
决策树是一个经典的分类算法,用if,else构成,可以分类,回归,也可以拿来做集成模型的弱学习器,这里介绍sklearn中的决策树方法,cart树。
这就是决策树模型,每个节点2分类,这里会产生一个问题,算法怎么知道先用哪个节点来分类最合适呢?CART分类树算法使用基尼系数来决定用哪个特征进行分类,基尼系数代表了模型的不纯度,基尼系数越小,则不纯度越低,选用这个特征越好。
输入
样本X,属于的分类标签y
输出
我们希望生成一棵树,这棵树由很多个节点组成,我们希望得到对于分类最合适的节点。
损失函数
这里我们的损失函数是基尼系数,我们希望每次选节点时,选取可以让基尼系数最小的节点。假设有k个类别,每个类别有Ck个,共D个样本。那么基尼系数的表达式是:
对于某一个特征把样本化为两个群体时,基尼系数的表达式为:
对于离散型特征来说,把所有离散特征用来分类,并比较一下,选用最小的,然后接下来的节点继续这样做
对于连续特征,具体的思路如下,比如m个样本的连续特征A有m个,从小到大排列为a1,a2,...,am,CART算法取相邻两样本值的平均数,一共取得m-1个划分点,其中第i个划分点Ti表示为:Ti=ai+ai+1。对于这m-1个点,分别计算以该点作为二元分类点时的基尼系数。选择基尼系数最小的点作为分类点。对于多分类也是一样,反正都是度量基尼系数。
对于缺失值的处理,主要是改变一下基尼系数的算法,把缺失值和非缺失值的样本提出来,计算(非缺失的部分的基尼系数)*(非缺失/总样本数),以此作为基尼系数计算依据,然后分类。然后可以将缺失特征的样本同时划分入所有的子节点,不过将该样本的权重按各个子节点样本的数量比例来分配。
优化方法
决策树没有什么梯度下降等优化方法,就是计算然后比较,所以这里就介绍一下它的剪枝方法。
由于决策树容易过拟合,为了增加泛化性能,我们对决策树某些太细分的节点进行剪枝,那么怎么判断是否是太过细分了呢?CART采用的办法是后剪枝法,即先生成决策树,然后产生所有可能的剪枝后的CART树,然后使用交叉验证来检验各种剪枝的效果,选择泛化能力最好的剪枝策略
也就是说,CART树的剪枝算法可以概括为两步,
第一步是从原始决策树生成各种剪枝效果的决策树
第二步是用交叉验证来检验剪枝后的预测能力,选择泛化预测能力最好的剪枝后的树作为最终的CART树
首先我们看看剪枝的损失函数度量,在剪枝的过程中,对于任意的一刻子树T,其损失函数为:
其中,α为正则化参数,这和线性回归的正则化一样。C(Tt)为训练数据的预测误差,分类树是用基尼系数度量,回归树是均方差度量。|Tt|是子树T的叶子节点的数量。
对于任意节点,剪枝前和剪枝后的损失函数为:
当这两个相等时,代表可以剪枝
上面我们讲到,可以计算出每个子树是否剪枝的阈值α,如果我们把所有的节点是否剪枝的值α都计算出来,然后分别针对不同的αα所对应的剪枝后的最优子树做交叉验证。这样就可以选择一个最好的α,有了这个α,我们就可以用对应的最优子树作为最终结果
具体的算法实现过程可以看这里
伪代码
输入:训练集X,基尼系数阈值,样本个数阈值
输出:一颗决策树T
1) 对于当前节点的数据集为X,如果样本个数小于阈值或者没有特征,则返回决策子树,当前节点停止递归。
2) 计算样本集X的基尼系数,如果基尼系数小于阈值,则返回决策树子树,当前节点停止递归。
3) 计算当前节点现有的各个特征的各个特征值对数据集X的基尼系数。对于缺失值的处理按之前所说。
4) 在计算出来的各个特征的各个特征值对数据集X的基尼系数中,选择基尼系数最小的特征A和对应的特征值a。根据这个最优特征和最优特征值,把数据集划分成两部分X1和X2,同时建立当前节点的左右节点,左节点的数据集X为X1,右节点的数据集X为X2.
5) 对左右的子节点递归的调用1-4步,生成决策树。
如何解决回归问题
回归问题,用和方差度量损失,选择分出的两个集合的和方差最小的来进行节点分组:
前身(ID3和C4.5)
ID3通过信息论中的信息增益来寻找最优的节点,信息增益最大,就选哪个特征。无法处理缺失值,无法处理连续特征,且如果一个特征分类多,天然占优势。还有过拟合的问题,另外ID3的分裂不是2分裂的方式。
C4.5为了解决上述问题,不能处理连续特征:把连续特征离散化,选出每个连续特征的中间节点。对于特征分类多天然占优的情况:改进一下用信息增益比来度量(连续特征还是信息增益)。过拟合与CART树采用相同的剪枝处理。对缺失值处理:用CART树相同的方法。C4.5也有缺点,计算用的熵模型,费时间。只能用于分类,分裂方式还是多叉树。
2.随机森林RF
基本思想
随机森林是集成模型中的bagging模型,其基本思想是训练多棵树对同一数据进行预测,然后把这些树预测结果做一个投票,投票加总进行分类。抄一张现成的图:
随机采样:这里每棵树的训练在样本中有放回随机采样,一般采m个。
训练过程:随机森林的每棵树训练过程与cart有一点不同,他不会选最优节点来划分,而是先随机选一个特征子集,再在子集中选最优,增强泛化能力。
如何处理缺失值呢?和CART树一样,把缺失的特征的基尼系数*权重(意思就是你缺失了,你就乘以0.x,相比不缺失的特征,你就往后稍一稍),实在要用你分类了,缺失特征值的样本同时划入你分的两类中,同时这些样本要分别乘以权重(这里权重就是分开的比例)
其实这里就已经把随机森林介绍完了,输入输出,优化方法都是决策树那一套了。
伪代码
1.for i in n(n颗子树)
1.1 for i in m:随机采样数据集
1.2 随机选择特征子集,形成新的数据集
1.3 用数据集训练一颗决策树
2.统计n颗决策树的分类结果
3.梯度提升树GBDT
基本思想
GBDT是属于集成学习中的Boosting方法,bagging是多个训练器单独训练然后投票,而boosting方法是前面的学习结果要影响后面的一种方法(比如:这个弱学习器预测出来的一个样本的结果不如意,下一个弱学习器训练时,这个样本的权重就会增加,这样算损失函数的时候就会偏向这个样本多一些)
以下介绍基于GBDT回归树:GBDT基于boosting思想,用残差来拟合下一棵树,最终形成强学习器。什么叫拟合残差,举个例子是这样:如果你要吃一块饼,你先吃完一半(0.5),再吃剩下一半的一半(0.25),如此往复,最终拟合整个1。GBDT就是第一颗树尽量拟合,第二棵树拟合这个残差,第三棵树再拟合剩下的残差,f(最终) = f(初) + f(残1) + f(残2) +...第一轮的损失就是第二轮的残差,第二轮的损失就是第三轮残差。但是这个损失如何度量呢?
损失函数
前面说过,我们要知道每一轮的残差,就得知道损失是什么。如何度量损失,Freidman提出用“损失函数的负梯度”来拟合本轮损失近似值。这是借鉴了泰勒展开的思想。这也是为什么叫梯度提升树的原因。
下面具体来看一下,怎么度量残差:
第t轮,第i个样本的残差为:
这个是什么意思呢,其实就是损失函数(标量)对变量(标量)求导,很简单,如果损失函数度量是(y-x)2,那么就求它对x的导数。有了rti,我们就可以用
(xi,rti)(i=1,2,..m)来作为数据集,拟合下一轮的树了!
知道残差和损失函数的关系了,那么损失函数怎么定义呢?对于回归,一般可用
均方差
绝对损失
huber损失:是均方差和绝对损失的折合,超过了一定距离的异常点就用绝对损失。
分位数损失(暂时没看)
对于分类,一般可用指数损失函数,和对数损失函数
优化方法
我们拟合出一颗cart回归树之后还有一个步骤是确定回归的输出值,
以前的cart回归树是用叶子结点的均值,来作为回归的输出值(举个例子:特征a大于1的样本有3个,y值分别是5,4,3。于是就输出5,4,3的均值4作为回归结果)
但是在GBDT里不再是这样,GBDT的回归值c要取得让总体损失函数最小:
(举个例子,特征a大于1的样本有2个,y值是5,4,上一轮中拟合出来的数值是3,这一轮拟合出的值就是c使得(5-3-c)2+(4-3-c)2最小)
(Rtj,j=1,2,...,J代表其对应的叶子节点区域)
于是本轮的决策树的拟合函数如下:
(I就代表,属于这个区域就是1,不属于就是0)
于是最终的强学习器就是:
伪代码
GBDT回归算法:
输入是训练集样本X,最大迭代次数T, 损失函数L
输出强学习器,多棵树(注意:这里每棵树的输入由上一棵树决定了,不再是随机森林那样从样本里抽)
1.初始化弱学习器,
2.for t in T(树的数量,开始训练第二颗,第三颗树)
a.for i in m 计算样本的负梯度:
b.利用负梯度,组成新的数据集(xi,rti)(i=1,2,..m),拟合一颗新的cart树,得到第t颗回归树,其对应的叶子节点区域为Rtj,j=1,2,...,J。其中J为回归树t的叶子节点的个数。
c.for j in J,对新树的叶子区域进行最佳c值拟合
d.更新强学习器
3.循环完T轮,得到了强学习器
分类树
基本思想是和逻辑回归相似,用概率来表示分类,用极大似然概率来测量损失
对二分类来说,损失函数是:
(这里用的负的对数似然函数做损失函数,负号和sigmoid的函数形式抵消了)
根据损失函数形式,则有最佳c值为
此式子难以优化,近似一下为:
有了每一棵树的ctj于是强学习器得解,最后结果通过判断加总结果接近1或者-1来二分类。
对于多分类的公式,我不是很熟悉,但是基本思想是训练多个强学习器,用one vs more的思想进行分类,即通过一个强学习器先分成1类和剩下类,再通过一个强学习器把剩下类又分为1类和剩下类。
4.XGBOOST
基本思想
对GBDT的改进:
1.用二阶泰勒展开来做损失,更精密一些
2.树最后的回归值选择(ctj)选择跟GBDT不同,分类节点选择跟cart树也不同,对单颗树的分裂过程做了并行优化
3.缺失值的处理:对于缺失值的特征,通过比较所有缺失值在当前节点是进入左子树还是右子树来决定缺失值的处理方式。
损失函数
XGB的损失函数如下,加入了正则化:
(wtj是第j个叶子节点的最优值。这里的wtj和我们GBDT里使用的ctj是一个意思,J是叶子结点个数)
对前面的项进行二阶泰勒展开(不展开正则化项),最后残差为:
注意,这里残差不再是(xi,rti) i=1,2,3...m这样的数据集,而就是这样一个函数,但它其实是表达了类似的意思,就是把这一轮所有残差求和嘛。
上面的符号解释:
回顾GBDT,我们先分裂树,再通过逼近损失函数,来求ctj。但是XGB说:我要一次把两个都求了。见下面优化方法
优化方法
关于如何一次求解出决策树最优的所有J个叶子节点区域和每个叶子节点区域的最优解wtj,我们可以把它拆分成2个问题:
1) 如果我们已经求出了第t个决策树的J个最优的叶子节点区域,如何求出每个叶子节点区域的最优解wtj?
2) 对当前决策树做子树分裂决策时,应该如何选择哪个特征和特征值进行分裂,使最终我们的损失函数Lt最小?
对于第一个问题,我们直接用损失函数对wtj求偏导等于0就好了。
把式子带入原损失函数式,wtj取最优解时,损失函数为
对于第二个问题,XGBoost这里不使用GBDT中的均方误差,而是使用贪心法,即每次分裂都期望向着最小化损失函数的地方前进。于是这里就涉及到分裂的时候,损失函数如何变化的:
(这就是变化过程,这个式子代表第一层的损失函数减去第二层的损失函数,还是举个例子:假设损失函数是一百万元,转运到银行,途中要换乘几辆车,我们的任务是尽快把钱都掉在运输途中,给银行送去的钱越少越好,我们当然就希望每次转运的时候损失最多的钱。同理,我们就希望最大化上式子)
左右子树的一阶二阶导数和为GL,HL,GR,HL,生动的解释如下:
我们对每个分割点都试一试,看看哪个的最大,我们就用它分裂。由此我们一并解决了用哪个特征分类的问题,也解决了wtj等于什么的问题
伪代码
5.LightGBM
基本思想
对XGB的改进,
1.把特征值弄成直方图,减小节点数量,通过直方图来做节点判断,这样计算量小了许多。
2.树的分裂过程,不是所有节点同时二分(按层生长),而是一次只选择一个节点二分(leaf-wise)。
3.可直接使用category特征,对分类特征不用再进行one-hot编码了(one-hot对于类别很多的特征来说没有价值)