Git Product home page Git Product logo

dice's Introduction

PyPiVersion CondaVersion MITlicense PythonSupport Downloads

BuildStatusTests BuildStatusNotebooks

Diverse Counterfactual Explanations (DiCE) for ML

How to explain a machine learning model such that the explanation is truthful to the model and yet interpretable to people?

Ramaravind K. Mothilal, Amit Sharma, Chenhao Tan

FAT* '20 paper | Docs | Example Notebooks | Live Jupyter notebook Binder

Blog Post: Explanation for ML using diverse counterfactuals

Case Studies: Towards Data Science (Hotel Bookings) | Analytics Vidhya (Titanic Dataset)

Visualizing a counterfactual explanation

Explanations are critical for machine learning, especially as machine learning-based systems are being used to inform decisions in societally critical domains such as finance, healthcare, education, and criminal justice. However, most explanation methods depend on an approximation of the ML model to create an interpretable explanation. For example, consider a person who applied for a loan and was rejected by the loan distribution algorithm of a financial company. Typically, the company may provide an explanation on why the loan was rejected, for example, due to "poor credit history". However, such an explanation does not help the person decide what they should do next to improve their chances of being approved in the future. Critically, the most important feature may not be enough to flip the decision of the algorithm, and in practice, may not even be changeable such as gender and race.

DiCE implements counterfactual (CF) explanations that provide this information by showing feature-perturbed versions of the same person who would have received the loan, e.g., you would have received the loan if your income was higher by $10,000. In other words, it provides "what-if" explanations for model output and can be a useful complement to other explanation methods, both for end-users and model developers.

Barring simple linear models, however, it is difficult to generate CF examples that work for any machine learning model. DiCE is based on recent research that generates CF explanations for any ML model. The core idea is to setup finding such explanations as an optimization problem, similar to finding adversarial examples. The critical difference is that for explanations, we need perturbations that change the output of a machine learning model, but are also diverse and feasible to change. Therefore, DiCE supports generating a set of counterfactual explanations and has tunable parameters for diversity and proximity of the explanations to the original input. It also supports simple constraints on features to ensure feasibility of the generated counterfactual examples.

Installing DICE

DiCE supports Python 3+. The stable version of DiCE is available on PyPI.

pip install dice-ml

DiCE is also available on conda-forge.

conda install -c conda-forge dice-ml

To install the latest (dev) version of DiCE and its dependencies, clone this repo and run pip install from the top-most folder of the repo:

pip install -e .

If you face any problems, try installing dependencies manually.

pip install -r requirements.txt
# Additional dependendies for deep learning models
pip install -r requirements-deeplearning.txt
# For running unit tests
pip install -r requirements-test.txt

Getting started with DiCE

With DiCE, generating explanations is a simple three-step process: set up a dataset, train a model, and then invoke DiCE to generate counterfactual examples for any input. DiCE can also work with pre-trained models, with or without their original training data.

import dice_ml
from dice_ml.utils import helpers # helper functions
from sklearn.model_selection import train_test_split

dataset = helpers.load_adult_income_dataset()
target = dataset["income"] # outcome variable
train_dataset, test_dataset, _, _ = train_test_split(dataset,
                                                     target,
                                                     test_size=0.2,
                                                     random_state=0,
                                                     stratify=target)
# Dataset for training an ML model
d = dice_ml.Data(dataframe=train_dataset,
                 continuous_features=['age', 'hours_per_week'],
                 outcome_name='income')

# Pre-trained ML model
m = dice_ml.Model(model_path=dice_ml.utils.helpers.get_adult_income_modelpath(),
                  backend='TF2', func="ohe-min-max")
# DiCE explanation instance
exp = dice_ml.Dice(d,m)

For any given input, we can now generate counterfactual explanations. For example, the following input leads to class 0 (low income) and we would like to know what minimal changes would lead to a prediction of 1 (high income).

# Generate counterfactual examples
query_instance = test_dataset.drop(columns="income")[0:1]
dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class="opposite")
# Visualize counterfactual explanation
dice_exp.visualize_as_dataframe()

List of counterfactual examples

You can save the generated counterfactual examples in the following way.

# Save generated counterfactual examples to disk
dice_exp.cf_examples_list[0].final_cfs_df.to_csv(path_or_buf='counterfactuals.csv', index=False)

For more details, check out the docs/source/notebooks folder. Here are some example notebooks:

Supported methods for generating counterfactuals

DiCE can generate counterfactual examples using the following methods.

Model-agnostic methods

  • Randomized sampling
  • KD-Tree (for counterfactuals within the training data)
  • Genetic algorithm

See model-agnostic notebook for code examples on using these methods.

Gradient-based methods

The last two methods require a differentiable model, such as a neural network. If you are interested in a specific method, do raise an issue here.

Supported use-cases

Data

DiCE does not need access to the full dataset. It only requires metadata properties for each feature (min, max for continuous features and levels for categorical features). Thus, for sensitive data, the dataset can be provided as:

d = data.Data(features={
                   'age':[17, 90],
                   'workclass': ['Government', 'Other/Unknown', 'Private', 'Self-Employed'],
                   'education': ['Assoc', 'Bachelors', 'Doctorate', 'HS-grad', 'Masters', 'Prof-school', 'School', 'Some-college'],
                   'marital_status': ['Divorced', 'Married', 'Separated', 'Single', 'Widowed'],
                   'occupation':['Blue-Collar', 'Other/Unknown', 'Professional', 'Sales', 'Service', 'White-Collar'],
                   'race': ['Other', 'White'],
                   'gender':['Female', 'Male'],
                   'hours_per_week': [1, 99]},
         outcome_name='income')

Model

We support pre-trained models as well as training a model. Here's a simple example using Tensorflow.

sess = tf.InteractiveSession()
# Generating train and test data
train, _ = d.split_data(d.normalize_data(d.one_hot_encoded_data))
X_train = train.loc[:, train.columns != 'income']
y_train = train.loc[:, train.columns == 'income']
# Fitting a dense neural network model
ann_model = keras.Sequential()
ann_model.add(keras.layers.Dense(20, input_shape=(X_train.shape[1],), kernel_regularizer=keras.regularizers.l1(0.001), activation=tf.nn.relu))
ann_model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))
ann_model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(0.01), metrics=['accuracy'])
ann_model.fit(X_train, y_train, validation_split=0.20, epochs=100, verbose=0, class_weight={0:1,1:2})

# Generate the DiCE model for explanation
m = model.Model(model=ann_model)

Check out the Getting Started notebook to see code examples on using DiCE with sklearn and PyTorch models.

Explanations

We visualize explanations through a table highlighting the change in features. We plan to support an English language explanation too!

Feasibility of counterfactual explanations

We acknowledge that not all counterfactual explanations may be feasible for a user. In general, counterfactuals closer to an individual's profile will be more feasible. Diversity is also important to help an individual choose between multiple possible options.

DiCE provides tunable parameters for diversity and proximity to generate different kinds of explanations.

