Python 机器学习算法交易实用指南(三)(4)https://developer.aliyun.com/article/1523357
二阶损失函数近似
最重要的算法创新通过使用依赖于二阶导数的近似来降低评估损失函数的成本,类似于牛顿法找到稳定点。因此,在贪婪树扩展过程中评分潜在分割的速度相对于使用完整损失函数更快。
正如先前提到的,梯度提升模型是以增量方式训练的,其目标是最小化整体 H[M] 的预测误差和正则化惩罚的组合。用 m 步后集成对结果 y[i] 的预测表示为 ŷ[i]^((m)),l 是一个可微的凸损失函数,用来衡量结果和预测之间的差异,Ω 是一个随着整体 H[M] 复杂性增加而增加的惩罚项,增量假设 h[m] 的目标是最小化以下目标函数:
正则化惩罚有助于通过偏好选择使用简单且有预测性的回归树的模型来避免过拟合。例如,在 XGBoost 的情况下,回归树 h 的惩罚取决于每棵树的叶子数 T,每个终端节点的回归树得分 w,以及超参数 γ 和 λ。这在以下公式中总结如下:
因此,在每一步,算法都会贪婪地添加最能改进正则化目标的假设 h[m]。基于泰勒展开的损失函数的二阶近似加速了目标函数的评估,如以下公式所总结:
这里,g[i] 是添加新的学习器前给定特征值的损失函数的一阶梯度,而 h[i] 是相应的二阶梯度(或者海森矩阵)值,如下式所示:
XGBoost 算法是第一个利用损失函数的这种近似来计算给定树结构的最优叶子分数和相应损失函数值的开源算法。分数由终端节点中样本的梯度和 Hessian 的和的比率组成。它使用此值来评分分裂所导致的信息增益,类似于我们在前一章中看到的节点不纯度度量,但适用于任意损失函数(详细推导请参阅 GitHub 上的参考文献)。
简化的分裂查找算法
sklearn 的梯度提升实现找到枚举连续特征所有选项的最优拆分。这种精确的贪婪算法在计算上非常耗费资源,因为它必须首先按特征值对数据进行排序,然后对潜在的非常大数量的拆分选项进行评分和决策。当数据不适合内存或在多台机器上的分布式设置中进行训练时,此方法会面临挑战。
一种近似的分裂查找算法通过将特征值分配给用户确定的一组箱子来减少分裂点的数量,这也可以在训练期间大大减少内存需求,因为每个箱子只需要存储一个分裂。XGBoost 引入了一种分位数草图算法,也能将加权训练样本分成百分位箱子,以实现均匀分布。XGBoost 还引入了处理稀疏数据的能力,这些数据由缺失值、频繁的零梯度统计和一位有效编码引起,并且还可以为给定的分裂学习一个最优的默认方向。因此,该算法只需评估非缺失值。
相比之下,LightGBM 使用基于梯度的单侧采样(GOSS)来排除具有小梯度的大部分样本,并且仅使用其余部分来估算信息增益,并相应地选择分裂值。具有较大梯度的样本需要更多的训练,并且倾向于更多地对信息增益做出贡献。LightGBM 还使用排他性特征绑定来组合那些彼此互斥的特征,它们很少同时取非零值,以减少特征数量。因此,LightGBM 在发布时是最快的实现。
深度优先与叶子优先增长
LightGBM 与 XGBoost 和 CatBoost 在优先拆分哪些节点方面有所不同。LightGBM 以叶子方式决定分裂,即拆分最大化信息增益的叶子节点,即使这会导致不平衡树。相比之下,XGBoost 和 CatBoost 以深度方式扩展所有节点,并在添加更多级别之前首先拆分给定深度的所有节点。这两种方法以不同的顺序扩展节点,除了完整的树之外,它们将产生不同的结果。下图说明了这两种方法:
LightGBM 的叶子优先分割往往增加模型复杂性,并且可能加快收敛速度,但也增加了过拟合的风险。一个深度为 n 的以节点为优先的树最多有 2^(n )个叶子节点,而一个以叶子为优先的树具有 2^n 个叶子,可能会有更多的层次,并且某些叶子节点中包含相对较少的样本。因此,调整 LightGBM 的 num_leaves
设置需要额外的注意,库也允许我们同时控制 max_depth
以避免不必要的节点不平衡。LightGBM 的更近期版本还提供了深度为优先的树增长。
基于 GPU 的训练
所有新的实现支持在一个或多个 GPU 上进行训练和预测,以实现显著的加速。它们与当前的 CUDA 启用的 GPU 兼容。安装要求各不相同,并且正在快速发展。XGBoost 和 CatBoost 的实现适用于几个当前版本,但是 LightGBM 可能需要本地编译(请参阅 GitHub 获取相关文档链接)。
加速取决于库和数据类型,并且范围从低的单位数字倍增到数十倍的因子。仅需更改任务参数并且不需要其他超参数修改就能激活 GPU。
DART – 树的 dropout
2015 年,Rashmi 和 Gilad-Bachrach 提出了一个新模型来训练梯度提升树,旨在解决他们称之为过度专业化的问题:在后续迭代中添加的树往往只影响少数实例的预测,而对于其余实例的贡献较小。然而,该模型的样本外性能可能会受到影响,并且可能对先前在过程中添加的少数树的贡献过于敏感。
新的算法采用了 dropouts,成功地用于学习更准确的深度神经网络,其中 dropouts 在学习过程中静音了部分神经连接。因此,高层节点不能依赖少数连接来传递预测所需的信息。这种方法对于深度神经网络的成功做出了重要贡献,也已与其他学习技术一起使用,例如逻辑回归,以静音特征的随机份额。随机森林和随机梯度提升也会静音随机特征子集。
DART 在树的层次上运作,并且对整棵树进行静音,而不是对个别特征进行操作。使用 DART 生成的集成树的目标是更均匀地对最终预测作出贡献。在某些情况下,这已被证明对排名、回归和分类任务产生更准确的预测。该方法首先在 LightGBM 中实现,并且也适用于 XGBoost。
分类特征的处理
CatBoost 和 LightGBM 实现直接处理分类变量,无需虚拟编码。
CatBoost 的实现(其命名源自对分类特征的处理)包括处理这些特征的几个选项,除了自动独热编码外,还为几个特征的单独分类或多个特征组合分配数字值。换句话说,CatBoost 可以从现有特征的组合中创建新的分类特征。与个别特征或特征组合的分类水平相关的数字值取决于它们与结果值的关系。在分类情况下,这与观察正类别的概率有关,该概率在样本上累积计算,基于先验和平滑系数。有关更详细的数值示例,请参阅文档。
LightGBM 的实现将分类特征的水平分组以最大化(或最小化)与结果值相对于组内的均匀性。
XGBoost 的实现不直接处理分类特征,需要进行独热(或虚拟)编码。
其他功能和优化
XGBoost 通过在内存中保留压缩的列块来优化计算,在几个方面使计算多线程化,其中每个列都按相应特征值排序。XGBoost 在训练前仅计算一次此输入数据布局,并在整个过程中重复使用它以摊销额外的前期成本。使用可以并行完成的分位数时,对列的拆分统计信息的搜索变为线性扫描,并且易于支持列子抽样。
后来发布的 LightGBM 和 CatBoost 库构建在这些创新之上,并通过优化线程和减少内存使用量进一步加速了训练。由于它们的开源性质,这些库随着时间的推移往往会趋于一致。
XGBoost 还支持单调性约束。这些约束确保给定特征的值仅在其整个范围内与结果呈正相关或负相关。它们对于合并已知为真的模型的外部假设非常有用。
如何使用 XGBoost、LightGBM 和 CatBoost
XGBoost、LightGBM 和 CatBoost 提供多种语言的接口,包括 Python,并且具有与其他 sklearn 特性兼容的 sklearn 接口,例如 GridSearchCV
,以及它们自己的方法来训练和预测梯度提升模型。gbm_baseline.ipynb
笔记本展示了每个实现的 sklearn 接口的使用。这些库方法通常文档更好,而且也更容易使用,因此我们将使用它们来说明这些模型的使用。
该过程涉及创建特定于库的数据格式,调整各种超参数以及评估我们将在接下来的章节中描述的结果。 附带的笔记本包含gbm_tuning.py
,gbm_utils.py
和gbm_params.py
文件,共同提供以下功能,并生成相应的结果。
如何创建二进制数据格式
所有库都有自己的数据格式,用于预先计算特征统计信息以加速搜索分割点,如前所述。 这些也可以持久化以加速后续训练的开始。
以下代码为要与OneStepTimeSeriesSplit
一起使用的每个模型构造了二进制训练和验证数据集:
cat_cols = ['year', 'month', 'age', 'msize', 'sector'] data = {} for fold, (train_idx, test_idx) in enumerate(kfold.split(features)): print(fold, end=' ', flush=True) if model == 'xgboost': data[fold] = {'train': xgb.DMatrix(label=target.iloc[train_idx], data=features.iloc[train_idx], nthread=-1), # use avail. threads 'valid': xgb.DMatrix(label=target.iloc[test_idx], data=features.iloc[test_idx], nthread=-1)} elif model == 'lightgbm': train = lgb.Dataset(label=target.iloc[train_idx], data=features.iloc[train_idx], categorical_feature=cat_cols, free_raw_data=False) # align validation set histograms with training set valid = train.create_valid(label=target.iloc[test_idx], data=features.iloc[test_idx]) data[fold] = {'train': train.construct(), 'valid': valid.construct()} elif model == 'catboost': # get categorical feature indices cat_cols_idx = [features.columns.get_loc(c) for c in cat_cols] data[fold] = {'train': Pool(label=target.iloc[train_idx], data=features.iloc[train_idx], cat_features=cat_cols_idx), 'valid': Pool(label=target.iloc[test_idx], data=features.iloc[test_idx], cat_features=cat_cols_idx)}
可用选项略有不同:
xgboost
允许使用所有可用线程lightgbm
明确地将为验证集创建的分位数与训练集对齐catboost
实现需要使用索引而不是标签来识别特征列
如何调整超参数
许多超参数在gbm_params.py
中列出。 每个库都有参数设置来:
- 指定总体目标和学习算法
- 设计基学习器
- 应用各种正则化技术
- 在训练过程中处理提前停止
- 启用 GPU 或在 CPU 上进行并行化
每个库的文档详细介绍了可能引用相同概念的各种参数,但是在库之间具有不同名称的参数。 GitHub 存储库包含指向突出显示xgboost
和lightgbm
相应参数的网站的链接。
目标和损失函数
这些库支持几种提升算法,包括树的梯度提升和线性基学习器,以及 LightGBM 和 XGBoost 的 DART。 LightGBM 还支持我们之前描述的 GOSS 算法,以及随机森林。
梯度提升的吸引力在于有效支持任意可微损失函数,每个库都提供了用于回归,分类和排名任务的各种选项。 除了选择的损失函数之外,在训练和交叉验证期间还可以使用其他评估指标来监视性能。
学习参数
梯度提升模型通常使用决策树来捕获特征交互,个体树的大小是最重要的调整参数。 XGBoost 和 CatBoost 将max_depth
默认设置为 6。 相反,LightGBM 使用默认的num_leaves
值为 31,这对应于平衡树的五个级别,但对级别的数量没有约束。 为了避免过拟合,num_leaves
应低于2^(max_depth)。 例如,对于表现良好的max_depth
值为 7,您应将num_leaves
设置为 70–80,而不是 2⁷=128,或者直接限制max_depth
。
树的数量或增强迭代次数定义了整体集合的规模。所有库都支持early_stopping
,一旦损失函数在给定迭代次数内不再改善,就会终止训练。因此,通常最好设置大量迭代次数,并根据验证集上的预测性能停止训练。
正则化
所有库都实现了对基本学习器的正则化策略,例如样本数量的最小值或分割和叶节点所需的最小信息增益。
它们还支持通过学习速率通过收缩来在整个集合层次上实现正则化,限制新树的贡献。还可以通过回调函数实现自适应学习速率,随着训练的进行而降低学习速率,这在神经网络的上下文中已经成功使用过。此外,梯度提升损失函数可以通过* L1 或 L2 进行正则化,类似于通过修改Ω( h [m] *)或通过增加添加更多树的惩罚γ来描述的 Ridge 和 Lasso 线性回归模型。
这些库还允许使用装袋或列子抽样来随机化树的生长,用于随机森林,并减少整体方差以去相关预测误差。为了保护免受过拟合,对于近似分割查找的特征量化,添加更大的箱子作为另一种选择。
随机化网格搜索
为了探索超参数空间,我们为我们想要测试的关键参数指定值。sklearn 库支持RandomizedSearchCV
,从指定的分布中随机抽样一部分参数组合进行交叉验证。我们将实现一个自定义版本,允许我们利用早停,同时监视当前表现最佳的组合,因此我们可以在满意结果时中止搜索过程,而不是事先指定一组迭代次数。
为此,我们根据每个库的参数指定参数网格,使用itertools
库提供的内置笛卡尔product
生成器生成所有组合,并随机shuffle
结果。在 LightGBM 的情况下,我们会自动根据当前num_leaves
值设置max_depth
,如下所示:
param_grid = dict( # common options learning_rate=[.01, .1, .3], colsample_bytree=[.8, 1], # except catboost # lightgbm num_leaves=[2 ** i for i in range(9, 14)], boosting=['gbdt', 'dart'], min_gain_to_split=[0, 1, 5], # not supported on GPU all_params = list(product(*param_grid.values())) n_models = len(all_params) # max number of models to cross-validate shuffle(all_params)
然后,我们执行交叉验证如下:
GBM = 'lightgbm' for test_param in all_params: cv_params = get_params(GBM) cv_params.update(dict(zip(param_grid.keys(), test_param))) if GBM == 'lightgbm': cv_params['max_depth'] = int(ceil(np.log2(cv_params['num_leaves']))) results[n] = run_cv(test_params=cv_params, data=datasets, n_splits=n_splits, gb_machine=GBM)
run_cv
函数实现了三个库的交叉验证。对于light_gbm
示例,该过程如下:
def run_cv(test_params, data, n_splits=10): """Train-Validate with early stopping""" result = [] cols = ['rounds', 'train', 'valid'] for fold in range(n_splits): train = data[fold]['train'] valid = data[fold]['valid'] scores = {} model = lgb.train(params=test_params, train_set=train, valid_sets=[train, valid], valid_names=['train', 'valid'], num_boost_round=250, early_stopping_rounds=25, verbose_eval=50, evals_result=scores) result.append([model.current_iteration(), scores['train']['auc'][-1], scores['valid']['auc'][-1]]) return pd.DataFrame(result, columns=cols)
train()
方法还会生成存储在scores
字典中的验证分数。当早停生效时,最后一次迭代也是最佳分数。有关更多详细信息,请参见 GitHub 上的完整实现。
如何评估结果
使用 GPU,我们可以在几分钟内训练一个模型,并在几个小时内评估数百个参数组合,而使用 sklearn 实现则需要多天。对于 LightGBM 模型,我们探索了使用库处理分类变量的因子版本和使用独热编码的虚拟版本。
结果存储在 model_tuning.h5
HDF5 存储中。模型评估代码样本在 eval_results.ipynb
笔记本中。
跨模型的交叉验证结果
当比较四次测试运行中三个库的平均交叉验证 AUC 时,我们发现 CatBoost 为表现最佳模型产生了稍高的 AUC 分数,同时也产生了最广泛的结果分布,如下图所示:
表现最佳的 CatBoost 模型使用以下参数(详见笔记本):
max_depth
为 12,max_bin
为 128max_ctr_complexity
为 2,限制了分类特征的组合数量one_hot_max_size
为 2,排除了二元特征的数值变量分配random_strength
不等于 0 以随机化分裂的评估
训练相对于 LightGBM 和 XGBoost 稍慢(都使用 GPU),平均每个模型 230 秒。
对 LightGBM 和 XGBoost 模型表现最好的更详细的分析显示,LightGBM 因子模型的性能几乎与其他两个模型相当,但模型复杂度要低得多。它平均只包含 41 棵树,深度为三级,每棵树最多有八个叶子节点,并且还使用了 min_gain_to_split
形式的正则化。它在训练集上过拟合明显较少,训练 AUC 仅略高于验证 AUC。它还训练速度更快,每个模型只需 18 秒,因为它的复杂度更低。实际上,这个模型更可取,因为它更有可能产生良好的样本外表现。具体细节如下表所示:
LightGBM 虚拟 | XGBoost 虚拟 | LightGBM 因子 | |
验证 AUC | 68.57% | 68.36% | 68.32% |
训练 AUC | 82.35% | 79.81% | 72.12% |
learning_rate |
0.1 | 0.1 | 0.3 |
max_depth |
13 | 9 | 3 |
num_leaves |
8192 | 8 | |
colsample_bytree |
0.8 | 1 | 1 |
min_gain_to_split |
0 | 1 | 0 |
轮数 | 44.42 | 59.17 | 41.00 |
时间 | 86.55 | 85.37 | 18.78 |
下图显示了不同 max_depth
设置对 LightGBM 和 XGBoost 模型的验证分数的影响:较浅的树产生了更广泛的结果范围,需要与适当的学习率和正则化设置相结合,以产生前面表格中显示的强结果:
与之前显示的 DecisionTreeRegressor
不同,我们也可以使用线性回归来评估不同特征在验证 AUC 分数方面的统计显著性。对于 LightGBM 虚拟模型,其中回归解释了结果变异的 68%,我们发现只有 min_gain_to_split
正则化参数不显著,如下图所示:
在实践中,深入了解模型如何进行预测非常重要,特别是对于投资策略,决策者通常需要合理的解释。
如何解释 GBM 结果
理解模型为什么预测某个结果对于多种原因都非常重要,包括信任、可操作性、责任和调试。通过模型发现的特征与结果之间的非线性关系以及特征之间的相互作用也在学习更多关于所研究现象的基本驱动因素时具有价值。
了解树集成方法(如梯度提升或随机森林模型)所做预测的见解的常见方法是将特征重要性值归因于每个输入变量。这些特征重要性值可以根据单个预测的情况或整个数据集(即所有样本)全局计算,以获得模型进行预测的更高级别视角。
特征重要性
有三种主要方法来计算全局特征重要性值:
- Gain: 这种经典方法由 Leo Breiman 在 1984 年引入,使用给定特征的所有分割所贡献的损失或杂质的总减少。其动机在很大程度上是启发式的,但它是一种常用的特征选择方法。
- 分割计数:这是一种替代方法,它计算使用特征进行分割决策的频率,根据选择特征以达到所得信息增益。
- 排列:这种方法随机排列测试集中的特征值,并测量模型误差的变化量,假设重要特征应该会导致预测误差大幅增加。不同的排列选择导致该基本方法的替代实现。
对于单个预测计算特征重要性值的方法较少,因为可用的模型无关解释方法比树特定方法慢得多。
所有梯度提升实现在训练后都提供特征重要性得分作为模型属性。XGBoost 库提供五个版本,如下列表所示:
total_gain
和gain
作为其每个分割的平均值total_cover
作为使用特征时每个分割的样本数weight
作为前面值的分割计数
可使用训练模型的.get_score()
方法和相应的importance_type
参数获取这些值。对于性能最佳的 XGBoost 模型,结果如下(total度量具有 0.8 的相关性,cover
和total_cover
也是如此):
虽然不同月份和年份的指标占主导地位,但最近 1 个月的回报是从total_gain
的角度来看第二重要的特征,并且根据weight
度量经常被使用,但由于它平均应用于相对较少的实例而产生较低的平均收益(有关实施细节,请参见笔记本)。
偏依赖图
除了个体特征对模型预测的总体贡献之外,偏依赖图还可视化目标变量与一组特征之间的关系。梯度提升树的非线性特性导致这种关系取决于所有其他特征的值。因此,我们将对这些特征进行边际化处理。通过这样做,我们可以将偏依赖性解释为期望的目标响应。
我们只能为单个特征或特征对可视化偏依赖性。后者会产生等高线图,显示不同特征值的组合如何产生不同的预测概率,如下所示:
fig, axes = plot_partial_dependence(gbrt=gb_clf, X=X_dummies_clean, features=['month_9', 'return_1m', 'return_3m', ('return_1m', 'return_3m')], feature_names=['month_9','return_1m', 'return_3m'], percentiles=(0.01, 0.99), n_jobs=-1, n_cols=2, grid_resolution=250)
经过一些额外的格式化(请参阅配套笔记本),我们得到了以下图表:
右下角的图显示了在消除[1%,99%]百分位数处的离群值后,给定滞后 1 个月和 3 个月收益值范围的情况下,下个月产生正回报的概率的依赖性。 month_9
变量是一个虚拟变量,因此呈阶梯函数样式的图。我们还可以将依赖性可视化为 3D,如下代码所示:
targets = ['return_1m', 'return_3m'] target_feature = [X_dummies_clean.columns.get_loc(t) for t in targets] pdp, axes = partial_dependence(gb_clf, target_feature, X=X_dummies_clean, grid_resolution=100) XX, YY = np.meshgrid(axes[0], axes[1]) Z = pdp[0].reshape(list(map(np.size, axes))).T fig = plt.figure(figsize=(14, 8)) ax = Axes3D(fig) surf = ax.plot_surface(XX, YY, Z, rstride=1, cstride=1, cmap=plt.cm.BuPu, edgecolor='k') ax.set_xlabel(' '.join(targets[0].split('_')).capitalize()) ax.set_ylabel(' '.join(targets[1].split('_')).capitalize()) ax.set_zlabel('Partial Dependence') ax.view_init(elev=22, azim=30)
这产生了以下 1 个月回报方向对滞后 1 个月和 3 个月回报的偏依赖的 3D 图:
SHapley Additive exPlanations
在 2017 年 NIPS 会议上,来自华盛顿大学的 Scott Lundberg 和 Su-In Lee 提出了一种新的更准确的方法来解释树集成模型对输出的贡献,称为SHapley Additive exPlanations,或SHAP值。
这个新算法基于这样一个观察:树集成的特征归因方法,如我们之前看过的方法,是不一致的——即,使特征对输出的影响增加的模型变化可能会降低该特征的重要性值(请参阅 GitHub 上有关此的详细说明)。
SHAP 值统一了协作博弈理论和局部解释的思想,并且根据期望值显示其在理论上是最优、一致和局部准确的。最重要的是,Lundberg 和 Lee 开发了一种算法,成功将计算这些与模型无关的、可加的特征归因方法的复杂度从 O(TLD^M) 减少到 O(TLD²),其中 T 和 M 分别是树和特征的数量,D 和 L 是树的最大深度和叶子节点数。这一重要的创新使得对先前难以处理的具有数千棵树和特征的模型的预测能够在几秒钟内解释。一个开源实现于 2017 年底发布,并且兼容 XGBoost、LightGBM、CatBoost 和 sklearn 树模型。
Shapley 值源自博弈论,是一种为协作游戏中的每个玩家分配值的技术,反映了他们对团队成功的贡献。SHAP 值是对基于树的模型的博弈论概念的一种改编,并且为每个特征和每个样本计算。它们衡量了一个特征对给定观察结果的模型输出的贡献。因此,SHAP 值提供了不同的洞察力,说明了特征的影响如何在样本间变化,这在这些非线性模型中交互作用效应的作用中非常重要。
如何按特征总结 SHAP 值
要获得对多个样本的特征重要性的高级概述,有两种绘制 SHAP 值的方法:对所有样本进行简单平均,类似于先前计算的全局特征重要性度量(如下图左侧面板所示),或者绘制散点图来显示每个样本的每个特征的影响(如下图右侧面板所示)。使用兼容库的训练模型和匹配输入数据非常容易产生,如下面的代码所示:
# load JS visualization code to notebook shap.initjs() # explain the model's predictions using SHAP values explainer = shap.TreeExplainer(model) shap_values = explainer.shap_values(X_test) shap.summary_plot(shap_values, X_test, show=False)
下图右侧的散点图按照所有样本的 SHAP 值对特征进行排序,然后显示了每个特征对模型输出的影响,其值由特征的值的函数表示,通过其颜色表示,红色表示高值,蓝色表示低值,相对于特征的范围:
如何使用力图解释预测
下面的力图显示了各种特征及其值对模型输出的累积影响,在这种情况下,模型输出为 0.6,比基准值 0.13(提供的数据集上的平均模型输出)高得多。突出显示为红色的特征增加了输出。十月份是最重要的特征,将输出从 0.338 增加到 0.537,而 2017 年则降低了输出。
因此,我们获得了模型如何到达特定预测的详细分解,如下图所示:
我们还可以同时计算大量数据点或预测的力图,并使用聚类可视化来深入了解数据集中某些影响模式的普遍程度。以下图表显示了前 1000 个观察结果的力图,旋转了 90 度,水平堆叠,并按照不同特征对给定观察结果的影响排序。该实现使用层次凝聚聚类对特征 SHAP 值上的数据点进行标识以识别这些模式,并以交互方式显示结果进行探索性分析(参见笔记本),如以下代码所示:
shap.force_plot(explainer.expected_value, shap_values[:1000,:], X_test.iloc[:1000])
这将产生以下输出:
如何分析特征交互
最后,SHAP 值使我们能够通过将这些相互作用与主要效应分离来获得对不同特征之间的相互作用效应的额外洞察。shap.dependence_plot
可以定义如下:
shap.dependence_plot("return_1m", shap_values, X_test, interaction_index=2, title='Interaction between 1- and 3-Month Returns')
它显示了 1 个月回报的不同值(x 轴)如何影响结果(y 轴上的 SHAP 值),并由 3 个月回报区分:
SHAP 值在每个单独预测的水平上提供了细粒度的特征归因,并通过(交互式)可视化实现了对复杂模型的更丰富检查。本节开头显示的 SHAP 摘要散点图提供了比全局特征重要性条形图更加差异化的见解。单个聚类预测的力图允许进行更详细的分析,而 SHAP 依赖图捕获了相互作用效应,并因此提供了比局部依赖图更准确和详细的结果。
SHAP 值的限制与任何当前特征重要性度量一样,涉及对高度相关的变量影响的归因,因为它们相似的影响可能以任意方式被分解。
摘要
在本章中,我们探讨了梯度提升算法,该算法用于以顺序方式构建集成,添加一个只使用非常少的特征的浅层决策树,以改善已经做出的预测。我们看到了梯度提升树如何可以非常灵活地应用于广泛的损失函数,并提供了许多机会来调整模型以适应给定的数据集和学习任务。
最近的实现大大简化了梯度提升的使用,通过加速训练过程并提供更一致和详细的特征重要性以及单个预测驱动因素的洞察。在下一章中,我们将转向贝叶斯方法来进行机器学习。