catboost机器学习

1 CatBoost简介

参考论文
Anna Veronika Dorogush, Andrey Gulin, Gleb Gusev, Nikita Kazeev, Liudmila Ostroumova Prokhorenkova, Aleksandr Vorobev "Fighting biases with dynamic boosting". arXiv:1706.09516, 2017
Anna Veronika Dorogush, Vasily Ershov, Andrey Gulin "CatBoost: gradient boosting with categorical features support". Workshop on ML Systems at NIPS 2017

本文参考:一文详尽解释CatBoost
https://cloud.tencent.com/developer/article/1546808

CatBoost是俄罗斯的搜索巨头Y andex在2017年开源的机器学习库,也是Boosting族算法的一种,同前面介绍过的XGBoost和LightGBM类似,依然是在GBDT算法框架下的一种改进实现,是一种基于对称决策树(oblivious trees)算法的参数少、支持类别型变量和高准确性的GBDT框架,主要解决的痛点是高效合理地处理类别型特征(Categorical features),这个从它的名字就可以看得出来,CatBoost是由catgoricalboost组成,另外是处理梯度偏差(Gradient bias)以及预测偏移(Prediction shift)问题,提高算法的准确性和泛化能力。

集成学习.png

2 CatBoost的主要特性

  • 1 无需调参即可获得较高的模型质量,采用默认参数就可以获得非常好的结果,减少在调参上面花的时间
  • 2 支持类别型变量,无需对非数值型特征进行预处理
  • 3 快速、可扩展的GPU版本,可以用基于GPU的梯度提升算法实现来训练你的模型,支持多卡并行
  • 4 提高准确性,提出一种全新的梯度提升机制来构建模型以减少过拟合
  • 5 快速预测,即便应对延时非常苛刻的任务也能够快速高效部署模型

3 CatBoost对类别型变量的处理

3.1 类别型变量

类别型变量(Categorical features)是指其值是离散的集合且相互比较并无意义的变量,比如用户的ID、产品ID、颜色等。因此,这些变量无法在二叉决策树当中直接使用。常规的做法是将这些类别变量通过预处理的方式转化成数值型变量再用模型进行训练,比如用一个或者若干个数值来代表一个类别型特征。

3.2 类别型变量的一般处理方式
  • 1 目前广泛用于低势(一个有限集的元素个数是一个自然数)类别特征的处理方法是One-hot encoding:将原来的特征删除,然后对于每一个类别加一个0/1的用来指示是否含有该类别的数值型特征。One-hot encoding可以在数据预处理时完成,也可以在模型训练的时候完成,从训练时间的角度,后一种方法的实现更为高效,CatBoost对于低势类别特征也是采用后一种实现
  • 2 在高势特征当中,比如 user ID,这种编码方式会产生大量新的特征,造成维度灾难。一种折中的办法是可以将类别分组成有限个的群体再进行 One-hot encoding。一种常被使用的方法是根据目标变量统计(Target Statistics,以下简称TS)进行分组,目标变量统计用于估算每个类别的目标变量期望值。甚至有人直接用TS作为一个新的数值型变量来代替原来的类别型变量。重要的是,可以通过对TS数值型特征的阈值设置,基于对数损失、基尼系数或者均方差,得到一个对于训练集而言将类别一分为二的所有可能划分当中最优的那个。
3.3 CatBoost采用TS特征的优势

在LightGBM当中,类别型特征用每一步梯度提升时的梯度统计(Gradient Statistics,以下简称GS)来表示。虽然为建树提供了重要的信息,但是这种方法有以下两个缺点:

  • 增加计算时间,因为需要对每一个类别型特征,在迭代的每一步,都需要对GS进行计算;
  • 增加存储需求,对于一个类别型变量,需要存储每一次分离每个节点的类别。
    为了克服这些缺点,LightGBM以损失部分信息为代价将所有的长尾类别归位一类,作者声称这样处理高势特征时比起 One-hot encoding还是好不少。不过如果采用TS特征,那么对于每个类别只需要计算和存储一个数字。

因此,采用TS作为一个新的数值型特征是最有效、信息损失最小的处理类别型特征的方法。TS也被广泛采用,在点击预测任务当中,这个场景当中的类别特征有用户、地区、广告、广告发布者等。

4 XGBoost、Light GBM和CatBoost比较

从结构到性能,一文概述XGBoost、Light GBM和CatBoost的同与不同
https://zhuanlan.zhihu.com/p/34698733

  • 2014 年 3 月,XGBOOST 最早作为研究项目,由陈天奇提出
  • 2017 年 1 月,微软发布首个稳定版 LightGBM
  • 2017 年 4 月,俄罗斯顶尖技术公司 Yandex 开源 CatBoost
4.1 每个模型是如何处理属性分类变量的?

