三国演义!CatBoost vs. LightGBM vs. XGBoost 到底谁更强?

点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”


作者:Alvira Swalin

编译:ronghuaiyang

前戏

CatBoost,LightGBM,XGBoost就好像3个美女在你面前,到底选哪一个呢?当然了,标准答案是:我全都要!不过,即便如此,我们还是要仔细的比较一下,看看到底有什么不一样。

谁会赢呢?代价是什么?我们拭目以待!

640?wx_fmt=jpeg

最近,我参加了Kaggle竞赛(斯坦福大学的WIDS Datathon),使用各种增强算法,我进入了前十名。从那时起,我对每种模型的工作原理非常好奇,包括参数优化、优缺点,因此决定写这个博客。尽管最近神经网络重新出现并流行起来,但我仍然专注于增强算法,因为它们在有限的训练数据、很少的训练时间和很少的参数调整专业知识的情况下仍然更有用。

640?wx_fmt=png

由于XGBoost(通常被称为GBM杀手)在机器学习领域已经存在很长一段时间了,并且有许多专门的文章介绍它,因此本文将更多地关注CatBoost和LGBM。以下是我们将涵盖的主题:

  • 结构差异

  • 各算法对类别变量的处理

  • 理解参数

  • 数据集的实现

  • 各算法的性能

LightGBM和XGBoost的结构差异

LightGBM采用一种新的基于梯度的单边采样(GOSS)技术过滤出数据实例来寻找分割值,而XGBoost使用预先排序算法和基于直方图的算法来计算最佳分割。这里的实例指的是观察样本。

首先让我们了解预排序分割是如何工作的

  • 对于每个节点,枚举所有特征

  • 对于每个特征,按特征值对实例进行排序

  • 使用线性扫描基于该特征的信息增益来决定最佳分割

  • 对所有的特征采用最好的分割方案

简单地说,基于直方图的算法将一个特征的所有数据点分成离散的部分,并使用这些部分来寻找直方图的分割值。虽然它在训练速度上比预先排序的算法要有效,在训练速度上仍然落后于GOSS算法。

那么是什么使得这种GOSS方法有效呢?在AdaBoost中,样本的权重是衡量样本重要性的一个很好的指标。然而,在梯度增强决策树(GBDT)中,由于没有固有的样本权重,因此AdaBoost采样方法不能直接应用。因此,下面是基于梯度的采样。

梯度表示损失函数正切的斜率,因此,从逻辑上讲,如果数据点的梯度在某种意义上较大,这些点的误差较大,对于寻找最优分割点非常重要

GOSS保留所有梯度较大的实例,并对梯度较小的实例进行随机采样。假设我有500K行数据,其中10k行有更高的梯度。因此,我的算法将选择(10k行更高的梯度+ x%的剩余490k行随机选择)。假设x为10%,选择的总行数为500K中的59k,在此基础上找到拆分值。

这里的基本假设是带有小梯度的训练实例的样本具有较小的训练误差,并且这些样本已经训练的很好了。

为了保持相同的数据分布,在计算信息增益时,GOSS为梯度较小的数据实例乘上一个常数。因此,GOSS在减少数据实例的数量和保持学习决策树的准确性之间取得了良好的平衡。

640?wx_fmt=png

在LGBM中高梯度/误差的叶子用来进一步生长


这些模型如何处理类别型变量?

CatBoost

CatBoost具有提供类别列索引的灵活性,因此可以使用onehotmax_size将其编码为独热编码(对于所有不同值数量小于或等于给定参数值的特征,使用独热编码)。

如果在cat_features参数中没有传递任何内容,CatBoost将把所有列视为数值变量。

注意:如果某一列中有字符串值,而catfeatures中没有支持的话,CatBoost将抛出一个错误。而且,具有默认int类型的列在默认情况下将被视为数值,必须在catfeatures中指定它,以便算法将其视为类别变量。

640?wx_fmt=png

对于剩余的分类列,如果它们的类别数量大于onehotmax_size,CatBoost使用了一种有效的编码方法,这种编码方法类似于平均编码,但是可以减少过拟合。这个过程是这样的

  1. 以随机顺序排列输入观测值的集合,生成多个随机排列

  2. 将标签值从浮点数或类别转换为整数

  3. 所有类别特征值按下式转换为数值:

640?wx_fmt=png

其中,对于当前具有类别特征值的对象,CountInClass是标签值等于“1”的次数。

Prior是分子的初值,它是由启动参数决定的。TotalCount是具有与当前对象相匹配的类别特征值的对象总数(直到当前对象)。

