如何使用lightgbm Tweedie目标将SHAP值从原始单位转换为本机单位?

4
Shapley Additive Explanations(SHAP值)的实用性在于了解每个特征对模型预测的贡献。对于某些目标,例如以RMSE为目标函数的回归,SHAP值以标签值的本机单位表示。例如,如果估计住房成本,SHAP值可以表示为美元。如下所示,这并非所有目标函数的情况。特别是,Tweedie回归目标不会产生本机单位的SHAP值。这是一个解释问题,因为我们想知道住房成本如何受到特征的影响,以+/-美元为单位。

鉴于此,我的问题是:当使用Tweedie回归目标解释模型时,我们如何将每个单独特征的SHAP值转换为目标标签的数据空间?

我不知道是否有任何包当前实现了这样的转换。 shap作者自己发布的包中仍存在这个问题未解决。

我以下面的R实现lightgbm说明这个问题的细节:

library(tweedie)
library(lightgbm)

set.seed(123)

tweedie_variance_power <- 1.2

labels <- rtweedie(1000, mu = 1, phi = 1, power = tweedie_variance_power)
hist(labels)

feat1 <- labels + rnorm(1000) #good signal for label with some noise
feat2 <-rnorm(1000) #garbage feature 
feat3 <-rnorm(1000) #garbage feature 

features <- cbind(feat1, feat2, feat3)

dTrain <- lgb.Dataset(data = features,
                      label = labels)

params <- c(objective = 'tweedie',
            tweedie_variance_power = tweedie_variance_power)

mod <- lgb.train(data = dTrain,
                 params = params,
                 nrounds = 100)

#Predictions in the native units of the labels
predsNative <- predict(mod, features, rawscore = FALSE)
#Predictions in the raw format
predsRaw <- predict(mod, features, rawscore = TRUE)

#We do not expect these values to be equal
all.equal(predsTrans, predsRaw)
"Mean relative difference: 1.503072"

#We expect values to be equal if raw scores are exponentiated
all.equal(predsTrans, exp(predsRaw))
"TRUE" #... our expectations are correct

#SHAP values 
shapNative <- predict(mod, features, rawscore = FALSE, predcontrib = TRUE)
shapRaw <- predict(mod, features, rawscore = TRUE, predcontrib = TRUE )

#Are there differences between shap values when rawscore is TRUE or FALSE?
all.equal(shapNative, shapRaw)
"TRUE" #outputs are identical, that is surprising!

#So are the shap values in raw or native formats?
#To anwser this question we can sum them

#testing raw the raw case first
all.equal(rowSums(shapRaw), predsRaw)
"TRUE" 

#from this we can conclude that shap values are not in native units,
#regardless of whether rawscore is TRUE or FALSE

#Test native scores just to prove point
all.equal(rowSums(shapNative), predsNative)
"Mean relative difference: 1.636892" # reaffirms that shap values are not in native units

#However, we can perform this operation on the raw shap scores
#to get the prediction in the native value
all.equal(exp(rowSums(shapRaw)), predsNative)
'TRUE'

#reversing the operations does not yield the same result
all.equal(rowSums(exp(shapRaw)), predsNative)
"Mean relative difference: 0.7662481"

#The last line is relevant because it implies 
#The relationship between native predictions
#and exponentiated shap values is not linear

#So, given the point of SHAP is to understand how each 
#feature impacts the prediction in its native units
#the raw shap values are not as useful as they could be

#Thus, how how would we convert 
#each of these four raw shap value elements to native units,
#thus understanding their contributions to their predictions
#in currency of native units?
shapRaw[1,]
-0.15429227  0.04858757 -0.27715359 -0.48454457

原帖和编辑

我理解SHAP值是在回归时标签/响应的本机单位,SHAP值的总和近似于模型的预测。

我正在尝试从LightGBM包中提取SHAP值,使用Tweedie回归目标,但发现SHAP值不是标签的本机单位,并且它们并不相加到预测值。

看起来它们必须被指数化,这是正确的吗?

附注:我理解SHAP值矩阵的最后一列代表基础预测,并且必须添加。

可重现的例子:

library(tweedie)
library(caret)
library(lightgbm)

set.seed(123)

tweedie_variance_power <- 1.2

labels <- rtweedie(1000, mu = 1, phi = 1, power = tweedie_variance_power)
hist(labels)

feat1 <- labels + rnorm(1000) #good signal for label with some noise
feat2 <-rnorm(1000) #garbage feature 
feat3 <-rnorm(1000) #garbage feature 

features <- cbind(feat1, feat2, feat3)

dTrain <- lgb.Dataset(data = features,
                      label = labels)

