You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

二分类任务中CatBoost模型的SHAP值获取与解读问题

我来帮你解决这个用SHAP解释CatBoost二分类模型的问题,这是处理不平衡分类时很常见的困惑点,咱们一步步拆解:

为什么你得到的是二维SHAP值和单个基准值?

在二分类任务中,SHAP的TreeExplainer默认只返回正类的SHAP贡献(二维数组:样本数×特征数),对应的expected_value是正类的基准预测值(比如训练集上正类的平均概率或对数几率)。这是因为二分类的两类预测是互补的(0类的预测结果可以通过1类推导),所以SHAP默认只输出一类的结果来节省空间。

1. 构造三维(类别数×样本数×特征数)的SHAP值

有两种简单可靠的方法实现你的需求:

方法一:手动互补生成两类SHAP值

因为0类的特征贡献和1类完全相反,你可以直接用正类SHAP值的相反数作为0类的SHAP值,组合成三维数组:

import numpy as np

# 先获取正类的SHAP值(原有代码的逻辑)
explainer = shap.TreeExplainer(catboost_clf)
shap_values_pos = explainer.shap_values(X)

# 组合成三维数组:[0类SHAP值, 1类SHAP值]
shap_values_both = np.array([-shap_values_pos, shap_values_pos])
# 此时维度为 (2, 1747360, 13),完全符合你的期望

方法二:用CatBoost原生API获取SHAP值

CatBoost本身支持直接输出SHAP值,通过get_feature_importance指定类型即可,再提取需要的部分:

# 使用CatBoost原生方法获取SHAP相关结果
shap_raw = catboost_clf.get_feature_importance(Pool(X, y), type="ShapValues")
# 返回的数组是 (样本数, 特征数+1),最后一列是模型的原始预测值(对数几率)
shap_values_pos = shap_raw[:, :-1]  # 提取前13列作为正类SHAP值

# 同样组合成三维数组
shap_values_both = np.array([-shap_values_pos, shap_values_pos])
2. 提取类别级SHAP值并分析模型

得到三维数组后,你可以针对每个类别做针对性分析:

计算类别专属的特征重要性

通过平均绝对SHAP值衡量每个特征对某一类预测的整体影响:

import pandas as pd

# 1类的特征重要性(平均绝对SHAP值)
class1_importance = np.mean(np.abs(shap_values_both[1]), axis=0)
# 0类的特征重要性
class0_importance = np.mean(np.abs(shap_values_both[0]), axis=0)

# 把重要性和特征名对应起来排序查看
feature_names = X.columns
class1_importance_df = pd.DataFrame(
    {"feature": feature_names, "importance": class1_importance}
).sort_values(by="importance", ascending=False)

绘制类别专属的SHAP可视化图

用SHAP的绘图工具直观分析,比如1类的汇总图(展示特征对预测的正负影响):

# 1类的SHAP汇总图
shap.summary_plot(shap_values_both[1], X, title="SHAP Summary for Class 1 (Positive Class)")
# 0类的SHAP汇总图
shap.summary_plot(shap_values_both[0], X, title="SHAP Summary for Class 0 (Negative Class)")

单个样本的类别级贡献分析

查看某个样本中,每个特征对两类预测的具体影响:

sample_idx = 0  # 选第一个样本举例

# 分析该样本对1类的贡献
shap.waterfall_plot(shap.Explanation(
    values=shap_values_both[1][sample_idx],
    base_values=explainer.expected_value,
    data=X.iloc[sample_idx],
    feature_names=X.columns
), title="Feature Contributions to Class 1 Prediction (Sample {})".format(sample_idx))

# 分析该样本对0类的贡献(注意基准值要换成1 - 正类基准值,概率场景下)
shap.waterfall_plot(shap.Explanation(
    values=shap_values_both[0][sample_idx],
    base_values=1 - explainer.expected_value,
    data=X.iloc[sample_idx],
    feature_names=X.columns
), title="Feature Contributions to Class 0 Prediction (Sample {})".format(sample_idx))
3. 关于单个expected_value的问题

你看到的expected_value是正类的基准预测值:

  • 如果模型用Logloss损失,默认输出是对数几率,此时expected_value是训练集上正类的平均对数几率
  • 如果你设置model_output="probability",expected_value就是训练集上正类的平均概率

对应0类的基准值可以直接推导:

  • 对数几率场景:-explainer.expected_value
  • 概率场景:1 - explainer.expected_value

如果需要两类的基准值数组,可以手动构造:

# 概率场景下的两类基准值
base_values_both = np.array([1 - explainer.expected_value, explainer.expected_value])
小细节优化
  • 你代码里的trainx_preds = catboost_clf.predict(X_test)变量名有点混淆,建议改成test_preds更清晰
  • 使用SHAP时不需要把X包装成CatBoost的Pool,直接传入DataFrame或numpy数组即可,SHAP会自动处理

内容的提问来源于stack exchange,提问作者Dhvani Shah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 18:35:27