数学上,可以用以下公式表示:

640?wx_fmt=png

LightGBM

与CatBoost类似,LightGBM也可以通过输入特征名来处理类别特征。它不转换为独热编码,但是比独单热编码快得多。LGBM使用一种特殊的算法来查找类别特征的分割值。

640?wx_fmt=png

注意:在为LGBM构造数据集之前,应该将分类特征转换为int类型,即使你通过categorical_feature参数传递,它也不接受字符串值

XGBoost

与CatBoost或LGBM不同,XGBoost不能单独处理类别特征,它只接受类似于Random Forest的数值。因此,在向XGBoost提供分类数据之前,必须执行各种编码,如标签编码、平均编码或独热编码。

超参数的相似性

所有这些模型都有许多需要优化的参数,但是我们只讨论重要的参数。下面是这些参数的列表,根据它们的功能以及不同模型中的对应参数列出来的。

640?wx_fmt=png

在数据集上的实现

我使用的是2015年航班延误的Kaggle数据,因为它既有类别特征,也有数值特征,大约有500万行,这个数据集将有助于判断使用不同类型的提升模型在准确性和速度上的表现,我将使用这个数据的10%子集~ 500k行。

以下是用于建模的特征:

  • MONTH, DAY, DAY_OF_WEEK:数据类型int

  • AIRLINE和FLIGHT_NUMBER:数据类型int

  • ORIGINAIRPORTDESTINATIONAIRPORT:数据类型字符串

  • DEPARTURE_TIME:数据类型float

  • ARRIVAL_DELAY:这是目标变量,转换为布尔变量,指示延时是否超过10分钟

  • DISTANCE和AIR_TIME:数据类型float

import pandas as pd, numpy as np, time	
from sklearn.model_selection import train_test_split	
data = pd.read_csv("flights.csv")	
data = data.sample(frac = 0.1, random_state=10)	
data = data[["MONTH","DAY","DAY_OF_WEEK","AIRLINE","FLIGHT_NUMBER","DESTINATION_AIRPORT",	
                 "ORIGIN_AIRPORT","AIR_TIME", "DEPARTURE_TIME","DISTANCE","ARRIVAL_DELAY"]]	
data.dropna(inplace=True)	
data["ARRIVAL_DELAY"] = (data["ARRIVAL_DELAY"]>10)*1	
cols = ["AIRLINE","FLIGHT_NUMBER","DESTINATION_AIRPORT","ORIGIN_AIRPORT"]	
for item in cols:	
    data[item] = data[item].astype("category").cat.codes +1	
train, test, y_train, y_test = train_test_split(data.drop(["ARRIVAL_DELAY"], axis=1), data["ARRIVAL_DELAY"],	
                                                random_state=10, test_size=0.25)
XGBoost
import xgboost as xgb	
from sklearn import metrics	
def auc(m, train, test): 	
    return (metrics.roc_auc_score(y_train,m.predict_proba(train)[:,1]),	
                            metrics.roc_auc_score(y_test,m.predict_proba(test)[:,1]))	
# Parameter Tuning	
model = xgb.XGBClassifier()	
param_dist = {"max_depth": [10,30,50],	
              "min_child_weight" : [1,3,6],	
              "n_estimators": [200],	
              "learning_rate": [0.05, 0.1,0.16],}	
grid_search = GridSearchCV(model, param_grid=param_dist, cv = 3, 	
                                   verbose=10, n_jobs=-1)	
grid_search.fit(train, y_train)	
grid_search.best_estimator_	
model = xgb.XGBClassifier(max_depth=50, min_child_weight=1,  n_estimators=200,\	
                          n_jobs=-1 , verbose=1,learning_rate=0.16)	
model.fit(train,y_train)	
auc(model, train, test)
LightGBM
import lightgbm as lgb	
from sklearn import metrics	
def auc2(m, train, test): 	
    return (metrics.roc_auc_score(y_train,m.predict(train)),	
                            metrics.roc_auc_score(y_test,m.predict(test)))	
lg = lgb.LGBMClassifier(silent=False)	
param_dist = {"max_depth": [25,50, 75],	
              "learning_rate" : [0.01,0.05,0.1],	
              "num_leaves": [300,900,1200],	
              "n_estimators": [200]	
             }	