params <- c(objective = 'tweedie',
            tweedie_variance_power = tweedie_variance_power)

mod <- lgb.train(data = dTrain,
                 params = params,
                 nrounds = 100)

preds <- predict(mod, features)

plot(preds, labels,
     main = paste('RMSE =', 
                  RMSE(pred = preds, obs = labels)))

#shap values are summing to negative values?
shap_vals <- predict(mod, features, predcontrib = TRUE, rawscore = FALSE)
shaps_sum <- rowSums(shap_vals)
plot(shaps_sum, labels, 
     main = paste('RMSE =', 
                  RMSE(pred = shaps_sum, obs = labels)))

#maybe we need to exponentiate?
shap_vals_exp <- exp(shap_vals)
shap_vals_exp_sum <- rowSums(shap_vals_exp)
#still looks a little weird, overpredicting 
plot(shap_vals_exp_sum, labels,
     main = paste('RMSE =',
                  RMSE(pred = shap_vals_exp_sum, obs = labels)))

编辑

操作顺序是先求和,然后对SHAP值进行指数运算,这将给出以本机单位为预测的结果。尽管我仍不清楚如何将特征级别的值转换为本机响应单位。

shap_vals_sum_exp <- exp(shaps_sum)
plot(shap_vals_sum_exp, labels,
     main = paste('RMSE =',
                  RMSE(pred = shap_vals_sum_exp, obs = labels)))
1个回答

6
我将展示如何在 Python 中以原始分数和原始单位来协调 shap 值和模型预测。希望这能帮助您了解在 R 中的实现。
第一步:生成数据集。
# pip install tweedie
import tweedie
y = tweedie.tweedie(1.2,1,1).rvs(size=1000)
X = np.random.randn(1000,3)

第二步:拟合模型

from lightgbm.sklearn import LGBMRegressor
lgb = LGBMRegressor(objective = 'tweedie')
lgb.fit(X,y)

第三步:了解Shap值是什么。

第0个数据点的Shap值。

shap_values = lgb.predict(X, pred_contrib=True)
shap_values[0]
array([ 0.36841812, -0.15985678,  0.28910617, -0.27317984])

前三个是相对于基线的模型贡献,即shap值本身:

shap_values[0,:3].sum()
0.4976675073764354

第四个是原始分中的基线:
shap_values[0,3]
-0.2731798364061747

它们中的一些加起来等于原始分数中的模型预测:

shap_values[0,:3].sum() + shap_values[0,3]
0.22448767097026068

让我们检查原始模型预测:

preds = lgb.predict(X, raw_score=True)
preds[0]
0.2244876709702609

编辑. 从原始分数到原始单位的转换

要将 Tweedie(以及泊松和伽马)分布的原始分数转换为原始单位,您需要了解两个事实:

  1. 原始单位是原始值的 exp 值。
  2. sumexp 值是 expsproduct 值。

演示:

  1. 原始单位中的第 0 次预测:
lgb.predict([X[0,:]])
array([0.39394102])

在原始分数空间中,第0行的Shap值为:
shap_values = lgb.predict(X, pred_contrib=True, raw_score=True)
shap_values[0]
array([-0.77194274, -0.08343294,  0.22740536, -0.30358374])
  1. 将 shap 值转换为原始单位(指数乘积):
np.prod(np.exp(shap_values[0]))
0.3939410249402226

在我看来,它看起来很相似。


1
感谢您的贡献。虽然您提供的方法描述了如何提取目标函数(对数似然)单位的SHAP值,但这些值不是标签的本机单位。我后来了解到,将其转换回本机单位并不简单,更多讨论请参见:https://github.com/slundberg/shap/pull/1041 - kdoherty
1
请问您的问题具体是什么?我认为您在协调 shap 值和预测方面遇到了困难。我向您展示了如何在原始得分空间中进行操作。但是您似乎不接受这个答案。为什么呢?您是否在将原始得分转换为原始单位时遇到了困难?请注意,这是回归问题,对数似然并不相关。 - Sergey Bushmanov
1
问题在于原始分数空间不是您在答案中描述的y单位(我所称的本地单位)。目标是以第1步中描述的y单位从每个特征提取SHAP值,使它们在y单位下总和为预测值。 - kdoherty
2
我向您展示了两种做法:原始分数和原始数据。这些数字在小数点后的15位数字之前是相同的。 - Sergey Bushmanov
@SergeyBushmanov 我不太确定 - 我正在使用Python API,而不是sklearn,但predict_contrib给了我预测值,而不是shap值。无论如何,我会仔细看看,如果确实是这样,我会提出一个问题。 - desertnaut
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接