dice_exp = exp.generate_counterfactuals(query_instance,
                total_CFs=4, desired_class="opposite",
                proximity_weight=1.5, diversity_weight=1.0)

Additionally, it may be the case that some features are harder to change than others (e.g., education level is harder to change than working hours per week). DiCE allows input of relative difficulty in changing a feature through specifying feature weights. A higher feature weight means that the feature is harder to change than others. For instance, one way is to use the mean absolute deviation from the median as a measure of relative difficulty of changing a continuous feature. By default, DiCE computes this internally and divides the distance between continuous features by the MAD of the feature's values in the training set. We can also assign different values through the feature_weights parameter.

# assigning new weights
feature_weights = {'age': 10, 'hours_per_week': 5}
# Now generating explanations using the new feature weights
dice_exp = exp.generate_counterfactuals(query_instance,
                total_CFs=4, desired_class="opposite",
                feature_weights=feature_weights)

Finally, some features are impossible to change such as one's age or race. Therefore, DiCE also allows inputting a list of features to vary.

dice_exp = exp.generate_counterfactuals(query_instance,
                total_CFs=4, desired_class="opposite",
                features_to_vary=['age','workclass','education','occupation','hours_per_week'])

It also supports simple constraints on features that reflect practical constraints (e.g., working hours per week should be between 10 and 50 using the permitted_range parameter).

For more details, check out this notebook.

The promise of counterfactual explanations

Being truthful to the model, counterfactual explanations can be useful to all stakeholders for a decision made by a machine learning model that makes decisions.

  • Decision subjects: Counterfactual explanations can be used to explore actionable recourse for a person based on a decision received by a ML model. DiCE shows decision outcomes with actionable alternative profiles, to help people understand what they could have done to change their model outcome.
  • ML model developers: Counterfactual explanations are also useful for model developers to debug their model for potential problems. DiCE can be used to show CF explanations for a selection of inputs that can uncover if there are any problematic (in)dependences on some features (e.g., for 95% of inputs, changing features X and Y change the outcome, but not for the other 5%). We aim to support aggregate metrics to help developers debug ML models.
  • Decision makers: Counterfactual explanations may be useful to decision-makers such as doctors or judges who may use ML models to make decisions. For a particular individual, DiCE allows probing the ML model to see the possible changes that lead to a different ML outcome, thus enabling decision-makers to assess their trust in the prediction.
  • Decision evaluators: Finally, counterfactual explanations can be useful to decision evaluators who may be interested in fairness or other desirable properties of an ML model. We plan to add support for this in the future.

Roadmap

Ideally, counterfactual explanations should balance between a wide range of suggested changes (diversity), and the relative ease of adopting those changes (proximity to the original input), and also follow the causal laws of the world, e.g., one can hardly lower their educational degree or change their race.

We are working on adding the following features to DiCE:

  • Support for using DiCE for debugging machine learning models
  • Constructed English phrases (e.g., desired outcome if feature was changed) and other ways to output the counterfactual examples
  • Evaluating feature attribution methods like LIME and SHAP on necessity and sufficiency metrics using counterfactuals (see this paper)
  • Support for Bayesian optimization and other algorithms for generating counterfactual explanations
  • Better feasibility constraints for counterfactual generation

Citing

If you find DiCE useful for your research work, please cite it as follows.

Ramaravind K. Mothilal, Amit Sharma, and Chenhao Tan (2020). Explaining machine learning classifiers through diverse counterfactual explanations. Proceedings of the 2020 Conference on Fairness, Accountability, and Transparency.

Bibtex:

@inproceedings{mothilal2020dice,
        title={Explaining machine learning classifiers through diverse counterfactual explanations},
        author={Mothilal, Ramaravind K and Sharma, Amit and Tan, Chenhao},
        booktitle={Proceedings of the 2020 Conference on Fairness, Accountability, and Transparency},
        pages={607--617},
        year={2020}
}

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.

dice's People

Contributors

amit-sharma avatar azz147 avatar bdavj avatar bgalvao avatar christophm avatar daikikatsuragawa avatar danielemorotti avatar david-cortes avatar divyat09 avatar gaugup avatar gregorybchris avatar hsm207 avatar lange-martin avatar microsoftopensource avatar msftgits avatar raam93 avatar riedgar-ms avatar rmazzine avatar soundarya98 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dice's Issues

Float features wrongly converted to integers in dice_ml.Data

There is currently a bug that turns all float features into integers.

Problems causing this:

A reprex:

import dice_ml
from dice_ml.utils import helpers # helper functions
# Dataset for training an ML model
df = helpers.load_adult_income_dataset()
d = dice_ml.Data(dataframe=df,
                         continuous_features=['age', 'hours_per_week'],
                         outcome_name='income')
print(df.head())
print(d.get_decimal_precisions())

df2 = df.copy()
# Make it float
df2['age'] = df2['age'] + 0.1 

d2 = dice_ml.Data(dataframe=df,
                         continuous_features=['age', 'hours_per_week'],
                         outcome_name='income')

print(df2.head())

print(d.get_decimal_precisions())

DiCE genetic question

You have based DiCE genetic on GeCo if I understand correctly. Is there a way to 'group' features as they define in their paper;
"The first statement says that education and income are correlated: GeCo will consider only counterfactual values that occur together in the data."
So for instance if we have feature A and B, make sure that the algorithm always generates counterfactuals for which the combination of A=a and B=b exists in the training data.

'LogisticRegression' object has no attribute backend

Hey there,
I'm trying to create an exp like this example
exp = dice_ml.Dice(data,m)
I'm using # XGBClassifier and # LogisticRegression as prediction model
but all the time I encounter in this error :
AttributeError: 'LogisticRegression' object has no attribute 'backend'
AttributeError: 'XGBClassifier ' object has no attribute 'backend'

Anybody can help me please?I will appreciate a lot!
Thanks
Hen

TypeError: get_decimal_precisions() got an unexpected keyword argument 'output_type'

d = dice_ml.Data(dataframe=pipdata, continuous_features=['A','B','C','D','E','F','H','I','J','K'],outcome_name='G')from tensorflow.keras.models import Sequential
from tensorflow.python.keras.layers import Dense
from tensorflow.python.keras.wrappers.scikit_learn import KerasRegressor
import tensorflow.compat.v1 as tf
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.ERROR)
from sklearn.model_selection import train_test_split
sess = tf.InteractiveSession()

Generating train and test data

datasetX = pipdata.drop("G",axis=1)
datasetY = pipdata["G"]

X_train,X_test,Y_train,Y_test = train_test_split(datasetX,datasetY,test_size=0.2,random_state=0)

Fitting a dense neural network model

ann_model = Sequential()
ann_model.add(Dense(6, input_shape=(X_train.shape[1],), activation=tf.nn.relu))
ann_model.add(Dense(1, activation=tf.nn.sigmoid))
ann_model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
history=ann_model.fit(X_train, y_train, validation_split=0.20, epochs=30, verbose=0)
#, class_weight={0:1,1:2})
history
m = dice_ml.Model(model=ann_model,backend='sklearn')

