<jupyter_start><jupyter_text>Pick a modelPossibilities:- "logreg": Use and train a logistic regression- "decisiontree": Use and train a decision tree- "randomforest": Use and train a random forest Pick a bias to check__Please, age must be selected between 25 and 60.__Possibilities:- "sexm": to study a bias on gender in favor of men- "sexf": to study a bias on gender in favor of women- "xx+": to study a bias on age in favor of people over the threshold (example: "30+" means bias in favor of people over 30 years old")- "xx-": to study a bias on age in favor of people under the threshold (example: "40-" means bias in favor of people below 40 years old")- "sexX xx+": to study bias on cross-subgroups. use a space between gender and age (example: "sexm 25+")<jupyter_code>MODEL_TYPE = "randomforest"
BIAS_TYPE = "sexf 30+"

dict_infos = get_model_bias(MODEL_TYPE, BIAS_TYPE)
#
%%capture
explainers_fairness = create_all_explainers_fairness_objects(dict_infos)<jupyter_output><empty_output><jupyter_text>Pick a plot type- "default": display a grouped bar plot which can bench all models or all groups.- "radar": display a radar plot which can bench all models or all groups.- "stacked": display a stacked bar plot which can bench all models or all groups (it's cumulative).- "heatmap": display a heatmap plot which can bench all models or all groups (models by row, metrics by columns). Pick bias metrics to controlYou CANNOT change metrics for the default plot. Which is use on the base model.- "TPR": Compare probabilities of correctly according the credit (TP)- "TNR": Compare probabilities of correctly NOT according the credit (TN)- "FPR": Compare probabilities of wrongly according the credit (FP)- "FNR": Compare probabilities of wrongly NOT according the credit (FN)- "PPV": Compare precision of according the credit (True Positive / (True Positive + False Positive))- "NPV": Compare precision of NOT according the credit (True Negative / (True Negative + False Negative))- "FDR": Compare ratio of wrongly according the credit over all predictions of according the credit (FP/(TP+FP))- "FOR": Compare ratio of wrongly NOT according the credit over all predictions of NOT according the credit (FN/(TN+FN))- "ACC": Compare accuracies (precision of correctly according and NOT according the credit) ((TP+TN)/(TP+TN+FP+FN))- "STP": Compare ratio of according the credit ((TP+FP)/(TP+TN+FP+FN))<jupyter_code>METRICS_LIST = ["TPR", "ACC", "PPV", "FPR", "STP", "FDR"]
GRAPHIC = "radar"
# %%javascript
# IPython.OutputArea.auto_scroll_threshold = 9999;
display_all(METRICS_LIST, GRAPHIC, explainers_fairness, dict_infos)<jupyter_output>Let's check the fairness performance of the selected model according to the specific populations declared:
  This is a default graph produced by the dalex library. It does not take into account selection made for METRICS_LIST and GRAPHIC.
    
    
Bias detected in 1 metric: FPR

Conclusion: your model cannot be called fair because 1 criterion exceeded acceptable limits set by epsilon.
It does not mean that your model is unfair but it cannot be automatically approved based on these metrics.

Ratios of metrics, based on 'female_old'. Parameter 'epsilon' was set to 0.8 and therefore metrics should be within (0.8, 1.25)
                   TPR       ACC       PPV       FPR       STP
female_young  1.010101  0.862152  0.825338  1.140496  0.984181
male_old      0.998990  0.943168  0.954490  1.375207  1.072316
male_young    0.994949  0.918984  0.912669  1.152066  1.005650<jupyter_text>__Let's go deeper on the 'best' model and compare metrics on differents groups__ Pick a model to analyse fairness metrics on groups- "base": for the model you picked initialy- "remove": for the model without the biased variable- "sampling_p": for the model with preferential resampling- "sampling_u": for the model with uniform resampling- "weights": for the model with reweighting- "roc-pivot": for the model with switches close to the decision frontier<jupyter_code>SELECTED_MODEL = "base"
display_groups_fairness(SELECTED_MODEL, explainers_fairness, METRICS_LIST)<jupyter_output><empty_output><jupyter_text>Manual version:  First resultsLooking for bias<jupyter_code># Define Dalex parameters
explainer = dx.Explainer(eval(dict_infos['model_name']), X, y, verbose=False)

protected = dict_infos['protected']
privileged = dict_infos['privileged']
#fairness_object = explainer.model_fairness(protected = protected, privileged = privileged)

fairness_object.fairness_check(epsilon = 0.8)
fairness_object.plot()
xx<jupyter_output>Bias detected in 2 metrics: FPR, STP

Conclusion: your model is not fair because 2 or more criteria exceeded acceptable limits set by epsilon.

Ratios of metrics, based on 'female_old'. Parameter 'epsilon' was set to 0.8 and therefore metrics should be within (0.8, 1.25)
                   TPR       ACC       PPV       FPR       STP
female_young  0.818278  0.841191  0.862027  0.815552  0.762874
male_old      0.995749  0.941687  0.956044  1.374322  1.068263
male_young    0.952179  0.918114  0.926740  1.041591  0.946108<jupyter_text>__Doing nothing. Is that so bad, really ?__If all bars are within (0.8, 1.25), then according to your criteria you model is not biased. However, if you set a threshold for the age, are you sure that moving it a little bit (± 5 years) will not return a biased result ?Try it to be sure !__If a biased have been detected, have a look to see how you can mitigate it !__  Mitigate in Preprocessing  Mitigate option 1: Just remove the biased variable ?Let's try the same but set the biased variable out of the training part.<jupyter_code>explainer_rm = dx.Explainer(eval(dict_infos['model_rm_name']), X, y, verbose=False)
fobject_rm = explainer_rm.model_fairness(protected = protected, privileged = privileged, label='with_removed_columns')

fobject_rm.fairness_check(epsilon = 0.8)
fobject_rm.plot(objects=[fairness_object])<jupyter_output>Bias detected in 2 metrics: ACC, PPV

Conclusion: your model is not fair because 2 or more criteria exceeded acceptable limits set by epsilon.

Ratios of metrics, based on 'female_old'. Parameter 'epsilon' was set to 0.8 and therefore metrics should be within (0.8, 1.25)
                   TPR       ACC       PPV       FPR       STP
female_young  0.937500  0.798278  0.788957  1.215889  0.956776
male_old      0.925000  0.928659  0.986503  1.091537  0.960280
male_young    0.940625  0.897909  0.916564  1.072539  0.946262<jupyter_text>How did you model evolved regarding to your fairness metrics ? Is it better without the column ? If yes, you're lucky the kind of naive preprocessing used to be useless most of the time. Usually the protected and biased variable is correlated with others explanatory variables and then removing it do not hel to unbiased your model ! If no, well that kind of normal, let's see more appropriate ways to deal with biased models.  Mitigate option 2: Re-sampling your dataDid you look at the distribution of the biased variable ? Maybe some values of the variable are under-represented or over-represented. Resampling training data would help to mitigate bias due to this king of issue.<jupyter_code>model_p = resampling_model(eval(dict_infos['model_name']), explainer, protected, type_resampling="preferential")
model_u = resampling_model(eval(dict_infos['model_name']), explainer, protected, type_resampling="uniform")

explainer_p = dx.Explainer(model_p, X, y, verbose = False)
explainer_u = dx.Explainer(model_u, X, y, verbose = False)

fobject_p = explainer_p.model_fairness(protected, privileged, label='preferential_resampling')
fobject_u = explainer_u.model_fairness(protected, privileged, label='uniform_resampling')

# plotting
fairness_object.plot([fobject_p, fobject_u])<jupyter_output><empty_output><jupyter_text>Mitigate option 3: Re-weighting your data<jupyter_code>model_w = reweighting_model(eval(dict_infos['model_name']))

explainer_w = dx.Explainer(model_w, X, y, verbose = False)

fobject_w = explainer_w.model_fairness(protected, privileged, label='reweighted')

fairness_object.plot([fobject_p, fobject_w])

fairness_object.plot([fobject_p, fobject_u, fobject_w], type = "radar", metrics=['FDR', 'STP', 'TPR', 'TNR', 'FOR'])<jupyter_output><empty_output><jupyter_text>Mitigate in Postprocessing  Mitigate option 4: ROC-pivot methodThis method of mitigation aims to change predictions for items close to the decision frontier.  It switches labels if an observation is from the unprivileged group and on the left (wrong side) of the cutoff.Note that It can also switches labels if an observation is from the privileged group and on the right of the cutoff.<jupyter_code>explainer_roc = dx.Explainer(eval(dict_infos['model_name']), X, y, verbose=False)
explainer_roc = dx.fairness.roc_pivot(explainer_roc, protected, privileged, theta = 0.02, verbose = False)
fobject_roc = explainer_roc.model_fairness(protected, privileged, label='roc')

fairness_object.plot([fobject_roc, fobject_u, fobject_w])
#%%capture
xx = create_all_explainers_fairness_objects(dict_infos)
    
from copy import copy
clf_u = copy(clf_logreg)
clf_p = copy(clf_logreg)

indices_uniform = dx.fairness.resample(protected, y, verbose = False)
indices_preferential = dx.fairness.resample(protected,
                                y, 
                                type = 'preferential', # different type 
                                probs = explainer.y_hat, # requires probabilities
                                verbose = False)


clf_u.fit(X.iloc[indices_uniform, :], y[indices_uniform])
clf_p.fit(X.iloc[indices_preferential, :], y[indices_preferential])
weights = dx.fairness.reweight(protected, y, verbose = False)

clf_weighted = Pipeline(steps=[('preprocessor', preprocessor),
                      ('classifier', LogisticRegression(random_state=123))])

kwargs = {clf_weighted.steps[-1][0] + '__sample_weight': weights}

clf_weighted.fit(X,y, **kwargs)
exp3 = dx.Explainer(clf_u, X, y, verbose = False)
exp4 = dx.Explainer(clf_p, X, y, verbose = False)
exp5 = dx.Explainer(clf_weighted, X, y, verbose = False)


fobject1 = explainer.model_fairness(protected, privileged, label='base')
fobject2 = explainer_no_sex.model_fairness(protected, privileged, label='remove')
fobject3 = exp3.model_fairness(protected, privileged, label='res_unif')
fobject4 = exp4.model_fairness(protected, privileged, label='res_pref')
fobject5 = exp5.model_fairness(protected, privileged, label='reweighted')


# plotting
fobject1.plot([fobject2, fobject4, fobject3, fobject5])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08679996209608642, 'fraction_numerical': 0.03790391357907704, 'mean_word_length': 4.827719491993374, 'pattern_counts': {'":': 28, '<': 30, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '274937', 'license': 'MIT', 'n_tokens_mistral': 3599, 'n_tokens_neox': 3171, 'n_words': 1162, 'path': 'notebooks/playing notebooks/front_notebook.ipynb', 'repo_name': 'datacraft-paris/ethical-ai-toolkit', 'size': '10553'}