【问题标题】:How to get SHAP values for each class on a multiclass classification problem in python如何在python中的多类分类问题上获取每个类的SHAP值
【发布时间】:2022-11-05 04:33:49
【问题描述】:

我有以下数据框:

import pandas as pd
import random

import xgboost
import shap

foo = pd.DataFrame({'id':[1,2,3,4,5,6,7,8,9,10],
                   'var1':random.sample(range(1, 100), 10),
                   'var2':random.sample(range(1, 100), 10),
                   'var3':random.sample(range(1, 100), 10),
                   'class': ['a','a','a','a','a','b','b','c','c','c']})

我想为此运行分类算法以预测 3 classes

因此,我将数据集拆分为训练和测试,然后运行 ​​xgboost

cl_cols = foo.filter(regex='var').columns
X_train, X_test, y_train, y_test = train_test_split(foo[cl_cols],
                                                        foo[['class']],
                                                        test_size=0.33, random_state=42)


model = xgboost.XGBClassifier(objective="binary:logistic")
model.fit(X_train, y_train)

现在我想获得平均 SHAP 值每个班级

以下代码产生了我想要的,但它使用绝对SHAP 值以计算平均值,但我只需要平均值

shap_values = shap.TreeExplainer(model).shap_values(X_test)
shap.summary_plot(shap_values, X_test)

此外,该图将class 标记为 0、1、2。我怎么知道 0,1 & 2 对应于原始的哪个class

因为这段代码:

shap.summary_plot(shap_values, X_test,
                 class_names= ['a', 'b', 'c'])

和这段代码

shap.summary_plot(shap_values, X_test,
                 class_names= ['b', 'c', 'a'])

所以我不再确定这个传说了。 有任何想法吗 ?