new_d = dice_ml.Data(features={
'A':[-130, 4500],
'B':[-150,7000],
'C':[-54,6200],
'D':[-150,8900],
'E':[-26,1250],
'F':[-26,2545],
'H':[-625,8270],
'I':[-30,1581],
'J':[-135,7364],
'K':[-72,4666]},
outcome_name='G')
exp = dice_ml.Dice(new_d,m)
exp

---> 37 self.precisions = self.data_interface.get_decimal_precisions(output_type="dict")
38 if self.data_interface.outcome_name in self.precisions:
39 self.outcome_precision = [self.precisions[self.data_interface.outcome_name]]

TypeError: get_decimal_precisions() got an unexpected keyword argument 'output_type'

Loading Pytorch customer models

Hello! Thank you so much for the great work!

But could you please tell me how to load a trained pytorch models .pth? I always get this error:

AttributeError: '_IncompatibleKeys' object has no attribute 'seek'. You can only torch.load from a file that is seekable. Please pre-load the data into a buffer like io.BytesIO and try to load from it instead.

Thank you!

Query instances are all classified as (original outcome: 0)

I have a credit scoring dataset with a binary target variable (bad borrower=1, good borrower=0). I have trained a RF, XGB and MLP and then wanted to generate counterfactuals on the exact same observation (where bad borrower=1), for each of the models.

Now, for some reason the MLP outputs "Query instance (original outcome: 0)". The RF and XGB both correctly output "Query instance (original outcome: 1) for the same instance.

I then created a subsample of the dataset with only the bad borrowers and each of them were classified as "Query instance (original outcome : 0)" with the MLP classifier. It seems as if sklearn.neural_network.MLPClassifier() does not work properly with the DiCE framework.

Creating DiCE Explanation Instance

I am attempting to use DiCE on a classification problem and form a DiCE Explanation Instance as such:

# DiCE explanation instance
exp = dice_ml.Dice(d,m)

However, after spending time on this I am running into difficulty in defining m for this purpose.

I have created a neural network configuration and the model trained successfully.

>>> from tensorflow.keras.models import Sequential
>>> from tensorflow.python.keras.layers import Dense
>>> from tensorflow.python.keras.wrappers.scikit_learn import KerasRegressor

>>> sess = tf.InteractiveSession()

>>> # Fitting a dense neural network model
>>> ann_model = Sequential()
>>> ann_model.add(Dense(20, input_shape=(x1.shape[1],), activation=tf.nn.relu))
>>> ann_model.add(Dense(1, activation=tf.nn.sigmoid))
>>> ann_model.compile(loss='binary_crossentropy', metrics=['accuracy'])
>>> ann_model.fit(x1, y, validation_split=0.20, epochs=100)

Epoch 1/100
320/320 [==============================] - 0s 226us/sample - loss: 1.3851 - acc: 0.6750 - val_loss: 1.1140 - val_acc: 0.6375
Epoch 2/100
320/320 [==============================] - 0s 74us/sample - loss: 0.9420 - acc: 0.6969 - val_loss: 0.9910 - val_acc: 0.6375
...

The model is of the type:

tensorflow.python.keras.engine.sequential.Sequential

However, when I attempt to generate the DiCE model:

# Generate the DiCE model for explanation
m = model.Model(model=ann_model)

I get the error:

NameError                                 Traceback (most recent call last)

<ipython-input-15-ec54e1758be4> in <module>()
      1 # Generate the DiCE model for explanation
----> 2 m = model.Model(model=ann_model)

NameError: name 'model' is not defined

For reference, I am using TensorFlow 1.15 and Python 3.6.9. Any help or advice greatly appreciated.

IndexError when model has no categorical features

Hi there

I run into an error when I try to use DiCE with a Keras model that only uses numerical features.
The error I get is IndexError: list index out of range.

From the paper, I understood that DiCE should work with only numerical features as well?
In the code, it seems that when no categorical features are present, still code is called to work with the categorical features which throws the IndexError in public_data_interface.py .

Here is a reproducible example which is adapted from one of your notebooks:

import tensorflow as tf
from tensorflow import keras
import dice_ml
from dice_ml.utils import helpers # helper functions

dataset = helpers.load_adult_income_dataset()
categorical_features = ['workclass', 'education', 'marital_status', 'occupation', 'race', 'gender']
dataset =  dataset.drop(categorical_features, axis=1)

features = dataset.columns
features = [feature for feature in features if feature != 'income']
d = dice_ml.Data(dataframe=dataset, continuous_features=features, outcome_name='income')

sess = tf.InteractiveSession()
train, _ = d.split_data(d.normalize_data(d.one_hot_encoded_data))
X_train = train.loc[:, train.columns != 'income']
y_train = train.loc[:, train.columns == 'income']

ann_model = keras.Sequential()
ann_model.add(keras.layers.Dense(20, input_shape=(X_train.shape[1],), kernel_regularizer=keras.regularizers.l1(0.001), activation=tf.nn.relu))
ann_model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))

ann_model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(0.01), metrics=['accuracy'])
ann_model.fit(X_train, y_train, validation_split=0.20, epochs=1, verbose=1,
class_weight={0:1,1:2})
query_instance = {'age':22,
                  'hours_per_week': 45}
m = dice_ml.Model(model=ann_model)
exp = dice_ml.Dice(d, m)
dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=1, desired_class="opposite")

AttributeError: 'Tensor' object has no attribute 'numpy'

Generate counterfactual examples

dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class="opposite")#,proximity_weight=1.5, diversity_weight=1.0)

Visualize counterfactual explanation

dice_exp.visualize_as_dataframe()

--> 116 temp_preds = self.model.get_output(input_instance).numpy()
117 return np.array([preds[(self.num_output_nodes-1):] for preds in temp_preds], dtype=np.float32)
118

AttributeError: 'Tensor' object has no attribute 'numpy'

'desired_range' is not working, 'permitted_range' returns value out of the range, flexibility of 'permitted_range'

Hi, I have the three questions.

Firstly, I get this error when I try to use 'desired_range'.

Desired_Range_Error

Secondly, sometimes permitted_range cannot ensure the controlled feature is in the range. For instance, the second image shows that the age is not in the range of 23 and 24.

Lastly, it seems that I have to set the range all for continuous features when using 'permitted_range'. For instance, in the second image I have to control another continuous feature 'hours_per_week' although I only want to control the 'age' feature.
Otherwise, I will get a key error because I only control one continuous feature-'age'.
How can I only control one continuous feature by DICE?

Image2_Permitted_Range

Thank you very much

How to save the dice explainer object to file?

Hi, I'm trying to write the dice_ml object to a file and I'm getting the follow error:

backend = 'TF'+tf.__version__[0]
dice_model = dice_ml.Model(model=model, backend=backend)

exp = dice_ml.Dice(d, dice_model)

from joblib import load, dump

with open("explainer_dice.joblib", "wb") as f:
    dump(exp, f, compress="lz4")
TypeError: cannot pickle '_thread.RLock' object