CatBoost
CatBoost 可赋予分类变量指标,进而通过独热最大量得到独热编码形式的结果(独热最大量:在所有特征上,对小于等于某个给定参数值的不同的数使用独热编码)。
如果在 CatBoost 语句中没有设置「跳过」,CatBoost 就会将所有列当作数值变量处理。
注意,如果某一列数据中包含字符串值,CatBoost 算法就会抛出错误。另外,带有默认值的 int 型变量也会默认被当成数值数据处理。在 CatBoost 中,必须对变量进行声明,才可以让算法将其作为分类变量处理
对于可取值的数量比独热最大量还要大的分类变量,CatBoost 使用了一个非常有效的编码方法,这种方法和均值编码类似,但可以降低过拟合情况。

它的具体实现方法如下:

  • 将输入样本集随机排序,并生成多组随机排列的情况。
  • 将浮点型或属性值标记转化为整数。
  • 将所有的分类特征值结果都根据以下公式,转化为数值结果。

LightGBM
和 CatBoost 类似,LighGBM 也可以通过使用特征名称的输入来处理属性数据;它没有对数据进行独热编码,因此速度比独热编码快得多。LGBM 使用了一个特殊的算法来确定属性特征的分割值。
注意,在建立适用于 LGBM 的数据集之前,需要将分类变量转化为整型变量;此算法不允许将字符串数据传给分类变量参数。

XGBoost
和 CatBoost 以及 LGBM 算法不同,XGBoost 本身无法处理分类变量,而是像随机森林一样,只接受数值数据。因此在将分类数据传入 XGBoost 之前,必须通过各种编码方式:例如标记编码、均值编码或独热编码对数据进行处理。

5 三种算法的超参数

所有的这些模型都需要调节大量参数,但我们只谈论其中重要的。以下是将不同算法中的重要参数按照功能进行整理的表格。


三种算法的超参数.png
6 用鸢尾花数据集实例演示
from catboost import CatBoostClassifier

import numpy as np
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
data = iris.data
target = iris.target
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=666)
model = CatBoostClassifier()
model.fit(X_train,y_train)
# 测试集预测
y_pred = model.predict(X_test)
# 模型评估
print('The rmse of prediction is:', mean_squared_error(y_test, y_pred) ** 0.5)

The rmse of prediction is: 0.0

estimator = CatBoostClassifier()
params = {'depth': [4, 7, 10],
          'learning_rate' : (0,0.5,0.01),
         'l2_leaf_reg': [1,4,9],
         'iterations': [300]}

cat_cv = GridSearchCV(estimator, params)
clf = cat_cv.fit(X_train, y_train)
y_pred = clf.predict(X_test)
mean_squared_error(y_test, y_pred)
print('Best parameters found by grid search are:', cat_cv.best_params_)
Best parameters found by grid search are: {'depth': 4, 'iterations': 300, 'l2_leaf_reg': 1, 'learning_rate': 0.01}

在默认参数下,均方误差为 0

尽管近年来神经网络复兴并大为流行,但是 boosting 算法在训练样本量有限、所需训练时间较短、缺乏调参知识等场景依然有其不可或缺的优势。本文从算法结构差异、每个算法的分类变量时的处理、算法在数据集上的实现等多个方面对 3 种代表性的 boosting 算法 CatBoost、Light GBM 和 XGBoost 进行了对比;虽然本文结论依据于特定的数据集,但通常情况下,XGBoost 都比另外两个算法慢。

最近,我参加了 kaggle 竞赛 WIDS Datathon,并通过使用多种 boosting 算法,最终排名前十。从那时开始,我就对这些算法的内在工作原理非常好奇,包括调参及其优劣势,所以有了这篇文章。尽管最近几年神经网络复兴,并变得流行起来,但我还是更加关注 boosting 算法,因为在训练样本量有限、所需训练时间较短、缺乏调参知识的场景中,它们依然拥有绝对优势。

2014 年 3 月,XGBOOST 最早作为研究项目,由陈天奇提出2017 年 1 月,微软发布首个稳定版 LightGBM2017 年 4 月,俄罗斯顶尖技术公司 Yandex 开源 CatBoost

由于 XGBoost(通常被称为 GBM 杀手)已经在机器学习领域出现了很久,如今有非常多详细论述它的文章,所以本文将重点讨论 CatBoost 和 LGBM,在下文我们将谈到:

算法结构差异每个算法的分类变量时的处理如何理解参数算法在数据集上的实现每个算法的表现

LightGBM 和 XGBoost 的结构差异

在过滤数据样例寻找分割值时,LightGBM 使用的是全新的技术:基于梯度的单边采样(GOSS);而 XGBoost 则通过预分类算法和直方图算法来确定最优分割。这里的样例(instance)表示观测值/样本。

首先让我们理解预分类算法如何工作:

对于每个节点,遍历所有特征对于每个特征,根据特征值分类样例进行线性扫描,根据当前特征的基本信息增益,确定最优分割选取所有特征分割结果中最好的一个

