如何用SHAP包手动分组特征?实现RNN模型3D特征的整体SHAP值计算
问题:用SHAP计算RNN模型的整体特征重要性(按特征维度聚合)
我需要用SHAP计算RNN模型的特征重要性,模型输入是[N_instances × N_times × N_features]的3D特征矩阵,预测[N_instances × 1]的输出。比如化工反应器12小时内每小时的温度、压力时间序列(N_features=2),用来预测化学品总质量,现在只能得到每个时间点的温度/压力对应的SHAP值(共2×12个),想要获取每个特征的整体SHAP值(仅2个)。
目前我的代码实现如下(已将3D数据展平为2D适配SHAP):
# Imports from keras.models import Sequential from keras.layers import Dense, LSTM import shap import numpy as np # Generate random 3-D X data and 1-D y data. N_TIMES = 10 N_INSTANCES = 256 N_FEATURES = 3 X = np.random.random((N_INSTANCES, N_TIMES, N_FEATURES)) y = np.random.random((N_INSTANCES, 1)) # Write a simple model model1 = Sequential() model1.add(LSTM(8, input_shape=(N_TIMES, N_FEATURES), return_sequences = False, stateful=False, activation='relu')) model1.add(Dense(1)) # Compile and train the model on data model1.compile(loss='mean_squared_error', optimizer='adam', metrics=['MeanSquaredError']) history = model1.fit(X, y, batch_size=16, epochs=3, verbose = 0 ) # Define function to take in a 2D feature array and return model predictions def model_for_shap(X_flat): X = X_flat.reshape((X_flat.shape[0], N_TIMES, N_FEATURES)) return model1.predict(X) X_flat = X.reshape((N_INSTANCES, N_TIMES*N_FEATURES)) # Run SHAP background = X.reshape((X.shape[0], X.shape[1]*X.shape[2])) e = shap.Explainer(model_for_shap, background) shap_values = e.shap_values(X[:10].reshape((10, N_TIMES*N_FEATURES))) print(f"Shape of shap_values: {shap_values.shape}")
输出为:
Shape of shap_values: (10, 30)
这符合预期,但我希望每次屏蔽整个特征的时间序列,得到对应N_FEATURES个特征的SHAP值(而非30个)。我认为Partition masker可能有用,但不清楚如何调整clustering参数实现目标。
解决方案:使用Partition Masker按特征维度分组
完全可以实现你的需求,核心是通过shap.PartitionMasker定义特征分组规则,把同一特征的所有时间点归为一组,这样SHAP会计算每组(即每个特征整体)的重要性。
步骤1:构建聚类分组矩阵
生成一个聚类数组,让同一特征的所有时间点对应同一个聚类ID。比如N_TIMES=10、N_FEATURES=3时,聚类数组为[0,0,...0, 1,1,...1, 2,2,...2](每个数字重复10次)。
代码实现:
# 生成聚类分组:同一特征的所有时间点属于同一组 clustering = np.repeat(np.arange(N_FEATURES), N_TIMES)
步骤2:初始化Partition Masker并适配SHAP Explainer
用上述聚类数组初始化PartitionMasker,并传入SHAP Explainer。修改后的SHAP部分代码如下:
# 生成聚类分组 clustering = np.repeat(np.arange(N_FEATURES), N_TIMES) # 初始化Partition Masker:按分组屏蔽特征 masker = shap.PartitionMasker( masker=background, clustering=clustering ) # 使用带masker的Explainer e = shap.Explainer(model_for_shap, masker=masker, background=background) shap_values = e.shap_values(X[:10].reshape((10, N_TIMES*N_FEATURES))) # 查看结果形状:(10, 3),对应10个样本×3个特征的整体SHAP值 print(f"Shape of aggregated shap_values: {shap_values.shape}")
关键说明
clustering参数的作用是告知SHAP哪些特征属于同一组,同一组的特征会被同时屏蔽/暴露,从而计算该组的整体重要性。- 这里的分组逻辑是“同一特征的所有时间步为一组”,完美匹配你需要的“屏蔽整个特征时间序列”的需求。
- 最终的
shap_values形状为(N_samples, N_FEATURES),每个值对应一个特征的整体SHAP重要性。
验证结果
运行修改后的代码,输出会是:
Shape of aggregated shap_values: (10, 3)
这正是你需要的每个特征的整体SHAP值。
内容的提问来源于stack exchange,提问作者pckinnunen
相关产品推荐
相关产品推荐

