CatBoost:天然支持类别特征的梯度提升

FreeGuideOnline 最新 2026-06-16

CatBoost 分类特征处理完全指南

CatBoost(Categorical Boosting)是 Yandex 开源的梯度提升决策树(GBDT)框架,它以天然支持类别特征而闻名。与其他 GBDT 库(如 XGBoost、LightGBM)需要手动进行数值编码不同,CatBoost 可以直接接收原始的字符串或整数形式的类别特征,并在训练过程中自动完成高效、防过拟合的特征编码。本教程将从零开始讲解 CatBoost 处理类别特征的原理、使用方法与调优技巧。

为什么类别特征处理如此重要

在结构化数据中,类别特征(例如城市名、商品品类、用户性别等)往往占据很大比例。传统机器学习算法要求输入必须是数值,因此我们需要将这些离散类别转换为数字。常见的转换方法有:

  • 标签编码(Label Encoding):将每个类别映射为一个整数。但会引入虚假的大小关系(例如“北京=1,上海=2”会让模型以为上海大于北京,通常不适合树模型,但适用于有序类别)。
  • 独热编码(One-Hot Encoding):为每个类别创建二元特征。当类别数量极多(高基数)时,特征维度爆炸,严重拖慢训练且容易过拟合。
  • 目标编码(Target Encoding):用目标变量的均值替换类别。但直接使用全局均值会导致严重的数据泄漏(用训练集的标签信息“偷看”该样本的标签),使模型在测试集上表现不佳。

CatBoost 解决的核心问题就是:如何在不造成目标泄漏的前提下,为类别特征生成有效的数值表示

CatBoost 类别特征编码原理

CatBoost 采用了一种改良的目标编码方法,配合独特的训练流程,从根本上杜绝了过拟合。其核心思想包含以下三个层面。

1. 有序目标统计(Ordered Target Statistics)

这是 CatBoost 实现无偏类别编码的关键。传统目标编码使用整个训练集的标签均值,CatBoost 则引入了一个“时间序列”般的概念——训练样本被随机排列,对于每一个样本,其类别编码值仅依赖于排列中它之前的那些样本,完全不使用当前样本及未来样本的标签。

具体计算公式(以回归任务为例):

对于第 k 个样本,其类别 v 的编码值为: [ \text{Encoded} = \frac{\sum_{j < k} [x_{j, v} = 1] \cdot y_j + a \cdot P}{n_{v, prior} + a} ]

其中:

  • (n_{v, prior}) 是排列中位于第 k 个样本之前、类别为 v 的样本数量。
  • (P) 是整个数据集中目标变量的平均值(先验)。
  • (a) 是先验的权重(平滑参数,大于 0)。
  • 分子是这些样本的目标值总和加上先验加权。

这种设计使得编码值不再泄漏该样本自身的标签信息,测试时则使用全部训练数据的统计量。推理阶段,CatBoost 会为每个类别存储其整体统计信息,保证预测的一致性。

2. 类别特征组合(Feature Combinations)

单一类别的编码有时无法捕捉多个类别之间的交互关系(例如“城市”和“设备品牌”的组合对购买概率有非线性影响)。CatBoost 在训练时采用贪心策略,将当前树已经使用过的分裂特征与所有类别特征进行组合,生成新的组合类别特征,再对这些组合特征计算有序目标统计。

例如原始特征为 citybrand,在某一棵树中 city 被选为分裂节点后,下一棵树的候选特征就可能包含 city_brand_combination,即 city='北京' AND brand='A' 这样的组合。此操作由超参数 max_ctr_complexity 控制,越高则组合越复杂,模型可能更强但也更慢、更容易过拟合。

3. 对称树与非对称树的类别处理

CatBoost 默认使用对称树(Oblivious Decision Tree),每一层所有节点都使用相同的分裂条件和特征。在对称树中,类别特征的编码值在树的同一层中会被重复使用,这天然抑制了过拟合。如果设置 grow_policy=Lossguide 改用非对称树,类别特征的处理逻辑与默认模式一致,但组合特征的数量会更高,需要谨慎调整 max_ctr_complexity

在 CatBoost 中使用分类特征(代码实战)

我们通过一个完整的分类任务示例,演示如何让 CatBoost 自动识别并处理类别特征。

安装与数据准备

pip install catboost

假设有一个银行客户流失预测数据集,包含以下列: RowNumber, CustomerId, Surname, CreditScore, Geography, Gender, Age, Tenure, Balance, ...

import pandas as pd
from catboost import CatBoostClassifier, Pool
from sklearn.model_selection import train_test_split

# 读取数据
data = pd.read_csv('Churn_Modelling.csv')

# 无关特征和无用列
drop_cols = ['RowNumber', 'CustomerId', 'Surname']
X = data.drop(columns=drop_cols + ['Exited'])
y = data['Exited']

# 划分训练/测试集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

指定类别特征列

CatBoost 提供了三种方式告诉模型哪些列是类别特征:

  1. Pool 构造函数中指定(推荐)

    cat_features = ['Geography', 'Gender']  # 也可以是列索引列表
    train_pool = Pool(X_train, y_train, cat_features=cat_features)
    test_pool = Pool(X_test, y_test, cat_features=cat_features)
    
  2. 直接传递给模型的 fit 方法

    model = CatBoostClassifier(iterations=500, learning_rate=0.1, depth=6, verbose=100)
    model.fit(X_train, y_train, cat_features=cat_features, eval_set=(X_test, y_test))
    
  3. 在 Pandas DataFrame 中将类别列声明为 category 类型

    X_train['Geography'] = X_train['Geography'].astype('category')
    X_train['Gender'] = X_train['Gender'].astype('category')
    # CatBoost 会自动检测 category 列,无需额外声明
    model.fit(X_train, y_train, eval_set=(X_test, y_test))
    