How to use dice for a pipeline that performs one-hot-encoding when requesting prediction to tensorflow model

Hi,

I hope I can explain my current situation clear enough.

At the moment I've been trying to evaluate multiple model-agnostic methods to generate explanations on a Keras TensorFlow neural network model. Since each XAI method has different requirements and some need to have a numerical encoding to perform the perturbances on categorical data,

The approach that I had implemented to be able to use multiple XAI methods is to have a "numerical encoded" dataset with 18 features (3 numerical and 15 categorical) where categories are encoded as integers. To feed this dataset to the keras NN model I used a column transformer pipeline that performs a ohe-hot-encoding of the 15 categorical variables and a min-max scaler for the numerical variables. Thus, when I call for a prediction i usually include the column transformer

nn.predict(ct.transform(x))

I am trying to initialize the dice explainer however I haven't been able to figure out how I could connect this pipeline step to the model and provide it to dice. If I tried to load the ordinal encoded dataset and provide the NN model, I get an error during initialization stating

"Input 0 of layer dense_104 is incompatible with the layer: expected axis -1 of input shape to have value 66 but received input with shape [1, 65]"

My column transformer takes 18 features and transforms them in 66 columns. I will continue to debug, but I would appreciate if you could let me know if the current design of my pipeline could be integrated to DICE explainer.

Let me know if something is not clear enough and I will try to further explain.

Thanks a lot

`visualize_as_dataframe` heading mistakenly produces "new outcome: 1" regardless of `desired_class` in `generate_counterfactuals`

import dice_ml
from dice_ml.utils import helpers # helper functions

# Dataset for training an ML model
d = dice_ml.Data(dataframe=helpers.load_adult_income_dataset(),
                 continuous_features=['age', 'hours_per_week'],
                 outcome_name='income')
# Pre-trained ML model
m = dice_ml.Model(model_path=dice_ml.utils.helpers.get_adult_income_modelpath(), backend="TF2")

# DiCE explanation instance
exp = dice_ml.Dice(d,m)

query_instance = {'age':22,
    'workclass':'Private',
    'education':'HS-grad',
    'marital_status':'Single',
    'occupation':'Service',
    'race': 'White',
    'gender':'Female',
    'hours_per_week': 45}

# Generate counterfactual examples
dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class=0)
# Visualize counterfactual explanation
dice_exp.visualize_as_dataframe()

Produces:

image

Problem with local feature importance in TF2

I'm using dice with sklearn models and it works great, but when using it with Tensorflow2 I can't get the important features.

Code where it gives error:

       query = pd.DataFrame([self.xts[i]], columns=self.id_list)
        e1 = exp.generate_counterfactuals(
            query,
            total_CFs=10,
            desired_class="opposite", 
            features_to_vary=lst_features,
            verbose=True,
            posthoc_sparsity_algorithm="binary",
        )
        imp = exp.local_feature_importance([self.xts[i]], cf_examples_list=e1.cf_examples_list)

This code works with sklearn but with TF2 I get this error

  ....
  imp = exp.local_feature_importance([self.xts[i]], cf_examples_list=e1.cf_examples_list)
  AttributeError: 'CounterfactualExamples' object has no attribute 'cf_examples_list'

Can important local features be obtained with TF2?

AttributeError: module 'tensorflow' has no attribute 'get_default_session'

Installed dice-ml from pypi.

import dice_ml
from dice_ml.utils import helpers # helper functions

d = dice_ml.Data(dataframe=helpers.load_adult_income_dataset(),
                 continuous_features=['age', 'hours_per_week'],
                 outcome_name='income')

m = dice_ml.Model(model_path=dice_ml.utils.helpers.get_adult_income_modelpath())

exp = dice_ml.Dice(d,m)

Getting the following error

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-d3dad7eed1c9> in <module>
      8 m = dice_ml.Model(model_path=dice_ml.utils.helpers.get_adult_income_modelpath())
      9 # DiCE explanation instance
---> 10 exp = dice_ml.Dice(d,m)