简单说,直方图算法在某个特征上将所有数据点划分到离散区域,并通过使用这些离散区域来确定直方图的分割值。虽然在计算速度上,和需要在预分类特征值上遍历所有可能的分割点的预分类算法相比,直方图算法的效率更高,但和 GOSS 算法相比,其速度仍然更慢。

为什么 GOSS 方法如此高效?

在 Adaboost 中,样本权重是展示样本重要性的很好的指标。但在梯度提升决策树(GBDT)中,并没有天然的样本权重,因此 Adaboost 所使用的采样方法在这里就不能直接使用了,这时我们就需要基于梯度的采样方法。

梯度表征损失函数切线的倾斜程度,所以自然推理到,如果在某些意义上数据点的梯度非常大,那么这些样本对于求解最优分割点而言就非常重要,因为算其损失更高。

GOSS 保留所有的大梯度样例,并在小梯度样例上采取随机抽样。比如,假如有 50 万行数据,其中 1 万行数据的梯度较大,那么我的算法就会选择(这 1 万行梯度很大的数据+x% 从剩余 49 万行中随机抽取的结果)。如果 x 取 10%,那么最后选取的结果就是通过确定分割值得到的,从 50 万行中抽取的 5.9 万行。

在这里有一个基本假设:如果训练集中的训练样例梯度很小,那么算法在这个训练集上的训练误差就会很小,因为训练已经完成了。

为了使用相同的数据分布,在计算信息增益时,GOSS 在小梯度数据样例上引入一个常数因子。因此,GOSS 在减少数据样例数量与保持已学习决策树的准确度之间取得了很好的平衡。

高梯度/误差的叶子,用于 LGBM 中的进一步增长

每个模型是如何处理属性分类变量的?

CatBoost

CatBoost 可赋予分类变量指标,进而通过独热最大量得到独热编码形式的结果(独热最大量:在所有特征上,对小于等于某个给定参数值的不同的数使用独热编码)。

如果在 CatBoost 语句中没有设置「跳过」,CatBoost 就会将所有列当作数值变量处理。

注意,如果某一列数据中包含字符串值,CatBoost 算法就会抛出错误。另外,带有默认值的 int 型变量也会默认被当成数值数据处理。在 CatBoost 中,必须对变量进行声明,才可以让算法将其作为分类变量处理。

对于可取值的数量比独热最大量还要大的分类变量,CatBoost 使用了一个非常有效的编码方法,这种方法和均值编码类似,但可以降低过拟合情况。它的具体实现方法如下:

1. 将输入样本集随机排序,并生成多组随机排列的情况。

2. 将浮点型或属性值标记转化为整数。

3. 将所有的分类特征值结果都根据以下公式,转化为数值结果。

其中 CountInClass 表示在当前分类特征值中,有多少样本的标记值是「1」;Prior 是分子的初始值,根据初始参数确定。TotalCount 是在所有样本中(包含当前样本),和当前样本具有相同的分类特征值的样本数量。

可以用下面的数学公式表示:

LightGBM

和 CatBoost 类似,LighGBM 也可以通过使用特征名称的输入来处理属性数据;它没有对数据进行独热编码,因此速度比独热编码快得多。LGBM 使用了一个特殊的算法来确定属性特征的分割值。

注意,在建立适用于 LGBM 的数据集之前,需要将分类变量转化为整型变量;此算法不允许将字符串数据传给分类变量参数。

XGBoost

和 CatBoost 以及 LGBM 算法不同,XGBoost 本身无法处理分类变量,而是像随机森林一样,只接受数值数据。因此在将分类数据传入 XGBoost 之前,必须通过各种编码方式:例如标记编码、均值编码或独热编码对数据进行处理。

超参数中的相似性

所有的这些模型都需要调节大量参数,但我们只谈论其中重要的。以下是将不同算法中的重要参数按照功能进行整理的表格。

实现

在这里,我使用了 2015 年航班延误的 Kaggle 数据集,其中同时包含分类变量和数值变量。这个数据集中一共有约 500 万条记录,因此很适合用来同时评估比较三种 boosting 算法的训练速度和准确度。我使用了 10% 的数据:50 万行记录。

以下是建模使用的特征:

月、日、星期:整型数据航线或航班号:整型数据出发、到达机场:数值数据出发时间:浮点数据到达延误情况:这个特征作为预测目标,并转为二值变量:航班是否延误超过 10 分钟距离和飞行时间:浮点数据

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)

Light GBM

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 调参时,很难对分类特征赋予指标。因此,我同时给出了不传递分类特征时的调参结果,并评估了两个模型:一个包含分类特征,另一个不包含。我单独调整了独热最大量,因为它并不会影响其他参数。

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 都比另外两个算法慢。

所以,你更喜欢哪个算法呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值