30 分钟看懂 CatBoost(Python代码)
Posted Python数据科学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了30 分钟看懂 CatBoost(Python代码)相关的知识,希望对你有一定的参考价值。
编码方法,也是CatBoost最重要的创新。
2、基于贪心策略的特征交叉方法
使用Ordered Target Statistics 方法将类别特征转化成为数值特征以后,会影响到特征交叉,因为数值特征无法有效地进行交叉。
依然以风控领域的预测信贷用户是否会违约为例,假设 city="北京市" 且 job="保安" 的用户信用特别好,但不是北京市所有的用户都信用好,也不是所有的保安都信用特别好。只有北京市的保安这个群体才信用好。
如果我们将 city转换为数值编码,也将保安转换为数值编码之后,我们得到两个数,这两个数相乘是没有意义的,我们无法表示 北京市的保安这个群体。
为了有效地利用特征交叉,CatBoost 在将类别特征转换为数值编码的同时,会自动生成 交叉特征。
如果让全部的类别特征之间都进行交叉,两两交叉,三三交叉,四四交叉,这个复杂度是指数级的,特征维度一定会爆炸。
CatBoost使用一种贪心的策略来进行特征交叉。生成tree的第一次分裂,CatBoost不使用任何交叉特征。在后面的分裂中,CatBoost会使用生成tree所用到的全部原始特征和交叉特征 跟 数据集中的全部 类别特征进行交叉。
在定义CatBoost模型时,我们可以用\'max_ctr_complexity\' 来控制允许的特征交叉的最大特征数量,如果设置为3,那么生成tree时所用到的交叉特征最多只会来自3个特征的交叉,也就是我们只能表示 city=\'北京市\' 且 job=\'保安\' 且 education=\'高中\'这样的三阶交叉特征,而无法表示 city=\'北京市\' 且 job=\'保安\' 且 education=\'高中\' 且 hobby=\'抽烟\' 这样的四阶交叉特征。
3、避免预测偏移的 Ordered Boosting 方法。
使用XGBoost或者LightGBM做模型时,我们可能经常会发现模型在训练集上拟合的很好,train_auc甚至达到了1.0, 但是在验证集上却差了很多, va_auc 可能只有0.7。这当然有可能是因为tree的数量太多了,或者是每棵tree的leaves太多了,总之模型太复杂了造成了过拟合。
但也有一些XGBoost和LightGBM自身算法的缺陷因素。我们知道LightGBM在训练下一棵tree的时候,需要计算前面这些tree构成的加法模型在所有样本上的一阶梯度和二阶梯度(Loss对模型预测结果的导数),然后用这些梯度来决定下一棵树的结构和叶子节点取值。
但是我们计算的这些一阶梯度和二阶梯度值是问题的。前面的这些tree都是在这些样本上训练的,现在我们又在这些样本上估计模型预测结果的一阶和二阶梯度。我们应该换一些新的样本才更合理。但是我们从哪里找这些新的样本呢?
CatBoost 的作者故伎重演。先将样本随机打乱,然后每个样本只使用排序在它前面的样本来训练模型。用这样的模型来估计这个样本预测结果的一阶和二阶梯度。然后用这些梯度构建一棵tree的结构,最终tree的每个叶子节点的取值,是使用全体样本进行计算的。
这就是Ordered Boosting的主要思想。可以有效地减少梯度估计的误差,缓解预测偏移。但是会增加较多的计算量,影响训练速度。
在定义CatBoost模型时,我们可以用\'boosting_type\'这个参数来设置是使用Ordered Boosting 还是 LightGBM那样的 Plain Boosting。如果不显式设置,CatBoost会根据样本和特征数量自己决定。
4、使用对称二叉树作为基模型,有正则作用且预测极快
XGBoost和LightGBM采用的基模型是普通的二叉树,但是CatBoost采用的是对称的二叉树。
这种对树结构上的约束有一定的正则作用。更为重要的是,它可以让CatBoost模型的推断过程极快。
对于CatBoost的tree的预测过程来说,每个特征的分裂都是独立的,不分先后顺序,多个样本可以一起预测。
三、使用范例#!pip install catboost
import catboost as cb
print(cb.__version__)
1.0.4
from IPython.display import display
import datetime,json
import numpy as np
import pandas as pd
import catboost as cb
from catboost.datasets import titanic
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import f1_score,roc_auc_score,accuracy_score
import plotly.graph_objs as go
import plotly.express as px
def printlog(info):
nowtime = datetime.datetime.now().strftime(\'%Y-%m-%d %H:%M:%S\')
print("\\n"+"=========="*8 + "%s"%nowtime)
print(info+\'...\\n\\n\')
#================================================================================
# 一,准备数据
#================================================================================
printlog("step1: preparing data...")
dfdata,dftest = titanic()
display(dfdata.head())
label_col = "Survived"
# 填充空值特征
dfnull = pd.DataFrame(dfdata.isnull().sum(axis=0),columns = ["null_cnt"]).query("null_cnt>0")
print("null_features:")
print(dfnull)
dfdata.fillna(-9999, inplace=True)
dftest.fillna(-9999, inplace=True)
# 刷选类别特征
cate_cols = [x for x in dfdata.columns
if dfdata[x].dtype not in [np.float32,np.float64] and x!=label_col]
for col in cate_cols:
dfdata[col] = pd.Categorical(dfdata[col])
dftest[col] = pd.Categorical(dftest[col])
# 分割数据集
dftrain,dfvalid = train_test_split(dfdata, train_size=0.75, random_state=42)
Xtrain,Ytrain = dftrain.drop(label_col,axis = 1),dftrain[label_col]
Xvalid,Yvalid = dfvalid.drop(label_col,axis = 1),dfvalid[label_col]
cate_cols_indexs = np.where(Xtrain.columns.isin(cate_cols))[0]
# 整理成Pool
pool_train = cb.Pool(data = Xtrain, label = Ytrain, cat_features=cate_cols)
pool_valid = cb.Pool(data = Xvalid, label = Yvalid, cat_features=cate_cols)
#================================================================================
# 二,设置参数
#================================================================================
printlog("step2: setting parameters...")
iterations = 1000
early_stopping_rounds = 200
params =
\'learning_rate\': 0.05,
\'loss_function\': "Logloss",
\'eval_metric\': "Accuracy",
\'depth\': 6,
\'min_data_in_leaf\': 20,
\'random_seed\': 42,
\'logging_level\': \'Silent\',
\'use_best_model\': True,
\'one_hot_max_size\': 5, #类别数量多于此数将使用ordered target statistics编码方法,默认值为2。
\'boosting_type\':"Ordered", #Ordered 或者Plain,数据量较少时建议使用Ordered,训练更慢但能够缓解梯度估计偏差。
\'max_ctr_complexity\': 2, #特征组合的最大特征数量,设置为1取消特征组合,设置为2只做两个特征的组合,默认为4。
\'nan_mode\': \'Min\'
#================================================================================
# 三,训练模型
#================================================================================
printlog("step3: training model...")
model = cb.CatBoostClassifier(
iterations = iterations,
early_stopping_rounds = early_stopping_rounds,
train_dir=\'catboost_info/\',
**params
)
#直接训练
model.fit(
pool_train,
eval_set=pool_valid,
plot=True
)
print("model.get_all_params():")
print(model.get_all_params() )
#5折交叉验证
cv_data= cb.cv(
cb.Pool(dfdata.drop(label_col,axis = 1), dfdata[label_col], cat_features=cate_cols_indexs),
params,
fold_count = 3,
plot=True
)
print(\'Best validation accuracy score: :.2f±:.2f on step \'.format(
np.max(cv_data[\'test-Accuracy-mean\']),
cv_data[\'test-Accuracy-std\'][np.argmax(cv_data[\'test-Accuracy-mean\'])],
np.argmax(cv_data[\'test-Accuracy-mean\'])
))
#================================================================================
# 四,评估模型
#================================================================================
printlog("step4: evaluating model ...")
y_pred_train = model.predict(Xtrain)
y_pred_valid = model.predict(Xvalid)
train_score = f1_score(Ytrain,y_pred_train)
valid_score = f1_score(Yvalid,y_pred_valid)
print(\'train f1_score: :.5 \'.format(train_score))
print(\'valid f1_score: :.5 \\n\'.format(valid_score))
#feature importance
dfimportance = model.get_feature_importance(prettified=True)
dfimportance = dfimportance.sort_values(by = "Importances").iloc[-20:]
fig_importance = px.bar(dfimportance,x="Importances",y="Feature Id",title="Feature Importance")
display(dfimportance)
display(fig_importance)
#score distribution
y_test_prob = model.predict_proba(dftest)[:,-1]
trace1 = go.Histogram(x = y_test_prob,histnorm = \'probability\',nbinsx=50)
layout = go.Layout(title = "Score Distribution",xaxis="title":"score",yaxis = "title":"frequecy")
fig_distribution = go.Figure(data = [trace1])
fig_distribution.update_layout(layout)
display(fig_distribution)
#================================================================================
# 五,使用模型
#================================================================================
printlog("step5: using model ...")
y_pred_test = model.predict(dftest)
y_pred_test_prob = model.predict_proba(dftest)
print("y_pred_test:\\n",y_pred_test[:10])
print("y_pred_test_prob:\\n",y_pred_test_prob[:10])
#================================================================================
# 六,保存模型
#================================================================================
printlog("step6: saving model ...")
model_dir = \'catboost_model\'
model.save_model(model_dir)
model_loaded = cb.CatBoostClassifier()
model.load_model(model_dir)
catboost原理以及Python代码
原论文:
http://learningsys.org/nips17/assets/papers/paper_11.pdf
catboost原理:
One-hot编码可以在预处理阶段或在训练期间完成。后者对于训练时间而言能更有效地执行,并在Catboost中执行。
类别特征:
为了减少过拟合以及使用整个数据集进行训练,Catboost使用更有效的策略。
1、对输入的观察值的集合进行随机排列,生成多个随机排列;
2、给定一个序列,对于每个例子,对于相同类别的例子我们计算平均样本值;
3、使用如下公式将所有的分类特征值转换为数值:
让,那么可以代替为
在这里,我们还增加了先验值P和参数a>0,即为先验的权重。添加先验是一种常见的做法,它有助于减少从低频类别获得的噪声。
特征组合:
在数据集中,组合的数量随类别特征个数成指数型增长,在算法中不太可能考虑所有。在当前树考虑新的拆分时,Catboost以贪婪的方式考虑组合。
1、 第一次分裂不考虑任何组合在树上;
2、 对于下一次分类,在有所有类别特征的数据集的当前树,Catboost包含了所有的组合和分类特征。组合值即被转换为数字;
3、 Catboost还以以下方式生成数值和类别特征的组合:在树中选择的所有分裂视为具有两个值的类别,并在组合中也类似使用。
python代码:
import catboost
model = CatBoostClassifier(iterations=17000,
# depth = 6,
learning_rate = 0.03,
custom_loss=\'AUC\',
eval_metric=\'AUC\',
bagging_temperature=0.83,
od_type=\'Iter\',
rsm = 0.78,
od_wait=150,
metric_period = 400,
l2_leaf_reg = 5,
thread_count = 20,
random_seed = 967
)
model.fit(tr_x, tr_y, eval_set=(te_x, te_y),use_best_model=True)
pre= model.predict_proba(te_x)[:,1].reshape((te_x.shape[0],1))
train[test_index]=pre
test_pre[i, :]= model.predict_proba(test_x)[:,1].reshape((test_x.shape[0],1))
print (roc_auc_score(te_y, pre))
cv_scores.append(roc_auc_score(te_y, pre))
以上是关于30 分钟看懂 CatBoost(Python代码)的主要内容,如果未能解决你的问题,请参考以下文章
Python实现GWO智能灰狼优化算法优化Catboost分类模型(CatBoostClassifier算法)项目实战