<jupyter_start><jupyter_text>Counterfactuals benchmark Data and modelThis counterfactual benchmark will use a model for a loan approval generated in the [benchmark models repository](https://github.com/ruivieira/benchmark-models/tree/main/minimal-numerical).The model is a minimal classification model with numerical-only input features.The input features are:- Age- Debt- Years employed- IncomeThe classification label is a binary `0,1` corresponding to not approved and approved.<jupyter_code>from joblib import load
from alibi.explainers import CounterFactual
import pandas as pd
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

tf.compat.v1.disable_eager_execution()
tf.compat.v1.disable_v2_behavior() # disable TF2 behaviour as alibi code still relies on TF1 constructs
clf = load("models/model.joblib")
predict_fn = lambda x: clf.predict_proba(x)
shape = (1,4)
print(shape)<jupyter_output>(1, 4)<jupyter_text>Search with defaultsWe will use the following values:- `feature_range`, the feature ranges can be extracted from the training data[^1][^1]: https://github.com/ruivieira/benchmark-models/blob/main/minimal-numerical/notebooks/01-data-exploration.ipynb<jupyter_code>explainer = CounterFactual(predict_fn, 
                           feature_range=([18.0,1.0,0.0,0.0], [80.0, 7.0, 30.0, 300.0]),
                           shape=shape, 
                           target_class=[1], 
                           target_proba=0.6,
                           learning_rate_init=1.0, # default 0.1,
                           eps=1.0,
                           early_stop=200,
                           tol=0.09)
df = np.array([21.0, 3.5, 5.0, 100]).reshape(1, -1)
df
print(predict_fn(df))
print(clf.predict(df))
p = [(score, predict_fn(np.array([21.0, score, 5.0, 100]).reshape(1, -1))[0][1]) for score in np.linspace(0.0, 7.0, num=100)]
plt.plot([x[0] for x in p], [y[1] for y in p])
cf = explainer.explain(df)
cf['params']
print(cf.data['cf']['X'])
print("diff: ", cf.data['cf']['X'] - df)
np.linalg.norm(cf.data['cf']['X'] - df)
predict_fn(cf.data['cf']['X'])<jupyter_output><empty_output><jupyter_text>DiCE counterfactuals<jupyter_code>import dice_ml
from dice_ml.utils import helpers # helper functions<jupyter_output><empty_output><jupyter_text>Although DiCE can use the original training data, to generate counterfactuals we can simply use the dataset's metadata along with the feature domain.The domains are available in the original model's [documentation](https://github.com/ruivieira/benchmark-models/tree/main/minimal-numerical).<jupyter_code>features = {
    'Age': [13, 81],
    'Debt': [0, 27],
    'YearsEmployed': [0, 20],
    'Income': [0, 10000]}

d = dice_ml.data.Data(features=features, outcome_name='Approved')
from dice_ml.model_interfaces import klearn_model

m = dice_ml.model.Model(model=clf, backend={"model": "sklearn_model.SklearnModel", "explainer": "dice_sklearn.DiceSklearn"})
# df = np.array([21.0, 0.1, 0.1, 100]).reshape(1, -1)
query = {'Age': 21, 'Debt': 0.1, 'YearsEmployed': 0.1, 'Income': 100}
exp = dice_ml.Dice(d, m)
dice_exp = dice_ml.generate_counterfactuals(query,
                total_CFs=4, desired_class="1",
                proximity_weight=1.5, diversity_weight=1.0)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1365021328458257, 'fraction_numerical': 0.038695917123705056, 'mean_word_length': 3.922038980509745, 'pattern_counts': {'":': 2, '<': 16, '<?xml version=': 0, '>': 16, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '364781', 'license': 'Apache-2.0', 'n_tokens_mistral': 1192, 'n_tokens_neox': 1106, 'n_words': 260, 'path': 'benchmarks/alibi/benchmark.ipynb', 'repo_name': 'ruivieira/explainability-experiments', 'size': '3282'}