使用Sklearn Pipeline做分类任务时Shap树解释器绘图报错求助
问题描述
使用Sklearn Pipeline完成泰坦尼克号分类任务后,调用SHAP可视化时出现多种错误,代码及报错如下:
完整代码
from sklearn.datasets import fetch_openml from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.impute import SimpleImputer from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.preprocessing import OrdinalEncoder import shap # ----------------------------------------------------------------------------- # Data # ----------------------------------------------------------------------------- X, y = fetch_openml("titanic", version=1, as_frame=True, return_X_y=True) categorical_columns = ["pclass", "sex", "embarked"] numerical_columns = ["age", "sibsp", "parch", "fare"] X = X[categorical_columns + numerical_columns] # [1309, 7] , there is Nan values. X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42) # ----------------------------------------------------------------------------- # Data preprocessing # ----------------------------------------------------------------------------- categorical_encoder = OrdinalEncoder( handle_unknown="use_encoded_value", unknown_value=-1, encoded_missing_value=-1 ) numerical_imputer = SimpleImputer(strategy="mean") preprocessing = ColumnTransformer( [ ("cat", categorical_encoder, categorical_columns), ("num", numerical_imputer, numerical_columns), ], verbose_feature_names_out=False, ) # ----------------------------------------------------------------------------- # Pipeline # ----------------------------------------------------------------------------- rf = Pipeline( [ ("preprocess", preprocessing), ("classifier", RandomForestClassifier(random_state=42)), ] ) rf.fit(X_train, y_train) print(f"RF train accuracy: {rf.score(X_train, y_train):.3f}") print(f"RF test accuracy: {rf.score(X_test, y_test):.3f}") # ----------------------------------------------------------------------------- # Shap # ----------------------------------------------------------------------------- explainer = shap.Explainer(rf["classifier"], feature_names=rf["preprocess"].get_feature_names_out()) X_test_processed = rf['preprocess'].transform(X_test) shap_values = explainer(X_test_processed)
报错情况
- 执行
shap.summary_plot(shap_values, X_test_processed):TypeError: 仅整数标量数组可转换为标量索引 - 执行
shap.summary_plot(shap_values, X_test_processed, plot_type="bar"):TypeError: 仅整数标量数组可转换为标量索引 - 执行
shap.plots.beeswarm(shap_values):ValueError: 蜂群图不支持绘制具有多维实例的解释结果! - 执行
shap.plots.bar(shap_values):IndexError: 列表索引超出范围
解决思路
核心问题原因
RandomForestClassifier即使是二分类任务,默认也会输出每个类别的概率,属于多输出模型。因此explainer(X_test_processed)返回的SHAP值是三维数组(样本数 × 特征数 × 类别数),而多数SHAP可视化函数仅支持处理单输出(二维数组)的情况,这是所有报错的根源。
具体修复步骤
提取单类别SHAP值
二分类任务中通常关注正类(比如泰坦尼克号任务的“存活”类)的SHAP值,只需从三维数组中提取对应类别的维度:# 先查看类别顺序,确认正类索引 print(rf["classifier"].classes_) # 假设正类是索引1的类别,提取对应SHAP值 shap_values = shap_values[..., 1]修改SHAP代码块
更新后的SHAP部分代码如下:# ----------------------------------------------------------------------------- # Shap # ----------------------------------------------------------------------------- explainer = shap.Explainer(rf["classifier"], feature_names=rf["preprocess"].get_feature_names_out()) X_test_processed = rf['preprocess'].transform(X_test) shap_values = explainer(X_test_processed) # 提取正类SHAP值,适配可视化要求 shap_values = shap_values[..., 1]重新运行可视化函数
现在以下代码均可正常执行:- 摘要散点图:
shap.summary_plot(shap_values, X_test, feature_names=rf["preprocess"].get_feature_names_out()) - 特征重要性条形图:
shap.summary_plot(shap_values, X_test, plot_type="bar", feature_names=rf["preprocess"].get_feature_names_out()) - 蜂群图:
shap.plots.beeswarm(shap_values) - 特征重要性条形图(另一种接口):
shap.plots.bar(shap_values)
- 摘要散点图:
额外优化建议
- 传入原始测试集
X_test而非预处理后的X_test_processed,可视化时会显示原始特征取值(如性别显示male/female,而非编码后的0/1),可读性更强。
内容的提问来源于stack exchange,提问作者Mohammad
相关产品推荐
相关产品推荐