~/anaconda3/envs/diceml/lib/python3.6/site-packages/dice_ml/dice.py in __init__(self, data_interface, model_interface, **kwargs)
     15         """
     16 
---> 17         self.decide_implementation_type(data_interface, model_interface, **kwargs)
     18 
     19     def decide_implementation_type(self, data_interface, model_interface, **kwargs):

~/anaconda3/envs/diceml/lib/python3.6/site-packages/dice_ml/dice.py in decide_implementation_type(self, data_interface, model_interface, **kwargs)
     21 
     22         self.__class__  = decide(data_interface, model_interface)
---> 23         self.__init__(data_interface, model_interface, **kwargs)
     24 
     25 # To add new implementations of DiCE, add the class in explainer_interfaces subpackage and import-and-return the class in an elif loop as shown in the below method.

~/anaconda3/envs/diceml/lib/python3.6/site-packages/dice_ml/explainer_interfaces/dice_tensorflow1.py in __init__(self, data_interface, model_interface)
     26 
     27         # create TensorFLow session if one is not already created
---> 28         if tf.get_default_session() is not None:
     29             self.dice_sess = tf.get_default_session()
     30         else:

AttributeError: module 'tensorflow' has no attribute 'get_default_session'

My pip freeze result

absl-py==0.11.0
appnope==0.1.0
argon2-cffi==20.1.0
astunparse==1.6.3
async-generator==1.10
attrs==20.2.0
backcall==0.2.0
bleach==3.2.1
cachetools==4.1.1
certifi==2020.6.20
cffi==1.14.3
chardet==3.0.4
dataclasses==0.7
decorator==4.4.2
defusedxml==0.6.0
dice-ml==0.4
entrypoints==0.3
future==0.18.2
gast==0.3.3
google-auth==1.22.1
google-auth-oauthlib==0.4.2
google-pasta==0.2.0
grpcio==1.33.2
h5py==2.10.0
idna==2.10
importlib-metadata==2.0.0
ipykernel==5.3.4
ipython==7.16.1
ipython-genutils==0.2.0
ipywidgets==7.5.1
jedi==0.17.2
Jinja2==2.11.2
joblib==0.17.0
jsonschema==3.2.0
jupyter==1.0.0
jupyter-client==6.1.7
jupyter-console==6.2.0
jupyter-core==4.6.3
jupyterlab-pygments==0.1.2
Keras-Preprocessing==1.1.2
lazy-import==0.2.2
Markdown==3.3.3
MarkupSafe==1.1.1
mistune==0.8.4
mkl-fft==1.2.0
mkl-random==1.1.1
mkl-service==2.3.0
nbclient==0.5.1
nbconvert==6.0.7
nbformat==5.0.8
nest-asyncio==1.4.2
notebook==6.1.4
numpy @ file:///opt/concourse/worker/volumes/live/e6a5a904-ea21-4841-4eec-d53e1ac1014c/volume/numpy_and_numpy_base_1603479626870/work
oauthlib==3.1.0
olefile==0.46
opt-einsum==3.3.0
packaging==20.4
pandas==0.25.3
pandocfilters==1.4.3
parso==0.7.1
pexpect==4.8.0
pickleshare==0.7.5
Pillow @ file:///opt/concourse/worker/volumes/live/06069510-e277-4aed-54f4-6dfdcb84a461/volume/pillow_1603822272490/work
prometheus-client==0.8.0
prompt-toolkit==3.0.3
protobuf==3.13.0
ptyprocess==0.6.0
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.20
Pygments==2.7.2
pyparsing==2.4.7
pyrsistent==0.17.3
python-dateutil==2.8.1
pytz==2020.1
pyzmq==19.0.2
qtconsole==4.7.7
QtPy==1.9.0
requests==2.24.0
requests-oauthlib==1.3.0
rsa==4.6
scikit-learn==0.23.2
scipy==1.5.3
Send2Trash==1.5.0
six==1.15.0
tensorboard==2.3.0
tensorboard-plugin-wit==1.7.0
tensorflow==2.3.1
tensorflow-cpu==2.3.1
tensorflow-estimator==2.3.0
tensorflow-gpu==1.1.0
termcolor==1.1.0
terminado==0.9.1
testpath==0.4.4
threadpoolctl==2.1.0
torch==1.7.0
torchaudio==0.7.0a0+ac17b64
torchvision==0.8.1
tornado==6.0.4
traitlets==4.3.3
typing-extensions @ file:///tmp/build/80754af9/typing_extensions_1598376058250/work
urllib3==1.25.11
wcwidth==0.2.5
webencodings==0.5.1
Werkzeug==1.0.1
widgetsnbextension==3.5.1
wrapt==1.12.1
zipp==3.4.0

If I change torch or tensorflow version other issues crop up. .

If fresh dice-ml install done in a new env where dice only install torch and tensorflow, then the error is
AttributeError: module 'torch.jit' has no attribute '_script_if_tracing'

DiceRandom is not able to handle case when requested counterfactuals is zero

exp = Dice(d, m, method="random")
query_instance = x_train[1:2]
e1 = exp.generate_counterfactuals(query_instance, total_CFs=0, desired_range=None,desired_class="opposite",
permitted_range=None, features_to_vary="all")
e1.visualize_as_dataframe(show_only_changes=True)


ValueError Traceback (most recent call last)
in
2 query_instance = x_train[1:2]
3 e1 = exp.generate_counterfactuals(query_instance, total_CFs=0, desired_range=None,desired_class="opposite",
----> 4 permitted_range=None, features_to_vary="all")
5 e1.visualize_as_dataframe(show_only_changes=True)

c:\users\gaugup\documents\github\dice\dice_ml\explainer_interfaces\explainer_base.py in generate_counterfactuals(self, query_instances, total_CFs, desired_class, desired_range, permitted_range, features_to_vary, stopping_threshold, posthoc_sparsity_param, posthoc_sparsity_algorithm, verbose, **kwargs)
87 posthoc_sparsity_algorithm=posthoc_sparsity_algorithm,
88 verbose=verbose,
---> 89 **kwargs)
90 cf_examples_arr.append(res)
91 return CounterfactualExplanations(cf_examples_list=cf_examples_arr)

c:\users\gaugup\documents\github\dice\dice_ml\explainer_interfaces\dice_random.py in _generate_counterfactuals(self, query_instance, total_CFs, desired_range, desired_class, permitted_range, features_to_vary, stopping_threshold, posthoc_sparsity_param, posthoc_sparsity_algorithm, sample_size, random_seed, verbose)
136 cfs_df = cfs_df.sample(total_CFs)
137 cfs_df.reset_index(inplace=True, drop=True)
--> 138 self.cfs_pred_scores = self.predict_fn(cfs_df)
139 cfs_df[self.data_interface.outcome_name] = self.get_model_output_from_scores(self.cfs_pred_scores)
140

c:\users\gaugup\documents\github\dice\dice_ml\explainer_interfaces\dice_random.py in predict_fn(self, input_instance)
225 def predict_fn(self, input_instance):
226 """prediction function"""
--> 227 return self.model.get_output(input_instance)

c:\users\gaugup\documents\github\dice\dice_ml\model_interfaces\base_model.py in get_output(self, input_instance)
38 input_instance = self.transformer.transform(input_instance)
39 if self.model_type == "classifier":
---> 40 return self.model.predict_proba(input_instance)
41 else:
42 return self.model.predict(input_instance)

~\Anaconda3\envs\env1\lib\site-packages\sklearn\utils\metaestimators.py in (*args, **kwargs)
118
119 # lambda, but not partial, allows help() to work with update_wrapper
--> 120 out = lambda *args, **kwargs: self.fn(obj, *args, **kwargs)
121 # update the docstring of the returned function
122 update_wrapper(out, self.fn)

~\Anaconda3\envs\env1\lib\site-packages\sklearn\pipeline.py in predict_proba(self, X)
472 Xt = X
473 for _, name, transform in self._iter(with_final=False):
--> 474 Xt = transform.transform(Xt)
475 return self.steps[-1][-1].predict_proba(Xt)
476

~\Anaconda3\envs\env1\lib\site-packages\sklearn\compose_column_transformer.py in transform(self, X)
563 "data given during fit."
564 )
--> 565 Xs = self._fit_transform(X, None, _transform_one, fitted=True)
566 self._validate_output(Xs)
567

~\Anaconda3\envs\env1\lib\site-packages\sklearn\compose_column_transformer.py in _fit_transform(self, X, y, func, fitted)
442 message=self._log_message(name, idx, len(transformers)))
443 for idx, (name, trans, column, weight) in enumerate(
--> 444 self._iter(fitted=fitted, replace_strings=True), 1))
445 except ValueError as e:
446 if "Expected 2D array, got 1D array instead" in str(e):

~\Anaconda3\envs\env1\lib\site-packages\joblib\parallel.py in call(self, iterable)
1027 # remaining jobs.
1028 self._iterating = False
-> 1029 if self.dispatch_one_batch(iterator):
1030 self._iterating = self._original_iterator is not None
1031

~\Anaconda3\envs\env1\lib\site-packages\joblib\parallel.py in dispatch_one_batch(self, iterator)
845 return False
846 else:
--> 847 self._dispatch(tasks)
848 return True
849

~\Anaconda3\envs\env1\lib\site-packages\joblib\parallel.py in _dispatch(self, batch)
763 with self._lock:
764 job_idx = len(self._jobs)
--> 765 job = self._backend.apply_async(batch, callback=cb)
766 # A job can complete so quickly than its callback is
767 # called before we get here, causing self._jobs to

~\Anaconda3\envs\env1\lib\site-packages\joblib_parallel_backends.py in apply_async(self, func, callback)
204 def apply_async(self, func, callback=None):
205 """Schedule a func to be run"""
--> 206 result = ImmediateResult(func)
207 if callback:
208 callback(result)

~\Anaconda3\envs\env1\lib\site-packages\joblib_parallel_backends.py in init(self, batch)
568 # Don't delay the application, to avoid keeping the input
569 # arguments in memory
--> 570 self.results = batch()
571
572 def get(self):

~\Anaconda3\envs\env1\lib\site-packages\joblib\parallel.py in call(self)
251 with parallel_backend(self._backend, n_jobs=self._n_jobs):
252 return [func(*args, **kwargs)
--> 253 for func, args, kwargs in self.items]
254
255 def reduce(self):

~\Anaconda3\envs\env1\lib\site-packages\joblib\parallel.py in (.0)
251 with parallel_backend(self._backend, n_jobs=self._n_jobs):
252 return [func(*args, **kwargs)
--> 253 for func, args, kwargs in self.items]
254
255 def reduce(self):

~\Anaconda3\envs\env1\lib\site-packages\sklearn\utils\fixes.py in call(self, *args, **kwargs)
220 def call(self, *args, **kwargs):
221 with config_context(**self.config):
--> 222 return self.function(*args, **kwargs)

~\Anaconda3\envs\env1\lib\site-packages\sklearn\pipeline.py in _transform_one(transformer, X, y, weight, **fit_params)
731
732 def _transform_one(transformer, X, y, weight, **fit_params):
--> 733 res = transformer.transform(X)
734 # if we have a weight for this transformer, multiply output
735 if weight is None:

~\Anaconda3\envs\env1\lib\site-packages\sklearn\pipeline.py in _transform(self, X)
558 Xt = X
559 for _, _, transform in self._iter():
--> 560 Xt = transform.transform(Xt)
561 return Xt
562

~\Anaconda3\envs\env1\lib\site-packages\sklearn\preprocessing_data.py in transform(self, X, copy)
884 accept_sparse='csr', copy=copy,
885 estimator=self, dtype=FLOAT_DTYPES,
--> 886 force_all_finite='allow-nan')
887
888 if sparse.issparse(X):

~\Anaconda3\envs\env1\lib\site-packages\sklearn\base.py in _validate_data(self, X, y, reset, validate_separately, **check_params)
419 out = X
420 elif isinstance(y, str) and y == 'no_validation':
--> 421 X = check_array(X, **check_params)
422 out = X
423 else:

~\Anaconda3\envs\env1\lib\site-packages\sklearn\utils\validation.py in inner_f(*args, **kwargs)
61 extra_args = len(args) - len(all_args)
62 if extra_args <= 0:
---> 63 return f(*args, **kwargs)
64
65 # extra_args > 0

~\Anaconda3\envs\env1\lib\site-packages\sklearn\utils\validation.py in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator)
670 " minimum of %d is required%s."
671 % (n_samples, array.shape, ensure_min_samples,
--> 672 context))
673
674 if ensure_min_features > 0 and array.ndim == 2:

ValueError: Found array with 0 sample(s) (shape=(0, 2)) while a minimum of 1 is required by StandardScaler.

Testing with other Dice explainers, kdtree/genetic and it seems they handle this scenario much more gracefully.

Getting the following error : "feature "" has a value outside the dataset"

When generating the counterfactual explanation, i'm getting an error related to a feature value which value is considered to be outside of the dataset. This is strange for me, since the variable is a dummy and is not allowed to vary. The variable name is not in the list of feature to vary. My target variable is continous. Perhaps i'm missing something, could someone help, please.

Sincerely yours

One-hot encoding in the Data class is not stateful.

Data.one_hot_encode_data() being an alias for pd.get_dummies(df, columns=categorical_features) would one-hot-encode every dataframe using the categories present in df. This is problematic when you need to one-hot-encode a set of counter-factuals (post-hoc) that do not include all categories of a particular feature.

If pos-hoc pre-processing of data is not intended for the Data class, I suggest prefixing the method with "_", or otherwise include the stateful methods (in this case: a method to use the exact scheme that has been used in encoding the train/test data for arbitrary data).

dice-ml pip install uses older version of scipy

While running
d = dice_ml.Data(dataframe=dataset, continuous_features=['age', 'hours_per_week'], outcome_name='income')
I get an error

---> 25 from scipy.misc import comb
     26 from ..utils import indexable, check_random_state, safe_indexing
     27 from ..utils.validation import _num_samples, column_or_1d

ImportError: cannot import name 'comb'

This has been depreciated
from scipy.misc import comb

update needed

from scipy.special import comb
https://docs.scipy.org/doc/scipy-1.2.1/reference/generated/scipy.misc.comb.html

original outcome is not same as query instance.

I have the following query instance, but in the output original outcome is not same as query instance, I assume they should be same otherwise it is useless.

dice_exp = exp.generate_counterfactuals({ 'five_star_rate': 3.5, 'nights_booked': 1.0 }, total_CFs = 4, desired_class="opposite")
dice_exp.visualize_as_dataframe()

Query instance (original outcome : 1)

 # five_star_rate nights_booked label
1 0.0 127.7 0.564298

Diverse Counterfactual set (new outcome : 0)

  # five_star_rate nights_booked label
1 0.0 66.2 0.215
2 0.0 66.2 0.215
3 0.0 77.0 0.280
4 0.0 62.5 0.196

Confusing running time on different datasets

I am trying to find CF 5 examples on twodatasets: iris, wine. However, sometimes it takes more time for iris than wine (25sec VS 3sec). Moreover, the time spent on iris also fluctuates. The iris dataset owns lessfeatures, which I expect to be fast. So, what is the relationship of explanation time and feature number (and query number and CF examples number). There are 5 queries and each query is required to find 5 CF examples.

Saving the generated examples as a dataframe

Hello everyone,

I am looking for a way to export the generated examples to something like a pandas-dataframe.
Maybe, I am missing something but I tried to do so for sometime now but could not find a solution.
I think and hope that this is somehow possible. The output generated with the ".visualize_as_dataframe()" looks already like a dataframe but I do not see a way to save the displayed examples.
I also tried the "to_json()"-method. However, I could no figure out yet, how to get from there to a dataframe.

I am grateful for any suggestion.
Best regards, Arnim

Error when using continuous features 'capital_gain' and 'capital_loss' on Adult Income

Hi,
The examples you provided for Adult Income work perfectly.
However, when 'capital_gain' and 'capital_loss' are used in addition to 'age' and 'hours_per_week', DiCE has some trouble to deal with those features. I suspect it may have something to do with the fact that they are highly skewed. Did you experience the same thing? Is that the reason why they are not used in the examples you provided? Thanks!

InvalidArgumentError: Input is not invertible. [Op:MatrixInverse]

Hi again,
I understand you must be extremely busy. I appreciate your time.

I was now trying to use the same ANN model as in your notebook, but with the German credit data. The model trains fine. But I get the following error message when I try to run the line:
dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class=1)

I have copier the entire error message below. Thanks

divide by zero encountered in double_scalars

InvalidArgumentError Traceback (most recent call last)
in
1 # generate counterfactuals
----> 2 dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class=1)

~\Anaconda3\lib\site-packages\dice_ml\dice_interfaces\dice_tensorflow2.py in generate_counterfactuals(self, query_instance, total_CFs, desired_class, proximity_weight, diversity_weight, categorical_penalty, algorithm, features_to_vary, yloss_type, diversity_loss_type, feature_weights, optimizer, learning_rate, min_iter, max_iter, project_iter, loss_diff_thres, loss_converge_maxiter, verbose, init_near_query_instance, tie_random, stopping_threshold, posthoc_sparsity_param)
92 self.update_hyperparameters(proximity_weight, diversity_weight, categorical_penalty)
93
---> 94 query_instance, test_pred = self.find_counterfactuals(query_instance, desired_class, optimizer, learning_rate, min_iter, max_iter, project_iter, loss_diff_thres, loss_converge_maxiter, verbose, init_near_query_instance, tie_random, stopping_threshold, posthoc_sparsity_param)
95
96 return exp.CounterfactualExamples(self.data_interface, query_instance,

~\Anaconda3\lib\site-packages\dice_ml\dice_interfaces\dice_tensorflow2.py in find_counterfactuals(self, query_instance, desired_class, optimizer, learning_rate, min_iter, max_iter, project_iter, loss_diff_thres, loss_converge_maxiter, verbose, init_near_query_instance, tie_random, stopping_threshold, posthoc_sparsity_param)
418
419 # get gradients
--> 420 grads = tape.gradient(loss_value, self.cfs)
421
422 # freeze features other than feat_to_vary_idxs

~\Anaconda3\lib\site-packages\tensorflow_core\python\eager\backprop.py in gradient(self, target, sources, output_gradients, unconnected_gradients)
1027 output_gradients=output_gradients,
1028 sources_raw=flat_sources_raw,
-> 1029 unconnected_gradients=unconnected_gradients)
1030
1031 if not self._persistent:

~\Anaconda3\lib\site-packages\tensorflow_core\python\eager\imperative_grad.py in imperative_grad(tape, target, sources, output_gradients, sources_raw, unconnected_gradients)
75 output_gradients,
76 sources_raw,
---> 77 compat.as_str(unconnected_gradients.value))

~\Anaconda3\lib\site-packages\tensorflow_core\python\eager\backprop.py in _gradient_function(op_name, attr_tuple, num_inputs, inputs, outputs, out_grads, skip_input_indices)
139 return [None] * num_inputs
140
--> 141 return grad_fn(mock_op, *out_grads)
142
143

~\Anaconda3\lib\site-packages\tensorflow_core\python\ops\linalg_grad.py in _MatrixDeterminantGrad(op, grad)
362 a = op.inputs[0]
363 c = op.outputs[0]
--> 364 a_adj_inv = linalg_ops.matrix_inverse(a, adjoint=True)
365 multipliers = array_ops.reshape(grad * c,
366 array_ops.concat([array_ops.shape(c), [1, 1]],

~\Anaconda3\lib\site-packages\tensorflow_core\python\ops\gen_linalg_ops.py in matrix_inverse(input, adjoint, name)
1401 raise
1402 except _core._NotOkStatusException as e:
-> 1403 _ops.raise_from_not_ok_status(e, name)
1404 # Add nodes to the TensorFlow graph.
1405 if adjoint is None:

~\Anaconda3\lib\site-packages\tensorflow_core\python\framework\ops.py in raise_from_not_ok_status(e, name)
6604 message = e.message + (" name: " + name if name is not None else "")
6605 # pylint: disable=protected-access
-> 6606 six.raise_from(core._status_to_exception(e.code, message), None)
6607 # pylint: enable=protected-access
6608

~\Anaconda3\lib\site-packages\six.py in raise_from(value, from_value)

InvalidArgumentError: Input is not invertible. [Op:MatrixInverse]

Customised model

Hi, I notice that the model you have trained is using the one hot encoded training data for categorical features, e.g. for workclass , you have three features to represent it -- workclass_Government, workclass_Other/Unknown, workclass_Private

However, we often train models just using one single feature to represent it with 0 (Government), 1(Other/Unknown), 2(Private). Is there a simple way that I can use my model directly when I am trying to generate the counterfactural example for a certain query instance or I have to convert all of the categorical features into one hot encored format when I am training the model in order to use " exp = dice_ml.Dice". ?

Thanks!

Yan

Questions on the loss function of distance

I have one question about the loss function in measuring the proximity between generated counterexamples c and input instance x. From your paper, formula (5) and (6) illustrates the distance loss function. However, I understand (6) is not a differentiable function. How do you cope with this problem?

I also check your code in dice_pytorch.py, and it seems like you only measure the distance by calculating the L_1 distance.

def compute_dist(self, x_hat, x1):
"""Compute weighted distance between two vectors."""
return torch.sum(torch.mul((torch.abs(x_hat - x1)), self.feature_weights_list), dim=0)
def compute_proximity_loss(self):
"""Compute the second part (distance from x1) of the loss function."""
proximity_loss = 0.0
for i in range(self.total_CFs):
proximity_loss += self.compute_dist(self.cfs[i], self.x1)
return proximity_loss/(torch.mul(len(self.minx[0]), self.total_CFs))

Thank you for your time. Looking forward to your reply.

TypeError: 'XGBClassifier' object is not callable

Sorry if this is a silly question, but I copied the notebook DiCE_with_advanced_options.ipynb and just changed the model to xgboost. When I try to run the line
dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class="opposite")
I get the error in the title. Would you be able to tell me what I'm doing wrong? Thanks

Some question related to FAT20 paper.

Hi all, I was going through the paper and find it really interesting. I was exploring the DICE code and I was not able to figure out which configurations were used in the FAT20 paper. What would be the appropriate steps for me to reproduce the dpp based method described in the paper (with the corresponding numbers). Any help in this regard would be appreciated.

Thanks,
Prateek

Why use the MAD in normalized features?

The MAD is mainly for the heterogeneous features(different features have different scales, ranges).
If you normalize the features with a min-max scaler. all features are mapped into [0,1].

In the adult example, the data interface normalizes the features, why does the defaulted setting is "inverse_mad"? From my understanding, the l2 distance is good. The paper "COUNTERFACTUAL EXPLANATIONS WITHOUT
OPENING THE BLACK BOX: AUTOMATED DECISIONS AND THE GDPR" P18, Equation 5, the authors also suggest the l2 distance.

Do you find any differences between these two kinds of distance ?

[Question] Does DiCE support user-constrained feature ranges?

I'd like to enforce constraints on the ranges that each feature can take when being perturbed - for example limiting 'hours_per_week' to anything within [40,60]. Is this currently possible, either directly through the interface or by changing some internals?

I read about it in your paper, but haven't yet found out how to implement this in practice. Relevant excerpt:

(...) to provide constraints on feature manipulation. They can be specified in two ways. First, as box constraints on feasible ranges for each feature, within which CF examples need to be searched."

Thanks in advance!
Siebe

TypeError: type numpy.ndarray doesn't define __round__ method

I'm having an issue with the version of DiCE installed from pip.
Here's the exact traceback:

File "CounterfactualDriver.py", line 71, in <module>
    dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=1, desired_class="opposite")
  File "~/miniconda3/envs/python_env/lib/python3.6/site-packages/dice_ml/dice_interfaces/dice_pytorch.py", line 97, in generate_counterfactuals
    query_instance, test_pred = self.find_counterfactuals(query_instance, desired_class, optimizer, learning_rate, min_iter, max_iter, project_iter, loss_diff_thres, loss_converge_maxiter, verbose, init_near_query_instance, tie_random, stopping_threshold, posthoc_sparsity_param)
  File "~/miniconda3/envs/python_env/lib/python3.6/site-packages/dice_ml/dice_interfaces/dice_pytorch.py", line 367, in find_counterfactuals
    desired_class = 1.0 - round(test_pred)
TypeError: type numpy.ndarray doesn't define __round__ method

Here is my environment setup:

dice-ml=0.2
numpy = 1.19.1
pandas = 1.1.1
scikit-learn = 0.23.2
tensorflow=2.3.0
torch=1.6.0
torchvision=0.7.0
h5py=2.10.0

Do you have any advice on how to move past this error? Are my package versions of the requirements for dice-ml correct?

Thanks so much!

Number of Counterfactual Examples

Can I generate more than 4 counterfactual explanations with the Dice package?

All Dice examples have exactly 4 counterfactual explanations via the "total_CFs=4" code.

Changing "total_CFs" to different numbers will cause errors as well.

Thanks

AttributeError: 'DiceTensorFlow1' object has no attribute 'num_ouput_nodes'`

Traceback (most recent call last):
File "G:/nus/Deep_explanation/Dice_baseline.py", line 22, in
dice_exp = exp.generate_counterfactuals(query_instance, total_CFs=4, desired_class="opposite")
File "F:\Anaconda\lib\site-packages\dice_ml\explainer_interfaces\dice_tensorflow1.py", line 139, in generate_counterfactuals
self.do_loss_initializations(yloss_type, diversity_loss_type, feature_weights)
File "F:\Anaconda\lib\site-packages\dice_ml\explainer_interfaces\dice_tensorflow1.py", line 336, in do_loss_initializations
self.yloss = self.compute_yloss(self.yloss_type)
File "F:\Anaconda\lib\site-packages\dice_ml\explainer_interfaces\dice_tensorflow1.py", line 250, in compute_yloss
temp_logits = temp_logits[:, (self.num_ouput_nodes-1):]
AttributeError: 'DiceTensorFlow1' object has no attribute 'num_ouput_nodes'

When I run the example codes in the readme, this error occurs. I dive into the source code of class "DiceTensorFlow1" and its father class "ExplainerBase". They indeed do not have this attribute.

High rate of incorrect predictions

Hello,

I'm not sure if this is something I'm doing wrong or you've encountered this before. Consider the following actions:

In the adult dataset I put aside a validation set, which neither my model nor DiCE has seen. From those, I select a number of samples whose age = 31, and use those to generate counterfactuals by varying everything other than age. Other than this I am not using any weights.
After that, I use my model to get a prediction on these generated counterfactuals. In a lot of the cases my model's prediction is not what DiCE thinks it would give.

Is there any way for me to increase DiCE's fidelity to my model?

Thanks

'PublicData' object has no attribute 'split_data'

Hi,

I am following the tutorial posted in

When I tried to split the data, I got PublicData object has no attribute 'split_data'. Is this because it has removed in the new version? if yes then how can I split the data. I appreciate your help.

Selection_332

Infinite loop in specific conditions

while((abs(diff)>10e-4) and (np.sign(diff*old_diff) > 0) and

The post-hoc sparsity enhancement can enter in an infinite loop in certain conditions (I tested with all numerical data, with data ranging, mostly, from -1 to 1). If you need, I can attach here the data. The cause is, for small numbers, sometimes the rate of change is very small or even zero, then, it stucks in this step.

I think one possible solution is to check the difference between the new diff and old difference old_diff, something like:

        if (old_diff == diff) or ((old_diff-diff) < 1e-5):
            n_tolerance += 1
        else:
            n_tolerance = 0

and adding another stop condition on the while loop: n_tolerance <= max_tolerance

Handle unknown categories in Dice explainer predict_fn

Hello,

I would appreciate some help when handling unknown categories values in new data predicted with the DICE explainer predict_fn method.

Well, we have implemented the DICE technique to be used in our ANN model trained with our historical data, which contains categorical features. Once we tested our model and tried to predict new data containing unknown categories values in categorical features using explainer predict_fn we had some issues.

You may find following a code example demonstrating our problem.

Have you had these kind of problems before?

import dice_ml
from dice_ml.utils import helpers # helper functions

# Tensorflow libraries
import pandas as pd
import numpy as np
import tensorflow as tf
from tensorflow import keras

dataset = helpers.load_adult_income_dataset()


d = dice_ml.Data(dataframe=dataset, continuous_features=['age', 'hours_per_week'], outcome_name='income')

train, _ = d.split_data(d.normalize_data(d.one_hot_encoded_data))

X_train = train.loc[:, train.columns != 'income']
y_train = train.loc[:, train.columns == 'income']

ann_model = keras.Sequential()
ann_model.add(keras.layers.Dense(20, input_shape=(X_train.shape[1],), kernel_regularizer=keras.regularizers.l1(0.001), activation=tf.nn.relu))
ann_model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))

ann_model.compile(loss='binary_crossentropy', optimizer=tf.keras.optimizers.Adam(0.01), metrics=['accuracy'])
ann_model.fit(X_train, y_train, validation_split=0.20, epochs=2, verbose=0, class_weight={0:1,1:2})
# the training will take some time for 100 epochs.

backend = 'TF'+tf.__version__[0] # TF2
model = dice_ml.Model(model=ann_model, backend=backend)

exp = dice_ml.Dice(d, model)

# Real world new data
new_data = pd.DataFrame({'age': 40, 'workclass': 'Private', 'education': 'Bachelors','marital_status': 'Married',
                        'occupation': 'Business', 'race': 'other', 'gender': 'Male',
                         'hours_per_week': 30}, index=[32562])
query_instance  = d.prepare_query_instance(query_instance=dict(new_data.iloc[0]), encode=True)
print(query_instance.shape)
query_instance  = np.array([query_instance.iloc[0].values])

exp.predict_fn(tf.constant(query_instance, dtype=tf.float32))[0][0]
InvalidArgumentError: Matrix size-incompatible: In[0]: [1,31], In[1]: [29,20] [Op:MatMul]

We know you use pd.get_dummies in your implementation for categorical features, we also know that OneHotEncoder has the handle_unknown attribute that when set to ignore would solve this problem.

from sklearn.preprocessing import OneHotEncoder

category = ['workclass', 'education', 'marital_status', 'occupation', 'race', 'gender']
aux = []
for c in category:
    aux.append(list(dataset[c].unique()))

enc = OneHotEncoder(categories=aux, handle_unknown="ignore", sparse=False)
enc.fit(dataset[category])

enc.transform(new_data[category])

Do you have any other suggestions to help us?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.