第 9 章 · 04 LightGBM 与 CatBoost 交易信号 本节摘要:本节聚焦两大工业级梯度提升实现——微软的 LightGBM 与 Yandex 的 CatBoost。sklearn 的 GBM 在大数据上太慢,这两大库用各自的工程创新把训练速度提到一个新的量级,并在类别特征处理上各有绝活。本节讲清四件事:LightGBM 的「叶子生长 + 直方图加速 + GOSS 采样」如何让它在千万级样本上飞起来;CatBoost 的「有序目标编码 + 对称树」如何天然处理类别特征并抑制目标泄漏;如何用这两大库生成多前瞻期(1/5/21 日)的交易信号,并用 Spearman IC 与 Alphalens 评估信号质量;以及二者相对 sklearn GBM 的实战差异。
本节摘要:本节聚焦两大工业级梯度提升实现——微软的 LightGBM 与 Yandex 的 CatBoost。sklearn 的 GBM 在大数据上太慢,这两大库用各自的工程创新把训练速度提到一个新的量级,并在类别特征处理上各有绝活。本节讲清四件事:LightGBM 的「叶子生长 + 直方图加速 + GOSS 采样」如何让它在千万级样本上飞起来;CatBoost 的「有序目标编码 + 对称树」如何天然处理类别特征并抑制目标泄漏;如何用这两大库生成多前瞻期(1/5/21 日)的交易信号,并用 Spearman IC 与 Alphalens 评估信号质量;以及二者相对 sklearn GBM 的实战差异。读完本节,你能选定适合自己数据的 GBM 库,并跑出可对比的多档信号。
内容来源:原项目
12_gradient_boosting_machines/05_trading_signals_with_lightgbm_and_catboost.ipynb,汉化并套用体系化模板。
⚠️ 学习提示:目标编码(把类别映射为目标均值)是经典的泄漏陷阱——必须在交叉验证折叠内做编码,否则训练时就偷看了测试段的标签。CatBoost 的有序编码正是为解决这个而设计的。
阅读完本节,你应当能够:
sklearn 的 GradientBoostingClassifier 算法正确但工程落后:它每次分裂要遍历全部样本全部特征找最优切点,样本上一旦破百万就慢得难以接受。XGBoost、LightGBM、CatBoost 三大库各自用工程创新解决了这个瓶颈:
| 库 | 出品方 | 核心创新 | 强项 |
|---|---|---|---|
| XGBoost | 华盛顿大学(2014) | 系统级优化、正则化目标 | 通用性强 |
| LightGBM | 微软(2017) | 叶子生长 + 直方图 + GOSS | 速度快、内存省 |
| CatBoost | Yandex(2017) | 有序目标编码 + 对称树 | 类别特征强、不易过拟合 |
本节聚焦后两个,因为本书的实战 notebook 用它们生成交易信号。
传统 GBM(包括 sklearn)按层生长——同一层的所有节点都分裂完,再进入下一层。LightGBM 改为按叶子生长——每次选当前收益最大的叶子继续分裂,类似最佳优先搜索。
叶子生长能更快降低损失(每步都选最优),但容易长出深的不平衡树,在小数据上可能过拟合——LightGBM 用 num_leaves 与 max_depth 联合限制。在金融大数据上,它的速度优势极为明显。
把连续特征离散化成 b 个桶(典型 b=255),分裂搜索从「遍历所有样本」变成「遍历所有桶」——复杂度从 O(n) 降到 O(b),且 b \ll n。更进一步,直方图做差加速:父节点直方图减去一个子节点的直方图,直接得到另一个子节点的直方图,省一半计算。再配合 GOSS(Gradient-based One-Side Sampling,按梯度大小对样本采样)和 EFB(Exclusive Feature Bundling,互斥特征捆绑),LightGBM 在千万级样本上仍能秒级完成一轮训练。
金融数据里「行业」「月份」「市值桶」都是类别特征。传统做法是 one-hot,但类别多时维度爆炸。目标编码(target encoding,把类别映射为该类别的目标均值)更紧凑,但有致命陷阱:如果在全样本上算编码,训练时就偷看了所有样本的标签,严重的目标泄漏。
CatBoost 的解法是「有序」:对第 i 个样本,它类别特征的编码只用排在它前面的样本算——类似在线学习,每个样本看到的「历史」都不同。这从机制上杜绝了未来信息泄漏。
CatBoost 还用对称树:同一层所有节点用同一个切分条件。这种结构规则,评估速度快,且对噪声更鲁棒,在中小数据集上往往比 LightGBM 更不易过拟合。
| 维度 | LightGBM | CatBoost |
|---|---|---|
| 生长策略 | leaf-wise | level-wise 对称 |
| 类别特征 | 原生支持(_split by category) | 有序目标编码(无泄漏) |
| 速度 | 大数据极快 | 中小数据稳健 |
| 过拟合风险 | 大数据低、小数据较高 | 整体偏低 |
| 典型用法 | 大量样本 + 数值特征 | 类别特征多 + 中等样本 |
notebook 读取第 4 章构造的 model_data,划分多个前瞻标签:
data = pd.read_hdf('data.h5', 'model_data').sort_index().loc[idx[:, :'2016'], :] labels = sorted(data.filter(like='_fwd').columns) # 1d/5d/21d 前瞻收益 features = data.columns.difference(labels).tolist() lookaheads = [1, 5, 21] categoricals = ['year', 'month', 'sector', 'weekday']
lookaheads = [1, 5, 21] 表示同时对 1 日、5 日、21 日(约一个月)前瞻收益建模——不同持有期的信号特征差异很大,1 日信号高频高换手,21 日信号低频更稳。
⚠️ lookahead 与 leak-ahead 不要混淆:这里的
lookahead是「预测多远的未来」,是合法的设计参数;leak-ahead(前视偏差)是「训练时偷看了未来标签」,是 bug。两者英文相近但含义相反。
import lightgbm as lgb from scipy.stats import spearmanr for lookahead in lookaheads: label = f'r{lookahead:02}_fwd' for fold, (train_idx, test_idx) in enumerate(cv.split(X, y)): train_set = lgb.Dataset(X.loc[train_idx], y.loc[train_idx, label], categorical_feature=categoricals) model = lgb.train(params, train_set) y_pred = model.predict(X.loc[test_idx]) ic = spearmanr(y.loc[test_idx, label], y_pred).correlation
用 Spearman 秩相关(而非 Pearson)做 IC 评估,因为秩相关对异常值鲁棒,且只看「预测排名是否对」——这正是多空选股关心的。
from catboost import Pool, CatBoostRegressor pool = Pool(X, y, cat_features=categoricals) model = CatBoostRegressor(iterations=1000, learning_rate=0.05, depth=6) model.fit(train_pool, eval_set=test_pool, early_stopping_rounds=50)
cat_features 直接传类别列名,CatBoost 内部做有序编码,你不用手动 one-hot——这是它最省心的地方。early_stopping_rounds 是早停,验证集 50 轮无改善即停。
信号预测出来后,在跑 Zipline 回测之前先用 Alphalens 评估:
from alphalens.utils import get_clean_factor_and_forward_returns from alphalens.tears import create_summary_tear_sheet factor_data = get_clean_factor_and_forward_returns(factor, prices, periods=(1,5,21)) create_summary_tear_sheet(factor_data)
重点看三件事:
| 指标 | 含义 | 期望 |
|---|---|---|
| 分位组单调性 | 5 档分位组的累计收益是否单调 | 单调 → 信号有方向性 |
| IC 衰减 | IC 随持有期的变化 | 短期 IC 高 → 适合短持有期 |
| 换手率 | 信号变化的频繁程度 | 过高 → 交易成本吃掉利润 |
这一步能把「IC 看起来不错但换手过高」的伪信号在回测前识别出来,节省大量调试时间。
下一节,我们看 SHAP 模型解释——用博弈论的方法把「黑盒树模型的预测」拆解成每个特征的贡献,看模型到底学到了什么。