如果我们正在使用Pipeline,我该如何获取treeinterpreter的树贡献?

3
我正在使用sklearns' pipeline函数进行one hot编码建模,几乎与this帖子完全相同。
在使用Pipeline后,我无法再获取树的贡献。出现以下错误:

AttributeError: 'Pipeline' object has no attribute 'n_outputs_'

我尝试了一些treeinterpreter参数的调整,但卡住了。
因此我的问题是:在使用sklearns Pipeline时,有没有任何方法可以从树中获取贡献?
<强>编辑2-由Venkatachalam请求的真实数据:
# Data DF to train model
df = pd.DataFrame(
  [['SGOHC', 'd',   'onetwothree',  'BAN',  488.0580347,    960 ,841,   82, 0.902497027,    841 ,0.548155625    ,0.001078211,   0.123958333 ,1],
   ['ABCDEFGHIJK',  'SOC'   ,'CON','CAN',   680.84, 1638,   0,  0,  0   ,0  ,3.011140743    ,0.007244358,   1   ,0],
   ['Hello',    'AA',   'onetwothree',  'SPEAKER',  5823.230967,    2633,   1494    ,338    ,0.773761714    ,1494,  12.70144386 ,0.005743015,   0.432586403,    8]], 
  columns=['B','C','D','E','F','G','H','I','J','K','L','M', 'N', 'target'])

# Create test and train set (useless, but for the example...) 
from sklearn.model_selection  import train_test_split

# Define X and y 
X = df.drop('target', axis=1)
y = df['target']

# Create Train and Test Sets 
X_train, X_validation, Y_train, Y_validation = train_test_split(X, y, test_size=0.20, random_state=1)


 # Make the pipeline and model 
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import OneHotEncoder
import numpy as np
import pandas as pd
from sklearn import set_config
from sklearn.model_selection import ParameterGrid
from sklearn.ensemble import RandomForestRegressor
import matplotlib.pyplot as plt

rfr = Pipeline([('preprocess',
                   ColumnTransformer([('ohe',
                                       OneHotEncoder(handle_unknown='ignore'), [1])])),
                  ('rf', RandomForestRegressor())])

rfr.fit(X_train, Y_train)


# The New, Real data that we need to predict & explain! 

new_data = pd.DataFrame(
  [['DEBTYIPL', 'de',   'onetwothreefour',  'BANAAN',   4848.0580347,   923460  ,823441,    5,  0.902497027,    43  ,0.548155625    ,0.001078211,   0.123958333 ],
   ['ABCDEFGHIJK',  'SOC'   ,'CON','CAN23', 680.84, 1638,   0,  0,  0   ,0  ,1.011140743    ,4.007244358,   1   ],
   ['Hello_NO', 'AAAAa',    'onetwothree',  'SPEAKER',  5823.230967,    123,    32  ,22 ,0.773761714    ,1678,  12.70144386 ,0.005743015,   0.432586403]], 
  columns=['B','C','D','E','F','G','H','I','J','K','L','M', 'N'])
new_data.head()

# Predicting the values 
rfr.predict(new_data)

# Now the error... the contributions: 
from treeinterpreter import treeinterpreter as ti
prediction, bias, contributions = ti.predict(rfr[-1], rfr[:-1].fit_transform(new_data))

#ValueError: Number of features of the model must match the input. Model n_features is 2 and input n_features is 3 
2个回答

3
您可以通过索引管道对象 model[-1] 来获取最后的估计器,同样地,我们可以通过 model[:-1] 来获取一个新的管道(以捕获所有转换步骤),但不包括分类器。
因此,这就是您需要做的!
prediction, bias, contributions = ti.predict(model[-1], model[:-1].transform(df))

1
再次感谢。它在虚拟数据上运行正常,但是如果我在我的真实数据上运行它(测试数据中有许多新的唯一值),我会得到以下错误:ValueError:模型的特征数量必须与输入匹配。模型n_features为108,输入n_features为90可能是在Pipeline中,我们缺少了跳过部分吗?正如您在此处的先前答案中提到的那样。[https://dev59.com/rL7pa4cB1Zd3GeqPuj0v] - R overflow
1
不,它应该适用于任何数量的唯一值。你能提供一些可重复的示例吗? - Venkatachalam
抱歉回复晚了(花了很长时间来生成可重现的示例)。请参见EDIT2。非常感谢@Venkatachalam。 - R overflow
1
在我的解决方案中发现了错误。它应该是.transform而不是fit_transform。这意味着它将重复使用训练阶段学习到的类别,而不是从给定数据中重新学习。 - Venkatachalam
1
太棒了!赏金已添加。也许还有一个问题(希望你能帮忙),在得到贡献之后,我尝试将它们放入DF中:contributions_df = pd.DataFrame(data=contributions, columns=df.columns),现在我在这里遇到了相同的问题(df.columns与“转换”df不匹配)。你知道我怎么才能使df的形状正确吗?错误是:ValueError: Shape of passed values is (20277, 108), indices imply (20277, 14)。 - R overflow
我认为新的列名可以像这样获取。 - Venkatachalam

1
要访问Pipeline的拟合模型,只需从管道中检索._final_estimator属性。
from treeinterpreter import treeinterpreter as ti
prediction, bias, contributions = ti.predict(model._final_estimator, model[0].fit_transform(df))

请注意,可以通过调用 sklearn 工具 check_is_fitted 来验证估计器是否已安装。
from sklearn.utils.validation import check_is_fitted
check_is_fitted(model._final_estimator)

谢谢@Miguel。我尝试了你的解决方案,但是会出现以下错误:ValueError: could not convert string to float: 'female' - R overflow
1
是的,df 上缺少独热编码器,model[0].fit_transform(df) 应该可以工作。 - Miguel Trejo
感谢 @Miguel!! 不确定为什么,但它在真实数据上也失败了(请参见编辑2)。有什么想法吗? - R overflow
1
考虑到 rfr[:-1].fit_transform(X_train)rfr[:-1].fit_transform(df),请注意 df 中有一个特征 X_train 没有,例如,df 在列 C 中有 d, SOC, AA,而 X_train 只有 SOC, AA。因此,在将数据拆分为测试集时,请确保两个集合包含相同的类别。 - Miguel Trejo
因此,我使用了管道功能(带有忽略参数)。为了能够处理“现实世界”的数据(可能包含新类)。您可以在这里找到该示例。这导致了一个问题,即我无法从管道模型中获取树贡献(管道是虚拟和模型数据)。希望您能帮助我。 - R overflow
@Roverflow 如果你知道在真实数据中只会收到一个额外的类别,那么你可以将 .add_dummy_feature 应用于训练数据,并使用值 -1 表示未知。然而,如果你知道有几个类别在真实数据中可见,我建议使用另一种编码类型,比如均值编码。其他编码类型的完整参考可以在这里找到。 - Miguel Trejo

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