【问题讨论】:

    标签: python python-3.x machine-learning xgboost shap


    【解决方案1】:

    我有同样的问题,也许这个问题可以帮助:https://github.com/slundberg/shap/issues/764

    我还没有测试过,但似乎顺序应该与调用model.predict_proba() 时的顺序相同。在上面的链接中,建议使用摘要图的class_names=model.classes_ 选项。

    【讨论】:

      【解决方案2】:

      通过做一些研究并在this post 和@Alessandro Nesti 的回答的帮助下,这是我的解决方案:

      foo = pd.DataFrame({'id':[1,2,3,4,5,6,7,8,9,10],
                         'var1':random.sample(range(1, 100), 10),
                         'var2':random.sample(range(1, 100), 10),
                         'var3':random.sample(range(1, 100), 10),
                         'class': ['a','a','a','a','a','b','b','c','c','c']})
      
      cl_cols = foo.filter(regex='var').columns
      X_train, X_test, y_train, y_test = train_test_split(foo[cl_cols],
                                                              foo[['class']],
                                                              test_size=0.33, random_state=42)
      
      
      model = xgboost.XGBClassifier(objective="multi:softmax")
      model.fit(X_train, y_train)
      
      def get_ABS_SHAP(df_shap,df):
          #import matplotlib as plt
          # Make a copy of the input data
          shap_v = pd.DataFrame(df_shap)
          feature_list = df.columns
          shap_v.columns = feature_list
          df_v = df.copy().reset_index().drop('index',axis=1)
          
          # Determine the correlation in order to plot with different colors
          corr_list = list()
          for i in feature_list:
              b = np.corrcoef(shap_v[i],df_v[i])[1][0]
              corr_list.append(b)
          corr_df = pd.concat([pd.Series(feature_list),pd.Series(corr_list)],axis=1).fillna(0)
       
          # Make a data frame. Column 1 is the feature, and Column 2 is the correlation coefficient
          corr_df.columns  = ['Variable','Corr']
          corr_df['Sign'] = np.where(corr_df['Corr']>0,'red','blue')
          
          shap_abs = np.abs(shap_v)
          k=pd.DataFrame(shap_abs.mean()).reset_index()
          k.columns = ['Variable','SHAP_abs']
          k2 = k.merge(corr_df,left_on = 'Variable',right_on='Variable',how='inner')
          k2 = k2.sort_values(by='SHAP_abs',ascending = True)
          
          k2_f = k2[['Variable', 'SHAP_abs', 'Corr']]
          k2_f['SHAP_abs'] = k2_f['SHAP_abs'] * np.sign(k2_f['Corr'])
          k2_f.drop(columns='Corr', inplace=True)
          k2_f.rename(columns={'SHAP_abs': 'SHAP'}, inplace=True)
          
          return k2_f
      
      foo_all = pd.DataFrame()
      
      for k,v in list(enumerate(model.classes_)):
      
          foo = get_ABS_SHAP(shap_values[k], X_test)
          foo['class'] = v
          foo_all = pd.concat([foo_all,foo])
      
      import plotly_express as px
      px.bar(foo_all,x='SHAP', y='Variable', color='class')
      

      这导致

      【讨论】:

      • 该代码对我不起作用。所以我尝试按照上面的答案修复它。我仍然得到一些警告。我认为最好的办法是修复它们并在答案中发布更新的代码。
      【解决方案3】:

      SHAP 值作为列表返回。您可以通过它们的索引访问相关的 SHAP 绝对值。

      对于 0 类的摘要图,代码为

      shap.summary_plot(shap_values[0], X_test)
      

      【讨论】:

        【解决方案4】:

        这是@quant的代码的更新代码:

        import pandas as pd
        import random
        
        import numpy as np
        
        import xgboost
        import shap
        
        from sklearn.model_selection import train_test_split
        
        import plotly_express as px
        
        
        foo = pd.DataFrame({'id':[1,2,3,4,5,6,7,8,9,10],
                           'var1':random.sample(range(1, 100), 10),
                           'var2':random.sample(range(1, 100), 10),
                           'var3':random.sample(range(1, 100), 10),
                           'class': ['a','a','a','a','a','b','b','c','c','c']})
        
        foo['class'], _ = pd.factorize(foo['class'], sort = True)
        
        cl_cols = foo.filter(regex='var').columns
        X_train, X_test, y_train, y_test = train_test_split(foo[cl_cols],
                                                                foo[['class']],
                                                                test_size=0.33, random_state=42)
        
        model = xgboost.XGBClassifier(objective="multi:softmax")
        model.fit(X_train, y_train)
        
        shap_values = shap.TreeExplainer(model).shap_values(X_test)
        
        
        
        
        
        
        
        def get_ABS_SHAP(df_shap,df):
            #import matplotlib as plt
            # Make a copy of the input data
            shap_v = pd.DataFrame(df_shap)
            feature_list = df.columns
            shap_v.columns = feature_list
            df_v = df.copy().reset_index().drop('index',axis=1)
            
            # Determine the correlation in order to plot with different colors
            corr_list = list()
            for i in feature_list:
                b = np.corrcoef(shap_v[i],df_v[i])[1][0]
                corr_list.append(b)
            corr_df = pd.concat([pd.Series(feature_list),pd.Series(corr_list)],axis=1).fillna(0)
         
            # Make a data frame. Column 1 is the feature, and Column 2 is the correlation coefficient
            corr_df.columns  = ['Variable','Corr']
            corr_df['Sign'] = np.where(corr_df['Corr']>0,'red','blue')
            
            shap_abs = np.abs(shap_v)
            k=pd.DataFrame(shap_abs.mean()).reset_index()
            k.columns = ['Variable','SHAP_abs']
            k2 = k.merge(corr_df,left_on = 'Variable',right_on='Variable',how='inner')
            k2 = k2.sort_values(by='SHAP_abs',ascending = True)
            
            k2_f = k2[['Variable', 'SHAP_abs', 'Corr']]
            k2_f['SHAP_abs'] = k2_f['SHAP_abs'] * np.sign(k2_f['Corr'])
            k2_f.drop(columns='Corr', inplace=True)
            k2_f.rename(columns={'SHAP_abs': 'SHAP'}, inplace=True)
            
            return k2_f
        
        foo_all = pd.DataFrame()
        
        for k,v in list(enumerate(model.classes_)):
        
            foo = get_ABS_SHAP(shap_values[k], X_test)
            foo['class'] = v
            foo_all = pd.concat([foo_all,foo])
        
        px.bar(foo_all,x='SHAP', y='Variable', color='class')
        

        【讨论】:

          猜你喜欢
          • 2019-12-15
          • 2021-03-09
          • 2023-02-13
          • 2021-03-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-01
          • 2016-04-07
          相关资源
          最近更新 更多