Package: xai-benchmark
xai-benchmarkOpen and extensible benchmark for XAI methodsDescriptionXAIB is an open benchmark that provides a way to compare different XAI methods using broad set of metrics that measure different aspects of interpretabilityInstallationpip3installxai-benchmarkRemember to create virtual environment if you need one.After the installation you can verify the package by printing out its version:importxaibprint(xaib.__version__)To use all explainers you should also installexplainers_requirements.txtwhich can be done
directlypip3install-rhttps://raw.githubusercontent.com/oxid15/xai-benchmark/master/explainers_requirements.txtResultsUpdated results table can be foundhosted hereHow to useIntroductionXAIB is build to bring various data, models and explainers together and
measure quality of an explainer in different ways.The setup is formed from particular Dataset, Model and Explainer.Case stands for some interpretability quality which we are trying to proxy numerically
using Metrics. Since there are always more than one way to measure something, one Case
may (and should ideally) have several metrics inside.Read more onCasesandMetricsin documentation.ReproduceFull run will be available soon.Until then, there are regularpipelinesFor the implementation of the evaluation procedure, you can visitxaib/evaluationTry methodAll explanation methods in XAIB have the same input and output interface which allows to
use them easily and compare.If you want to run an Explainer and see the results you can do this:fromxaib.explainers.feature_importance.lime_explainerimportLimeExplainerfromxaib.evaluationimportDatasetFactory,ModelFactory# Get the dataset and train the modeltrain_ds,test_ds=DatasetFactory().get('synthetic')model=ModelFactory(train_ds,test_ds).get('svm')# You can also get the default one using ExplainerFactoryexplainer=LimeExplainer(train_ds,labels=[0,1])# Obtain batch from datasetsample=[test_ds[i]['item']foriinrange(10)]# Obtain explanationsexplanations=explainer.predict(sample,model)print(explanations)Evaluate methodTo evaluate some existing method on all
cases you should create a default setup and run itfromxaib.evaluationimportDatasetFactory,ModelFactoryfromxaib.evaluation.example_selectionimportExplainerFactory,ExperimentFactoryfromxaib.evaluation.utilsimportvisualize_resultstrain_ds,test_ds=DatasetFactory().get('synthetic')model=ModelFactory(train_ds,test_ds).get('knn')explainer=ExplainerFactory(train_ds,model).get('knn')# Run all experiments on chosen methodexperiment_factory=ExperimentFactory(repo_path='results',explainers={'knn':explainer},test_ds=test_ds,model=model,batch_size=10)experiments=experiment_factory.get('all')fornameinexperiments:experiments[name]()# Save plot to the results foldervisualize_results('results','results/results.png')How to contributeAny contributions are welcome! You can help to extend
the picture of XAI-methods quality by adding:New DatasetNew ModelNew ExplainerNew MetricNew Case (?)- please, fill the issue first to discussAdd datasetNew datasets may extend our understanding of how different explainers
behave in context of different domains and tasks.To add your dataset, you should provide a Wrapper, which will
download or access prepared data from disk.Create data wrapperFirst you need to create a wrapper with required interface and fieldsimportnumpyasnpfromxaibimportDatasetclassNewDataset(Dataset):"""Here the documentation on data should be filled"""def__init__(self,split,*args,**kwargs)->None:super().__init__(*args,**kwargs)# It is important to set the name# the name will be used to identify a datasetself.name='new_dataset'# While creating you can download and cache data,# define splits, etcifsplit=='train':self._data=[(0,1,2),(3,4,5),(6,7,8)]self._labels=[0,1,0]elifsplit=='test':self._data=[(9,10,11),(12,13,14)]self._labels=[1,0]def__getitem__(self,index):# This form of returning items is required - Dict[str, np.ndarray[Any]]return{'item':np.asarray(self._data[index]),'label':np.asarray(self._labels[index])}def__len__(self):returnlen(self._data)Test new datasetBefore adding your implementation directly into source code, it would be useful to
test how it will work with standard XAIB setupfromxaib.evaluationimportDatasetFactory,ModelFactoryfromxaib.evaluation.feature_importanceimportExplainerFactory,ExperimentFactoryfromxaib.evaluation.utilsimportvisualize_results# Here you create your datatrain_ds,test_ds=NewDataset('train'),NewDataset('test')# And then pass it furthermodel=ModelFactory(train_ds,test_ds).get('svm')explainers=ExplainerFactory(train_ds,model,labels=[0,1]).get('all')experiment_factory=ExperimentFactory(repo_path='results',explainers=explainers,test_ds=test_ds,model=model,batch_size=10)experiments=experiment_factory.get('all')fornameinexperiments:experiments[name]()visualize_results('results','results/results.png')Integrate new datasetFinally you can integrate your dataset into the source code.To do that you need to add it intoxaib.datasetsmodule
and then make a constructor for the Factory.# xaib/evaluation/dataset_factory.py# ...from..datasets.new_datasetimportNewDataset# ...# Create a constructor - function that will build your dataset# it should provide all defaults neededdefnew_dataset():returnNewDataset('train'),NewDataset('test')classDatasetFactory(Factory):def__init__(self)->None:# ...# add it to the factoryself._constructors['new_dataset']=lambda:new_dataset()Add modelNew models and model classes provide information on how good explainers
are in some particular cases.Create model wrapperFirst model wrapper should be implemented. It has many
required methods that should be implemented.
For examplefitandevaluatemethods are needed
to be able to train the model on different datasets
see specification inxaib/baseand examples inxaib/evaluation/model_factory.pyimportnumpyasnpfromxaibimportModelclassNewModel(Model):"""Here the documentation on model should be filled"""def__init__(self,const,*args,**kwargs):super().__init__(*args,**kwargs)self.const=const# It is important to set the name# the name will be used to identify a modelself.name='new_model'deffit(self,x,y):passdefevaluate(self,x,y):passdefpredict(self,x):returnnp.array([self.constfor_inrange(len(x))])defsave(self,filepath,*args,**kwargs):withopen(filepath,'w')asf:f.write(str(self.const))defload(self,filepath,*args,**kwargs):withopen(filepath,'r')asf:self.const=float(f.read())# load does not return anything - just fills# internal stateTest new modelBefore adding your implementation directly into source code, it would be useful to
test how it will work with standard XAIB setupfromxaib.evaluationimportDatasetFactoryfromxaib.evaluation.feature_importanceimportExplainerFactory,ExperimentFactoryfromxaib.evaluation.utilsimportvisualize_results# Create your modelmodel=NewModel(const=1)train_ds,test_ds=DatasetFactory().get('synthetic')explainers={'shap':ExplainerFactory(train_ds,model,labels=[0,1]).get('shap')}experiment_factory=ExperimentFactory(repo_path='results',explainers=explainers,test_ds=test_ds,model=model,# and put it herebatch_size=10)experiments=experiment_factory.get('all')fornameinexperiments:experiments[name]()visualize_results('results','results/results.png')Integrate new modelFinally you can integrate your model into the source code.To do that you need to add it intoxaib.modelsmodule
and then make a constructor for the Factory.# xaib/evaluation/model_factory.py# ...from..models.new_modelimportNewModel# ...# Create a constructor - function that will build your modeldefnew_model(const):returnNewModel(const=const)classModelFactory(Factory):def__init__(self)->None:# ...# add it to the factoryself._constructors['new_model']=lambda:new_model(const=1)Add explainerExplainers are the heart of this benchmark, they are being thorougly tested
and the more of them added, the more we knowCreate wrapperExplainers wrappers are less demanding than model's which makes them
easier to be implementedimportnumpyasnpfromxaibimportExplainerclassNewExplainer(Explainer):def__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)# name is essential to put explainer into# results table correctlyself.name='new_explainer'defpredict(self,x,model,*args,**kwargs):returnnp.random.rand(len(x),len(x[0]))Test new explainerBefore adding your implementation directly into source code, it would be useful to
test how it will work with standard XAIB setupfromxaib.evaluationimportDatasetFactory,ModelFactoryfromxaib.evaluation.feature_importanceimportExperimentFactoryfromxaib.evaluation.utilsimportvisualize_resultstrain_ds,test_ds=DatasetFactory().get('synthetic')model=ModelFactory(train_ds,test_ds).get('svm')explainers={'new_explainer':NewExplainer()}experiment_factory=ExperimentFactory(repo_path='results',explainers=explainers,test_ds=test_ds,model=model,batch_size=10)experiments=experiment_factory.get('all')fornameinexperiments:experiments[name]()visualize_results('results','results/results.png')Integrate new explainerFinally you can integrate your explainer into the source code.To do that you need to add it intoxaib.explainersmodule
and then make a constructor for the Factory.# xaib/evaluation/feature_importance/explainer_factory.py# ...from...explainers.feature_importance.new_explainerimportNewExplainer# ...# Create a constructor - function that will build your explainerdefnew_explainer():returnNewExplainer()classExplainerFactory(Factory):def__init__(self)->None:# ...# add it to the factoryself._constructors['new_explainer']=lambda:new_explainer()Add metricMetrics are ways to numerically assess the quality of explainers and are parts of
CasesCreate metricFirst you need to create a Metric object - which will accept and explainer and data
and return some valuefromxaibimportMetricclassNewMetric(Metric):def__init__(self,ds,model*args,**kwargs):super().__init__(ds,model,*args,**kwargs)self.name='new_metric'self.direction='down'defcompute(self,explainer,*args,batch_size=1,expl_kwargs=None,**kwargs):ifexpl_kwargsisNone:expl_kwargs={}dl=SimpleDataloader(self._ds,batch_size=batch_size)forbatchintqdm(dl):# get explanationsex=expl.predict(batch['item'],self._model,**expl_kwargs)# Here compute and return your metricreturnnp.random.rand()Test new metricBefore adding your implementation directly into source code, it would be useful to
test how it will work with standard XAIB setupSince metrics are more low-level objects, they need special treatment
when tested. Basically you need to create metric and append it to the existing
Case of choice.fromxaib.evaluationimportDatasetFactory,ModelFactoryfromxaib.evaluation.feature_importanceimportExplainerFactoryfromxaib.evaluation.utilsimportvisualize_results,experimenttrain_ds,test_ds=DatasetFactory().get('synthetic')model=ModelFactory(train_ds,test_ds).get('svm')explainers=ExplainerFactory(train_ds,model,labels=[0,1]).get('all')metric=NewMetric(test_ds,model)@experiment('results',explainers=explainers,metrics_kwargs={'other_disagreement':dict(expls=list(explainers.values()))})defcoherence():case=CoherenceCase(test_ds,model)case.add_metric('new_metric',metric)returncasecoherence()visualize_results('results','results/results.png')Integrate new metric# xaib/cases/feature_importance/coherence_case.py# ...from...metrics.feature_importanceimportNewMetricclassCoherenceCase(Case):def__init__(self,ds:Dataset,model:Model,*args:Any,**kwargs:Any)->None:super().__init__(ds,model,*args,**kwargs)# ...self._metric_objs['new_metric']=NewMetric(ds,model)LicenseMITVersionsThis project follows Semantic Versioning -semver.org