目录
- 一、集成学习
- 概览
- 1、介绍
- 2、学习器
- 3、boosting和bagging比较
- 1、样本选择
- 2、样例权重
- 3、预测函数
- 4、计算
- 5、其他
- 4、结合策略
- 二、Adaboost
- 1、介绍
- 2、运行过程
- 3、特点
- 4、代码示例
- 三、随机森林
- 1、介绍
- 2、随机森林生成
- 3、特点
- 4、优缺点
- 5、代码示例
- 6、参数介绍
- 四、GBDT
- 1、介绍
- 2、回归树算法流程
- 3、GBDT加法模型:前向分布算法
- 4、加法模型算法过程
- 5、Shrinkage
- 6、优点
- 7、基学习器选择
- 五、XGBoost
- 1、介绍
- 2、算法思想
- 3、模型表示
- 4、训练过程
- 1、损失函数的理解
- 2、正则项的理解
- 3、叶子分数的求解
- 4、找出树的最优结构
- 5、代码实现
- 6、参数解释
- 7、模型保存
- 六、LightGBM
- 1、介绍
- 2、直方图算法
- 3、Leaf-wise算法
- 4、单边梯度采样算法
- 5、代码实现
- 6、参数解释
一、集成学习
结构化数据挖掘,机器学习树模型处理效果最好,非结构化数据挖掘,深度学习人工智能模型较好,所以有了深度学习之后还需要继续学习机器学习。
概览
1、介绍
集成学习(ensemble learning)本身不是一个单独的机器学习算法,而是通过构建并结合多个机器学习器来完成学习任务。
机器学习可用于分类问题集成、回归问题集成、特征选取集成、异常点检测集成等。
思想:通过训练若干个个体学习器,通过一定的结合策略,就可以最终形成一个强学习器。
如果个体学习器预测准确率很差,使用集成学习后会导致好的结果更好,同时也会导致差的更差。所有需要提高个体学习器性能,在集成学习算法的思路是好(的学习器)而不同(不同的学习器)。
2、学习器
- 所有个体学习器都是一个种类的,或者说是同质的。比如都是决策树个体学习器,或者都是神经网络个体学习器。
- 所有的个体学习器不全是一个种类的,或者同质。比如我们有一个分类问题,对训练集才有支持向量机个体学习器,逻辑回归个体学习器、朴素贝叶斯个体学习器来学习,再通过某种结合策略来确定最终的分类强学习器。
同质个体学习器:应用最广泛,采用决策树模型或神经网络
-
同质个体学习器按照个体学习器之间是否存在依赖关系分为两类
-
第一个是个体学习器之间存在强依赖关系,一系列个体学习器基本都需要串行生成,代表算法是boosting系列算法;boosting(每轮训练更新权重)中典型算法为adaboost算法。
-
第二个是个体学习器之间不存在强依赖关系,一系列个体学习器可以并行生成,代表算法是bagging和随机森林(Random Forest)。可以理解为有放回的随机抽样。
3、boosting和bagging比较
1、样本选择
- boosting:每一轮的训练集不变,只是训练集中每个样例在分类器中的权重发生变化,而权值是根据上一轮的分类结果进行调整;
- bagging:训练集是在原始集中有放回选取的,从原始集中选出的各轮训练集之间是独立的。
2、样例权重
- boosting:根据错误率不断调整样例的权值,错误率越大则权重越大,因此boosting的分类精度通常要优于bagging;
- bagging:使用均匀取样,每个样例的权重相等;
3、预测函数
- boosting:每个弱分类器都有相应的权重,对于分类误差小的分类器会有更大的权值
- bagging:所有预测函数的权重相等
4、计算
- boosting:各个预测函数只能顺序生成,因为后一个模型参数需要前一轮模型的结果;
- bagging:各个预测函数可以并行生成,对于神经网络这样极为耗时的学习方法,bagging可通过并行训练节省大量时间开销;
5、其他
- boosting和bagging都可以有效地提高分类的准确性,在大多数数据集中,boosting的准确性比bagging高
- 在有些数据集中,boosting会引起退化-overfit。
4、结合策略
- 平均法
对于数值类的回归预测问题,通常使用的结合策略是平均法,也就是说,对于若干个弱学习器的输出进行平均得到最终的预测输出- 算术平均
- 加权平均
- 投票法
分类问题的预测,通常使用的是投票法。假设我们的预测类别是{c1,c2,…ck},对于任意一个预测样本x,我们的T个弱学习器的预测结果分别是(h1(x),h2(x)…hT(x))- 相对多数投票法:少数服从多数
- 绝对多数投票法:票要通过半数
- 加权投票法:每个弱学习器的分类票数要乘以一个权重
- 学习法
代表方法是stacking,当使用stacking的结合策略时,我们不是对弱学习器的结果做简单的逻辑处理,而是再加上一层学习器,也就是说,我们训练集弱学习器的学习结果作为输入,讲训练集的输出作为输出,重新训练一个学习器来得到最终结果
弱学习器称为初级学习器,将用于结合的学习器称为次级学习器。对于测试集,我们首先用初级学习器预测一次,得到次级学习器的输入样本,再用次级学习器预测一次,得到最终的预测结果。
二、Adaboost
1、介绍
adaboost算法是基于boosting思想的机器学习算法,adaboost是adaptive(自适应) boosting的缩写
自适应:
- 样本权重的自适应更新:每次用全部的样本,前一个基本分类器被错误分类样本的权值会增大,而正确分类的权值减小,用来训练下一个基本分类器
- 预测函数的权重自适应更新:在每轮迭代中加入一个新的弱分类器,直到达到某预定足够小错误率或达到预先指定的最大迭代次数才最终合成强分类器
- 弱学习器的数量自适应更新:在每轮迭代中加入一个新的弱分类器,直到达到某预定足够小错误率或达到预先指定的最大迭代次数才最终合成强分类器
重点记忆:2个权重,1个是根据上轮结果计算出的,一个是算法权重,用做结合策略用的
2、运行过程
-
计算样本权重:训练数据中的每个样本,赋予其权重,即样本权重,用向量D表示,这些权重都初始化成相等值。假设有m个样本的训练集,设定每个样本的权重都是相等的,即1/m
{(x1,y1),(x2,y2),…,(xm,ym)} -
计算错误率:利用第一个弱学习算法h1对其进行学习,学习完成后进行错误率ε的统计:
ε = 未正确分类的样本数目 / 所有样本数目
-
计算弱学习算法权重:弱学习算法也有一个权重,用向量α表示,利用错误率计算权重α:
α = 1/2 ㏑[ (1-ε) / 2 ] -
更新样本权重:在第一次学习完成后,需要重新调整样本的权重,以使得在第一分类中被错分的样本的权重,在接下来的学习中可以重点对其进行学习
其中ht(xi) = yi 表示对第i个样本训练正确,不等于则表示分类错误,Zt是一个归一化因子:Zt = sum(D) -
重复进行学习,经过t轮的学习后,就会得到t个弱学习算法、权重、弱分类器的输出以及最终的Adaboost算法的输出
3、特点
- 简单,不用做特征筛选
- 在大多数数据集中,boosting的准确性比bagging高
- 在Adaboost算法在邮件过滤、文本分类方面都有很好的性能
- Adaboost对于噪音数据和异常敏感数据,在每次迭代的时候会给噪声点较大的权重
- Adaboost是一种串行算法,运行速度较慢
4、代码示例
from sklearn.linear_model import LogisticRegression
lr= LogisticRegression()
from sklearn.ensemble import AdaBoostClassifier
ada= AdaBoostClassifier(base_estimator= lr, n_estimators= 20)
ada.fit(Xtrain, Ytrain)
ada.score(Xtest,Ytest)
由于Adaboost方法之后又延伸出了更好用的方法,所有参数不再关注。
三、随机森林
1、介绍
随机森林(random forest)算法是一种重要的基于bagging的集成学习方法。其将若干个弱分类器(决策树)的分类结果进行投票选择,从而组成一个强分类器,这就是随机森林bagging的思想。
2、随机森林生成
RF = 决策树 + bagging + 随机属性选择
bagging:有放回的行数据选择
随机属性选择:无放回的列属性选择
- 从样本集(m)中通过自助采样(随机且有放回地)方式产生m个样本
- 假设样本特征数目为n(属性),对m个样本随机选择n个特征中的n‘个特征,用建立决策树的方式获得最佳分割点
- 重复t次
- 多数投票机制来进行预测
-自助采样法(bootstap sampling),即对于m个样板的原始训练集,每次先随机采集一个样本放入采样集,接着把该样本放回,也就是说下次采样时该样本仍有可能被采集到
-
根据袋外误差选择最优的n’:对于每棵树都有一部分样本而没有被抽取到,这样的样本就被称为袋外样本,随机森林对袋外样本的预测错误率被称为袋外误差(out-of-bag error,OOB Error)
-
计算流程:
- 对于每个样本,计算它作为OOB样本的树对它的分类情况;
- 以投票的方式确定该样本的分类结果;
- 将误差分类样本个数占总数的比率作为随机森林的袋外误差。
-
随机森林计算特征X的重要性:
- 对于随机森林中的每一颗决策树,使用相应的OOB(袋外数据)来计算它的袋外数据误差,记为errOOB1;
- 随机地对袋外数据OOB所有样本的特征X加入噪声干扰,再次计算它的袋外数据误差,记为errOOB2。假设随机森林中有N课树,那么对于特征x的重要性 = ∑(errOOB2-errOOB1)/N;
- 用这个表达式作为相应特征的重要性度量的值是因为:若给某个特征随机加入噪声后,袋外准确率大幅降低,则说明这个特征对于样本的分类结果影响很大,也就是说它的重要程度比较高。
-
随机森林分类效果(错误率)与2个因素有关:
- 森林中任意两棵树的相关性:相关性越大、错误率越大;
- 树的生长参数:树越深,单颗树的分类能力越强,树的相关性也变强;
- 选择的特征个数n‘:n’越大,单颗树的分类能力越强,树的相关性也变强。
3、特点
- 2个随机性:样本的随机性、特征的随机性
- 2个随机性的引入对随机森林的分类性能至关重要。由于他们的引入,使得随机森林不容易陷入过拟合,并且具有很好的抗噪声能力(比如:对缺省值不敏感)
4、优缺点
优点:
- 具有极好的准确率,不容易过拟合(样本随机、特征随机);
- 能够有效地运行在大数据集上(特征随机,并行计算);
- 能够处理具有高维特征的输入样本,而且不需要降维,能够评估各个特征在分类问题上的重要性;
- 在生成过程中,靠袋外数据作为验证集,可有效地提高模型准确率;
- 常用算法中,唯一一种可以一定程度上允许有缺失值的情形的模型(有缺失值的样本,在自助采样时自动跳过)。
缺点:
- 随机森林在解决回归问题时并没有像它在分类中表现的那么好,这是因为它并不能给出一个连续的输出。当进行回归时,随机森林不能做出超越训练集数据范围的预测,这可能导致在某些还有特定噪声的数据进行建模时出现的过拟合;
- 对于许多统计建模者来说,随机森林给人的感觉像是一个黑盒子——几乎无法控制模型内部的运行,只能在不同的参数和随机种子之间进行尝试。
5、代码示例
from sklearn.ensemble import RandomForestClassifier
rf= RandomForestClassifier(oob_score= True, n_jobs= -1)
rf.fit(Xtrain, Ytrain)
from sklearn.metrics import classification_report
print(classification_report(Ytest, rf.predict(Xtest)))
6、参数介绍
n_estimators: Int = 100, # 树的数量,列属性较多时,相应调大,列数影响较大
*,
criterion: Literal['gini', 'entropy', 'log_loss'] = "gini", # 分裂质量,一般情况下用基尼系数,信息熵(如果预测概率或者和概率有关,则用该参数),log_loss,不是随机森林或决策树中常用的分割准则。'log_loss' 通常用于评估分类模型的性能,特别是那些可以输出概率预测(如逻辑回归、神经网络等)的模型。它衡量的是预测概率分布与真实类别分布之间的差异
max_depth: Int | None = None, # 树的最大深度,如果过拟合,则调小
min_samples_split: float | int = 2,# min_samples_split最小分裂样本数、min_samples_leaf(叶子节点最小样本数)、min_weight_fraction_leaf最小基本参数
min_samples_leaf: float | int = 1,
min_weight_fraction_leaf: Float = 0,# 不太常见但非常重要的参数,特别是在处理不平衡的数据集或带有权重的数据集时,参数用于控制树中叶节点样本权重的最小比例
max_features: float | int | Literal['sqrt', 'log2'] = "sqrt",#设置特征采样方法,int特征采样个数,float特征采样比例,sqrt比如100列,设置为sqrt,则每次采样为根号下100个特征,即10个,如果为log2,则是2为底100的对数
max_leaf_nodes: Int | None = None, # 最大叶子节点个数
min_impurity_decrease: Float = 0,# 用于控制节点分裂时的最小不纯度减少量,只有当分裂后导致的不纯度减少量大于或等于该值时,节点才会被分裂。较大的值会导致更少的分裂,生成较简单的树;而较小的值则可能导致更多的分裂,生成更复杂的树
bootstrap: bool = True,#是否使用自助法对原始数据集进行有放回的随机抽样来构建每棵决策树
oob_score: bool = False,#用于评估模型好坏的参数,表示是否采用袋外样本来评估模型的泛化能力,为true表示在随机森林的构建过程中,会计算并使用袋外得分来评估模型的性能,为false表示不使用袋外得分来评估模型性能。这是默认值
n_jobs: Int | None = None,# 并行计算的任务数,-1:这意味着随机森林将使用所有可用的处理器核心来并行处理数据
random_state: Int | None = None,#参数用于指定随机数生成器的种子(seed)。由于样本和特征的随机性选择,每次运行随机森林时,得到的模型可能会稍有不同(尽管在大数据集上,这种差异可能不会很大)。通过设置 random_state 参数,可以确保每次运行代码时,都使用相同的随机数生成器种子,从而使结果可复现
verbose: Int = 0,#控制模型训练过程中的日志信息输出,用于调试和监视模型训练的进度;0:不输出任何信息;1:输出进度条;verbose>1:输出每棵树构建的详细信息
warm_start: bool = False,#True:模型会在前一次训练的基础上继续训练,而不是重新开始;False(默认值),模型会重新开始训练,忽略之前的训练结果;
class_weight: Mapping | Sequence[Mapping] | Literal['balanced', 'balanced_subsample'] | None = None,#用于处理数据集中类别不平衡问题."balanced":表示样本类别的权重将根据类别在训练数据中的频率自动进行调整;none:假设所有类别的权重都是相等
ccp_alpha: float = 0,#后剪枝参数,目前模型针对该参数存在bug,先不关注该参数
max_samples: float | int | None = None #样本的采样比例
四、GBDT
1、介绍
GBDT(Gradient Boosting Decision Tree,梯度提升决策树)又称MART(Multiple Additive Regression Tree),是一种迭代的决策树算法,该算法由多棵决策树组成,所有树的结论累加起来做最终答案。它在被提出之初就和SVM一起被认为是泛化能力较强的算法。
- GBDT中的树是回归树(不是分类树),GBDT用来做回归预测,调整后也可以用于分类;
- GBDT主要有三个概念组成:Regression(回归) Decision(决策) Tree、Gradient Boosting(梯度提升)即GB,Shrinkage(缩减、收敛)
注意:决策树无法做出超出训练集取值范围的预测,所以不能用来做预测。
2、回归树算法流程
回归树示例如下:
回归算法流程,以最小二乘回归树生成算法为例:
输入:训练数据集D;
输出:回归树f(x)
在训练数据集所在的输入空间中,递归地将每个区域划分为2个子区域并决定每个子区域上的输出值,构建二叉决策树:
- 1、选择最优切分变量j与切分点s,求解
遍历遍历j,对固定的切分变量j扫描切分点s,选择使图中计算式达到最小值的对(j,s) - 2、用选定的对(j,s)划分区域并决定相应的输出值:
- 3、继续对2个子区域调用步骤1、2,直至满足停止条件
- 4、将输入空间划分为M个区域R1,R2,…,Rm,生成决策树:
3、GBDT加法模型:前向分布算法
- 介绍:前向分布算法(forward stage wise algorithm):从前往后,每一步只学习一个基函数及其系数(结构),逐步逼近优化目标函数,那么就可以简化复杂度。这一学习过程称之为boosting。具体地,我们从一个常量预测开始,每次学习一个新的函数,过程如下:
- 示例
以年龄预测为例,简单起见训练集只有4个人A,B,C,D,他们的年龄分别是14,16,24,26.其中A,B分别是高一、三学生;C,D分别是应届生和工作2年员工。如果用同一棵传统的回归决策树来训练,会得到如下结果:
使用GBDT来做这件事,由于数据太少,我们限定叶子节点最多有2个,即每棵树都只有一个分支,并且只限定学2棵树,会得到如下结果:
在第一棵树分支和上图一样,由于A,B年龄相近,C,D年龄相近,它们平均被分为2拨,每拨用平均年龄作为预测值。此时计算残差(残差的意为:A的预测值 + A的残差 = A的实际值),所以A的残差就是14-15=-1(注意A的预测值是指前面所有树累加的和,这里前面只有一棵树所以直接是15,如果还有树,则需要都累加起来作为A的预测值)。进而得到A,B,C,D的残差分别为-1,1,-1,1.然后用残差代替A,B,C,D的原值,去第二棵树学习,如果我们的预测值和他们的残差相等,则只需要把第二棵树的结论累加到第一棵树上就能得到真实年龄了。这里的数据显然是我们可以做的,第二棵树只有2个值1和-1,直接分成2个节点,此时所有人的残差都是0,即每个人都得到了真实的预测值。
该例子很直观的看到,预测值等于所有树值的累加,如A的预测值 ,输入14, 在树1节点中输出值为15,14-15=-1,所以树2的输入值为-1,在树2的输出值结果为-1 ,那么A的值即为树1、树2输出结果之和,为 15 + (-1) = 14
GBDT核心思路:残差树,残差中存在未被充分挖掘的信息,下一棵树会充分挖掘未被挖掘的信息。
4、加法模型算法过程
回归问题的提升树算法叙述如下:
- 初始化f0(x) = 0
- 对m = 1,2,…,m
- 计算残差
r mi = yi - f m-1(xi) , i = 1,2,…,N - 拟合残差r mi学习一个回归树,得到T(x∅m)
- 更新fm(x) = f m-1(x) + T
- 计算残差
- 得到回归问题提升树 f M(x) = ∑T(x∅m)
5、Shrinkage
- boosting的最大好处在于每一步的残差计算其实变相地增大了分错instance权重,而已经分对的instance则都趋向于0。这样后面的树就能越来越专注前面被分错的instance。
- Shrinkage(缩减)的思想认为,每次走一小步逐渐逼近结果的效果要比每次迈一大步很快逼近结果的方式更容易避免过拟合。即它不完全信任每一课残差树,它认为每棵树只学到了一部分,累加的时候也只累加一小部分,通过多学几棵树弥补不足。
- 即Shrinkage仍然以残差作为学习目标,但对应残差学习出来的效果,只累加一小部分(step残差)逐步逼近目标,step一般比较小,如0.01~0.001,导致各个数的残差是渐变的而不是陡变的。直觉上也很好理解,不像直接用残差一步修复误差,而是只修复一点点,其实就是把大步切成很多小步。本质上Shrinkage为每棵树设置了一个weight,累加时要乘以这个weight。Shrinkage与learning_rate(学习率)是等价的,用于控制步长,只是做算法模型的人不同而已,现在更常用的是learning_rate。
6、优点
对比Adaboost,虽然同属boosting一族,但梯度提升方法有更多优点:
- 与Adaboost不同,梯度提升方法在迭代的每一步构建一个能够沿着梯度最陡的方向降低损失(steepest-descent)的学习器来弥补已有模型的不足;
- 经典的Adaboost算法只能出来采用指数损失函数的二分类学习任务,而梯度提升方法通过设置不同的可微损失函数可以处理各类学习任务(多分类、回归、排序等),应用范围更大;
- Adaboost算法对异常点比较敏感,而梯度提升算法通过引入bagging思想、加入正则项等方法能够有效地抵御训练数据中的噪音,具有很好的健壮性。
7、基学习器选择
为什么梯度提升方法倾向于选择决策树(通常是cart树)作为基学习器呢,与决策树算法自身的优点关系很大:
- 决策树可以认为是if-then规则的集合,易于理解,可解释性强,预测速度快;
- 决策树算法相比于其他的算法需要更少的特征工程,比如可以不用做特征标准化,可以很好的处理字段缺失的数据,也可以不用关心特征间是否相互依赖等;
- 决策树能够自动组合多个特征,它可以毫无压力地处理特征间的交互关系并且是非参数化的,因此你不必担心异常值或者数据是否线性可分。
五、XGBoost
1、介绍
残差树(GBDT)解决了“好而不同”中“不同”的效果最好的方法。而XGBoost解决了“好而不同中”的“好”并且使“好”、“不同”两相配合。
XGBoost全程为eXtreme Gradient Boosting,译为极限梯度提升算法。它由陈天奇设计,致力于让提升树突破自身的计算极限,以实现快速运算、性能优秀的工程目标。和传统算法比,XGBoost做了很多改进,能够比其他梯度提升的集成算法更加快速,并且被认为是在分类和回归性能上拥有超高性能的先进评估器。
2、算法思想
XGBoost算法思想就是不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一棵树,其实是学习一个新函数,去拟合上次预测的残差。当我们训练完成得到k棵树,我们要预测一个样本的分数,其实是根据这个样板的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点对应一个分数,最后只需要将每棵树对应的分数加起来就是该样本的预测值:
3、模型表示
4、训练过程
1、损失函数的理解
2、正则项的理解
3、叶子分数的求解
obj表示这棵树的结构有多好,值越小,代表结构越好,是衡量第t棵树结构好坏的标准。当我们制定一个树的结构时,obj代表我们在目标函数上面最多减少多少。我们可以把它叫做结构分数(structure score)。这个就是类似Gini系数一样更加一般的树结构进行打分的函数。
4、找出树的最优结构
我们无法枚举所有可能的树结构然后选取最优的,所以选择一种贪婪算法来代替:首先从单个叶节点开始,迭代分裂来给树添加节点。节点切分后的增益:
遍历所有的特征,所有阈值,选择Gain最大的进行切分,递归的构建决策树。
注意:从以上公式可以看出,XGBoost中2个比较重要内容:泰勒展开函数可以直接容纳各种各样的原始的损失函数,比较有泛用性,解决问题的场景更多;泰勒展开引入了二阶导,结果更好,效果更准,为了防止过拟合,引入了正则项来控制过拟合。
5、代码实现
# 推荐使用更加友好的sklearn接口,缺陷是不接收缺失值,xgboost的原生库自动处理缺失值的,尝试把缺失值单独分为一个节点进行处理
from xgboost.sklearn import XGBClassifier
XGB= XGBClassifier()
XGB.fit(Xtrain, Ytrain)
from sklearn.metrics import classification_report
print(classification_report(Ytest, XGB.predict(Xtest)))
6、参数解释
n_estimators: Int = 100, # 树的最大数量
max_depth:# 树的最大深度,一般为个位数
max_leaves:#最大叶子节点
max_bin:# 最大分箱数量,直方图算法,参见LightGBM算法
learning_rate:#学习率,默认值为0.1 shrinkage,出现cannot be converged报错时,调小该值一般可解决
verbosity#日志输出
booster:#默认值为gbtree,取值范围:[gbtree,gblinear or dart],基学习器gbtree即为GBDT;gblinear线性回归,该参数只有XGBoost有,处理序列数据时使用,比如时间序列、预测;dart(dropoat):一般不用,待剪枝的加权平均数,用于剪枝,控制过拟合。一般由正则项控制,不用该参数
subsample#默认值1,样本的采样比例
colsample_bytree#默认值1,特征的采样比例,模型不够稳健时,调小该参数和subsample参数
reg_alpha:#L1正则,默认值0
reg_lambda:#L2正则,默认值1,L1为0,L2不为0是因为L2收敛性相对更加平缓,如果解决过拟合问题,主要调整L2;如果不只是过拟合,还有特征选择问题(如剔除特征、特征存在相关性),则同步调整L1
scale_pos_weight:#默认值None,控制样本类别不平衡,等价于sklearn中的class_weight参数
7、模型保存
import joblib # 方便模型部署
joblib.dump(XGB, "model.joblib")
会将库、使用的参数一起保存
六、LightGBM
1、介绍
常用的机器学习算法,如神经网络等算法,都可以以小批量的方式训练,训练数据的大小不会受到内存限制。而GBDT在每一次迭代的时候,都需要遍历整个训练数据多次,如果把整个训练数据装进内存则会限制训练数据的大小;如果不装进内存,反复读写训练数据又会耗时较大。LightGBM主要是为了解决GBDT在海量数据遇到的问题,更好的用于工业实践。
LightGBM由微软推出,性能相比XGBoost好很多,两者准确率差不多。原理上它和GBDT、XGBoost类似,都采用损失函数的负梯度作为当前决策树的残差近似值,去拟合新的决策树。
在很多方面比XGBoost表现更为优秀:
- 更快的训练效率
- 低内存使用
- 更高的准确率(实际差不多)
- 支持并行化学习
- 可处理大规模数据
- 支持直接使用category(类别)特征,不需编码
主要特点: - 基于Histogarm(直方图)的决策树算法
- 带深度限制的Leaf-wise的叶子生长策略
- 直方图做差加速
- 直接支持类别特征(Categorical Feature)
- Cache命中率优化
- 基于直方图的稀疏特征优化
- 多线程优化
2、直方图算法
直方图算法(Histogarm,algorithm),直观理解就是自动分箱,算法思想也很简单,首先将连续的浮点数据转换为bin数据,具体过程是首先确定对于每一个特征需要多少的桶bin,然后均分,将属于该桶的样本数据更新为bin的值,最后用直方图表示。
- 使用bin替代原始数据相当于增加了正则化;
- 使用bin意味着很多数据的细节特征被放弃了,相似的数据可能被划分到相同的桶中,这样的数据直接的差异就消失了;
- bin数量选择决定了正则化的程度,bin越少惩罚越严重,欠拟合风险越高;
- 构建直方图时不需要对数据进行排序(比XGBoost快),因为预先设定了bin的范围;
- 直方图除了保存划分阈值和当前bin内样本数以外还保存了当前bin内所有样本的一阶梯度和(一阶梯度和的平方的均值等价于均方损失);
- 阈值的选取是按照直方图从大到小遍历,使用了上面的一阶梯度和,目的是得到划分之后loss最大的特征及阈值。
数据量较大时使用该算法分箱处理,数据量小时不建议。
LightGBM另一个优化是Histogarm做差加速。一个叶子的直方图可以由它的父节点的直方图与它兄弟的直方图做差得到,在速度上可以提升一倍。通常构造直方图时,需要遍历该叶子上的所有数据,但直方图做差仅需遍历直方图的k个桶。在实际构建树的过程中,LightGBM还可以先计算直方图小的叶子节点,然后利用直方图做差来获得直方图大的叶子节点,这样就可以用非常微小的代价得到它兄弟叶子的直方图。XGBoost在进行预排序时只考虑非零值进行加速,而LightGBM也采用类似策略:只用非零特征构建直方图。
3、Leaf-wise算法
在Histogarm算法之上,LightGBM进行进一步的优化。首先它抛弃了大多数GBDT工具使用的按层生长(leaf-wise)的决策树生长策略,而使用了带有深度限制的按叶子生长(leaf-wise)算法。好处是代码简洁、速度更快。
XGBoost采用的是按层生长(level-wise learning)策略,能够同时分裂同一层的叶子,从而进行多线程优化,不容易过拟合;但不加区分的对待同一层的叶子,带来了很多没必要的开销。因为实际上很大叶子的分裂增益较低,没必要进行搜索和分裂。
LightGBM采用Leaf-wise生长策略,每次从当前所有叶子中找到分裂增益最大(一般也是数据量最大)的一个叶子,然后分裂,如此循环。因此同level-wise相比,在分裂次数相同的情况下,Leaf-wise可以降低更多的误差,得到更好的精度。Leaf-wise的缺点是可能会长出比较深的决策树,产生过拟合。因此LightGBM在Leaf-wise之上增加了一个最大深度的限制,在保证高效率的同时防止过拟合。
Leaf-wise只分析原始数据中的一个子区域,每棵树建立时只分析原始数据的一个子区域充分挖掘,未分析的部分,就会落在这棵树的残差中,在第二棵树中继续挖掘,第二未分析的部分,就会落在这棵树的残差中,在第三棵树中继续挖掘,持续挖掘。
LightGBM会首先选中一个区域,不断挖掘信息,分裂下一节点,比如第二层有2个节点,选择其中一个节点继续分裂,第三层及之后亦是如此;而XGBoost则是其中一个节点触发预剪枝条件后该节点就不会再分裂了,但是会按层分裂,如果条件同时满足,则2个节点都会分裂。不同点是LightGBM只会选择其中一个节点持续分裂,XGBoost如果两个节点都满足条件则会持续分裂。
4、单边梯度采样算法
LightGBM已放弃使用单边梯度采样算法,暂不关注。
5、代码实现
- 写法1:sklearn形式
# 写法1:sklearn形式
from lightgbm import LGBMClassifier
lgb= LGBMClassifier()# device_type= "gpu" 控制使用的是GPU、CPU,默认使用CPU,GPU侧重浮点运算,CPU可以做浮点运算、逻辑运算,神经网络只有浮点运算,所以神经网络更适合使用GPU
lgb.fit(Xtrain, Ytrain)
from sklearn.metrics import classification_report
print(classification_report(Ytest, lgb.predict(Xtest)))
- 写法2:lgb形式
import lightgbm
train= lightgbm.Dataset(Xtrain, label= Ytrain) # 数据从dataframe转化为二级制array
test= lightgbm.Dataset(Xtest, label= Ytest)
lgb = lightgbm.train(params={}, train_set= train, valid_sets= test)
from sklearn.metrics import classification_report
print(classification_report(Ytest, lgb.predict(Xtest)> 0.5))
区别:
- 使用sklearn方式数据可以用dataframe格式,lgb形式则不可以用dataframe格式,只能用二级制的array(numpy);
- 传参时,如果使用默认值则输入params={},否则需要在params中增加参数字典项;
- sklearn形式数据类别都需要编码,lgb形式数据不需要编码,会自动帮助编码。
6、参数解释
boosting, default=gbdt, 可选项:gbdt, rf, dart,rf(随机森林)使用时会引入随机森林优点,但是会失去gbdt优点即残差树,好处是不容易受到极端值影响,问题时可能导致异常信息没挖掘到 参数别名boost、boosting_type,调参时一定要用参数原名
num_leaves, default=31 叶子节点最大数量,等价于max_depth=30,官方推荐控制该num_leaves,一般不要太小,最少20+
learning_rate,default=0.1,步长,学习率
num_iterations,default=100,控制树的最大数量
subsample_for_bin,default=200000 # 用来构建直方图的数据的数量
subsample#默认值1.0,样本的采样比例
colsample_bytree#默认值1,特征的采样比例,模型不够稳健时,调小该参数和subsample参数
reg_lambda:default=0.0 L2正则系数,如果没有特别严重过拟合,一般不要改,因为本身已经分箱了