Catboost原理

  1. 首先了解一下boost(集成学习)

集成学习(ensemble learning)是一种通过组合多个分类器或回归器来提高预测准确性的机器学习技术。它的基本思想是将多个分类器或回归器的预测结果进行加权平均或投票,从而获得比单一分类器或回归器更好的预测结果。

集成学习通常可以分为两种类型:一种是并行集成方法,例如bagging和random forest,这种方法通过并行训练多个独立的分类器或回归器,最后将它们的预测结果进行平均投票;另一种是串行集成方法,例如boosting和stacking,这种方法通过串行训练多个分类器或回归器,每一轮训练都会调整训练数据的权重或者特征,使得下一轮训练更加关注之前分类错误的样本或者特征,从而最终得到一个较为强大的分类器或回归器。

集成学习可以有效地降低模型的方差(variance),提高预测的准确性,尤其在处理高维度、复杂度较高的数据集时表现突出。常见的集成学习算法包括bagging、random forest、boosting、stacking等。

集成学习是通过将多个弱分类器进行组合变成一个强分类器

多模态是将多个不同领域的模型相结合达到任务需求

什么是catboost

CatBoost(categorical boosting)是 Yandex 开源的机器学习算法。它可以与深度学习框架轻松集成。它可以处理多种数据类型,以帮助解决企业今天面临的各种问题。CatBoost 和 XGBoost、LightGBM 并称为 GBDT 的三大主流神器,都是在 GBDT 算法框架下的一种改进实现。XGBoost 被广泛的应用于工业界,LightGBM 有效的提升了 GBDT 的计算效率,而 Yandex 的 CatBoost 号称是比 XGBoost 和 LightGBM 在算法准确率等方面表现更为优秀的算法。

CatBoost 是一种基于 对称决策树(oblivious trees)为基学习器实现的参数较少、支持类别型变量和高准确性的GBDT框架,主要解决的痛点是高效合理地处理类别型特征,这一点从它的名字中可以看出来,CatBoost 是由 CategoricalBoosting 组成。此外,CatBoost 还解决了梯度偏差(Gradient Bias)以及预测偏移(Prediction shift)的问题,从而减少过拟合的发生,进而提高算法的准确性和泛化能力。

优点:参数少,支持类别变量,准确率高

  • 对分类型特征的处理。这使得我们在训练模型之前可以考虑不用再通过特征工程去处理分类型特征
  • 预测偏移处理。这可以减少模型的过拟合,提升模型预测效果
分类特征处理

一般算法通常只接受数值型数据输入,因此在训练模型前需要对分类型特征进行特征工程处理。在特征工程中,不可避免的会受我们的主观经验影响,选用什么样的方法处理呢?是选用分箱处理、one-hot编码、TS(target statistics)编码、label编码还是其它方式中的一种呢?具体怎么实施呢?这可能需要反复调试,看那种效果好。CatBoost算法包含了对分类型特征的处理方式,主要包括:空值处理、ordered TS编码、特征组合。

  1. 分箱处理: 分箱处理是将连续的数值型特征划分为离散的区间,即将一个连续的特征值域划分为多个离散的区间,也称为离散化。通常采用等宽分箱或等频分箱方法。等宽分箱是将特征值域平均分成n份,每份为一个区间;等频分箱是将特征值域划分为n个区间,每个区间包含相同数量的样本。(该方法只适合数值类型特征)
    假设我们有一个连续的数值型特征“年龄”,其取值范围为18到100岁。我们可以将其划分为5个等宽的区间,每个区间宽度为16岁,即18-34岁、35-51岁、52-68岁、69-85岁、86-100岁。这样,每个样本的年龄特征就被转换为对应的区间,成为一个离散特征。这种离散化的方式可以使得模型更加稳定,同时可以消除年龄的细节差异对模型的影响,更好地捕捉年龄对目标变量的影响
  2. One-hot编码: One-hot编码是将离散特征转换为二进制向量的过程。对于有n个取值的离散特征,可以将其转换为n维的二进制向量,其中每一维代表一个取值,如果样本的特征取值为该维的取值,则对应维的值为1,否则为0。这种编码方式可以使得离散特征在模型中的表达更加充分。
  3. TS(target statistics)编码: TS编码是一种基于目标变量的特征编码方式,它是一种基于目标变量的平滑方法。在TS编码中,对于一个离散特征的每一个取值,可以计算该取值下目标变量的均值或方差等统计量,然后用这些统计量来代替原来的离散特征取值。这样可以使得模型更加稳定,减少过拟合的风险。(该方法只适合数值类型特征,例如数字信号、模拟信号)

假设有一个数字信号序列为:1 0 1 1 0 0 1 1,采用4位码元长度的TS编码方法,则可以将该数字信号序列分割为以下码元: 1 0 1 1 0 0 1 1 对于每个码元,可以根据规定的映射表将其转换为一个二进制码。例如,可以将“1011”映射为“1100”,将“0011”映射为“0011”,则上述数字信号序列的TS编码结果为: 1100 0011

  1. Label编码: Label编码是将离散特征转换为数值型的过程。对于有n个取值的离散特征,可以将其转换为0到n-1的整数,其中每个整数代表一个特征取值。这种编码方式可以使得离散特征在模型中的表达更加简单,但可能会引入一些不必要的偏差。

