文章选自Medium,机器之心编译,原文点此跳转。算法
尽管近年来神经网络复兴并大为流行,可是 boosting 算法在训练样本量有限、所需训练时间较短、缺少调参知识等场景依然有其不可或缺的优点。本文从算法结构差别、每一个算法的分类变量时的处理、算法在数据集上的实现等多个方面对 3 种表明性的 boosting 算法 CatBoost、Light GBM 和 XGBoost 进行了对比;虽然本文结论依据于特定的数据集,但一般状况下,XGBoost 都比另外两个算法慢。
最近,我参加了 kaggle 竞赛 WIDS Datathon,并经过使用多种 boosting 算法,最终排名前十。从那时开始,我就对这些算法的内在工做原理很是好奇,包括调参及其优劣势,因此有了这篇文章。尽管最近几年神经网络复兴,并变得流行起来,但我仍是更加关注 boosting 算法,由于在训练样本量有限、所需训练时间较短、缺少调参知识的场景中,它们依然拥有绝对优点。bash
因为 XGBoost(一般被称为 GBM 杀手)已经在机器学习领域出现了好久,现在有很是多详细论述它的文章,因此本文将重点讨论 CatBoost 和 LGBM,在下文咱们将谈到:网络
在过滤数据样例寻找分割值时,LightGBM 使用的是全新的技术:基于梯度的单边采样(GOSS);而 XGBoost 则经过预分类算法和直方图算法来肯定最优分割。这里的样例(instance)表示观测值/样本。dom
首先让咱们理解预分类算法如何工做:机器学习
简单说,直方图算法在某个特征上将全部数据点划分到离散区域,并经过使用这些离散区域来肯定直方图的分割值。虽然在计算速度上,和须要在预分类特征值上遍历全部可能的分割点的预分类算法相比,直方图算法的效率更高,但和 GOSS 算法相比,其速度仍然更慢。 函数
在 Adaboost 中,样本权重是展现样本重要性的很好的指标。但在梯度提高决策树(GBDT)中,并无自然的样本权重,所以 Adaboost 所使用的采样方法在这里就不能直接使用了,这时咱们就须要基于梯度的采样方法。学习
梯度表征损失函数切线的倾斜程度,因此天然推理到,若是在某些意义上数据点的梯度很是大,那么这些样本对于求解最优分割点而言就很是重要,由于算其损失更高。测试
GOSS 保留全部的大梯度样例,并在小梯度样例上采起随机抽样。好比,假若有 50 万行数据,其中 1 万行数据的梯度较大,那么个人算法就会选择(这 1 万行梯度很大的数据+x% 从剩余 49 万行中随机抽取的结果)。若是 x 取 10%,那么最后选取的结果就是经过肯定分割值获得的,从 50 万行中抽取的 5.9 万行。ui
在这里有一个基本假设:若是训练集中的训练样例梯度很小,那么算法在这个训练集上的训练偏差就会很小,由于训练已经完成了。编码
为了使用相同的数据分布,在计算信息增益时,GOSS 在小梯度数据样例上引入一个常数因子。所以,GOSS 在减小数据样例数量与保持已学习决策树的准确度之间取得了很好的平衡。
CatBoost 可赋予分类变量指标,进而经过独热最大量获得独热编码形式的结果(独热最大量:在全部特征上,对小于等于某个给定参数值的不一样的数使用独热编码)。
若是在 CatBoost 语句中没有设置「跳过」,CatBoost 就会将全部列看成数值变量处理。
注意,若是某一列数据中包含字符串值,CatBoost 算法就会抛出错误。另外,带有默认值的 int 型变量也会默认被当成数值数据处理。在 CatBoost 中,必须对变量进行声明,才可让算法将其做为分类变量处理。
对于可取值的数量比独热最大量还要大的分类变量,CatBoost 使用了一个很是有效的编码方法,这种方法和均值编码相似,但能够下降过拟合状况。它的具体实现方法以下:
1. 将输入样本集随机排序,并生成多组随机排列的状况。
2. 将浮点型或属性值标记转化为整数。
3. 将全部的分类特征值结果都根据如下公式,转化为数值结果。
其中 CountInClass 表示在当前分类特征值中,有多少样本的标记值是「1」;Prior 是分子的初始值,根据初始参数肯定。TotalCount 是在全部样本中(包含当前样本),和当前样本具备相同的分类特征值的样本数量。
能够用下面的数学公式表示:
和 CatBoost 相似,LighGBM 也能够经过使用特征名称的输入来处理属性数据;它没有对数据进行独热编码,所以速度比独热编码快得多。LGBM 使用了一个特殊的算法来肯定属性特征的分割值。
注意,在创建适用于 LGBM 的数据集以前,须要将分类变量转化为整型变量;此算法不容许将字符串数据传给分类变量参数。
和 CatBoost 以及 LGBM 算法不一样,XGBoost 自己没法处理分类变量,而是像随机森林同样,只接受数值数据。所以在将分类数据传入 XGBoost 以前,必须经过各类编码方式:例如标记编码、均值编码或独热编码对数据进行处理。
全部的这些模型都须要调节大量参数,但咱们只谈论其中重要的。如下是将不一样算法中的重要参数按照功能进行整理的表格。
在这里,我使用了 2015 年航班延误的 Kaggle 数据集,其中同时包含分类变量和数值变量。这个数据集中一共有约 500 万条记录,所以很适合用来同时评估比较三种 boosting 算法的训练速度和准确度。我使用了 10% 的数据:50 万行记录。
如下是建模使用的特征:
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)
复制代码
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 调参时,很难对分类特征赋予指标。所以,我同时给出了不传递分类特征时的调参结果,并评估了两个模型:一个包含分类特征,另外一个不包含。我单独调整了独热最大量,由于它并不会影响其余参数。
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)
复制代码
为了评估模型,咱们应该同时考虑模型的速度和准确度表现。
请记住,CatBoost 在测试集上表现得最好,测试集的准确度最高(0.816)、过拟合程度最小(在训练集和测试集上的准确度很接近)以及最小的预测和调试时间。但这个表现仅仅在有分类特征,并且调节了独热最大量时才会出现。若是不利用 CatBoost 算法在这些特征上的优点,它的表现效果就会变成最差的:仅有 0.752 的准确度。所以咱们认为,只有在数据中包含分类变量,同时咱们适当地调节了这些变量时,CatBoost 才会表现很好。
第二个使用的是 XGBoost,它的表现也至关不错。即便不考虑数据集包含有转换成数值变量以后能使用的分类变量,它的准确率也和 CatBoost 很是接近了。可是,XGBoost 惟一的问题是:它太慢了。尤为是对它进行调参,很是使人崩溃(我用了 6 个小时来运行 GridSearchCV——太糟糕了)。更好的选择是分别调参,而不是使用 GridSearchCV。
最后一个模型是 LightGBM,这里须要注意的一点是,在使用 CatBoost 特征时,LightGBM 在训练速度和准确度上的表现都很是差。我认为这是由于它在分类数据中使用了一些修正的均值编码方法,进而致使了过拟合(训练集准确率很是高:0.999,尤为是和测试集准确率相比之下)。但若是咱们像使用 XGBoost 同样正常使用 LightGBM,它会比 XGBoost 更快地得到类似的准确度,若是不是更高的话(LGBM—0.785, XGBoost—0.789)。
最后必须指出,这些结论在这个特定的数据集下成立,在其余数据集中,它们可能正确,也可能并不正确。但在大多数状况下,XGBoost 都比另外两个算法慢。
因此,你更喜欢哪一个算法呢?