We live in a world that is getting more divided each day. In some parts of the world, the differences and inequalities between races, ethnicities, and sometimes sexes are aggravating. The data we use for modeling is in the major part a reflection of the world it derives from. And the world can be biased, so data and therefore model will likely reflect that. The introduction to this topic is well presented in Fairness and machine learning.

We propose a way in which ML engineers can easily check if their model is biased.

Fairness module is still work-in-progres and new features will be added over time.

In [1]:

```
import dalex as dx
import numpy as np
```

In [2]:

```
dx.__version__
```

Out[2]:

To showcase the abilities of the module, we will be using the German Credit dataset to assign risk for each credit-seeker.

This simple task may require using an interpretable *decision tree classifier*.

In [3]:

```
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder
from sklearn.tree import DecisionTreeClassifier
# credit data
data = dx.datasets.load_german()
# risk is the target
X = data.drop(columns='risk')
y = data.risk
categorical_features = ['sex', 'job', 'housing', 'saving_accounts', "checking_account", 'purpose']
numerical_features = ['age', 'duration', 'credit_amount']
categorical_transformer = Pipeline(steps=[
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
preprocessor = ColumnTransformer(transformers=[
('cat', categorical_transformer, categorical_features),
('num', 'passthrough', numerical_features)
])
clf = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', DecisionTreeClassifier(max_depth=7, random_state=123))
])
clf.fit(X, y)
```

Out[3]:

We create an `Explainer`

object to proceed with `dalex`

functionalities.

In [4]:

```
exp = dx.Explainer(clf, X, y)
```

In [5]:

```
exp.model_performance().result
```

Out[5]:

Let's say that performance is satisfying. To check if the model is biased, we will use the `fairness`

module from `dalex`

. Checking if the model is fair should be straightforward. Apart from the `dx.Explainer`

, we will need 2 parameters:

`protected`

- array-like with subgroup values that denote a sensitive attribute (protected variable) like sex, nationality etc. The fairness metrics will be calculated for each of those subgroups and compared.`privileged`

- a string representing one of the subgroups. It should be the one suspected of the most privilege.

In [6]:

```
# array with values like male_old, female_young, etc.
protected = data.sex + '_' + np.where(data.age < 25, 'young', 'old')
privileged = 'male_old'
```

We use a unified `dalex`

interface to create a `fairness`

explanation object. Use the `model_fairness()`

method:

In [7]:

```
fobject = exp.model_fairness(protected = protected, privileged=privileged)
```

The idea here is that ratios between scores of privileged and unprivileged metrics should be close to 1. The closer the more fair the model is. But to relax this criterion a little bit, it can be written more thoughtfully:

Where the `epsilon`

is a value between 0 and 1, it should be a minimum acceptable value of the ratio. On default, it is 0.8, which adheres to four-fifths rule (80% rule) often looked at in hiring, for example.

In [8]:

```
fobject.fairness_check(epsilon = 0.8) # default epsilon
```

**This model cannot be called fair!** Generally, each metric should be between **(epsilon, 1/epsilon)**. Metrics are calculated for each subgroup, and then their scores are divided by the score of the privileged subgroup. That is why we omit `male_old`

in this method. When at least 2 metrics have scores ratio outside of the epsilon range, the model may be declared unfair. In our case it cannot be decided automatically but the bias is visible and **FPR** (False Positive Rate) is preety important in case of risk assigning, so let's call our model **unfair**.

The `result`

attribute is `metric_scores`

where each row is divided by row indexed with *privileged* (in this case `male_old`

).

In [9]:

```
# to see all scaled metric values you can run
fobject.result
```

Out[9]:

In [10]:

```
# or unscaled ones via
fobject.metric_scores
```

Out[10]:

The `fairness`

explanation object includes plots that allow bias visualization from different perspectives:

`fairness_check`

plot`metric_scores`

plot

This is a visualization of the `fairness_check`

result.

In [11]:

```
fobject.plot()
```