CatBoost是俄罗斯的搜索巨头Yandex在2017年开源的机器学习库,是Categorical Features(类别型特征)+Gradient Boosting(梯度提升) ,也是基于梯度提升决策树的机器学习框架。
大多数流行的梯度提升算法利用决策树作为基本预测器。对于数值型特征使用决策树很方便,但是实际中,许多数据集包括类别型特征,这些特征对预测也很重要。类别型特征具有离散的值,比如省份名(山东,山西,河北等),城市名(北京,上海,南京等)。梯度提升算法中处理这类特征的最常用的方法就是在学习之前,也就是数据预处理阶段,将这些特征的值转换为数字。一般类别型特征会转化为一个或多个数值型特征(one-hot编码等)。
而Catboost从名字就可以看出:可以很好的处理类别型特征的梯度提升算法,该算法的改进之处就在于在学习的时候处理这些特征,而不是在数据预处理阶段,不需要任何显式的预处理来将类别转换为数字。
Catboost引入了两个关键的算法改进——实现了有序提升,排列驱动以代替经典算法和用于处理分类特征的创新算法。这些方法旨在解决prediction shift(普遍存在于梯度提升算法中)。
一种有效的处理类别特征的方法就是:使用一个计算出的数值(target statistic (TS))来代替,即第 k k k个训练样本的第 i i i个类别特征( x k i x_k^i xki),通常根据类别条件估计预期目标y,表达式为: x ^ k i = E ( y ∣ x i = x k i ) \hat{x}_k^i = E(y|x^i = x_k^i) x^ki=E(y∣xi=xki)
在决策树中,标签平均值将作为节点分裂的标准。这种方法被称为 Greedy Target Statistics , 简称 Greedy TS,用公式来表达就是:
x ^ k i = ∑ j = 1 n I { x j i = x k i } ⋅ y j ∑ j = 1 n I { x j i = x k i } \hat{x}_k^i = \frac{\sum_{j=1}^nI_{\{x_j^i=x_k^i\}}\cdot y_j} {\sum_{j=1}^nI_{\{x_j^i = x_k^i\}}} x^ki=∑j=1nI{xji=xki}∑j=1nI{xji=xki}⋅yj
这里的 I I I是 Iverson brackets(指示函数),即括号里的两个相等时取1,否则取0:
{ x j i = x k i } = { x j i = x k i 1 o t h e r w i s e 0 \{x_j^i = x_k^i\}=\begin{cases} x_j^i=x_k^i &1\\ otherwise &0 \end{cases} {xji=xki}={xji=xkiotherwise10
如果某个标称值 x k i x_k^i xki,只有一条记录的时候,则这个标称值转换成数字后就等于该记录的标签值。这样的处理过程,显然会造成过拟合。
对于这样的低频类别来说是有噪声的,通常用一些先验值p来平滑它:
x ^ k i = ∑ j = 1 n I { x j i = x k i } ⋅ y j + a ⋅ P ∑ j = 1 n I { x j i = x k i } + a \hat{x}_k^i = \frac{\sum_{j=1}^nI_{\{x_j^i=x_k^i\}}\cdot y_j+a\cdot P} {\sum_{j=1}^nI_{\{x_j^i = x_k^i\}}+a} x^ki=∑j=1nI{xji=xki}+a∑j=1nI{xji=xki}⋅yj+a⋅P
这里 a > 0 a>0 a>0为参数, P P P通常取作所有数据中目标变量的平均值。
但是这样的贪婪方法会带来是目标泄漏的问题:用 X K X_K XK 的目标值 y k y_k yk 来计算 x k i x_k^i xki;这就会造成条件偏移(condition shift),即:训练集合测试集的 x i ∣ y x^i|y xi∣y的分布不同。
举一个极端例子,来说明这可能会对所学习模型的泛化误差产生显著的影响:
假设第i个特征是类别型特征,并且该特征所有值都是唯一的,那么对于每一个类型A,在分类任务下,我们能得到 P ( Y = 1 ∣ x i = A ) = 0.5 P(Y=1|x^i=A)=0.5 P(Y=1∣xi=A)=0.5。
- 训练集里 x ^ k i = y k + a p 1 + a \hat{x}_k^i=\frac{y_k+ap}{1+a} x^ki=1+ayk+ap,这样只需要使用阈值 t = 0.5 + a p 1 + a t=\frac{0.5+ap}{1+a} t=1+a0.5+ap进行一次分割就能对所有的训练集诗句完美分类了。
- 而在测试集中, x k i x_k^i xki的值为 p p p,对模型进行预测:如果 p < t p<t p<t ,预测值为0,否则为1;最后精准度都是0.5。
避免条件偏移问题有很多方法,通常使用不包括 x k x_k xk样本的其他集合来计算 x k i x_k^i xki的值: D k ⊂ D − { x k } D_k \subset D-\{x_k\} Dk⊂D−{xk}:
x ^ k i = ∑ X j ∈ D k I { x j i = x k i } ⋅ y j + a ⋅ P ∑ X j ∈ D k I { x j i = x k i } + a \hat{x}_k^i = \frac{\sum_{X_j \in D_k}I_{\{x_j^i=x_k^i\}}\cdot y_j+a\cdot P} {\sum_{X_j \in D_k}I_{\{x_j^i = x_k^i\}}+a} x^ki=∑Xj∈DkI{xji=xki}+a∑Xj∈DkI{xji=xki}⋅yj+a⋅P
将数据集划分为两部分, 一部分用来计算TS,另一部分用来训练,这个方法虽然可以避免条件偏移的问题,但也减少了用于训练模型和计算统计信息数据的数据量。
乍一看,这种 l e a v e − o n e − o u t leave-one-out leave−one−out 的方法应该行得通,对训练样本 x k i x_k^i xki用排除了 x k x_k xk的剩下样本计算TS,对测试样本则用全量计算TS。但仔细想想,这没有解决target泄露的问题,如:一个不变的分类特征,这样又回到了之前所说的条件偏移的问题。
Catboost 使用了一种更有效的策略,这个策略是使用了排序原则(ordering principle)也是Catboost 的核心思想,这个是收到了在线学习算法的启发(在线学习算法是通过时间序列来获得训练样本)。
简单地说,就是TS值的计算依靠目前已经观察的样本集。为了适应标准的离线训练时,我们可以随机生成一个排列来实现带时序的训练集,CatBoost在不同的梯度提升步中使用不同的排列。
在每一步的梯度提升的过程中,也存在预测偏移 prediction shift 的问题,是由一种特殊的目标泄漏引起的。Catboost的解决方案称为有序增强( o r d e r e d ordered ordered b o o s t i n g boosting boosting),类似于Ordered TS方法。
梯度提升算法中,在提升步做法如下:
(1) h t = a r g m i n { h ∈ H } 1 n ∑ k = 1 n ( − g t ( x k , y k ) − h ( x k ) ) h_t=argmin_{\{h\in H\}}\frac{1}{n}\sum^n_{k=1}(-g^t(x_k,y_k)-h(x_k))\tag1 ht=argmin{h∈H}n1k=1∑n(−gt(xk,yk)−h(xk))(1)
h t h_t ht 为新生成的弱分类器, − g t ( x k , y k ) -g^t(x_k,y_k) −gt(xk,yk)为损失函数在当前模型的负梯度,即弱分类器要拟合的值
链式偏移可以描述如下:
(2) h t = a r g m i n { h ∈ H } E ( − g t ( x , y ) − h ( x ) ) 2 h_t=argmin_{\{h\in H\}}E(-g^t(x,y)-h(x))^2\tag2 ht=argmin{h∈H}E(−gt(x,y)−h(x))2(2)
预测偏移的实例分析:
以具有二次损失函数的回归任务为例,损失函数为 L ( y , y ^ ) = ( y − y ^ ) 2 L(y,\hat{y})=(y-\hat{y})^2 L(y,y^)=(y−y^)2,这里负梯度 − g t − 1 ( x k , y k ) = y k − F t − 1 ( x k ) -g^{t-1}(x_k,y_k)=y_k-F^{t-1}(x_k) −gt−1(xk,yk)=yk−Ft−1(xk)。
假设有两个特征 x 1 x^1 x1, x 2 x^2 x2独立同服从伯努利分布(p=0.5),并且 y = f ∗ ( X ) = c 1 x 1 + c 2 x 2 y=f^*(X)=c_1x^1+c_2x^2 y=f∗(X)=c1x1+c2x2。通过两次梯度提升(深度、步长均为1)的迭代后,我们得到模型: F = F 2 = h 1 + h 2 F=F^2=h^1+h^2 F=F2=h1+h2,假设 h 1 h^1 h1基于变量 x 1 x^1 x1, h 2 h^2 h2基于变量 x 2 x^2 x2。
得到以下两个定理(具体证明见原论文):
以上两个定理意味着:当我们在每个梯度步骤中使用独立的数据集时,训练后的模型是对 y = f ∗ ( x ) y =f^*(x) y=f∗(x)真实依赖关系的无偏估计。否则,使用相同的数据集,则会得到有偏估计的模型,且数据集越大,偏差越小。
为了解决上述提到的 prediction shift,方法如下:
CatBoost有两种选择树结构的模式:Ordered and Plain;
Plain模式:是对应于标准GBDT算法内嵌有序目标统计量的组合(使用了随机排列 σ 0 \sigma^0 σ0);
Ordered模式:是对算法1【Algorithm.1】的有效改进。
在CatBoost中,基本预测器是无关决策树,也称为决策表(类似规则 if…then…else )。这样的“无关”意味着在树的整个层次上使用相同的分割标准。这样的树是平衡的,不容易过度拟合,并允许在测试时显著加快执行。
在 Ordered Mode 中,在学习训练的过程中,维护的模型 M r , j M_{r,j} Mr,j,如 M r , j ( i ) M_{r,j}(i) Mr,j(i)则是用排列 σ r \sigma_r σr的前 j j j个样本来计算第 i i i个样本。在算法中每步迭代 t t t中,都在一个随机排列 σ r \sigma_r σr的基础上构建树 T t T_t Tt。
在所有树结构都建立好的情况下,对两种模式均采用标准梯度增强程序计算最终模型F的叶子节点值。对于分类变量就需要使用随机排列 σ r \sigma_r σr。
在 Ordered Mode 中,只储存和更新 M r , j ′ ( i ) : = M r , 2 j ( i ) M_{r,j}^{'}(i):=M_{r,2^j}(i) Mr,j′(i):=Mr,2j(i) ( j = 1 , ⋯   , [ l o g 2 n ] j =1,\cdots,[log_2n] j=1,⋯,[log2n] , σ r ( i ) < = 2 j + 1 \sigma_r(i)<= 2^{j+1} σr(i)<=2j+1),这样,计算模型的时间复杂度就从 O ( s O(s O(s n 2 ) n^2) n2)减少为 O ( s O(s O(s n ) n) n)
每次迭代中两种CatBoost模式不同模式的计算复杂度:
这里的 N T S , t N_{TS,t} NTS,t 是在迭代t中要计算TS的数量, C C C是在给定迭代中要考虑的候选分割集;由此可见,使用决策树实现的有序增强与使用有序TS的标准GBDT具有相同的渐近复杂度。
CatBoost的另一个重要细节是使用分类特征的组合作为附加分类特征,比如:在广告点击预测任务中捕获用户ID和广告主题的联合信息等,此类高阶依赖关系的信息。但是,组合数量随着数据集中分类特征的数量呈指数增长,所以,不可能处理所有这些组合。
CatBoost以贪婪的方式构造组合。也就是说:对于树的第一次分割,不考虑任何组合,对于树的下一个分割,CatBoost将当前树中用于以前分割的所有分类特性(及其组合)与数据集中的所有分类特性组合在一起。组合被动态转换为TS。
from catboost import CatBoostClassifier #分类
from catboost import CatBoostRegressor #回归
这里介绍几个比较常用的参数,详细参数的解释,见catboost官网
learning_rate(eta)=automatically or 0.03
学习率,数值or automatically
。数值越小,训练所需的迭代次数就越高。depth(max_depth)=6
树的深度。可以试任何不大于32的int
。推荐值:1-10l2_leaf_reg(reg_lambda)=3
L2正则化系数。可以是任何正值。iterations=1000
: 解决ml问题的树的最大数量one_hot_max_size=2
: 对于所有具有多个不同值小于或等于给定参数值的特性,使用one-hot编码。loss_function=’Logloss’
:损失函数od_type =IncToDec
:过拟合检查类型.{IncToDec;Iter}od_wait=20
:在最小化损失函数后的迭代次数;leaf_estimation_method =None
:迭代求解的方法,{Newton;Gradient}thread_count=-1
:训练时所用的cpu/gpu核数used_ram_limit=None
:CTR问题,计算时的内存限制gpu_ram_part=None
:GPU内存限制task_type=CPU
:训练的器件devices=None
:训练的GPU设备IDtree_count_
:返回模型中的树的数量(int
)。feature_importances_
:输出计算出的特征重要性(numpy.ndarray
)。best_score_
:返回针对每个验证数据集计算的每个度量方法的最佳结果。{'validation_0': {'Logloss': 0.6085537606941837, 'AUC': 0.0568}}
fit(X: 输入数据数据类型可以是,[list,pandas.DataFrame]; pandas.Series
y:None 训练的目标值
cat_features=None [list,pandas.DataFrame]:将类型特征的列,否则catboost不会对类型特征进行处理。
sample_weight=None: 输入数据的样本权重
logging_level=None: 控制是否输出日志信息,或者何种信息
plot=False: 训练过程中,绘制,度量值,所用时间等
eval_set=None: 验证集合,数据类型list(X, y) tuples)
predict(data:输入需要预测的数据,
返回验证样本所属类别,数据类型为np.array)
predict_proba(返回验证样本所属类别的概率,数据类型为np.array)
get_feature_importance(data=None,
type=EFstrType.FeatureImportance,
prettified=False,
thread_count=-1,
verbose=False
计算并返回特征重要性)
利用Amazon的员工编号相关信息,来分析和预测当员工申请访问某个编号的资源时,是否被允许访问。
from sklearn.model_selection import train_test_split
import catboost
import catboost.datasets
import matplotlib.pyplot as plt
# 加载数据
train_df, test_df = catboost.datasets.amazon()
# 训练
y = train_df["ACTION"]
X = train_df.drop("ACTION", axis=1)
X_test = test_df
categorical_columns = X.columns
categorical_columns_indices = list(range(len(X.columns)))
X_train, x_test, y_train, y_test = train_test_split(X, y,
train_size=0.7, random_state=2045)
cb = catboost.CatBoostClassifier(n_estimators=4000,
learning_rate= 0.01,
one_hot_max_size=2,
loss_function='Logloss',
eval_metric='AUC',
boosting_type='Ordered',
random_seed=2405,
use_best_model=True,
silent=True)
cb.fit(X_train, y_train,cat_features=categorical_columns_indices,eval_set=(x_test,
y_test),plot=True)
# 显示重要特征
fea_ = cb.feature_importances_
fea_name = cb.feature_names_
plt.figure(figsize=(10, 10))
plt.barh(fea_name,fea_,height =0.5)
plt.show()
Catboost介绍
CatBoost:比XGBoost更优秀的GBDT算法
catboost 论文笔记
Mastering The New Generation of Gradient Boosting