Label编码是将分类变量转换为数值,以便于机器学习算法的处理。常用的编码方法包括One-Hot编码、Label Encoding和Target Encoding等。下面以Label Encoding为例进行说明。 Label Encoding是将分类变量按照类别的顺序进行编码,通常将类别用数字1、2、3、…表示。例如,将“red”、“green”和“blue”三个颜色类别分别编码为1、2和3。 假设有如下的分类变量数据: colors = ['red', 'green', 'blue', 'red', 'red', 'blue', 'green'] 使用Python的sklearn库中的LabelEncoder类进行编码: from sklearn.preprocessing import LabelEncoder encoder = LabelEncoder() encoded_colors = encoder.fit_transform(colors) print(encoded_colors) 输出结果为: [2 1 0 2 2 0 1] 可以看到,分类变量被转换为了数字,其中“red”被编码为2,“green”被编码为1,“blue”被编码为0。这样,分类变量就可以被算法处理了。

这里要说明的是,不能说CarBoost算法的处理方式一定效果最好,在一些业务场景下主观经验也会非常有帮助,因此是否使用CatBoost算法来处理分类型特征取决于具体的业务场景。

  1. 空值处理
    在CatBoost包的说明文档中,类别型特征的空值处理做了这样的说明:CatBoost does not process categorical features in any specific way,意思就是不做特别处理,但具体怎么做的我不确定(没看过源码),我个人的理解是将每一个空值都当作一个区别于其它取值的唯一值,不仅是区别于那些非空值,空值之间也相互区别。
  2. ordered TS 编码
    这是基于TS编码改进的一种编码方式。对于一些类别基数比较小的特征,例如2~3类,可以直接使用one-hot编码,但对于类别基数较大的特征,one-hot编码会产生特征维度问题,这种情况下TS编码会合适一些。
    2.1 TS编码是一种有监督编码,其做法是:将类别取值转换为该类别为正类的概率。用公式来说明:

Catboost原理与使用(分类器)_catboost

其中Catboost原理与使用(分类器)_编码值_02表示x取值为Catboost原理与使用(分类器)_编码值_03、样本类别为1(即正类)的样本数量,Catboost原理与使用(分类器)_catboost_04表示x取值为Catboost原理与使用(分类器)_编码值_03的样本数量。


TS编码有一个缺点,极端情况下当训练集中某类取值只有一个样本、或者没有样本时,计算的编码值就失真了,也就是容易受噪声数据影响。

2.2 Greedy TS编码

针对TS编码存在的问题,可以用Greedy TS编码来处理。在Greedy TS编码中,增加了先验概率p来减轻噪声数据的影响。Greedy TS编码的计算公式如下

Catboost原理与使用(分类器)_catboost_06

其中a是一个大于0的参数,用来控制先验概率的影响程度;p是先验概率,即样本为正类的概率,计算方式是正类样本数除以样本总数。

Greedy TS编码也存在一个问题,即目标泄露,怎么理解?以上计算Greedy TS编码时用到了Catboost原理与使用(分类器)_编码值_02Catboost原理与使用(分类器)_catboost_04、p这几个统计量,当测试集和训练集中样本分布不一致时,这两个统计量的值就会不同,因此在训练集上计算Greedy TS编码会存在过拟合情况,也就是所谓的目标泄露。

2.3 ordered TS编码

CatBoost算法中采用了ordered TS编码方法来解决Greedy TS编码的目标泄露问题。ordered TS编码是基于排序的,在CatBoost算法中,会对样本进行多次洗牌,每次得到不同排序状态的样本集。为什么要排序?排序的目的产生一种随机性,减少过拟合。每一轮迭代、构建一个树时,都会选择一种排序状态的样本集,这样在不同轮次迭代中,不同排序状态的样本集综合起来,会使模型的方差更小,越不容易过拟合。

在某种排序状态 Catboost原理与使用(分类器)_编码值_09 下,样本 Catboost原理与使用(分类器)_编码值_03 在分类特征 Catboost原理与使用(分类器)_编码值_11下的值为 Catboost原理与使用(分类器)_过拟合_12Catboost原理与使用(分类器)_编码值_13的ordered TS编码值是基于排在其前面的样本 Catboost原理与使用(分类器)_过拟合_14计算的,在Catboost原理与使用(分类器)_过拟合_14中计算分类特征 Catboost原理与使用(分类器)_编码值_11下取值与Catboost原理与使用(分类器)_过拟合_12相同的样本的Greedy TS编码值,该值即为的ordered TS编码值。举例说明

在上图中,经过样本排序后,样本的排序情况为{4,3,7,2,6,1,5},计算样本4的ordered TS编码值时,由于没有样本排在其前面,因此其ordered TS编码值计算方式为

Catboost原理与使用(分类器)_catboost_18