如果没有指定任何类别特征,CatBoost 会把所有非数值列(object 类型)自动视为类别特征,但整数类型的类别特征不会被自动识别,需要手动声明。

训练并查看模型

model = CatBoostClassifier(
    iterations=500,
    learning_rate=0.05,
    depth=6,
    loss_function='Logloss',
    eval_metric='AUC',
    random_seed=42,
    verbose=100
)

model.fit(
    train_pool,
    eval_set=test_pool,
    plot=True         # 训练后显示评估曲线
)

# 预测
probs = model.predict_proba(test_pool)[:, 1]

训练过程中,控制台会实时打印迭代指标,同时因为 plot=True 会弹出评估曲线图,方便监控是否过拟合。

CatBoost 类别特征的高级选项

为了让模型既能充分学习类别信息,又不会过分复杂而泛化能力下降,CatBoost 提供了多个关键参数用于微调类别特征的处理行为。

控制有序目标统计的平滑

  • has_time:布尔值,默认为 False。当数据本身带有时间顺序(例如按时间排序的用户行为)时,应设置为 True。此时 CatBoost 将按照数据行出现的原始顺序进行有序目标统计,而不是随机排列。这对时间序列预测非常重要。
  • random_strength:目标统计中加入的随机噪声系数(≥0),用于减轻编码值对具体样本的过拟合。值越大,编码越保守。默认值为 1。

类别特征组合与 CTR 复杂度

  • max_ctr_complexity:允许的最大组合特征数量,代表“组合后类别基数”的上限。默认为 4。对于高基数类别,设置过大可能导致组合数爆炸内存溢出,设置过小可能漏掉重要交互。
  • one_hot_max_size:当某个类别特征的唯一值数量小于等于此阈值时,CatBoost 会直接使用独热编码(One-Hot)而非目标编码。这是因为它认为低基数类别采用独热编码更稳定且计算高效。默认值为 2(即类别数 ≤2 直接独热编码),可适当调大,例如设为 10。
  • ctr_leaf_count_limit:控制用于计算目标统计的最小样本数下限。防止对罕见类别使用过于极端的编码值,具有正则化效果。

示例:针对高基数类别的参数调整

假设 Geography 有 150 种不同取值,Gender 只有 2 种。可以这样配置:

model = CatBoostClassifier(
    iterations=1000,
    learning_rate=0.03,
    depth=8,
    max_ctr_complexity=3,        # 限制组合复杂度
    one_hot_max_size=10,         # <10个类别的特征直接用独热编码
    random_strength=1.5,         # 增加噪声
    ctr_leaf_count_limit=5,      # 至少5个样本才可靠
    cat_features=cat_features
)

处理缺失值与未知类别

类别特征中出现缺失值(NaN)或测试集包含训练集中未出现过的类别,是实际应用中的常见问题。

  • 训练时的缺失值:CatBoost 会将缺失值(NaN 或空字符串)视为一个独立的类别,参与目标统计编码。无需额外填充。
  • 测试时出现全新类别:CatBoost 会使用先验值 P(整个训练集的目标均值)来编码这个未知类别。这种行为保证了预测的稳定,不会抛错或中断。

你还可以通过 ignored_features 参数临时忽略某些类别特征参与训练,或使用 text_features 结合文本处理功能输入原始文字(CatBoost 从版本 0.19 开始支持文本特征)。

最佳实践与常见误区

  1. 始终正确划分类别特征
    不要将整数型类别特征遗漏,否则 CatBoost 会将其当作连续值处理,丧失类别信息。使用 cat_features 显式声明是安全做法。

  2. 优先使用 Pool 对象
    在数据量较大时,先将数据转换为 Pool 并传入 fit,会比通过 fit 参数指定 cat_features 具有更快的内部处理速度。

  3. 避免对类别特征进行手动编码
    如果已经用标签编码或目标编码将类别转成数值再输入 CatBoost,模型会失去“感知类别”的能力,无法按需进行有序目标统计和组合,效果可能反而不如原始字符串输入。

  4. 关注 one_hot_max_size
    适当地扩大独热编码阈值(如设为 10~20)可以加速低基数类别的算力消耗,同时不影响高基数类别的目标编码。这对数据中有很多二值或少量取值的特征非常有益。

  5. 交叉验证时小心目标泄漏
    即使 CatBoost 内部防止了泄漏,如果你在预处理阶段自行计算了全局目标统计,请务必使用交叉验证进行编码,否则仍会造成泄漏。最好的方法就是把原始类别列直接交给 CatBoost。

  6. 超参数调优顺序
    先固定类别相关参数(one_hot_max_sizemax_ctr_complexity 等)的值,调节 depthlearning_rateiterations 等常规参数;最后再微调 random_strengthctr_leaf_count_limit,避免调参空间过大。

总结

CatBoost 的 有序目标统计自动类别特征组合 机制使其在处理高基数、多交互的类别数据时具有天然优势。开发者几乎无需手动编码或担心目标泄漏,只需通过简洁的 API 指明哪一列是类别特征,就能获得强大的建模能力。掌握类别超参数的调节技巧后,CatBoost 可在工业级表格数据竞赛和业务预测中稳定占据领先位置。


本教程为“免费在线教程”原创内容,欢迎收藏分享。如需深入学习 CatBoost 回归、多分类或 GPU 加速,请继续关注我们的后续课程。