赞
踩
对比学习:CatBoost 和 Light GBM 和 XGBoost
Kaggle比赛使用各种增强算法,能进入前10名。尽管神经网络最近重新出现并流行起来,但传统提升算法,在训练数据有限,训练时间少,参数调优专业知识少的情况下,仍然更有用。
CatBoost 和 Light GBM 和 XGBoost发展时间表。
由于XGBoost(通常称为GBM Killer)已经在机器学习领域工作了更长的时间,并且有很多文章专门介绍它,因此本文将更多地关注CatBoost和LGBM。以下是我们将涵盖的主题:
LightGBM使用基于梯度的单侧采样(GOSS)技术来过滤掉用于查找拆分值的数据,而XGBoost使用预排序算法和基于直方图的算法来计算最佳拆分。
首先,让我们了解一下XGBoost预排序拆分的工作原理:
1、对于每个特征,枚举所有取值
2、对于每个特征,按值对数据进行排序
3、使用线性扫描确定以该特征的信息增益的最佳分割
4、在所有特征上采取最佳的分割方案
lightGBM基于直方图拆分的工作原理:
简单来说,基于直方图的算法将特征的所有取值进行分箱,数据被拆分为离散柱,并使用这些条柱来查找直方图的分割值。因此大大减小了最佳分割点的可能取值,也就是说不用把特征的每个取值都来计算信息增益,只用在离散后的分割点处计算信息增益即可,这也是分箱的精髓。
每个虽然在训练速度上,基于直方图拆分比预排序算法更有效,它枚举了预先排序的特征值上所有可能的分割点,但在速度方面仍然落后于GOSS。
那么,是什么让这种GOSS方法变得高效呢?
在AdaBoost中,样品重量是样品重要性的良好指标。然而,在梯度提升决策树(GBDT)中,没有原生样本权重,因此不能直接应用AdaBoost的采样方法。只能基于梯度的采样。
梯度表示损失函数正切的斜率,因此从逻辑上讲,如果数据点的梯度在某种意义上很大,则这些点对于找到最佳分割点非常重要,因为它们具有更高的误差。更能帮助损失函数到达最优点。
GOSS 保留所有梯度较大的实例数据,并对梯度较小的实例执行随机抽样。例如,假设我有 500K 行数据,其中 10k 行具有较高的梯度。所以我的算法会选择(10k行的较高梯度+ 剩余490k行的x%随机选择)。假设 x 为 10%,则选择的总行数为 500K 中的 59k,如果找到拆分值,则基于该值。注意,这里随机选取的10%的数据的权重会人为增大9倍,这样才能保证原始数据中,数据的分布一致,不然这样采样会改变原始的数据分布。这点需要理解。
这里采取的基本假设是,具有小梯度的训练实例的样本具有较小的训练误差,并且已经训练有素。
为了保持相同的数据分布,在计算信息增益时,GOSS为梯度较小的数据实例引入了常数乘数。因此,GOSS在减少数据实例数量和保持学习决策树的准确性之间实现了良好的平衡。
具有较高梯度/误差的叶子用于在LGBM中进一步生长,LGBM是leaf-wise的形式增长树。
CatBoost 最大的优势是:对于离散特征特别多的数据,效果比较好,可以灵活地提供分类列(cat_features)的索引,以便可以使用one_hot_max_size将其编码为单热编码(对具有不同值数小于或等于给定参数值的所有要素使用独热编码)。
如果您没有cat_features参数中传递任何内容,CatBoost 会将所有列视为数值变量。此时效果可能不是很好。
注: 如果cat_features中未提供具有字符串值的列,CatBoost 将引发错误。此外,默认情况下,具有默认int类型的列将被视为数字,必须在cat_features中指定它以使算法将其视为分类。
这里cat_features=[0,1,2]表示输入的特征数据中,第1,2,3列数据是离散特征数据,而没有指定的4,5,6列数据就是数值型特征。
对于类别的唯一数量大于 one_hot_max_size 的其余分类列(也就是离散特征取值大于one_hot_max_size ,用onehot编码则维度增加特别高,比如‘’民族‘’特征有56个,onehot编码后,就有56维,计算量一下暴增。),CatBoost 使用一种有效的编码方法,该方法类似于平均编码,但可减少过度拟合。这个过程是这样的:
这里理解一下为什么要对数据进行随机重排序?因为catboost采用的编码方式和数据的先后顺序有关,所以把数据打乱后,会得到多份编码不同的数据,这个有点“数据增强”的意思。比如图片我们旋转后会得到不同的数据,catboost随机打乱数据,就有点异曲同工。
比如从一个高斯分布中采样得到3个点a=0.1,b=-0.2,c=0.3。如果不区分数据顺序,那么这就是一次采样,而如果采用catboost,随机排列,会出现[a,b,c],[b.a,c],[b,c,a],[a,c,b]等等3!=6份数据,相当于做了6次不同的采样,所以比起不考虑顺序的1次采样,catboost进行了数据增强,相当于同一个分布,采样了6次,这样catboost的精度以及过拟合都会有所提高。
这个技巧比较巧妙。
这里用一个示例解释一下,我们设 Prior =0.5.我们对第6行数据进行编码。
TotalCount就是性别为男的数据,并且在待编码行(第六行)之前的数据,一共3条,分别是记录(2、4、5)。CountInClass 是之前特征为男性,并且标签为1的数据,就只有一行,就是第5行、所以TotalCount=3,CountInClass =1。
带入avg_target=(1+0.5)/(3+1)=0.375,因此,第六行的男这个值,会被替换为0.375。
在数学上,这可以用下面的等式来表示:
与CatBoost类似,LightGBM也可以通过输入特征名称来处理分类特征。它不会转换为独热编码,并且比独热编码快得多。LGBM使用特殊算法来查找分类特征的分割值
注: 在为 LGBM 构造数据集之前,应将分类要素转换为 int 类型。它不接受字符串值,即使您通过categorical_feature参数传递字符串值也是如此。
与CatBoost或LGBM不同,XGBoost本身无法处理分类特征,它只接受类似于随机森林的数值。因此,在向 XGBoost 提供分类数据之前,必须执行各种编码,如标签编码、平均编码或单热编码。
所有这些模型都有很多参数需要调整,但我们只介绍重要的参数。以下是这些参数根据其功能和不同型号的对应参数的列表。
我使用2015年航班延误的Kaggle数据集,因为它具有分类和数值特征。该数据集大约有 500 万行,将有利于判断每种类型增压的调优模型的速度和准确性的性能。我将采样使用此数据的10%子集〜500k行。不然训练太慢。
以下是用于建模的特征:
import pandas as pd, numpy as np, time from sklearn.model_selection import train_test_split, GridSearchCV from sklearn import metrics data = pd.read_csv('flights.csv') data = data.sample(frac=0.1, random_state=10) print(data.columns) data = data[["MONTH", "DAY", "DAY_OF_WEEK", "AIRLINE", "FLIGHT_NUMBER", "DESTINATION_AIRPORT", "ORIGIN_AIRPORT", "AIR_TIME", "DEPARTURE_TIME", "DISTANCE", "ARRIVAL_DELAY"]] print(data.head(5)) data.dropna(inplace=True) print(data.head(5)) 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)
import xgboost as xgb 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(tree_method='gpu_hist', gpu_id=0) param_dist = {"max_depth": [10,20,30], "min_child_weight" : [1,3], "n_estimators": [200], "learning_rate": [0.05, 0.1,0.5],} grid_search = GridSearchCV(model, param_grid=param_dist, cv = 3, verbose=10, n_jobs=-1) grid_search.fit(train, y_train) print(grid_search.best_estimator_) model = xgb.XGBClassifier(max_depth=20, min_child_weight=3, n_estimators=200,\ n_jobs=-1 , verbose=1,learning_rate=0.1 ,tree_method='gpu_hist', gpu_id=0) model.fit(train,y_train,verbose=10) print('xgboost model score') print(auc(model, train, test)) #xgboost model score (0.9999815408046491, 0.7848942647211888)
xgboost model score (0.9999815408046491, 0.7848942647211888)
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(verbose=1 ,device_type ='gpu') param_dist = {"max_depth": [25,50, 75], "learning_rate" : [0.01,0.05,0.1], "num_leaves": [300,900,1200], "num_iterations": [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) print('LGBMClassifier best_estimator:',grid_search.best_estimator_) d_train = lgb.Dataset(train, label=y_train, free_raw_data=False) # END learning_rate=0.05, max_depth=25, num_iterations=200, num_leaves=1200;, score=0.772 total time=10.6min params = {"max_depth": 25, "learning_rate": 0.05, "num_leaves": 1200, "num_iterations": 200, # 'device': "gpu",'gpu_device_id': 1, # 'gpu_platform_id': 1, # 'gpu_use_dp': 'false', # 'max_bin': 252, # 'num_gpu':1 } # Without Categorical Features model2 = lgb.train(params, d_train) print('lightGBM Without Categorical Features:', auc2(model2, train, test)) # lightGBM Without Categorical Features: (0.9820454074519167, 0.7842988189129978) # 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) print('lightGBM With Categorical Features:', auc2(model2, train, test)) # lightGBM With Categorical Features: (0.9849578833000501, 0.7765037053711114)
lightGBM Without Categorical Features: (0.9820454074519167, 0.7842988189129978)
在调整 CatBoost 的参数时,很难传递分类特征的索引。因此,我在不传递分类特征的情况下调整了参数,并评估了两个模型 :一个具有分类特征,另一个区分没有类别特征。我单独调整了one_hot_max_size,因为它不会影响其他参数。
import catboost 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': [7, 10,13], 'learning_rate': [0.03, 0.15, 0.2], 'l2_leaf_reg': [1, 4, 7], 'iterations': [300,500]} cb = catboost.CatBoostClassifier(task_type="GPU", devices='0') cb_model = GridSearchCV(cb, params, scoring="roc_auc", cv=3) cb_model.fit(train, y_train) print('CatBoostClassifier best_estimator:',cb_model.best_estimator_) print('CatBoostClassifier best_params_:',cb_model.best_params_) # With Categorical features clf = catboost.CatBoostClassifier(eval_metric="AUC", depth=10, iterations=500, l2_leaf_reg=9, learning_rate=0.15 , task_type="GPU", devices='0') clf.fit(train, y_train,plot=True) print('CatBoostClassifier Without Categorical Features:', auc(clf, train, test)) # CatBoostClassifier Without Categorical Features: (0.8411768106404672, 0.7571423657177548) # With Categorical features clf = catboost.CatBoostClassifier(eval_metric="AUC", one_hot_max_size=31, \ depth=10, iterations=300, l2_leaf_reg=4, learning_rate=0.15 , task_type="GPU", devices='0') clf.fit(train, y_train, cat_features=cat_features_index,plot=True) print('CatBoostClassifier With Categorical Features:', auc(clf, train, test)) # CatBoostClassifier With Categorical Features: (0.881573635663068, 0.8129717796545255)
为了评估模型,我们应该从速度和精度的角度来研究模型的性能。
牢记这一点,CatBoost在测试集上以最大的精度(0.816),最小的过拟合(训练和测试精度都接近),以及最小的预测时间和调整时间成为赢家。但这仅仅是因为我们考虑了分类变量并调整了one_hot_max_size。如果我们不利用CatBoost的这些功能,它的表现最差,准确度仅为0.752。因此,我们了解到,只有当数据中有分类变量并正确调整它们时,CatBoost才会表现良好。
我们的下一个表演者是XGBoost,通常效果很好。它的准确性非常接近CatBoost,即使忽略了我们在数据中有分类变量的事实,我们已经将其转换为数值以供其使用。但是,XGBoost的唯一问题是它太慢了。特别是调整其参数真的很令人沮丧(我花了6个小时来运行GridSearchCV - 非常糟糕的主意!更好的方法是单独调整参数,而不是使用GridSearchCV。
提示:使用GPU可能会所有加快,默认算法使用cpu,因此本论文在原作者上改进使用GPU,会快不少。但目前lgbm不支持cuda,无法使用英伟达的显卡加速。
最后,最后一个地方是Light GBM。这里需要注意的一件重要事情是,当使用cat_features时,它在速度和准确性方面表现不佳。我相信它表现不佳的原因是因为它对分类数据使用了某种修改的均值编码,导致过度拟合(训练精度相当高 - 与测试精度相比为0.999)。然而,如果我们像XGBoost一样正常使用它,它可以达到与XGBoost(LGBM - 0.785,XGBoost- 0.789)更快的速度相似的精度。
最后,我不得不说,这些观察结果对于这个特定的数据集是正确的,对于其他数据集可能有效,也可能不有效。但是,有一件事是正确的,那就是:XGBoost比其他两种算法慢。
那么你最喜欢哪一个呢?请评论原因。
任何反馈或改进建议将不胜感激!
原始论文
lightGBM 参数
数据集
xgboost参数
import matplotlib.pyplot as plt import pandas as pd, numpy as np, time from sklearn.model_selection import train_test_split, GridSearchCV from sklearn import metrics data = pd.read_csv('flights.csv') print('data.shape:',data.shape) data = data.sample(frac=0.1, random_state=10) print('after sample data.shape:',data.shape) print(data.columns) data = data[["MONTH", "DAY", "DAY_OF_WEEK", "AIRLINE", "FLIGHT_NUMBER", "DESTINATION_AIRPORT", "ORIGIN_AIRPORT", "AIR_TIME", "DEPARTURE_TIME", "DISTANCE", "ARRIVAL_DELAY"]] print(data.head(5)) data.dropna(inplace=True) print(data.head(5)) 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) import xgboost as xgb ### https://xgboost.readthedocs.io/en/stable/parameter.html 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(tree_method='gpu_hist', gpu_id=0) # param_dist = {"max_depth": [10,20,30], # "min_child_weight" : [1,3], # # "n_estimators": [200,500], # "subsample":[0.8,0.9,1], # "lambda":[3,6,9], # "alpha":[3,6], # # "learning_rate": [0.05, 0.1,0.5], # "learning_rate": [0.1], # } # grid_search = GridSearchCV(model, param_grid=param_dist, cv = 3, # verbose=10, n_jobs=-1) # grid_search.fit(train, y_train) # print('XGBClassifier best_params_:', grid_search.best_params_) # # alpha=3, lambda=3, learning_rate=0.1, max_depth=20, min_child_weight=1, # # subsample=0.8;, score=0.810 total time=38.6min model = xgb.XGBClassifier(max_depth=20, min_child_weight=3, n_estimators=1000,learning_rate=0.05, reg_lambda=1, alpha=1, # subsample=0.8, n_jobs=-1, verbose=10, tree_method='gpu_hist', gpu_id=0) model.fit(train,y_train,verbose=10) print('xgboost model score:',auc(model, train, test)) #xgboost model score (0.9999999975811984, 0.7899954118937722) #20% xgboost model score: (0.9999985093055754, 0.8220708882482494) import lightgbm as lgb ## https://lightgbm.readthedocs.io/en/latest/Parameters-Tuning.html 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(verbose=0, # learning_rate=0.5, # device_type='gpu',gpu_device_id= 0,gpu_platform_id=0,gpu_use_dp='false') # param_dist = { # "max_depth": [25, 50, 75], # # "learning_rate": [0.01, 0.05, 0.1], # "num_leaves": [100,300, 900], # # "num_iterations": [200,500], # # "feature_fraction": [0.7,0.8, 0.9, 1.0], # # "bagging_fraction": [0,7,0.8, 0.9, 1.0], # # LGBMClassifier best_params_: {'bagging_fraction': 0.8, 'feature_fraction': 1.0} # 'bagging_fraction': [0.8], 'feature_fraction': [1.0], # # # "lambda_l1": [0, 3, 6, 9], # # "lambda_l2": [0, 3, 6], # # LGBMClassifier best_params_: {'lambda_l1': 6, 'lambda_l2': 3, 'num_iterations': 500} # 'lambda_l1': [6], 'lambda_l2': [3], 'num_iterations': [500], # # LGBMClassifier best_params_: {'bagging_fraction': 0.8, 'feature_fraction': 1.0, # # 'lambda_l1': 6, 'lambda_l2': 3, 'max_depth': 75, 'num_iterations': 500, 'num_leaves': 300} # } # grid_search = GridSearchCV(lg, n_jobs=-1, param_grid=param_dist, cv=3, scoring="roc_auc", verbose=1) # grid_search.fit(train, y_train) # print('LGBMClassifier best_estimator:', grid_search.best_estimator_) # print('LGBMClassifier best_params_:', grid_search.best_params_) # # learning_rate=0.05, max_depth=50, num_iterations=200, num_leaves=300;, score=0.763 total time= 3.1min d_train = lgb.Dataset(train, label=y_train, free_raw_data=False) # END learning_rate=0.05, max_depth=25, num_iterations=200, num_leaves=1200;, score=0.772 total time=10.6min params = {"max_depth": 75, "learning_rate": 0.1, "num_leaves": 300, 'lambda_l1': 6, 'lambda_l2': 3, 'num_iterations': 2000, 'bagging_fraction': 0.8, 'feature_fraction': 1.0, # 'device': "gpu", 'gpu_device_id': 1, # 'gpu_platform_id': 1, # 'gpu_use_dp': 'false', # 'max_bin': 252, # 'num_gpu': 2 } # Without Categorical Features model2 = lgb.train(params, d_train) print('lightGBM Without Categorical Features:', auc2(model2, train, test)) # lightGBM Without Categorical Features: (0.9832532745589256, 0.7892403986379332) # # 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) print('lightGBM With Categorical Features:', auc2(model2, train, test)) # lightGBM With Categorical Features: (0.9849578833000501, 0.7765037053711114) import catboost 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': [7, 10,13], # # 'learning_rate': [0.03, 0.15, 0.2], # # 'l2_leaf_reg': [1, 4, 7], # # 'iterations': [300,500]} # # cb = catboost.CatBoostClassifier(task_type="GPU", # # devices='0') # # cb_model = GridSearchCV(cb, params, scoring="roc_auc", cv=3) # # cb_model.fit(train, y_train) # # print('CatBoostClassifier best_estimator:',cb_model.best_estimator_) # # print('CatBoostClassifier best_params_:',cb_model.best_params_) # # With Categorical features clf = catboost.CatBoostClassifier(eval_metric="AUC", depth=10, iterations=500, l2_leaf_reg=4, learning_rate=0.15 , task_type="GPU", devices='0') clf.fit(train, y_train,plot=True) print('CatBoostClassifier Without Categorical Features:', auc(clf, train, test)) # CatBoostClassifier Without Categorical Features: (0.8411768106404672, 0.7571423657177548) # 20% CatBoostClassifier Without Categorical Features: (0.8125473336833896, 0.767777462764133) # With Categorical features clf = catboost.CatBoostClassifier(eval_metric="AUC", one_hot_max_size=31, \ depth=10, iterations=500, l2_leaf_reg=4, learning_rate=0.15 , task_type="GPU", devices='0') clf.fit(train, y_train, cat_features=cat_features_index,plot=True) print('CatBoostClassifier With Categorical Features:', auc(clf, train, test)) plt.show() # CatBoostClassifier With Categorical Features: (0.881573635663068, 0.8129717796545255) # CatBoostClassifier With Categorical Features: (0.8969051893380249, 0.8243120308981184) # 20% CatBoostClassifier With Categorical Features: (0.8942350930660874, 0.8478804417411229)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。