grid_search = GridSearchCV(lg, n_jobs=-1, param_grid=param_dist, cv = 3, scoring="roc_auc", verbose=5)	
grid_search.fit(train,y_train)	
grid_search.best_estimator_	
d_train = lgb.Dataset(train, label=y_train)	
params = {"max_depth": 50, "learning_rate" : 0.1, "num_leaves": 900,  "n_estimators": 300}	
# Without Categorical Features	
model2 = lgb.train(params, d_train)	
auc2(model2, train, test)	
#With Catgeorical Features	
cate_features_name = ["MONTH","DAY","DAY_OF_WEEK","AIRLINE","DESTINATION_AIRPORT",	
                 "ORIGIN_AIRPORT"]	
model2 = lgb.train(params, d_train, categorical_feature = cate_features_name)	
auc2(model2, train, test)
CatBoost

当我们对CatBoost进行调优的时候,很难为类别特征传递索引。因此,我在没有传递类别特征的情况下对参数进行了优化,并对两个模型进行了评估:一个有类别特征,另一个没有类别特征。我单独调试了onehotmax_size,因为它不会影响其他参数。

import catboost as cb	
cat_features_index = [0,1,2,3,4,5,6]	
def auc(m, train, test): 	
    return (metrics.roc_auc_score(y_train,m.predict_proba(train)[:,1]),	
                            metrics.roc_auc_score(y_test,m.predict_proba(test)[:,1]))	
params = {'depth': [4, 7, 10],	
          'learning_rate' : [0.03, 0.1, 0.15],	
         'l2_leaf_reg': [1,4,9],	
         'iterations': [300]}	
cb = cb.CatBoostClassifier()	
cb_model = GridSearchCV(cb, params, scoring="roc_auc", cv = 3)	
cb_model.fit(train, y_train)	
With Categorical features	
clf = cb.CatBoostClassifier(eval_metric="AUC", depth=10, iterations= 500, l2_leaf_reg= 9, learning_rate= 0.15)	
clf.fit(train,y_train)	
auc(clf, train, test)	
With Categorical features	
clf = cb.CatBoostClassifier(eval_metric="AUC",one_hot_max_size=31, \	
                            depth=10, iterations= 500, l2_leaf_reg= 9, learning_rate= 0.15)	
clf.fit(train,y_train, cat_features= cat_features_index)	
auc(clf, train, test)

结果

640?wx_fmt=png

结尾

在评价模型时,应从速度和精度两个方面考察模型的性能。

要记住,CatBoost在测试集上以最大精度(0.816)、最小过拟合(训练和测试精度接近)和最小预测时间和调优时间胜出。但这只是因为我们考虑了类别变量并优化了onehotmax_size,如果我们不充分利用CatBoost的这些特性,结果会发现它是性能最差的,精度只有0.752。因此,我们了解到CatBoost只有在数据中包含类别变量并进行适当的优化时才能表现良好。

我们的下一个用的是XGBoost,它通常工作得很好。它的准确性非常接近于CatBoost,即使我们忽略了一个事实,即我们在数据中有类别变量,我们已经将它们转换为数值供其使用。然而,XGBoost的唯一问题是它太慢了。特别是调试它的参数实在让人郁闷(我花了6个小时运行GridSearchCV——失策了!)更好的方法是单独调试参数,而不是使用GridSearchCV。查看这篇博客,了解如何巧妙地优化参数。

最后一个地方是LightGBM。这里需要注意的一件重要事情是,在使用cat_features时,它的速度和准确性都很差。我认为它表现不佳的原因是它对分类数据使用了某种经过修改的均值编码,导致过拟合(训练精度相当高——与测试精度相比,为0.999)。但是,如果我们像XGBoost一样正常使用它,它可以实现与XGBoost相似(如果不是更高的话)的精度,而且速度要比XGBoost快得多。(LGBM - 0.785,XGBoost - 0.789)

最后,我必须说这些观察结果对于这个特定的数据集是正确的,对于其他数据集可能仍然有效,也可能无效。然而,有一件事通常是正确的,就是XGBoost比其他两种算法要慢。

英文原文链接:https://towardsdatascience.com/catboost-vs-light-gbm-vs-xgboost-5f93620723db



640?wx_fmt=png

往期精彩回顾


1、最全的AI速查表|神经网络,机器学习,深度学习,大数据

2、资源|10个机器学习和深度学习的必读免费课程

3、论文看吐了没有?做研究的同学瞧一瞧看一看啦,教你读论文:为什么读以及如何读

4、人人都能看得懂的深度学习介绍!全篇没有一个数学符号!

5、想找个数据科学家的工作吗?别再随大流了!


本文可以任意转载,转载时请注明作者及原文地址

640?wx_fmt=jpeg

请长按或扫描二维码关注本公众号

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值