计算样本6的ordered TS编码值时,排在其前面的样本为{4,3,7,2},在这4个样本中,特征取值为D的只有样本4,因此其ordered TS编码值计算方式为

Catboost原理与使用(分类器)_过拟合_19

简单来说就是,先进行排序,在同类型上的顺序进行计算,计算方式我是不是+ap/我排第几(从0开始算)+a

  1. 特征组合
    特征组合是指将样本集中的类别型特征进行组合组合后的特征作为样本集上新的类别型特征。这样做有什么好处?这其实也是特征工程中的一种方式,它丰富了特征的维度,在节点划分时多了一个候选特征,但不一定就是有用的,具体还是要结合实际的业务场景和数据来看。当样本集中类别型特征比较多时,特征组合会产生大批新特征,这在实际训练模型中是不可行的,那CatBoost算法中特征组合是怎么执行的?举例来说明

Catboost原理与使用(分类器)_catboost_20

(CatBoost算法用的基模型是对称树,其特点是每一层使用同样的特征来分裂,这个后面再细说)

在上图中,第一层分裂不做特征组合操作,此时选用了特征a作为分裂特征;进入第二层分裂,此时建树过程已使用的分裂特征集合为 { 特征a },将所有类别型特征与已使用的分裂特征集合中的特征进行两两组合,也就是与特征a进行两两组合,得到一批新的类别型特征,参与结点分裂(注意相同的特征不再参与两两组合,例如特征a不能再与特征a进行组合),最终选用了特征b作为分裂特征;进入第三层分裂,此时建树过程已使用的分裂特征集合为 { 特征a,特征b },同样的,将所有类别型特征与已使用的分裂特征集合中的特征进行两两组合,也就是与特征a、特征b进行两两组合,得到一批新的类别型特征,参与结点分裂,最终选用了特征c作为分裂特征。以上就是CatBoost算法中特征组合具体执行的过程,一句话总结就是:将所有的类别型特征与之前建树过程中已使用的特征进行两两组合。

还是有几点需要再说明一下:(1)如何某层分裂中用到了组合特征,那么这个组合特征还是可以与其它特征进行组合,参与到下次的分裂过程;(2) CatBoost算法还可以将数值型特征与类别型特征进行组合,操作方式是依据数值型特征的分裂阈值,将其处理为两个类别型特征,然后参与特征组合。例如某层分裂用到了数值型特征p,分裂阈值为p>10 ,那么就可以将特征p处理为“p>10”、“ p<=10 ”两个类别型特征。

  1. 预处理偏移
    在GBDT算法中,每一棵树都是为了拟合前一棵树上的梯度,构造树时所有的样本都参与了,一个样本参与了建树,然后又用这棵树去估计样本值,这样的估计就不是无偏估计,当测试集和训练集上的样本分布不一致时,模型就会因过拟合而性能不佳,即在测试集上产生了预测偏移。(关于预测偏移背后的数学原理,有兴趣的同学去看一下论文原文吧)。

catboost的主要特点

  1. 对称树

与 XGBoost 和 LightGBM 不同,CatBoost 构建对称**(平衡)树**。在每一步中,前一棵树的叶子都使用相同的条件进行拆分。选择损失最低的特征分割对并将其用于所有级别的节点。这种平衡的树结构有助于高效的 CPU 实现,减少预测时间,模型结构可作为正则化以防止过度拟合

在对称决策树中,只使用一个特性来构建每个树级别上的所有分支。我们使用图示方法更加清晰地观察三种类型的拆分:"FloatFeature"(浮点数特征)、"OneHotFeature" (独热编码特征)和 "OnlineCtr"(在线点击率(CTR)特征)

FloatFeature

模型没有类别型特征时,在可视化的树中只有 "FloatFeature" 节点。"FloatFeature" 拆分对应的节点包含特征索引和边界值,用于拆分对象。

boston = load_boston()
y = boston['target']
X = boston['data']
pool = catboost.Pool(data=X, label=y)
model = CatBoostRegressor(depth=2, verbose=False, iterations=1).fit(X, y)
model.plot_tree(tree_idx=0,pool=pool, )

在这个例子中,深度为0的节点表示对象被它们的第0个带边界值的特征分割。类似地,深度1的节点通过其具有边界值的第二个特征来分割对象。

OneHotFeature
titanic_df = titanic()

X = titanic_df[0].drop('Survived',axis=1)
y = titanic_df[0].Survived
# 分类变量的缺失值用"NAN"填充,代码略
pool = Pool(X, y, cat_features=cat_features_index, feature_names=list(X.columns))
model = CatBoostClassifier(
    max_depth=2, verbose=False, max_ctr_complexity=1, random_seed=42, iterations=2).fit(pool)
model.plot_tree(
    tree_idx=0,
    pool=pool # 对于一个需要使用独热编码的特征,"pool" 是一个必须的参数
)

第一棵树只包含一个由"OneHotFeature"特征产生的分裂。这种分割将"Sex=female"的对象放在左边,而"other"的对象放在右边。

参考:https://zhuanlan.zhihu.com/p/540956200,https://zhuanlan.zhihu.com/p/477750675