diff --git a/examples/demo_FACTS.ipynb b/examples/demo_FACTS.ipynb
index a99d26ef..7541cc86 100644
--- a/examples/demo_FACTS.ipynb
+++ b/examples/demo_FACTS.ipynb
@@ -1,775 +1,847 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Fairness auditing for subgroups using Fairness Aware Counterfactuals for Subgroups (FACTS).\n",
- "\n",
- "[FACTS](https://arxiv.org/abs/2306.14978) is an efficient, model-agnostic, highly parameterizable, and explainable framework for auditing subgroup fairness through counterfactual explanations. FACTS focuses on identifying a specific type of bias, i.e. the *difficulty in achieving recourse*. In short, it focuses on the population that has obtained the unfavorable outcome (*affected population*) by a ML model and tries to identify differences in the difficulty of changing the ML model's decision to obtain the favorable outcome, between affected subpopulations.\n",
- "\n",
- "In this notebook, we will see how to use this algorithm for discovering subgroups where the bias of a model (logistic regression for simplicity) between Males and Females is high.\n",
- "\n",
- "We will use the Adult dataset from UCI ([reference](https://archive.ics.uci.edu/ml/datasets/adult))."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Preliminaries"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Import dependencies\n",
- "\n",
- "As usual in python, the first step is to import all necessary packages."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [
+ "cells": [
{
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "WARNING:root:No module named 'tempeh': fetch_lawschool_gpa will be unavailable. To install, run:\n",
- "pip install 'aif360[LawSchoolGPA]'\n"
- ]
- }
- ],
- "source": [
- "from sklearn.model_selection import train_test_split\n",
- "from sklearn.linear_model import LogisticRegression\n",
- "from sklearn.compose import ColumnTransformer\n",
- "from sklearn.pipeline import Pipeline\n",
- "from sklearn.preprocessing import OneHotEncoder\n",
- "\n",
- "from aif360.sklearn.datasets.openml_datasets import fetch_adult\n",
- "from aif360.sklearn.detectors.facts.clean import clean_dataset\n",
- "from aif360.sklearn.detectors.facts import FACTS, FACTS_bias_scan\n",
- "\n",
- "from IPython.display import display\n",
- "\n",
- "import warnings\n",
- "warnings.filterwarnings(\"ignore\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Below, you can change the `random_seed` variable to `None` if you would like for the pseudo-random parts to actually change between runs. We have set it to a specific value for reproducibility."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "random_seed = 131313 # for reproducibility"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Load Dataset"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "source": [
+ "[](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/main/examples/demo_FACTS.ipynb)\n"
+ ],
+ "metadata": {
+ "id": "3VmYWXk6gRbj"
+ }
+ },
{
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
age
\n",
- "
workclass
\n",
- "
education-num
\n",
- "
marital-status
\n",
- "
occupation
\n",
- "
relationship
\n",
- "
race
\n",
- "
sex
\n",
- "
capital-gain
\n",
- "
capital-loss
\n",
- "
hours-per-week
\n",
- "
native-country
\n",
- "
income
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
0
\n",
- "
(16.999, 26.0]
\n",
- "
Private
\n",
- "
7.0
\n",
- "
Never-married
\n",
- "
Machine-op-inspct
\n",
- "
Own-child
\n",
- "
Black
\n",
- "
Male
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
FullTime
\n",
- "
United-States
\n",
- "
0
\n",
- "
\n",
- "
\n",
- "
1
\n",
- "
(34.0, 41.0]
\n",
- "
Private
\n",
- "
9.0
\n",
- "
Married-civ-spouse
\n",
- "
Farming-fishing
\n",
- "
Married
\n",
- "
White
\n",
- "
Male
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
OverTime
\n",
- "
United-States
\n",
- "
0
\n",
- "
\n",
- "
\n",
- "
2
\n",
- "
(26.0, 34.0]
\n",
- "
Local-gov
\n",
- "
12.0
\n",
- "
Married-civ-spouse
\n",
- "
Protective-serv
\n",
- "
Married
\n",
- "
White
\n",
- "
Male
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
FullTime
\n",
- "
United-States
\n",
- "
1
\n",
- "
\n",
- "
\n",
- "
3
\n",
- "
(41.0, 50.0]
\n",
- "
Private
\n",
- "
10.0
\n",
- "
Married-civ-spouse
\n",
- "
Machine-op-inspct
\n",
- "
Married
\n",
- "
Black
\n",
- "
Male
\n",
- "
7688.0
\n",
- "
0.0
\n",
- "
FullTime
\n",
- "
United-States
\n",
- "
1
\n",
- "
\n",
- "
\n",
- "
4
\n",
- "
(26.0, 34.0]
\n",
- "
Private
\n",
- "
6.0
\n",
- "
Never-married
\n",
- "
Other-service
\n",
- "
Not-in-family
\n",
- "
White
\n",
- "
Male
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
MidTime
\n",
- "
United-States
\n",
- "
0
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "qHiNaF-VgPB4"
+ },
+ "source": [
+ "# Fairness auditing for subgroups using Fairness Aware Counterfactuals for Subgroups (FACTS).\n",
+ "\n",
+ "[FACTS](https://arxiv.org/abs/2306.14978) is an efficient, model-agnostic, highly parameterizable, and explainable framework for auditing subgroup fairness through counterfactual explanations. FACTS focuses on identifying a specific type of bias, i.e. the *difficulty in achieving recourse*. In short, it focuses on the population that has obtained the unfavorable outcome (*affected population*) by a ML model and tries to identify differences in the difficulty of changing the ML model's decision to obtain the favorable outcome, between affected subpopulations.\n",
+ "\n",
+ "In this notebook, we will see how to use this algorithm for discovering subgroups where the bias of a model (logistic regression for simplicity) between Males and Females is high.\n",
+ "\n",
+ "We will use the Adult dataset from UCI ([reference](https://archive.ics.uci.edu/ml/datasets/adult))."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "4cavLAfZgPB5"
+ },
+ "source": [
+ "# Preliminaries"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "1FLtToZmgPB6"
+ },
+ "source": [
+ "## Import dependencies\n",
+ "\n",
+ "As usual in python, the first step is to import all necessary packages."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "qz_p2JzegPB6",
+ "outputId": "dc1206fb-14b9-4ac7-ce74-0f2d83ce6a61"
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "WARNING:root:No module named 'tempeh': fetch_lawschool_gpa will be unavailable. To install, run:\n",
+ "pip install 'aif360[LawSchoolGPA]'\n"
+ ]
+ }
],
- "text/plain": [
- " age workclass education-num marital-status \\\n",
- "0 (16.999, 26.0] Private 7.0 Never-married \n",
- "1 (34.0, 41.0] Private 9.0 Married-civ-spouse \n",
- "2 (26.0, 34.0] Local-gov 12.0 Married-civ-spouse \n",
- "3 (41.0, 50.0] Private 10.0 Married-civ-spouse \n",
- "4 (26.0, 34.0] Private 6.0 Never-married \n",
- "\n",
- " occupation relationship race sex capital-gain capital-loss \\\n",
- "0 Machine-op-inspct Own-child Black Male 0.0 0.0 \n",
- "1 Farming-fishing Married White Male 0.0 0.0 \n",
- "2 Protective-serv Married White Male 0.0 0.0 \n",
- "3 Machine-op-inspct Married Black Male 7688.0 0.0 \n",
- "4 Other-service Not-in-family White Male 0.0 0.0 \n",
- "\n",
- " hours-per-week native-country income \n",
- "0 FullTime United-States 0 \n",
- "1 OverTime United-States 0 \n",
- "2 FullTime United-States 1 \n",
- "3 FullTime United-States 1 \n",
- "4 MidTime United-States 0 "
+ "source": [
+ "from sklearn.model_selection import train_test_split\n",
+ "from sklearn.linear_model import LogisticRegression\n",
+ "from sklearn.compose import ColumnTransformer\n",
+ "from sklearn.pipeline import Pipeline\n",
+ "from sklearn.preprocessing import OneHotEncoder\n",
+ "\n",
+ "from aif360.sklearn.datasets.openml_datasets import fetch_adult\n",
+ "from aif360.sklearn.detectors.facts.clean import clean_dataset\n",
+ "from aif360.sklearn.detectors.facts import FACTS, FACTS_bias_scan\n",
+ "\n",
+ "from IPython.display import display\n",
+ "\n",
+ "import warnings\n",
+ "warnings.filterwarnings(\"ignore\")"
]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "# load the adult dataset and perform some simple preprocessing steps\n",
- "# See output for a glimpse of the final dataset's characteristics\n",
- "X, y, sample_weight = fetch_adult()\n",
- "data = clean_dataset(X.assign(income=y), \"adult\")\n",
- "display(data.head())\n",
- "\n",
- "# split into train-test data\n",
- "y = data['income']\n",
- "X = data.drop('income', axis=1)\n",
- "X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, random_state=random_seed, stratify=y)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Example Model to be used for Auditing\n",
- "\n",
- "We use the train set to train a simple logistic regression model. This will serve as the demonstrative model, which we will then treat as a black box and apply our algorithm.\n",
- "\n",
- "Of course, any model can be used in its place. Our purpose here is not to produce a good model, but to audit the fairness of an existing one."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "#### here, we incrementally build the example model. It consists of one preprocessing step,\n",
- "#### which is to turn categorical features into the respective one-hot encodings, and\n",
- "#### a simple scikit-learn logistic regressor.\n",
- "categorical_features = X.select_dtypes(include=[\"object\", \"category\"]).columns.to_list()\n",
- "categorical_features_onehot_transformer = ColumnTransformer(\n",
- " transformers=[\n",
- " (\"one-hot-encoder\", OneHotEncoder(), categorical_features)\n",
- " ],\n",
- " remainder=\"passthrough\"\n",
- ")\n",
- "model = Pipeline([\n",
- " (\"one-hot-encoder\", categorical_features_onehot_transformer),\n",
- " (\"clf\", LogisticRegression(max_iter=1500))\n",
- "])\n",
- "\n",
- "#### train the model\n",
- "model = model.fit(X_train, y_train)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Accuracy = 85.16%\n"
- ]
- }
- ],
- "source": [
- "# showcase model's accuracy\n",
- "y_pred = model.predict(X_test)\n",
- "print(f\"Accuracy = {(y_test.values == y_pred).sum() / y_test.shape[0]:.2%}\")"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# A Practical Example of FACTS\n",
- "\n",
- "The real essence of our work starts here. Specifically, we showcase the generation of candidate subpopulation groups and counterfactuals and the detection of those groups that exhibit the greatest unfairness, with respect to one of several metrics."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Load and Fit FACTS"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "# load FACTS framework with:\n",
- "# - the model to be audited\n",
- "# - protected attribute \"sex\" and\n",
- "# - assigning equal, unit weights to all features for cost computation.\n",
- "# - no features forbidden from changing, i.e. user can specify any features that cannot change at all.\n",
- "detector = FACTS(\n",
- " clf=model,\n",
- " prot_attr=\"sex\",\n",
- " feature_weights={f: 1 for f in X.columns},\n",
- " feats_not_allowed_to_change=[]\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "m1Ye20legPB8"
+ },
+ "source": [
+ "Below, you can change the `random_seed` variable to `None` if you would like for the pseudo-random parts to actually change between runs. We have set it to a specific value for reproducibility."
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Computing candidate subgroups.\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "t8KJD-ICgPB8"
+ },
+ "outputs": [],
+ "source": [
+ "random_seed = 131313 # for reproducibility"
+ ]
},
{
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "100%|██████████████████████████████████████████████████████████████████████████| 1046/1046 [00:00<00:00, 523287.45it/s]"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Tq79M4GxgPB8"
+ },
+ "source": [
+ "## Load Dataset"
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Number of subgroups: 563\n",
- "Computing candidate recourses for all subgroups.\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "HwnmXFJ_gPB8",
+ "outputId": "3cddbc05-73e7-44c5-8482-af3b942f5c6f"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
age
\n",
+ "
workclass
\n",
+ "
education-num
\n",
+ "
marital-status
\n",
+ "
occupation
\n",
+ "
relationship
\n",
+ "
race
\n",
+ "
sex
\n",
+ "
capital-gain
\n",
+ "
capital-loss
\n",
+ "
hours-per-week
\n",
+ "
native-country
\n",
+ "
income
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
(16.999, 26.0]
\n",
+ "
Private
\n",
+ "
7.0
\n",
+ "
Never-married
\n",
+ "
Machine-op-inspct
\n",
+ "
Own-child
\n",
+ "
Black
\n",
+ "
Male
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
FullTime
\n",
+ "
United-States
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
(34.0, 41.0]
\n",
+ "
Private
\n",
+ "
9.0
\n",
+ "
Married-civ-spouse
\n",
+ "
Farming-fishing
\n",
+ "
Married
\n",
+ "
White
\n",
+ "
Male
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
OverTime
\n",
+ "
United-States
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
(26.0, 34.0]
\n",
+ "
Local-gov
\n",
+ "
12.0
\n",
+ "
Married-civ-spouse
\n",
+ "
Protective-serv
\n",
+ "
Married
\n",
+ "
White
\n",
+ "
Male
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
FullTime
\n",
+ "
United-States
\n",
+ "
1
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
(41.0, 50.0]
\n",
+ "
Private
\n",
+ "
10.0
\n",
+ "
Married-civ-spouse
\n",
+ "
Machine-op-inspct
\n",
+ "
Married
\n",
+ "
Black
\n",
+ "
Male
\n",
+ "
7688.0
\n",
+ "
0.0
\n",
+ "
FullTime
\n",
+ "
United-States
\n",
+ "
1
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
(26.0, 34.0]
\n",
+ "
Private
\n",
+ "
6.0
\n",
+ "
Never-married
\n",
+ "
Other-service
\n",
+ "
Not-in-family
\n",
+ "
White
\n",
+ "
Male
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
MidTime
\n",
+ "
United-States
\n",
+ "
0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " age workclass education-num marital-status \\\n",
+ "0 (16.999, 26.0] Private 7.0 Never-married \n",
+ "1 (34.0, 41.0] Private 9.0 Married-civ-spouse \n",
+ "2 (26.0, 34.0] Local-gov 12.0 Married-civ-spouse \n",
+ "3 (41.0, 50.0] Private 10.0 Married-civ-spouse \n",
+ "4 (26.0, 34.0] Private 6.0 Never-married \n",
+ "\n",
+ " occupation relationship race sex capital-gain capital-loss \\\n",
+ "0 Machine-op-inspct Own-child Black Male 0.0 0.0 \n",
+ "1 Farming-fishing Married White Male 0.0 0.0 \n",
+ "2 Protective-serv Married White Male 0.0 0.0 \n",
+ "3 Machine-op-inspct Married Black Male 7688.0 0.0 \n",
+ "4 Other-service Not-in-family White Male 0.0 0.0 \n",
+ "\n",
+ " hours-per-week native-country income \n",
+ "0 FullTime United-States 0 \n",
+ "1 OverTime United-States 0 \n",
+ "2 FullTime United-States 1 \n",
+ "3 FullTime United-States 1 \n",
+ "4 MidTime United-States 0 "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# load the adult dataset and perform some simple preprocessing steps\n",
+ "# See output for a glimpse of the final dataset's characteristics\n",
+ "X, y, sample_weight = fetch_adult()\n",
+ "data = clean_dataset(X.assign(income=y), \"adult\")\n",
+ "display(data.head())\n",
+ "\n",
+ "# split into train-test data\n",
+ "y = data['income']\n",
+ "X = data.drop('income', axis=1)\n",
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, random_state=random_seed, stratify=y)"
+ ]
},
{
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "\n",
- "100%|█████████████████████████████████████████████████████████████████████████████| 563/563 [00:00<00:00, 50669.32it/s]"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "WdckzZdwgPB8"
+ },
+ "source": [
+ "## Example Model to be used for Auditing\n",
+ "\n",
+ "We use the train set to train a simple logistic regression model. This will serve as the demonstrative model, which we will then treat as a black box and apply our algorithm.\n",
+ "\n",
+ "Of course, any model can be used in its place. Our purpose here is not to produce a good model, but to audit the fairness of an existing one."
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Computing percentages of individuals flipped by each action independently.\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "9NuWT4LMgPB9"
+ },
+ "outputs": [],
+ "source": [
+ "#### here, we incrementally build the example model. It consists of one preprocessing step,\n",
+ "#### which is to turn categorical features into the respective one-hot encodings, and\n",
+ "#### a simple scikit-learn logistic regressor.\n",
+ "categorical_features = X.select_dtypes(include=[\"object\", \"category\"]).columns.to_list()\n",
+ "categorical_features_onehot_transformer = ColumnTransformer(\n",
+ " transformers=[\n",
+ " (\"one-hot-encoder\", OneHotEncoder(), categorical_features)\n",
+ " ],\n",
+ " remainder=\"passthrough\"\n",
+ ")\n",
+ "model = Pipeline([\n",
+ " (\"one-hot-encoder\", categorical_features_onehot_transformer),\n",
+ " (\"clf\", LogisticRegression(max_iter=1500))\n",
+ "])\n",
+ "\n",
+ "#### train the model\n",
+ "model = model.fit(X_train, y_train)"
+ ]
},
{
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "\n",
- "100%|████████████████████████████████████████████████████████████████████████████████| 590/590 [00:13<00:00, 43.37it/s]"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "iHQwJo_PgPB9",
+ "outputId": "88e849b7-f24a-4b87-fc48-256612ed3626"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Accuracy = 85.16%\n"
+ ]
+ }
+ ],
+ "source": [
+ "# showcase model's accuracy\n",
+ "y_pred = model.predict(X_test)\n",
+ "print(f\"Accuracy = {(y_test.values == y_pred).sum() / y_test.shape[0]:.2%}\")"
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Computing percentages of individuals flipped by any action with cost up to c, for every c\n"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "avuD6xf-gPB9"
+ },
+ "source": [
+ "# A Practical Example of FACTS\n",
+ "\n",
+ "The real essence of our work starts here. Specifically, we showcase the generation of candidate subpopulation groups and counterfactuals and the detection of those groups that exhibit the greatest unfairness, with respect to one of several metrics."
+ ]
},
{
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "\n",
- "100%|████████████████████████████████████████████████████████████████████████████████| 416/416 [00:12<00:00, 32.57it/s]\n"
- ]
- }
- ],
- "source": [
- "# generates candidate subpopulation groups for bias and candidate actions\n",
- "detector = detector.fit(X_test)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Detect Groups with Unfairness in Protected Subgroups (using \"Equal Choice for Recourse\" metric)\n",
- "\n",
- "Here we demonstrate the `bias_scan` method of our detector, which ranks subpopulation groups from most to least unfair, with respect to the chosen metric and, of course, the protected attribute.\n",
- "\n",
- "For the purposes of the demo, we use the \"Equal Choice for Recourse\" definition / metric. This posits that the classifier acts fairly for the group in question if the protected subgroups can choose among the same number of sufficiently effective actions to achieve recourse. By sufficiently effective we mean those actions (out of all candidates) which work for at least $100\\phi \\%$ (for some $\\phi \\in [0,1]$) of the subgroup.\n",
- "\n",
- "Given this definition, the respective unfairness *metric* is defined to be the difference in the number of sufficiently effective actions between the two protected subgroups.\n",
- "\n",
- "**Suggestion**: this metric may find utility in scenarios where the aim is to guarantee that protected subgroups have a similar range of options available to them when it comes to making adjustments in order to attain a favorable outcome. For example, when evaluating job candidates, the employer may wish to ensure that applicants from different backgrounds (that currently fail to meet expectations) have an equal array of career / retraining opportunities that may land them the job, so as to ensure diversity in all sectors of the company, which employ individuals with a plethora of roles."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Detects the top `top_count` most biased groups based on the given metric\n",
- "# available metrics are:\n",
- "# - equal-effectiveness\n",
- "# - equal-choice-for-recourse\n",
- "# - equal-effectiveness-within-budget\n",
- "# - equal-cost-of-effectiveness\n",
- "# - equal-mean-recourse\n",
- "# - fair-tradeoff\n",
- "# a short description for each metric is given below\n",
- "detector.bias_scan(\n",
- " metric=\"equal-choice-for-recourse\",\n",
- " phi=0.1,\n",
- " top_count=3\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "35vYtSBFgPB9"
+ },
+ "source": [
+ "## Load and Fit FACTS"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "If \u001b[1mage = (26.0, 34.0], hours-per-week = FullTime\u001b[0m:\n",
- "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.59%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.73%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m3.98%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.39%\u001b[39m.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
- "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.78%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m19.66%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m10.63%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m13.39%\u001b[39m.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-3.00\u001b[39m\n",
- "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse. Unfairness score = 3.\u001b[39m\n",
- "If \u001b[1mage = (26.0, 34.0], capital-loss = 0.0, hours-per-week = FullTime\u001b[0m:\n",
- "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.34%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.67%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m4.08%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.28%\u001b[39m.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
- "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.27%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m18.43%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m9.27%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m11.92%\u001b[39m.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-2.00\u001b[39m\n",
- "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse. Unfairness score = 2.\u001b[39m\n",
- "If \u001b[1mhours-per-week = FullTime, native-country = United-States\u001b[0m:\n",
- "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m41.66%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m2.62%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m1.79%\u001b[39m.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
- "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m46.78%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m10.08%\u001b[39m.\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m8.70%\u001b[39m.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-1.00\u001b[39m\n",
- "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse. Unfairness score = 1.\u001b[39m\n"
- ]
- }
- ],
- "source": [
- "# prints the result into a nicely formatted report\n",
- "detector.print_recourse_report(\n",
- " show_action_costs=False,\n",
- " show_subgroup_costs=True,\n",
- " show_unbiased_subgroups=False,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Example Output Breakdown"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us now disect the above example and the output we see, one step at a time.\n",
- "\n",
- "#### Prelude: $\\phi = 0.1$\n",
- "\n",
- "As we mentioned in the general description of this metric, this is the parameter that determines whether we consider an action sufficiently effective or not. So, here, we consider an action effective if it manages to flip the prediction for at least 10% of the individuals under study, and ineffective otherwise.\n",
- "\n",
- "#### **age = (26.0, 34.0], hours-per-week = FullTime**\n",
- "\n",
- "This is the first (hence, most biased) group. The group description is mostly self-explanatory: everything inside this block concerns all those (affected) individuals that are from 26 (not inclusive) to 34 years old and have a fulltime job. Now, since the output has the same structure for all groups, let us consider this group as an example and further disect the output we see in this block.\n",
- "\n",
- "#### *Protected subgroups 'Male' / 'Female'*\n",
- "\n",
- "We split the population of this group, according to the protected attribute. Hence, we distinguish between males that are 26-34 years old and have a fulltime job and females that are 26-34 years old and have a fulltime job.\n",
- "\n",
- "The \"covered\" percentage reported here in blue signifies that out of all affected females, 10.59% are 26-34 years old and have a fulltime job, while the respective percentage for males is 13.78%.\n",
- "\n",
- "#### *Make age = (41.0, 50.0], hours-per-week = OverTime*\n",
- "\n",
- "This is one of the 3 actions we have tried to apply on the individuals in the current subpopulation group. We report the action, along with its effectiveness and, optionally, the cost; here we omit the action cost because the \"Equal Choice for Recourse\" metric does not take it into account.\n",
- "\n",
- "At this point, let us give a more direct interpretation for the **effectiveness**. In this case, for example, the interpretation could be the following: if all females aged 26-34 with fulltime jobs change their age group to 41-50 years old and their working hours to overtime, then 7.73% of them will actually manage to receive the positive prediction from the model. The rest will still receive the negative prediction.\n",
- "\n",
- "#### *Protected Subgroups' Aggregate Cost*\n",
- "\n",
- "The \"aggregate cost of the above recourses\" message shows how we quantify the *cost of recourse* for all actions in each protected subgroup.\n",
- "\n",
- "This is derived directly from the definition of each metric. Here, for example, we use the \"Equal Choice for Recourse\" metric, which counts the number of effective actions available to each of the protected subgroups. In this group, females have no (sufficiently) effective actions, and as such we say that they gain 0 units. Males have 3 effective actions, so they gain 3 units.\n",
- "\n",
- "Finally, to keep the formalization of having costs everywhere, we rephrase this instead into males having a recourse cost of -3 and females having a recourse cost of 0.\n",
- "\n",
- "As we also mention in the next paragraph, the final bias score of the subgroup is nothing more than the absolute difference of these 2 costs.\n",
- "\n",
- "#### *Bias Deduction / Metric Application*\n",
- "\n",
- "Given the above, one can see that the (same) actions, if applied to females of the subpopulation group, cannot yield more than 10% effectiveness, while in males they achieve up to 19.66%! This is why we argue that, in the terms of bias of recourse, this group exhibits bias against females.\n",
- "\n",
- "This is, of course, with respect to the \"Equal Choice for Recourse\" metric, which posits that the 2 protected subgroups should have the same number of effective actions. Since none of the 3 actions are sufficiently effective for females, and all 3 of them are sufficiently effective for males, we score this group as having a bias measure of $|0 - 3| = 3$."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Example without Bias of Recourse\n",
- "\n",
- "For completeness, we also demonstrate how, for some choices of metrics and parameters, FACTS may fail to find any subpopulation groups that exhibit bias between the protected populations, and thus deduce that in this case there is no recourse related bias."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "detector.bias_scan(\n",
- " metric=\"equal-choice-for-recourse\",\n",
- " phi=0.7,\n",
- " top_count=3\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "3FEEyse9gPB9"
+ },
+ "outputs": [],
+ "source": [
+ "# load FACTS framework with:\n",
+ "# - the model to be audited\n",
+ "# - protected attribute \"sex\" and\n",
+ "# - assigning equal, unit weights to all features for cost computation.\n",
+ "# - no features forbidden from changing, i.e. user can specify any features that cannot change at all.\n",
+ "detector = FACTS(\n",
+ " clf=model,\n",
+ " prot_attr=\"sex\",\n",
+ " feature_weights={f: 1 for f in X.columns},\n",
+ " feats_not_allowed_to_change=[]\n",
+ ")"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "\u001b[1mWith the given parameters, no recourses showing unfairness have been found!\u001b[0m\n"
- ]
- }
- ],
- "source": [
- "# prints the result into a nicely formatted report\n",
- "detector.print_recourse_report(\n",
- " show_action_costs=False,\n",
- " show_subgroup_costs=True,\n",
- " show_unbiased_subgroups=False,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Aternative API\n",
- "\n",
- "We also provide a more succinct API in the form of a wrapper function. This is closer in style to the API of existing `aif360` detectors.\n",
- "\n",
- "The previous example could be run equivalently with the following."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "duEkwMgngPB-",
+ "outputId": "ddcb45a9-fa3e-466c-e492-bf6a8b26b2dc"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Computing candidate subgroups.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "100%|██████████████████████████████████████████████████████████████████████████| 1046/1046 [00:00<00:00, 523287.45it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Number of subgroups: 563\n",
+ "Computing candidate recourses for all subgroups.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "100%|█████████████████████████████████████████████████████████████████████████████| 563/563 [00:00<00:00, 50669.32it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Computing percentages of individuals flipped by each action independently.\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "100%|████████████████████████████████████████████████████████████████████████████████| 590/590 [00:13<00:00, 43.37it/s]"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Computing percentages of individuals flipped by any action with cost up to c, for every c\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "100%|████████████████████████████████████████████████████████████████████████████████| 416/416 [00:12<00:00, 32.57it/s]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# generates candidate subpopulation groups for bias and candidate actions\n",
+ "detector = detector.fit(X_test)"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "If \u001b[1mage = (26.0, 34.0], hours-per-week = FullTime\u001b[0m:\n",
- "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.59%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.73%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m3.98%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.39%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
- "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.78%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m19.66%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m10.63%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m13.39%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-3.00\u001b[39m\n",
- "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse.. Unfairness score = 3.\u001b[39m\n",
- "If \u001b[1mage = (26.0, 34.0], capital-loss = 0.0, hours-per-week = FullTime\u001b[0m:\n",
- "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.34%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.67%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m4.08%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.28%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
- "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.27%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m18.43%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m9.27%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m11.92%\u001b[39m and counterfactual cost = 2.0.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-2.00\u001b[39m\n",
- "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse.. Unfairness score = 2.\u001b[39m\n",
- "If \u001b[1mhours-per-week = FullTime, native-country = United-States\u001b[0m:\n",
- "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m41.66%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m2.62%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m1.79%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
- "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m46.78%\u001b[39m covered\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m10.08%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m8.70%\u001b[39m and counterfactual cost = 1.0.\n",
- "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-1.00\u001b[39m\n",
- "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse.. Unfairness score = 1.\u001b[39m\n"
- ]
- }
- ],
- "source": [
- "most_biased_subgroups = FACTS_bias_scan(\n",
- " X=X_test,\n",
- " clf=model,\n",
- " prot_attr=\"sex\",\n",
- " feature_weights={f: 1 for f in X.columns},\n",
- " feats_not_allowed_to_change=[],\n",
- " metric=\"equal-choice-for-recourse\",\n",
- " phi=0.1,\n",
- " top_count=3,\n",
- " verbose=False, # hides progress bars\n",
- " print_recourse_report=True,\n",
- " show_action_costs=True,\n",
- " show_subgroup_costs=True,\n",
- ")"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "cFPOLhkEgPB-"
+ },
+ "source": [
+ "## Detect Groups with Unfairness in Protected Subgroups (using \"Equal Choice for Recourse\" metric)\n",
+ "\n",
+ "Here we demonstrate the `bias_scan` method of our detector, which ranks subpopulation groups from most to least unfair, with respect to the chosen metric and, of course, the protected attribute.\n",
+ "\n",
+ "For the purposes of the demo, we use the \"Equal Choice for Recourse\" definition / metric. This posits that the classifier acts fairly for the group in question if the protected subgroups can choose among the same number of sufficiently effective actions to achieve recourse. By sufficiently effective we mean those actions (out of all candidates) which work for at least $100\\phi \\%$ (for some $\\phi \\in [0,1]$) of the subgroup.\n",
+ "\n",
+ "Given this definition, the respective unfairness *metric* is defined to be the difference in the number of sufficiently effective actions between the two protected subgroups.\n",
+ "\n",
+ "**Suggestion**: this metric may find utility in scenarios where the aim is to guarantee that protected subgroups have a similar range of options available to them when it comes to making adjustments in order to attain a favorable outcome. For example, when evaluating job candidates, the employer may wish to ensure that applicants from different backgrounds (that currently fail to meet expectations) have an equal array of career / retraining opportunities that may land them the job, so as to ensure diversity in all sectors of the company, which employ individuals with a plethora of roles."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "lApb3FmVgPB-"
+ },
+ "outputs": [],
+ "source": [
+ "# Detects the top `top_count` most biased groups based on the given metric\n",
+ "# available metrics are:\n",
+ "# - equal-effectiveness\n",
+ "# - equal-choice-for-recourse\n",
+ "# - equal-effectiveness-within-budget\n",
+ "# - equal-cost-of-effectiveness\n",
+ "# - equal-mean-recourse\n",
+ "# - fair-tradeoff\n",
+ "# a short description for each metric is given below\n",
+ "detector.bias_scan(\n",
+ " metric=\"equal-choice-for-recourse\",\n",
+ " phi=0.1,\n",
+ " top_count=3\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "QbuX_rC1gPB-",
+ "outputId": "8292d5e3-b86e-4f3a-fcb5-4eac0093e3ba"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "If \u001b[1mage = (26.0, 34.0], hours-per-week = FullTime\u001b[0m:\n",
+ "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.59%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.73%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m3.98%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.39%\u001b[39m.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
+ "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.78%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m19.66%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m10.63%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m13.39%\u001b[39m.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-3.00\u001b[39m\n",
+ "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse. Unfairness score = 3.\u001b[39m\n",
+ "If \u001b[1mage = (26.0, 34.0], capital-loss = 0.0, hours-per-week = FullTime\u001b[0m:\n",
+ "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.34%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.67%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m4.08%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.28%\u001b[39m.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
+ "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.27%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m18.43%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m9.27%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m11.92%\u001b[39m.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-2.00\u001b[39m\n",
+ "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse. Unfairness score = 2.\u001b[39m\n",
+ "If \u001b[1mhours-per-week = FullTime, native-country = United-States\u001b[0m:\n",
+ "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m41.66%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m2.62%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m1.79%\u001b[39m.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
+ "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m46.78%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m10.08%\u001b[39m.\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m8.70%\u001b[39m.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-1.00\u001b[39m\n",
+ "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse. Unfairness score = 1.\u001b[39m\n"
+ ]
+ }
+ ],
+ "source": [
+ "# prints the result into a nicely formatted report\n",
+ "detector.print_recourse_report(\n",
+ " show_action_costs=False,\n",
+ " show_subgroup_costs=True,\n",
+ " show_unbiased_subgroups=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HK5JNhNwgPB-"
+ },
+ "source": [
+ "### Example Output Breakdown"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xf-Qbq6agPB-"
+ },
+ "source": [
+ "Let us now disect the above example and the output we see, one step at a time.\n",
+ "\n",
+ "#### Prelude: $\\phi = 0.1$\n",
+ "\n",
+ "As we mentioned in the general description of this metric, this is the parameter that determines whether we consider an action sufficiently effective or not. So, here, we consider an action effective if it manages to flip the prediction for at least 10% of the individuals under study, and ineffective otherwise.\n",
+ "\n",
+ "#### **age = (26.0, 34.0], hours-per-week = FullTime**\n",
+ "\n",
+ "This is the first (hence, most biased) group. The group description is mostly self-explanatory: everything inside this block concerns all those (affected) individuals that are from 26 (not inclusive) to 34 years old and have a fulltime job. Now, since the output has the same structure for all groups, let us consider this group as an example and further disect the output we see in this block.\n",
+ "\n",
+ "#### *Protected subgroups 'Male' / 'Female'*\n",
+ "\n",
+ "We split the population of this group, according to the protected attribute. Hence, we distinguish between males that are 26-34 years old and have a fulltime job and females that are 26-34 years old and have a fulltime job.\n",
+ "\n",
+ "The \"covered\" percentage reported here in blue signifies that out of all affected females, 10.59% are 26-34 years old and have a fulltime job, while the respective percentage for males is 13.78%.\n",
+ "\n",
+ "#### *Make age = (41.0, 50.0], hours-per-week = OverTime*\n",
+ "\n",
+ "This is one of the 3 actions we have tried to apply on the individuals in the current subpopulation group. We report the action, along with its effectiveness and, optionally, the cost; here we omit the action cost because the \"Equal Choice for Recourse\" metric does not take it into account.\n",
+ "\n",
+ "At this point, let us give a more direct interpretation for the **effectiveness**. In this case, for example, the interpretation could be the following: if all females aged 26-34 with fulltime jobs change their age group to 41-50 years old and their working hours to overtime, then 7.73% of them will actually manage to receive the positive prediction from the model. The rest will still receive the negative prediction.\n",
+ "\n",
+ "#### *Protected Subgroups' Aggregate Cost*\n",
+ "\n",
+ "The \"aggregate cost of the above recourses\" message shows how we quantify the *cost of recourse* for all actions in each protected subgroup.\n",
+ "\n",
+ "This is derived directly from the definition of each metric. Here, for example, we use the \"Equal Choice for Recourse\" metric, which counts the number of effective actions available to each of the protected subgroups. In this group, females have no (sufficiently) effective actions, and as such we say that they gain 0 units. Males have 3 effective actions, so they gain 3 units.\n",
+ "\n",
+ "Finally, to keep the formalization of having costs everywhere, we rephrase this instead into males having a recourse cost of -3 and females having a recourse cost of 0.\n",
+ "\n",
+ "As we also mention in the next paragraph, the final bias score of the subgroup is nothing more than the absolute difference of these 2 costs.\n",
+ "\n",
+ "#### *Bias Deduction / Metric Application*\n",
+ "\n",
+ "Given the above, one can see that the (same) actions, if applied to females of the subpopulation group, cannot yield more than 10% effectiveness, while in males they achieve up to 19.66%! This is why we argue that, in the terms of bias of recourse, this group exhibits bias against females.\n",
+ "\n",
+ "This is, of course, with respect to the \"Equal Choice for Recourse\" metric, which posits that the 2 protected subgroups should have the same number of effective actions. Since none of the 3 actions are sufficiently effective for females, and all 3 of them are sufficiently effective for males, we score this group as having a bias measure of $|0 - 3| = 3$."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "qba9HsyEgPB-"
+ },
+ "source": [
+ "### Example without Bias of Recourse\n",
+ "\n",
+ "For completeness, we also demonstrate how, for some choices of metrics and parameters, FACTS may fail to find any subpopulation groups that exhibit bias between the protected populations, and thus deduce that in this case there is no recourse related bias."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "9ZazYqQ5gPB_"
+ },
+ "outputs": [],
+ "source": [
+ "detector.bias_scan(\n",
+ " metric=\"equal-choice-for-recourse\",\n",
+ " phi=0.7,\n",
+ " top_count=3\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "LNuBKASZgPB_",
+ "outputId": "f2c75748-e4af-4719-b774-c8904bb5f6f6"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[1mWith the given parameters, no recourses showing unfairness have been found!\u001b[0m\n"
+ ]
+ }
+ ],
+ "source": [
+ "# prints the result into a nicely formatted report\n",
+ "detector.print_recourse_report(\n",
+ " show_action_costs=False,\n",
+ " show_subgroup_costs=True,\n",
+ " show_unbiased_subgroups=False,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HIp_hVhLgPB_"
+ },
+ "source": [
+ "## Aternative API\n",
+ "\n",
+ "We also provide a more succinct API in the form of a wrapper function. This is closer in style to the API of existing `aif360` detectors.\n",
+ "\n",
+ "The previous example could be run equivalently with the following."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "RyIvVWtKgPB_",
+ "outputId": "cfc9da11-5120-48f1-fbad-63c5018f4b95"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "If \u001b[1mage = (26.0, 34.0], hours-per-week = FullTime\u001b[0m:\n",
+ "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.59%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.73%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m3.98%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.39%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
+ "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.78%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m19.66%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m10.63%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m13.39%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-3.00\u001b[39m\n",
+ "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse.. Unfairness score = 3.\u001b[39m\n",
+ "If \u001b[1mage = (26.0, 34.0], capital-loss = 0.0, hours-per-week = FullTime\u001b[0m:\n",
+ "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m10.34%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m7.67%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m4.08%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m5.28%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
+ "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m13.27%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m18.43%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (41.0, 50.0]\u001b[39m\u001b[0m with effectiveness \u001b[32m9.27%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mage = (34.0, 41.0]\u001b[39m, \u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m11.92%\u001b[39m and counterfactual cost = 2.0.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-2.00\u001b[39m\n",
+ "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse.. Unfairness score = 2.\u001b[39m\n",
+ "If \u001b[1mhours-per-week = FullTime, native-country = United-States\u001b[0m:\n",
+ "\tProtected Subgroup '\u001b[1mFemale\u001b[0m', \u001b[34m41.66%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m2.62%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m1.79%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m0.00\u001b[39m\n",
+ "\tProtected Subgroup '\u001b[1mMale\u001b[0m', \u001b[34m46.78%\u001b[39m covered\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = OverTime\u001b[39m\u001b[0m with effectiveness \u001b[32m10.08%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\tMake \u001b[1m\u001b[31mhours-per-week = BrainDrain\u001b[39m\u001b[0m with effectiveness \u001b[32m8.70%\u001b[39m and counterfactual cost = 1.0.\n",
+ "\t\t\u001b[1mAggregate cost\u001b[0m of the above recourses = \u001b[35m-1.00\u001b[39m\n",
+ "\t\u001b[35mBias against Female with respect to equal-choice-for-recourse.. Unfairness score = 1.\u001b[39m\n"
+ ]
+ }
+ ],
+ "source": [
+ "most_biased_subgroups = FACTS_bias_scan(\n",
+ " X=X_test,\n",
+ " clf=model,\n",
+ " prot_attr=\"sex\",\n",
+ " feature_weights={f: 1 for f in X.columns},\n",
+ " feats_not_allowed_to_change=[],\n",
+ " metric=\"equal-choice-for-recourse\",\n",
+ " phi=0.1,\n",
+ " top_count=3,\n",
+ " verbose=False, # hides progress bars\n",
+ " print_recourse_report=True,\n",
+ " show_action_costs=True,\n",
+ " show_subgroup_costs=True,\n",
+ ")"
+ ]
+ },
{
- "data": {
- "text/plain": [
- "[({'hours-per-week': 'FullTime', 'native-country': 'United-States'}, 1),\n",
- " ({'age': Interval(26.0, 34.0, closed='right'), 'hours-per-week': 'FullTime'},\n",
- " 3),\n",
- " ({'age': Interval(26.0, 34.0, closed='right'),\n",
- " 'capital-loss': 0.0,\n",
- " 'hours-per-week': 'FullTime'},\n",
- " 2)]"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "IGbEBY7NgPB_",
+ "outputId": "5796d312-5ef6-4b0f-c2ec-8888e3376552"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[({'hours-per-week': 'FullTime', 'native-country': 'United-States'}, 1),\n",
+ " ({'age': Interval(26.0, 34.0, closed='right'), 'hours-per-week': 'FullTime'},\n",
+ " 3),\n",
+ " ({'age': Interval(26.0, 34.0, closed='right'),\n",
+ " 'capital-loss': 0.0,\n",
+ " 'hours-per-week': 'FullTime'},\n",
+ " 2)]"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "most_biased_subgroups"
]
- },
- "execution_count": 13,
- "metadata": {},
- "output_type": "execute_result"
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3po1Pz8KgPB_"
+ },
+ "source": [
+ "# Short Description of all Definitions / Metrics of Subgroup Recourse Fairness\n",
+ "\n",
+ "Here we give a brief description of each of the metrics available in our framework apart from \"Equal Choice for Recourse\".\n",
+ "\n",
+ "## Equal Effectiveness\n",
+ "\n",
+ "The classifier is considered to act fairly for a population group if the same proportion of individuals in the protected subgroups can achieve recourse.\n",
+ "\n",
+ "**Suggestion**: this metric ignores costs altogether and compares only the percentage of males VS females that can cross the model's decision boundary by the same actions. We would use it in applications where the goal is equal impact, in the sense that a change (or a set thereof) affects the same proportion of individuals in the protected subgroups. For example, in a hiring scenario, a similar proportion of males and females are expected to benefit from the same change.\n",
+ "\n",
+ "## Equal Effectiveness within Budget\n",
+ "\n",
+ "The classifier is considered to act fairly for a population group if the same proportion of individuals in the protected subgroups can achieve recourse with a cost at most $c$, where $c$ is some user-provided cost budget.\n",
+ "\n",
+ "**Suggestion**: this metric is similar to the above, but puts a bound on how large the cost of an action can be. Could be used to limit changes with undesirably large cost, e.g., salary changes up to 10K.\n",
+ "\n",
+ "## Equal Cost of Effectiveness\n",
+ "\n",
+ "The classifier is considered to act fairly for a population group if the minimum cost required to be sufficiently effective in the protected subgroups is equal. Again, as in \"Equal Choice for Recourse\", by \"sufficiently effective\" we refer to those actions that successfully flip the model's decision for at least $100\\phi \\%$ (for $\\phi \\in [0,1]$) of the subgroup.\n",
+ "\n",
+ "**Suggestion**: this metric could be useful when an external factor imposes a specific threshold, e.g. in credit risk assessment, a guideline which states that the effort required to be 80% certain that you will have your loan accepted should be the same for males and females.\n",
+ "\n",
+ "## Equal (Conditional) Mean Recourse\n",
+ "\n",
+ "This definition extends the notion of *burden* from literature ([reference](https://dl.acm.org/doi/10.1145/3375627.3375812)) to the case where not all individuals may achieve recourse. Omitting some details, given any set of individuals, the **conditional mean recourse cost** is the mean recourse cost among the subset of individuals that can actually achieve recourse, i.e. by at least one of the available actions.\n",
+ "\n",
+ "Given the above, this definition considers the classifier to act fairly for a population group if the (conditional) mean recourse cost for the protected subgroups is the same.\n",
+ "\n",
+ "**Suggestion**: this metric compares the mean cost required to achieve recourse for the protected subgroups. It could be useful in a scenario like loan approval, where one needs to ensure that the cost of changes needed to receive the loan are the same for males and females on average.\n",
+ "\n",
+ "## Fair Effectiveness-Cost Trade-Off\n",
+ "\n",
+ "This is the strictest definition, which considers the classifier to act fairly for a population group only if the protected subgroups have the same effectiveness-cost distribution (checked in the implementation via a statistical test).\n",
+ "\n",
+ "Equivalently, Equal Effectiveness within Budget must hold for *every* value of the cost budget $c$.\n",
+ "\n",
+ "**Suggestion**: this metric considers all available actions and compares all their possible trade-offs between effectiveness and cost among the protected subgroups. This could be useful for cases where the protected attribute should have absolutely no impact on the available options to achieve recourse, such as in high-risk situations like estimating the risk of a convicted individual to act unlawfully in the future (as in the well known [COMPAS dataset](https://www.propublica.org/datastore/dataset/compas-recidivism-risk-score-data-and-analysis))."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "5sGwLyBNgPCA"
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.18"
+ },
+ "colab": {
+ "provenance": []
}
- ],
- "source": [
- "most_biased_subgroups"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Short Description of all Definitions / Metrics of Subgroup Recourse Fairness\n",
- "\n",
- "Here we give a brief description of each of the metrics available in our framework apart from \"Equal Choice for Recourse\".\n",
- "\n",
- "## Equal Effectiveness\n",
- "\n",
- "The classifier is considered to act fairly for a population group if the same proportion of individuals in the protected subgroups can achieve recourse.\n",
- "\n",
- "**Suggestion**: this metric ignores costs altogether and compares only the percentage of males VS females that can cross the model's decision boundary by the same actions. We would use it in applications where the goal is equal impact, in the sense that a change (or a set thereof) affects the same proportion of individuals in the protected subgroups. For example, in a hiring scenario, a similar proportion of males and females are expected to benefit from the same change.\n",
- "\n",
- "## Equal Effectiveness within Budget\n",
- "\n",
- "The classifier is considered to act fairly for a population group if the same proportion of individuals in the protected subgroups can achieve recourse with a cost at most $c$, where $c$ is some user-provided cost budget.\n",
- "\n",
- "**Suggestion**: this metric is similar to the above, but puts a bound on how large the cost of an action can be. Could be used to limit changes with undesirably large cost, e.g., salary changes up to 10K.\n",
- "\n",
- "## Equal Cost of Effectiveness\n",
- "\n",
- "The classifier is considered to act fairly for a population group if the minimum cost required to be sufficiently effective in the protected subgroups is equal. Again, as in \"Equal Choice for Recourse\", by \"sufficiently effective\" we refer to those actions that successfully flip the model's decision for at least $100\\phi \\%$ (for $\\phi \\in [0,1]$) of the subgroup.\n",
- "\n",
- "**Suggestion**: this metric could be useful when an external factor imposes a specific threshold, e.g. in credit risk assessment, a guideline which states that the effort required to be 80% certain that you will have your loan accepted should be the same for males and females.\n",
- "\n",
- "## Equal (Conditional) Mean Recourse\n",
- "\n",
- "This definition extends the notion of *burden* from literature ([reference](https://dl.acm.org/doi/10.1145/3375627.3375812)) to the case where not all individuals may achieve recourse. Omitting some details, given any set of individuals, the **conditional mean recourse cost** is the mean recourse cost among the subset of individuals that can actually achieve recourse, i.e. by at least one of the available actions.\n",
- "\n",
- "Given the above, this definition considers the classifier to act fairly for a population group if the (conditional) mean recourse cost for the protected subgroups is the same.\n",
- "\n",
- "**Suggestion**: this metric compares the mean cost required to achieve recourse for the protected subgroups. It could be useful in a scenario like loan approval, where one needs to ensure that the cost of changes needed to receive the loan are the same for males and females on average.\n",
- "\n",
- "## Fair Effectiveness-Cost Trade-Off\n",
- "\n",
- "This is the strictest definition, which considers the classifier to act fairly for a population group only if the protected subgroups have the same effectiveness-cost distribution (checked in the implementation via a statistical test).\n",
- "\n",
- "Equivalently, Equal Effectiveness within Budget must hold for *every* value of the cost budget $c$.\n",
- "\n",
- "**Suggestion**: this metric considers all available actions and compares all their possible trade-offs between effectiveness and cost among the protected subgroups. This could be useful for cases where the protected attribute should have absolutely no impact on the available options to achieve recourse, such as in high-risk situations like estimating the risk of a convicted individual to act unlawfully in the future (as in the well known [COMPAS dataset](https://www.propublica.org/datastore/dataset/compas-recidivism-risk-score-data-and-analysis))."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
},
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.18"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
\ No newline at end of file
diff --git a/examples/demo_gerryfair.ipynb b/examples/demo_gerryfair.ipynb
index efdb5983..6b6352f5 100644
--- a/examples/demo_gerryfair.ipynb
+++ b/examples/demo_gerryfair.ipynb
@@ -1,884 +1,918 @@
{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "pycharm": {
- "is_executing": false
- }
- },
- "outputs": [],
- "source": [
- "%matplotlib inline\n",
- "import warnings\n",
- "warnings.filterwarnings(\"ignore\")\n",
- "import sys\n",
- "sys.path.append(\"../\")\n",
- "from aif360.algorithms.inprocessing import GerryFairClassifier\n",
- "from aif360.algorithms.inprocessing.gerryfair.clean import array_to_tuple\n",
- "from aif360.algorithms.inprocessing.gerryfair.auditor import Auditor\n",
- "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_adult\n",
- "from sklearn import svm\n",
- "from sklearn import tree\n",
- "from sklearn.kernel_ridge import KernelRidge\n",
- "from sklearn import linear_model\n",
- "from aif360.metrics import BinaryLabelDatasetMetric\n",
- "from IPython.display import Image\n",
- "import pickle\n",
- "import matplotlib.pyplot as plt\n",
- "\n",
- "# load data set\n",
- "data_set = load_preproc_data_adult(sub_samp=1000, balance=True)\n",
- "max_iterations = 500"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**instantiate, fit, and predict** \n",
- "\n",
- "\n",
- "We first demonstrate how to instantiate a `GerryFairClassifier`, `train` it with respect to rich subgroup fairness, and `predict` the label of a new example. We remark that when we set the `print_flag = True` at each iteration of the algorithm we print the error, fairness violation, and violated group size of most recent model. The error is the classification error of the classifier. At each round the Learner tries to find a classifier that minimizes the classification error plus a weighted sum of the fairness disparities on all the groups that the Auditor has found up until that point. By contrast the Auditor tries to find the group at each round with the greatest rich subgroup disparity with respect to the Learner's model. We define `violated group size` as the size (as a fraction of the dataset size) of this group, and the `fairness violation` as the `violated group size` times the difference in the statistical rate (FP or FN rate) on the group vs. the whole population. \n",
- "\n",
- "In the example below we set `max_iterations=500` which is an order of magnitude less than the time to convergence observed in [the rich subgroup fairness empirical paper](https://arxiv.org/abs/1808.08166), but advise that this can be highly dataset dependent. Our target $\\gamma$-disparity is $\\gamma = .005$, our statistical rate is false positive rate or `FP`, and our cost-sensitive classification oracle is linear regression (more on that below). \n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {
- "pycharm": {
- "is_executing": true
- }
- },
- "outputs": [
+ "cells": [
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "iteration: 1, error: 0.263, fairness violation: 0.028780000000000007, violated group size: 0.217\n",
- "iteration: 2, error: 0.3815, fairness violation: 0.014390000000000003, violated group size: 0.217\n",
- "iteration: 3, error: 0.42099999999999993, fairness violation: 0.009593333333333339, violated group size: 0.283\n",
- "iteration: 4, error: 0.44075, fairness violation: 0.007195000000000002, violated group size: 0.217\n",
- "iteration: 5, error: 0.45260000000000006, fairness violation: 0.005756000000000001, violated group size: 0.217\n",
- "iteration: 6, error: 0.4605000000000001, fairness violation: 0.004796666666666668, violated group size: 0.283\n",
- "iteration: 7, error: 0.4661428571428572, fairness violation: 0.004111428571428572, violated group size: 0.217\n",
- "iteration: 8, error: 0.470375, fairness violation: 0.0035975000000000017, violated group size: 0.217\n",
- "iteration: 9, error: 0.4691111111111112, fairness violation: 0.0033906666666666677, violated group size: 0.283\n",
- "iteration: 10, error: 0.4681, fairness violation: 0.003225200000000001, violated group size: 0.283\n",
- "iteration: 11, error: 0.4672727272727271, fairness violation: 0.0030898181818181836, violated group size: 0.283\n",
- "iteration: 12, error: 0.4665833333333333, fairness violation: 0.0029769999999999996, violated group size: 0.217\n",
- "iteration: 13, error: 0.466, fairness violation: 0.0028815384615384627, violated group size: 0.283\n",
- "iteration: 14, error: 0.4655000000000001, fairness violation: 0.0027997142857142865, violated group size: 0.217\n",
- "iteration: 15, error: 0.46506666666666674, fairness violation: 0.002728800000000001, violated group size: 0.217\n",
- "iteration: 16, error: 0.4646875, fairness violation: 0.0026667500000000007, violated group size: 0.217\n",
- "iteration: 17, error: 0.4643529411764707, fairness violation: 0.002612000000000001, violated group size: 0.283\n",
- "iteration: 18, error: 0.46405555555555567, fairness violation: 0.002563333333333334, violated group size: 0.217\n",
- "iteration: 19, error: 0.4637894736842106, fairness violation: 0.0025197894736842096, violated group size: 0.217\n",
- "iteration: 20, error: 0.46354999999999996, fairness violation: 0.0024806000000000008, violated group size: 0.283\n",
- "iteration: 21, error: 0.4633333333333334, fairness violation: 0.0024451428571428584, violated group size: 0.217\n",
- "iteration: 22, error: 0.4631363636363638, fairness violation: 0.0024129090909090914, violated group size: 0.283\n",
- "iteration: 23, error: 0.46295652173913054, fairness violation: 0.002383478260869566, violated group size: 0.217\n",
- "iteration: 24, error: 0.4627916666666667, fairness violation: 0.002356500000000001, violated group size: 0.283\n",
- "iteration: 25, error: 0.4626400000000001, fairness violation: 0.0023316800000000018, violated group size: 0.283\n",
- "iteration: 26, error: 0.4625000000000001, fairness violation: 0.0023087692307692314, violated group size: 0.217\n",
- "iteration: 27, error: 0.4623703703703705, fairness violation: 0.0022875555555555557, violated group size: 0.217\n",
- "iteration: 28, error: 0.46224999999999994, fairness violation: 0.0022678571428571426, violated group size: 0.217\n",
- "iteration: 29, error: 0.46213793103448264, fairness violation: 0.0022495172413793106, violated group size: 0.217\n",
- "iteration: 30, error: 0.46203333333333335, fairness violation: 0.0022324000000000003, violated group size: 0.217\n",
- "iteration: 31, error: 0.46193548387096783, fairness violation: 0.0022163870967741935, violated group size: 0.217\n",
- "iteration: 32, error: 0.46184375, fairness violation: 0.0022013749999999993, violated group size: 0.217\n",
- "iteration: 33, error: 0.459969696969697, fairness violation: 0.0023319393939393944, violated group size: 0.283\n",
- "iteration: 34, error: 0.4582058823529412, fairness violation: 0.002454823529411765, violated group size: 0.217\n",
- "iteration: 35, error: 0.45654285714285714, fairness violation: 0.0025706857142857144, violated group size: 0.217\n",
- "iteration: 36, error: 0.4549722222222221, fairness violation: 0.0026801111111111114, violated group size: 0.283\n",
- "iteration: 37, error: 0.4534864864864866, fairness violation: 0.0027836216216216214, violated group size: 0.283\n",
- "iteration: 38, error: 0.45207894736842097, fairness violation: 0.0028816842105263162, violated group size: 0.283\n",
- "iteration: 39, error: 0.4507435897435898, fairness violation: 0.0029747179487179492, violated group size: 0.217\n",
- "iteration: 40, error: 0.44947499999999996, fairness violation: 0.0030631000000000005, violated group size: 0.217\n",
- "iteration: 41, error: 0.44826829268292684, fairness violation: 0.0031471707317073175, violated group size: 0.283\n",
- "iteration: 42, error: 0.4471190476190476, fairness violation: 0.0032272380952380955, violated group size: 0.217\n",
- "iteration: 43, error: 0.44602325581395347, fairness violation: 0.0033035813953488386, violated group size: 0.283\n",
- "iteration: 44, error: 0.44497727272727267, fairness violation: 0.0033764545454545453, violated group size: 0.283\n",
- "iteration: 45, error: 0.4439777777777778, fairness violation: 0.003446088888888888, violated group size: 0.217\n",
- "iteration: 46, error: 0.44302173913043474, fairness violation: 0.0035126956521739122, violated group size: 0.217\n",
- "iteration: 47, error: 0.44210638297872346, fairness violation: 0.0035764680851063826, violated group size: 0.217\n",
- "iteration: 48, error: 0.4412291666666666, fairness violation: 0.003637583333333332, violated group size: 0.217\n",
- "iteration: 49, error: 0.4403877551020407, fairness violation: 0.0036962040816326523, violated group size: 0.217\n",
- "iteration: 50, error: 0.4395600000000001, fairness violation: 0.0037524800000000003, violated group size: 0.217\n",
- "iteration: 51, error: 0.43876470588235295, fairness violation: 0.0038065490196078425, violated group size: 0.217\n",
- "iteration: 52, error: 0.438, fairness violation: 0.003858538461538461, violated group size: 0.283\n",
- "iteration: 53, error: 0.4372641509433963, fairness violation: 0.003908566037735848, violated group size: 0.217\n",
- "iteration: 54, error: 0.4365555555555556, fairness violation: 0.003956740740740741, violated group size: 0.283\n",
- "iteration: 55, error: 0.4358181818181819, fairness violation: 0.004003163636363636, violated group size: 0.217\n",
- "iteration: 56, error: 0.4351071428571429, fairness violation: 0.004047928571428571, violated group size: 0.217\n",
- "iteration: 57, error: 0.4344736842105262, fairness violation: 0.004091122807017543, violated group size: 0.217\n",
- "iteration: 58, error: 0.43381034482758624, fairness violation: 0.004132827586206895, violated group size: 0.217\n",
- "iteration: 59, error: 0.4331694915254237, fairness violation: 0.0041731186440677965, violated group size: 0.283\n",
- "iteration: 60, error: 0.43254999999999993, fairness violation: 0.004212066666666666, violated group size: 0.217\n",
- "iteration: 61, error: 0.4319508196721312, fairness violation: 0.004249737704918031, violated group size: 0.217\n",
- "iteration: 62, error: 0.4313709677419356, fairness violation: 0.004286193548387096, violated group size: 0.217\n",
- "iteration: 63, error: 0.43080952380952386, fairness violation: 0.004321492063492062, violated group size: 0.283\n",
- "iteration: 64, error: 0.430265625, fairness violation: 0.004355687499999999, violated group size: 0.283\n",
- "iteration: 65, error: 0.4297384615384615, fairness violation: 0.004388830769230769, violated group size: 0.283\n",
- "iteration: 66, error: 0.42922727272727274, fairness violation: 0.004420969696969697, violated group size: 0.217\n",
- "iteration: 67, error: 0.42873134328358203, fairness violation: 0.004452149253731343, violated group size: 0.217\n",
- "iteration: 68, error: 0.42824999999999996, fairness violation: 0.0044824117647058815, violated group size: 0.283\n",
- "iteration: 69, error: 0.42778260869565227, fairness violation: 0.004511797101449274, violated group size: 0.217\n",
- "iteration: 70, error: 0.42732857142857145, fairness violation: 0.004540342857142856, violated group size: 0.283\n",
- "iteration: 71, error: 0.42688732394366197, fairness violation: 0.004568084507042252, violated group size: 0.217\n",
- "iteration: 72, error: 0.4264583333333332, fairness violation: 0.004595055555555555, violated group size: 0.283\n",
- "iteration: 73, error: 0.42604109589041106, fairness violation: 0.004621287671232876, violated group size: 0.217\n",
- "iteration: 74, error: 0.4256351351351351, fairness violation: 0.0046468108108108095, violated group size: 0.283\n",
- "iteration: 75, error: 0.42524, fairness violation: 0.004671653333333331, violated group size: 0.217\n",
- "iteration: 76, error: 0.4248552631578947, fairness violation: 0.004695842105263155, violated group size: 0.217\n",
- "iteration: 77, error: 0.42448051948051946, fairness violation: 0.004719402597402596, violated group size: 0.217\n",
- "iteration: 78, error: 0.4239871794871795, fairness violation: 0.00475905128205128, violated group size: 0.217\n",
- "iteration: 79, error: 0.42363291139240505, fairness violation: 0.004781215189873418, violated group size: 0.283\n",
- "iteration: 80, error: 0.42328750000000015, fairness violation: 0.004802824999999999, violated group size: 0.283\n"
- ]
+ "cell_type": "markdown",
+ "source": [
+ "[](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/main/examples/demo_gerryfair.ipynb)"
+ ],
+ "metadata": {
+ "id": "_Y3xm4CthJnm"
+ }
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "iteration: 81, error: 0.4229506172839506, fairness violation: 0.004823901234567901, violated group size: 0.283\n",
- "iteration: 82, error: 0.4226219512195123, fairness violation: 0.004844463414634145, violated group size: 0.217\n",
- "iteration: 83, error: 0.4221807228915662, fairness violation: 0.004880216867469879, violated group size: 0.217\n",
- "iteration: 84, error: 0.42175, fairness violation: 0.004915119047619047, violated group size: 0.217\n",
- "iteration: 85, error: 0.4214470588235294, fairness violation: 0.004933882352941174, violated group size: 0.217\n",
- "iteration: 86, error: 0.4210348837209302, fairness violation: 0.004967348837209301, violated group size: 0.217\n",
- "iteration: 87, error: 0.420632183908046, fairness violation: 0.005000045977011494, violated group size: 0.283\n",
- "iteration: 88, error: 0.42035227272727277, fairness violation: 0.0050172045454545434, violated group size: 0.217\n",
- "iteration: 89, error: 0.4200786516853933, fairness violation: 0.005033977528089887, violated group size: 0.217\n",
- "iteration: 90, error: 0.4198111111111112, fairness violation: 0.005050377777777776, violated group size: 0.283\n",
- "iteration: 91, error: 0.4195824175824176, fairness violation: 0.0050664175824175805, violated group size: 0.217\n",
- "iteration: 92, error: 0.4193695652173913, fairness violation: 0.005082108695652173, violated group size: 0.217\n",
- "iteration: 93, error: 0.41916129032258065, fairness violation: 0.005097462365591397, violated group size: 0.217\n",
- "iteration: 94, error: 0.41895744680851066, fairness violation: 0.005112489361702126, violated group size: 0.217\n",
- "iteration: 95, error: 0.41875789473684216, fairness violation: 0.005127199999999998, violated group size: 0.217\n",
- "iteration: 96, error: 0.41856250000000006, fairness violation: 0.005141604166666665, violated group size: 0.283\n",
- "iteration: 97, error: 0.418979381443299, fairness violation: 0.005106494845360823, violated group size: 0.217\n",
- "iteration: 98, error: 0.41938775510204085, fairness violation: 0.005072102040816325, violated group size: 0.217\n",
- "iteration: 99, error: 0.4197878787878788, fairness violation: 0.0050384040404040376, violated group size: 0.217\n",
- "iteration: 100, error: 0.42018000000000005, fairness violation: 0.0050053799999999985, violated group size: 0.217\n",
- "iteration: 101, error: 0.42056435643564366, fairness violation: 0.004973009900990098, violated group size: 0.217\n",
- "iteration: 102, error: 0.42094117647058826, fairness violation: 0.00494127450980392, violated group size: 0.217\n",
- "iteration: 103, error: 0.4213106796116506, fairness violation: 0.004910155339805824, violated group size: 0.217\n",
- "iteration: 104, error: 0.4216730769230769, fairness violation: 0.004879634615384614, violated group size: 0.217\n",
- "iteration: 105, error: 0.4220285714285715, fairness violation: 0.004849695238095237, violated group size: 0.217\n",
- "iteration: 106, error: 0.4223773584905662, fairness violation: 0.004820320754716981, violated group size: 0.283\n",
- "iteration: 107, error: 0.42271962616822434, fairness violation: 0.004791495327102803, violated group size: 0.217\n",
- "iteration: 108, error: 0.4230555555555556, fairness violation: 0.0047632037037037035, violated group size: 0.217\n",
- "iteration: 109, error: 0.4233853211009175, fairness violation: 0.00473543119266055, violated group size: 0.217\n",
- "iteration: 110, error: 0.4237090909090908, fairness violation: 0.004708163636363636, violated group size: 0.217\n",
- "iteration: 111, error: 0.424027027027027, fairness violation: 0.004681387387387387, violated group size: 0.283\n",
- "iteration: 112, error: 0.42433928571428586, fairness violation: 0.004655089285714286, violated group size: 0.283\n",
- "iteration: 113, error: 0.4241238938053097, fairness violation: 0.004671504424778761, violated group size: 0.217\n",
- "iteration: 114, error: 0.42442982456140343, fairness violation: 0.004645754385964912, violated group size: 0.283\n",
- "iteration: 115, error: 0.42473043478260875, fairness violation: 0.0046204521739130425, violated group size: 0.283\n",
- "iteration: 116, error: 0.42502586206896553, fairness violation: 0.0045955862068965524, violated group size: 0.283\n",
- "iteration: 117, error: 0.42481196581196584, fairness violation: 0.004611948717948717, violated group size: 0.217\n",
- "iteration: 118, error: 0.4251016949152542, fairness violation: 0.004587576271186439, violated group size: 0.217\n",
- "iteration: 119, error: 0.42489075630252104, fairness violation: 0.004603731092436974, violated group size: 0.217\n",
- "iteration: 120, error: 0.4251750000000001, fairness violation: 0.0045798333333333325, violated group size: 0.217\n",
- "iteration: 121, error: 0.4249669421487604, fairness violation: 0.004595785123966942, violated group size: 0.283\n",
- "iteration: 122, error: 0.4247622950819671, fairness violation: 0.0046114754098360656, violated group size: 0.217\n",
- "iteration: 123, error: 0.42456097560975614, fairness violation: 0.00462691056910569, violated group size: 0.217\n",
- "iteration: 124, error: 0.42436290322580644, fairness violation: 0.004642096774193548, violated group size: 0.217\n",
- "iteration: 125, error: 0.4241680000000001, fairness violation: 0.00465704, violated group size: 0.217\n",
- "iteration: 126, error: 0.4239761904761905, fairness violation: 0.004671746031746031, violated group size: 0.217\n",
- "iteration: 127, error: 0.42425196850393704, fairness violation: 0.004648629921259842, violated group size: 0.217\n",
- "iteration: 128, error: 0.4240625, fairness violation: 0.004663171874999999, violated group size: 0.217\n",
- "iteration: 129, error: 0.4238759689922481, fairness violation: 0.004677488372093024, violated group size: 0.283\n",
- "iteration: 130, error: 0.42369230769230776, fairness violation: 0.004691584615384614, violated group size: 0.217\n",
- "iteration: 131, error: 0.42351145038167937, fairness violation: 0.004705465648854962, violated group size: 0.217\n",
- "iteration: 132, error: 0.4233333333333333, fairness violation: 0.004719136363636364, violated group size: 0.283\n",
- "iteration: 133, error: 0.423157894736842, fairness violation: 0.0047326015037594, violated group size: 0.217\n",
- "iteration: 134, error: 0.4229850746268656, fairness violation: 0.004745865671641791, violated group size: 0.217\n",
- "iteration: 135, error: 0.42281481481481475, fairness violation: 0.004758933333333335, violated group size: 0.283\n",
- "iteration: 136, error: 0.4226470588235294, fairness violation: 0.004771808823529411, violated group size: 0.217\n",
- "iteration: 137, error: 0.42248175182481745, fairness violation: 0.004784496350364964, violated group size: 0.283\n",
- "iteration: 138, error: 0.42231884057971014, fairness violation: 0.004797000000000002, violated group size: 0.283\n",
- "iteration: 139, error: 0.42215827338129497, fairness violation: 0.004809323741007196, violated group size: 0.283\n",
- "iteration: 140, error: 0.42200000000000004, fairness violation: 0.004821471428571429, violated group size: 0.217\n",
- "iteration: 141, error: 0.4218439716312057, fairness violation: 0.0048334468085106394, violated group size: 0.217\n",
- "iteration: 142, error: 0.42169014084507045, fairness violation: 0.004845253521126761, violated group size: 0.283\n",
- "iteration: 143, error: 0.4215384615384616, fairness violation: 0.004856895104895106, violated group size: 0.283\n",
- "iteration: 144, error: 0.4213888888888888, fairness violation: 0.004868375, violated group size: 0.217\n",
- "iteration: 145, error: 0.42124137931034483, fairness violation: 0.004879696551724138, violated group size: 0.217\n",
- "iteration: 146, error: 0.4210958904109589, fairness violation: 0.00489086301369863, violated group size: 0.217\n",
- "iteration: 147, error: 0.4209523809523809, fairness violation: 0.004901877551020409, violated group size: 0.217\n",
- "iteration: 148, error: 0.42081081081081084, fairness violation: 0.004912743243243244, violated group size: 0.217\n",
- "iteration: 149, error: 0.42067114093959734, fairness violation: 0.004923463087248323, violated group size: 0.283\n",
- "iteration: 150, error: 0.4205333333333334, fairness violation: 0.004934040000000001, violated group size: 0.217\n",
- "iteration: 151, error: 0.4203973509933776, fairness violation: 0.004944476821192053, violated group size: 0.217\n",
- "iteration: 152, error: 0.4202631578947368, fairness violation: 0.0049547763157894754, violated group size: 0.283\n",
- "iteration: 153, error: 0.4201307189542483, fairness violation: 0.00496494117647059, violated group size: 0.283\n",
- "iteration: 154, error: 0.42, fairness violation: 0.004974974025974027, violated group size: 0.283\n",
- "iteration: 155, error: 0.4198709677419355, fairness violation: 0.0049848774193548395, violated group size: 0.217\n",
- "iteration: 156, error: 0.4197435897435898, fairness violation: 0.004994653846153847, violated group size: 0.217\n",
- "iteration: 157, error: 0.4196178343949045, fairness violation: 0.0050043057324840766, violated group size: 0.217\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "is_executing": false
+ },
+ "id": "0xQSRpo5hI5H"
+ },
+ "outputs": [],
+ "source": [
+ "%matplotlib inline\n",
+ "import warnings\n",
+ "warnings.filterwarnings(\"ignore\")\n",
+ "import sys\n",
+ "sys.path.append(\"../\")\n",
+ "from aif360.algorithms.inprocessing import GerryFairClassifier\n",
+ "from aif360.algorithms.inprocessing.gerryfair.clean import array_to_tuple\n",
+ "from aif360.algorithms.inprocessing.gerryfair.auditor import Auditor\n",
+ "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_adult\n",
+ "from sklearn import svm\n",
+ "from sklearn import tree\n",
+ "from sklearn.kernel_ridge import KernelRidge\n",
+ "from sklearn import linear_model\n",
+ "from aif360.metrics import BinaryLabelDatasetMetric\n",
+ "from IPython.display import Image\n",
+ "import pickle\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "# load data set\n",
+ "data_set = load_preproc_data_adult(sub_samp=1000, balance=True)\n",
+ "max_iterations = 500"
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "iteration: 158, error: 0.4194936708860761, fairness violation: 0.005013835443037975, violated group size: 0.217\n",
- "iteration: 159, error: 0.41937106918239, fairness violation: 0.005023245283018869, violated group size: 0.283\n",
- "iteration: 160, error: 0.41925000000000007, fairness violation: 0.0050325375, violated group size: 0.217\n",
- "iteration: 161, error: 0.4191304347826087, fairness violation: 0.005041714285714285, violated group size: 0.217\n",
- "iteration: 162, error: 0.41901234567901235, fairness violation: 0.005050777777777778, violated group size: 0.283\n",
- "iteration: 163, error: 0.41889570552147243, fairness violation: 0.005059730061349694, violated group size: 0.283\n",
- "iteration: 164, error: 0.4191402439024391, fairness violation: 0.005039463414634148, violated group size: 0.283\n",
- "iteration: 165, error: 0.41938181818181824, fairness violation: 0.005019442424242424, violated group size: 0.217\n",
- "iteration: 166, error: 0.4192650602409639, fairness violation: 0.005028421686746988, violated group size: 0.217\n",
- "iteration: 167, error: 0.41950299401197605, fairness violation: 0.005008706586826348, violated group size: 0.217\n",
- "iteration: 168, error: 0.41973809523809513, fairness violation: 0.004989226190476189, violated group size: 0.217\n",
- "iteration: 169, error: 0.41997041420118336, fairness violation: 0.0049699763313609474, violated group size: 0.283\n",
- "iteration: 170, error: 0.4202, fairness violation: 0.00495095294117647, violated group size: 0.283\n",
- "iteration: 171, error: 0.4204269005847953, fairness violation: 0.004932152046783625, violated group size: 0.217\n",
- "iteration: 172, error: 0.42065116279069764, fairness violation: 0.00491356976744186, violated group size: 0.217\n",
- "iteration: 173, error: 0.4208728323699421, fairness violation: 0.004895202312138728, violated group size: 0.217\n",
- "iteration: 174, error: 0.42109195402298855, fairness violation: 0.004877045977011494, violated group size: 0.217\n",
- "iteration: 175, error: 0.4213085714285715, fairness violation: 0.004859097142857142, violated group size: 0.217\n",
- "iteration: 176, error: 0.42152272727272727, fairness violation: 0.0048413522727272715, violated group size: 0.217\n",
- "iteration: 177, error: 0.42173446327683617, fairness violation: 0.00482380790960452, violated group size: 0.217\n",
- "iteration: 178, error: 0.42161235955056187, fairness violation: 0.004833280898876404, violated group size: 0.217\n",
- "iteration: 179, error: 0.42182122905027924, fairness violation: 0.004815977653631285, violated group size: 0.217\n",
- "iteration: 180, error: 0.4220277777777778, fairness violation: 0.004798866666666665, violated group size: 0.217\n",
- "iteration: 181, error: 0.42223204419889504, fairness violation: 0.004781944751381214, violated group size: 0.283\n",
- "iteration: 182, error: 0.4224340659340659, fairness violation: 0.004765208791208789, violated group size: 0.217\n",
- "iteration: 183, error: 0.4226338797814208, fairness violation: 0.004748655737704917, violated group size: 0.217\n",
- "iteration: 184, error: 0.4228315217391304, fairness violation: 0.004732282608695651, violated group size: 0.217\n",
- "iteration: 185, error: 0.4230270270270271, fairness violation: 0.004716086486486487, violated group size: 0.217\n",
- "iteration: 186, error: 0.4229032258064515, fairness violation: 0.0047257311827957, violated group size: 0.283\n",
- "iteration: 187, error: 0.4230962566844919, fairness violation: 0.0047097433155080205, violated group size: 0.217\n",
- "iteration: 188, error: 0.4229734042553191, fairness violation: 0.00471931914893617, violated group size: 0.283\n",
- "iteration: 189, error: 0.4231640211640213, fairness violation: 0.004703534391534391, violated group size: 0.217\n",
- "iteration: 190, error: 0.42304210526315794, fairness violation: 0.004713042105263158, violated group size: 0.217\n",
- "iteration: 191, error: 0.4232303664921467, fairness violation: 0.004697455497382198, violated group size: 0.217\n",
- "iteration: 192, error: 0.42310937499999995, fairness violation: 0.004706895833333333, violated group size: 0.217\n",
- "iteration: 193, error: 0.42329533678756476, fairness violation: 0.004691502590673575, violated group size: 0.283\n",
- "iteration: 194, error: 0.4231752577319588, fairness violation: 0.004700876288659792, violated group size: 0.217\n",
- "iteration: 195, error: 0.4230564102564103, fairness violation: 0.004710153846153845, violated group size: 0.217\n",
- "iteration: 196, error: 0.4229387755102041, fairness violation: 0.004719336734693878, violated group size: 0.283\n",
- "iteration: 197, error: 0.4228223350253807, fairness violation: 0.004728426395939086, violated group size: 0.283\n",
- "iteration: 198, error: 0.4227070707070707, fairness violation: 0.004737424242424242, violated group size: 0.217\n",
- "iteration: 199, error: 0.4228894472361809, fairness violation: 0.004722341708542713, violated group size: 0.217\n",
- "iteration: 200, error: 0.42277499999999996, fairness violation: 0.004731279999999999, violated group size: 0.217\n",
- "iteration: 201, error: 0.4226616915422886, fairness violation: 0.004740129353233829, violated group size: 0.217\n",
- "iteration: 202, error: 0.4225495049504951, fairness violation: 0.00474889108910891, violated group size: 0.217\n",
- "iteration: 203, error: 0.42243842364532025, fairness violation: 0.004757566502463053, violated group size: 0.283\n",
- "iteration: 204, error: 0.42232843137254905, fairness violation: 0.004766156862745097, violated group size: 0.283\n",
- "iteration: 205, error: 0.422219512195122, fairness violation: 0.004774663414634145, violated group size: 0.217\n",
- "iteration: 206, error: 0.422111650485437, fairness violation: 0.004783087378640775, violated group size: 0.217\n",
- "iteration: 207, error: 0.4220048309178744, fairness violation: 0.00479142995169082, violated group size: 0.217\n",
- "iteration: 208, error: 0.4218990384615385, fairness violation: 0.004799692307692306, violated group size: 0.217\n",
- "iteration: 209, error: 0.42179425837320567, fairness violation: 0.004807875598086124, violated group size: 0.217\n",
- "iteration: 210, error: 0.4216904761904762, fairness violation: 0.004815980952380952, violated group size: 0.217\n",
- "iteration: 211, error: 0.42158767772511846, fairness violation: 0.0048240094786729856, violated group size: 0.217\n",
- "iteration: 212, error: 0.42148584905660386, fairness violation: 0.004831962264150944, violated group size: 0.217\n",
- "iteration: 213, error: 0.4213849765258215, fairness violation: 0.004839840375586855, violated group size: 0.283\n",
- "iteration: 214, error: 0.421285046728972, fairness violation: 0.0048476448598130835, violated group size: 0.217\n",
- "iteration: 215, error: 0.42118604651162794, fairness violation: 0.004855376744186045, violated group size: 0.217\n",
- "iteration: 216, error: 0.4210879629629629, fairness violation: 0.004863037037037037, violated group size: 0.283\n",
- "iteration: 217, error: 0.42099078341013824, fairness violation: 0.004870626728110601, violated group size: 0.283\n",
- "iteration: 218, error: 0.42089449541284396, fairness violation: 0.004878146788990825, violated group size: 0.217\n",
- "iteration: 219, error: 0.42079908675799094, fairness violation: 0.004885598173515983, violated group size: 0.283\n",
- "iteration: 220, error: 0.4207045454545455, fairness violation: 0.004892981818181818, violated group size: 0.283\n",
- "iteration: 221, error: 0.4206108597285068, fairness violation: 0.004900298642533936, violated group size: 0.283\n",
- "iteration: 222, error: 0.4205180180180179, fairness violation: 0.004907549549549549, violated group size: 0.217\n",
- "iteration: 223, error: 0.4204260089686098, fairness violation: 0.004914735426008968, violated group size: 0.217\n",
- "iteration: 224, error: 0.4203348214285714, fairness violation: 0.004921857142857143, violated group size: 0.283\n",
- "iteration: 225, error: 0.4202444444444444, fairness violation: 0.004928915555555555, violated group size: 0.217\n",
- "iteration: 226, error: 0.42015486725663725, fairness violation: 0.004935911504424777, violated group size: 0.217\n",
- "iteration: 227, error: 0.4200660792951542, fairness violation: 0.004942845814977973, violated group size: 0.217\n",
- "iteration: 228, error: 0.4199780701754386, fairness violation: 0.004949719298245614, violated group size: 0.217\n",
- "iteration: 229, error: 0.4198908296943231, fairness violation: 0.004956532751091703, violated group size: 0.283\n",
- "iteration: 230, error: 0.419804347826087, fairness violation: 0.004963286956521739, violated group size: 0.283\n",
- "iteration: 231, error: 0.4197186147186147, fairness violation: 0.004969982683982686, violated group size: 0.283\n",
- "iteration: 232, error: 0.4196336206896552, fairness violation: 0.004976620689655175, violated group size: 0.283\n",
- "iteration: 233, error: 0.419549356223176, fairness violation: 0.004983201716738197, violated group size: 0.283\n",
- "iteration: 234, error: 0.419465811965812, fairness violation: 0.0049897264957264945, violated group size: 0.217\n"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "iO2ueydohI5J"
+ },
+ "source": [
+ "**instantiate, fit, and predict**\n",
+ "\n",
+ "\n",
+ "We first demonstrate how to instantiate a `GerryFairClassifier`, `train` it with respect to rich subgroup fairness, and `predict` the label of a new example. We remark that when we set the `print_flag = True` at each iteration of the algorithm we print the error, fairness violation, and violated group size of most recent model. The error is the classification error of the classifier. At each round the Learner tries to find a classifier that minimizes the classification error plus a weighted sum of the fairness disparities on all the groups that the Auditor has found up until that point. By contrast the Auditor tries to find the group at each round with the greatest rich subgroup disparity with respect to the Learner's model. We define `violated group size` as the size (as a fraction of the dataset size) of this group, and the `fairness violation` as the `violated group size` times the difference in the statistical rate (FP or FN rate) on the group vs. the whole population.\n",
+ "\n",
+ "In the example below we set `max_iterations=500` which is an order of magnitude less than the time to convergence observed in [the rich subgroup fairness empirical paper](https://arxiv.org/abs/1808.08166), but advise that this can be highly dataset dependent. Our target $\\gamma$-disparity is $\\gamma = .005$, our statistical rate is false positive rate or `FP`, and our cost-sensitive classification oracle is linear regression (more on that below).\n"
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "iteration: 235, error: 0.4193829787234044, fairness violation: 0.00499619574468085, violated group size: 0.217\n",
- "iteration: 236, error: 0.41930084745762713, fairness violation: 0.005002610169491525, violated group size: 0.283\n",
- "iteration: 237, error: 0.4192194092827004, fairness violation: 0.005008970464135021, violated group size: 0.283\n",
- "iteration: 238, error: 0.41913865546218493, fairness violation: 0.00501527731092437, violated group size: 0.283\n",
- "iteration: 239, error: 0.4190585774058578, fairness violation: 0.005021531380753138, violated group size: 0.283\n",
- "iteration: 240, error: 0.4189791666666667, fairness violation: 0.005027733333333333, violated group size: 0.217\n",
- "iteration: 241, error: 0.41890041493775937, fairness violation: 0.005033883817427385, violated group size: 0.217\n",
- "iteration: 242, error: 0.4188223140495868, fairness violation: 0.0050399834710743805, violated group size: 0.217\n",
- "iteration: 243, error: 0.4187448559670781, fairness violation: 0.005046032921810699, violated group size: 0.217\n",
- "iteration: 244, error: 0.41890983606557386, fairness violation: 0.005032467213114753, violated group size: 0.217\n",
- "iteration: 245, error: 0.419073469387755, fairness violation: 0.005019012244897959, violated group size: 0.217\n",
- "iteration: 246, error: 0.41923577235772364, fairness violation: 0.005005666666666667, violated group size: 0.283\n",
- "iteration: 247, error: 0.4191578947368422, fairness violation: 0.005011757085020243, violated group size: 0.217\n",
- "iteration: 248, error: 0.41908064516129034, fairness violation: 0.005017798387096774, violated group size: 0.283\n",
- "iteration: 249, error: 0.4192409638554217, fairness violation: 0.005004618473895581, violated group size: 0.217\n",
- "iteration: 250, error: 0.4194000000000001, fairness violation: 0.004991544, violated group size: 0.217\n",
- "iteration: 251, error: 0.4195577689243028, fairness violation: 0.004978573705179282, violated group size: 0.283\n",
- "iteration: 252, error: 0.4197142857142857, fairness violation: 0.004965706349206349, violated group size: 0.217\n",
- "iteration: 253, error: 0.4198695652173912, fairness violation: 0.00495294071146245, violated group size: 0.217\n",
- "iteration: 254, error: 0.42002362204724414, fairness violation: 0.00494027559055118, violated group size: 0.217\n",
- "iteration: 255, error: 0.4201764705882353, fairness violation: 0.004927709803921568, violated group size: 0.217\n",
- "iteration: 256, error: 0.420328125, fairness violation: 0.004915242187499999, violated group size: 0.217\n",
- "iteration: 257, error: 0.42047859922178993, fairness violation: 0.004902871595330739, violated group size: 0.217\n",
- "iteration: 258, error: 0.42062790697674424, fairness violation: 0.004890596899224807, violated group size: 0.217\n",
- "iteration: 259, error: 0.4207760617760617, fairness violation: 0.004878416988416988, violated group size: 0.283\n",
- "iteration: 260, error: 0.42069615384615383, fairness violation: 0.004884692307692307, violated group size: 0.217\n",
- "iteration: 261, error: 0.42061685823754785, fairness violation: 0.004890919540229886, violated group size: 0.283\n",
- "iteration: 262, error: 0.42076335877862603, fairness violation: 0.004878877862595419, violated group size: 0.217\n",
- "iteration: 263, error: 0.4209087452471483, fairness violation: 0.004866927756653992, violated group size: 0.217\n",
- "iteration: 264, error: 0.4208295454545454, fairness violation: 0.004873151515151516, violated group size: 0.283\n",
- "iteration: 265, error: 0.4209735849056603, fairness violation: 0.004861313207547171, violated group size: 0.283\n",
- "iteration: 266, error: 0.4211165413533834, fairness violation: 0.004849563909774436, violated group size: 0.217\n",
- "iteration: 267, error: 0.42125842696629223, fairness violation: 0.0048379026217228475, violated group size: 0.217\n",
- "iteration: 268, error: 0.42139925373134324, fairness violation: 0.004826328358208956, violated group size: 0.283\n",
- "iteration: 269, error: 0.42131970260223045, fairness violation: 0.0048325873605947955, violated group size: 0.217\n",
- "iteration: 270, error: 0.42145925925925937, fairness violation: 0.00482111851851852, violated group size: 0.283\n",
- "iteration: 271, error: 0.42159778597785963, fairness violation: 0.004809734317343174, violated group size: 0.217\n",
- "iteration: 272, error: 0.42173529411764704, fairness violation: 0.004798433823529413, violated group size: 0.283\n",
- "iteration: 273, error: 0.4216556776556777, fairness violation: 0.004804703296703296, violated group size: 0.217\n",
- "iteration: 274, error: 0.42157664233576647, fairness violation: 0.00481092700729927, violated group size: 0.217\n",
- "iteration: 275, error: 0.42171272727272724, fairness violation: 0.004799745454545455, violated group size: 0.217\n",
- "iteration: 276, error: 0.42184782608695653, fairness violation: 0.004788644927536233, violated group size: 0.217\n",
- "iteration: 277, error: 0.4219819494584837, fairness violation: 0.004777624548736462, violated group size: 0.217\n",
- "iteration: 278, error: 0.42190287769784157, fairness violation: 0.004783856115107913, violated group size: 0.217\n",
- "iteration: 279, error: 0.42182437275985674, fairness violation: 0.004790043010752689, violated group size: 0.217\n",
- "iteration: 280, error: 0.42174642857142863, fairness violation: 0.004796185714285715, violated group size: 0.283\n",
- "iteration: 281, error: 0.42166903914590753, fairness violation: 0.0048022846975088965, violated group size: 0.283\n",
- "iteration: 282, error: 0.4218014184397163, fairness violation: 0.00479141134751773, violated group size: 0.217\n",
- "iteration: 283, error: 0.42172438162544174, fairness violation: 0.0047974840989399295, violated group size: 0.217\n",
- "iteration: 284, error: 0.4216478873239437, fairness violation: 0.004803514084507042, violated group size: 0.217\n",
- "iteration: 285, error: 0.42157192982456126, fairness violation: 0.004809501754385964, violated group size: 0.217\n",
- "iteration: 286, error: 0.4217027972027972, fairness violation: 0.004798755244755245, violated group size: 0.217\n",
- "iteration: 287, error: 0.4218327526132404, fairness violation: 0.004788083623693379, violated group size: 0.283\n",
- "iteration: 288, error: 0.4219618055555556, fairness violation: 0.004777486111111113, violated group size: 0.283\n",
- "iteration: 289, error: 0.4218858131487888, fairness violation: 0.004783480968858131, violated group size: 0.217\n",
- "iteration: 290, error: 0.4218103448275861, fairness violation: 0.004789434482758621, violated group size: 0.217\n",
- "iteration: 291, error: 0.42193814432989696, fairness violation: 0.004778941580756014, violated group size: 0.283\n",
- "iteration: 292, error: 0.42186301369863016, fairness violation: 0.0047848698630136985, violated group size: 0.217\n",
- "iteration: 293, error: 0.4217883959044368, fairness violation: 0.004790757679180888, violated group size: 0.217\n",
- "iteration: 294, error: 0.42171428571428576, fairness violation: 0.004796605442176871, violated group size: 0.217\n",
- "iteration: 295, error: 0.4216406779661017, fairness violation: 0.004802413559322035, violated group size: 0.217\n",
- "iteration: 296, error: 0.4215675675675675, fairness violation: 0.004808182432432432, violated group size: 0.217\n",
- "iteration: 297, error: 0.4214949494949495, fairness violation: 0.00481391245791246, violated group size: 0.283\n",
- "iteration: 298, error: 0.4214228187919464, fairness violation: 0.0048196040268456385, violated group size: 0.217\n",
- "iteration: 299, error: 0.42135117056856186, fairness violation: 0.0048252575250836115, violated group size: 0.217\n",
- "iteration: 300, error: 0.42128, fairness violation: 0.004830873333333335, violated group size: 0.283\n",
- "iteration: 301, error: 0.42120930232558146, fairness violation: 0.004836451827242525, violated group size: 0.217\n",
- "iteration: 302, error: 0.42113907284768215, fairness violation: 0.004841993377483444, violated group size: 0.217\n",
- "iteration: 303, error: 0.42106930693069305, fairness violation: 0.004847498349834984, violated group size: 0.217\n",
- "iteration: 304, error: 0.4211940789473684, fairness violation: 0.004837263157894738, violated group size: 0.283\n",
- "iteration: 305, error: 0.4211245901639345, fairness violation: 0.004842747540983607, violated group size: 0.283\n",
- "iteration: 306, error: 0.4210555555555555, fairness violation: 0.004848196078431373, violated group size: 0.217\n",
- "iteration: 307, error: 0.42098697068403895, fairness violation: 0.004853609120521175, violated group size: 0.283\n",
- "iteration: 308, error: 0.4209188311688312, fairness violation: 0.004858987012987015, violated group size: 0.283\n",
- "iteration: 309, error: 0.42085113268608415, fairness violation: 0.00486433009708738, violated group size: 0.283\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "is_executing": true
+ },
+ "id": "sgydei4GhI5K",
+ "outputId": "5ec1d33f-dce5-4bee-8f91-3f2901f5a15a"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "iteration: 1, error: 0.263, fairness violation: 0.028780000000000007, violated group size: 0.217\n",
+ "iteration: 2, error: 0.3815, fairness violation: 0.014390000000000003, violated group size: 0.217\n",
+ "iteration: 3, error: 0.42099999999999993, fairness violation: 0.009593333333333339, violated group size: 0.283\n",
+ "iteration: 4, error: 0.44075, fairness violation: 0.007195000000000002, violated group size: 0.217\n",
+ "iteration: 5, error: 0.45260000000000006, fairness violation: 0.005756000000000001, violated group size: 0.217\n",
+ "iteration: 6, error: 0.4605000000000001, fairness violation: 0.004796666666666668, violated group size: 0.283\n",
+ "iteration: 7, error: 0.4661428571428572, fairness violation: 0.004111428571428572, violated group size: 0.217\n",
+ "iteration: 8, error: 0.470375, fairness violation: 0.0035975000000000017, violated group size: 0.217\n",
+ "iteration: 9, error: 0.4691111111111112, fairness violation: 0.0033906666666666677, violated group size: 0.283\n",
+ "iteration: 10, error: 0.4681, fairness violation: 0.003225200000000001, violated group size: 0.283\n",
+ "iteration: 11, error: 0.4672727272727271, fairness violation: 0.0030898181818181836, violated group size: 0.283\n",
+ "iteration: 12, error: 0.4665833333333333, fairness violation: 0.0029769999999999996, violated group size: 0.217\n",
+ "iteration: 13, error: 0.466, fairness violation: 0.0028815384615384627, violated group size: 0.283\n",
+ "iteration: 14, error: 0.4655000000000001, fairness violation: 0.0027997142857142865, violated group size: 0.217\n",
+ "iteration: 15, error: 0.46506666666666674, fairness violation: 0.002728800000000001, violated group size: 0.217\n",
+ "iteration: 16, error: 0.4646875, fairness violation: 0.0026667500000000007, violated group size: 0.217\n",
+ "iteration: 17, error: 0.4643529411764707, fairness violation: 0.002612000000000001, violated group size: 0.283\n",
+ "iteration: 18, error: 0.46405555555555567, fairness violation: 0.002563333333333334, violated group size: 0.217\n",
+ "iteration: 19, error: 0.4637894736842106, fairness violation: 0.0025197894736842096, violated group size: 0.217\n",
+ "iteration: 20, error: 0.46354999999999996, fairness violation: 0.0024806000000000008, violated group size: 0.283\n",
+ "iteration: 21, error: 0.4633333333333334, fairness violation: 0.0024451428571428584, violated group size: 0.217\n",
+ "iteration: 22, error: 0.4631363636363638, fairness violation: 0.0024129090909090914, violated group size: 0.283\n",
+ "iteration: 23, error: 0.46295652173913054, fairness violation: 0.002383478260869566, violated group size: 0.217\n",
+ "iteration: 24, error: 0.4627916666666667, fairness violation: 0.002356500000000001, violated group size: 0.283\n",
+ "iteration: 25, error: 0.4626400000000001, fairness violation: 0.0023316800000000018, violated group size: 0.283\n",
+ "iteration: 26, error: 0.4625000000000001, fairness violation: 0.0023087692307692314, violated group size: 0.217\n",
+ "iteration: 27, error: 0.4623703703703705, fairness violation: 0.0022875555555555557, violated group size: 0.217\n",
+ "iteration: 28, error: 0.46224999999999994, fairness violation: 0.0022678571428571426, violated group size: 0.217\n",
+ "iteration: 29, error: 0.46213793103448264, fairness violation: 0.0022495172413793106, violated group size: 0.217\n",
+ "iteration: 30, error: 0.46203333333333335, fairness violation: 0.0022324000000000003, violated group size: 0.217\n",
+ "iteration: 31, error: 0.46193548387096783, fairness violation: 0.0022163870967741935, violated group size: 0.217\n",
+ "iteration: 32, error: 0.46184375, fairness violation: 0.0022013749999999993, violated group size: 0.217\n",
+ "iteration: 33, error: 0.459969696969697, fairness violation: 0.0023319393939393944, violated group size: 0.283\n",
+ "iteration: 34, error: 0.4582058823529412, fairness violation: 0.002454823529411765, violated group size: 0.217\n",
+ "iteration: 35, error: 0.45654285714285714, fairness violation: 0.0025706857142857144, violated group size: 0.217\n",
+ "iteration: 36, error: 0.4549722222222221, fairness violation: 0.0026801111111111114, violated group size: 0.283\n",
+ "iteration: 37, error: 0.4534864864864866, fairness violation: 0.0027836216216216214, violated group size: 0.283\n",
+ "iteration: 38, error: 0.45207894736842097, fairness violation: 0.0028816842105263162, violated group size: 0.283\n",
+ "iteration: 39, error: 0.4507435897435898, fairness violation: 0.0029747179487179492, violated group size: 0.217\n",
+ "iteration: 40, error: 0.44947499999999996, fairness violation: 0.0030631000000000005, violated group size: 0.217\n",
+ "iteration: 41, error: 0.44826829268292684, fairness violation: 0.0031471707317073175, violated group size: 0.283\n",
+ "iteration: 42, error: 0.4471190476190476, fairness violation: 0.0032272380952380955, violated group size: 0.217\n",
+ "iteration: 43, error: 0.44602325581395347, fairness violation: 0.0033035813953488386, violated group size: 0.283\n",
+ "iteration: 44, error: 0.44497727272727267, fairness violation: 0.0033764545454545453, violated group size: 0.283\n",
+ "iteration: 45, error: 0.4439777777777778, fairness violation: 0.003446088888888888, violated group size: 0.217\n",
+ "iteration: 46, error: 0.44302173913043474, fairness violation: 0.0035126956521739122, violated group size: 0.217\n",
+ "iteration: 47, error: 0.44210638297872346, fairness violation: 0.0035764680851063826, violated group size: 0.217\n",
+ "iteration: 48, error: 0.4412291666666666, fairness violation: 0.003637583333333332, violated group size: 0.217\n",
+ "iteration: 49, error: 0.4403877551020407, fairness violation: 0.0036962040816326523, violated group size: 0.217\n",
+ "iteration: 50, error: 0.4395600000000001, fairness violation: 0.0037524800000000003, violated group size: 0.217\n",
+ "iteration: 51, error: 0.43876470588235295, fairness violation: 0.0038065490196078425, violated group size: 0.217\n",
+ "iteration: 52, error: 0.438, fairness violation: 0.003858538461538461, violated group size: 0.283\n",
+ "iteration: 53, error: 0.4372641509433963, fairness violation: 0.003908566037735848, violated group size: 0.217\n",
+ "iteration: 54, error: 0.4365555555555556, fairness violation: 0.003956740740740741, violated group size: 0.283\n",
+ "iteration: 55, error: 0.4358181818181819, fairness violation: 0.004003163636363636, violated group size: 0.217\n",
+ "iteration: 56, error: 0.4351071428571429, fairness violation: 0.004047928571428571, violated group size: 0.217\n",
+ "iteration: 57, error: 0.4344736842105262, fairness violation: 0.004091122807017543, violated group size: 0.217\n",
+ "iteration: 58, error: 0.43381034482758624, fairness violation: 0.004132827586206895, violated group size: 0.217\n",
+ "iteration: 59, error: 0.4331694915254237, fairness violation: 0.0041731186440677965, violated group size: 0.283\n",
+ "iteration: 60, error: 0.43254999999999993, fairness violation: 0.004212066666666666, violated group size: 0.217\n",
+ "iteration: 61, error: 0.4319508196721312, fairness violation: 0.004249737704918031, violated group size: 0.217\n",
+ "iteration: 62, error: 0.4313709677419356, fairness violation: 0.004286193548387096, violated group size: 0.217\n",
+ "iteration: 63, error: 0.43080952380952386, fairness violation: 0.004321492063492062, violated group size: 0.283\n",
+ "iteration: 64, error: 0.430265625, fairness violation: 0.004355687499999999, violated group size: 0.283\n",
+ "iteration: 65, error: 0.4297384615384615, fairness violation: 0.004388830769230769, violated group size: 0.283\n",
+ "iteration: 66, error: 0.42922727272727274, fairness violation: 0.004420969696969697, violated group size: 0.217\n",
+ "iteration: 67, error: 0.42873134328358203, fairness violation: 0.004452149253731343, violated group size: 0.217\n",
+ "iteration: 68, error: 0.42824999999999996, fairness violation: 0.0044824117647058815, violated group size: 0.283\n",
+ "iteration: 69, error: 0.42778260869565227, fairness violation: 0.004511797101449274, violated group size: 0.217\n",
+ "iteration: 70, error: 0.42732857142857145, fairness violation: 0.004540342857142856, violated group size: 0.283\n",
+ "iteration: 71, error: 0.42688732394366197, fairness violation: 0.004568084507042252, violated group size: 0.217\n",
+ "iteration: 72, error: 0.4264583333333332, fairness violation: 0.004595055555555555, violated group size: 0.283\n",
+ "iteration: 73, error: 0.42604109589041106, fairness violation: 0.004621287671232876, violated group size: 0.217\n",
+ "iteration: 74, error: 0.4256351351351351, fairness violation: 0.0046468108108108095, violated group size: 0.283\n",
+ "iteration: 75, error: 0.42524, fairness violation: 0.004671653333333331, violated group size: 0.217\n",
+ "iteration: 76, error: 0.4248552631578947, fairness violation: 0.004695842105263155, violated group size: 0.217\n",
+ "iteration: 77, error: 0.42448051948051946, fairness violation: 0.004719402597402596, violated group size: 0.217\n",
+ "iteration: 78, error: 0.4239871794871795, fairness violation: 0.00475905128205128, violated group size: 0.217\n",
+ "iteration: 79, error: 0.42363291139240505, fairness violation: 0.004781215189873418, violated group size: 0.283\n",
+ "iteration: 80, error: 0.42328750000000015, fairness violation: 0.004802824999999999, violated group size: 0.283\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "iteration: 81, error: 0.4229506172839506, fairness violation: 0.004823901234567901, violated group size: 0.283\n",
+ "iteration: 82, error: 0.4226219512195123, fairness violation: 0.004844463414634145, violated group size: 0.217\n",
+ "iteration: 83, error: 0.4221807228915662, fairness violation: 0.004880216867469879, violated group size: 0.217\n",
+ "iteration: 84, error: 0.42175, fairness violation: 0.004915119047619047, violated group size: 0.217\n",
+ "iteration: 85, error: 0.4214470588235294, fairness violation: 0.004933882352941174, violated group size: 0.217\n",
+ "iteration: 86, error: 0.4210348837209302, fairness violation: 0.004967348837209301, violated group size: 0.217\n",
+ "iteration: 87, error: 0.420632183908046, fairness violation: 0.005000045977011494, violated group size: 0.283\n",
+ "iteration: 88, error: 0.42035227272727277, fairness violation: 0.0050172045454545434, violated group size: 0.217\n",
+ "iteration: 89, error: 0.4200786516853933, fairness violation: 0.005033977528089887, violated group size: 0.217\n",
+ "iteration: 90, error: 0.4198111111111112, fairness violation: 0.005050377777777776, violated group size: 0.283\n",
+ "iteration: 91, error: 0.4195824175824176, fairness violation: 0.0050664175824175805, violated group size: 0.217\n",
+ "iteration: 92, error: 0.4193695652173913, fairness violation: 0.005082108695652173, violated group size: 0.217\n",
+ "iteration: 93, error: 0.41916129032258065, fairness violation: 0.005097462365591397, violated group size: 0.217\n",
+ "iteration: 94, error: 0.41895744680851066, fairness violation: 0.005112489361702126, violated group size: 0.217\n",
+ "iteration: 95, error: 0.41875789473684216, fairness violation: 0.005127199999999998, violated group size: 0.217\n",
+ "iteration: 96, error: 0.41856250000000006, fairness violation: 0.005141604166666665, violated group size: 0.283\n",
+ "iteration: 97, error: 0.418979381443299, fairness violation: 0.005106494845360823, violated group size: 0.217\n",
+ "iteration: 98, error: 0.41938775510204085, fairness violation: 0.005072102040816325, violated group size: 0.217\n",
+ "iteration: 99, error: 0.4197878787878788, fairness violation: 0.0050384040404040376, violated group size: 0.217\n",
+ "iteration: 100, error: 0.42018000000000005, fairness violation: 0.0050053799999999985, violated group size: 0.217\n",
+ "iteration: 101, error: 0.42056435643564366, fairness violation: 0.004973009900990098, violated group size: 0.217\n",
+ "iteration: 102, error: 0.42094117647058826, fairness violation: 0.00494127450980392, violated group size: 0.217\n",
+ "iteration: 103, error: 0.4213106796116506, fairness violation: 0.004910155339805824, violated group size: 0.217\n",
+ "iteration: 104, error: 0.4216730769230769, fairness violation: 0.004879634615384614, violated group size: 0.217\n",
+ "iteration: 105, error: 0.4220285714285715, fairness violation: 0.004849695238095237, violated group size: 0.217\n",
+ "iteration: 106, error: 0.4223773584905662, fairness violation: 0.004820320754716981, violated group size: 0.283\n",
+ "iteration: 107, error: 0.42271962616822434, fairness violation: 0.004791495327102803, violated group size: 0.217\n",
+ "iteration: 108, error: 0.4230555555555556, fairness violation: 0.0047632037037037035, violated group size: 0.217\n",
+ "iteration: 109, error: 0.4233853211009175, fairness violation: 0.00473543119266055, violated group size: 0.217\n",
+ "iteration: 110, error: 0.4237090909090908, fairness violation: 0.004708163636363636, violated group size: 0.217\n",
+ "iteration: 111, error: 0.424027027027027, fairness violation: 0.004681387387387387, violated group size: 0.283\n",
+ "iteration: 112, error: 0.42433928571428586, fairness violation: 0.004655089285714286, violated group size: 0.283\n",
+ "iteration: 113, error: 0.4241238938053097, fairness violation: 0.004671504424778761, violated group size: 0.217\n",
+ "iteration: 114, error: 0.42442982456140343, fairness violation: 0.004645754385964912, violated group size: 0.283\n",
+ "iteration: 115, error: 0.42473043478260875, fairness violation: 0.0046204521739130425, violated group size: 0.283\n",
+ "iteration: 116, error: 0.42502586206896553, fairness violation: 0.0045955862068965524, violated group size: 0.283\n",
+ "iteration: 117, error: 0.42481196581196584, fairness violation: 0.004611948717948717, violated group size: 0.217\n",
+ "iteration: 118, error: 0.4251016949152542, fairness violation: 0.004587576271186439, violated group size: 0.217\n",
+ "iteration: 119, error: 0.42489075630252104, fairness violation: 0.004603731092436974, violated group size: 0.217\n",
+ "iteration: 120, error: 0.4251750000000001, fairness violation: 0.0045798333333333325, violated group size: 0.217\n",
+ "iteration: 121, error: 0.4249669421487604, fairness violation: 0.004595785123966942, violated group size: 0.283\n",
+ "iteration: 122, error: 0.4247622950819671, fairness violation: 0.0046114754098360656, violated group size: 0.217\n",
+ "iteration: 123, error: 0.42456097560975614, fairness violation: 0.00462691056910569, violated group size: 0.217\n",
+ "iteration: 124, error: 0.42436290322580644, fairness violation: 0.004642096774193548, violated group size: 0.217\n",
+ "iteration: 125, error: 0.4241680000000001, fairness violation: 0.00465704, violated group size: 0.217\n",
+ "iteration: 126, error: 0.4239761904761905, fairness violation: 0.004671746031746031, violated group size: 0.217\n",
+ "iteration: 127, error: 0.42425196850393704, fairness violation: 0.004648629921259842, violated group size: 0.217\n",
+ "iteration: 128, error: 0.4240625, fairness violation: 0.004663171874999999, violated group size: 0.217\n",
+ "iteration: 129, error: 0.4238759689922481, fairness violation: 0.004677488372093024, violated group size: 0.283\n",
+ "iteration: 130, error: 0.42369230769230776, fairness violation: 0.004691584615384614, violated group size: 0.217\n",
+ "iteration: 131, error: 0.42351145038167937, fairness violation: 0.004705465648854962, violated group size: 0.217\n",
+ "iteration: 132, error: 0.4233333333333333, fairness violation: 0.004719136363636364, violated group size: 0.283\n",
+ "iteration: 133, error: 0.423157894736842, fairness violation: 0.0047326015037594, violated group size: 0.217\n",
+ "iteration: 134, error: 0.4229850746268656, fairness violation: 0.004745865671641791, violated group size: 0.217\n",
+ "iteration: 135, error: 0.42281481481481475, fairness violation: 0.004758933333333335, violated group size: 0.283\n",
+ "iteration: 136, error: 0.4226470588235294, fairness violation: 0.004771808823529411, violated group size: 0.217\n",
+ "iteration: 137, error: 0.42248175182481745, fairness violation: 0.004784496350364964, violated group size: 0.283\n",
+ "iteration: 138, error: 0.42231884057971014, fairness violation: 0.004797000000000002, violated group size: 0.283\n",
+ "iteration: 139, error: 0.42215827338129497, fairness violation: 0.004809323741007196, violated group size: 0.283\n",
+ "iteration: 140, error: 0.42200000000000004, fairness violation: 0.004821471428571429, violated group size: 0.217\n",
+ "iteration: 141, error: 0.4218439716312057, fairness violation: 0.0048334468085106394, violated group size: 0.217\n",
+ "iteration: 142, error: 0.42169014084507045, fairness violation: 0.004845253521126761, violated group size: 0.283\n",
+ "iteration: 143, error: 0.4215384615384616, fairness violation: 0.004856895104895106, violated group size: 0.283\n",
+ "iteration: 144, error: 0.4213888888888888, fairness violation: 0.004868375, violated group size: 0.217\n",
+ "iteration: 145, error: 0.42124137931034483, fairness violation: 0.004879696551724138, violated group size: 0.217\n",
+ "iteration: 146, error: 0.4210958904109589, fairness violation: 0.00489086301369863, violated group size: 0.217\n",
+ "iteration: 147, error: 0.4209523809523809, fairness violation: 0.004901877551020409, violated group size: 0.217\n",
+ "iteration: 148, error: 0.42081081081081084, fairness violation: 0.004912743243243244, violated group size: 0.217\n",
+ "iteration: 149, error: 0.42067114093959734, fairness violation: 0.004923463087248323, violated group size: 0.283\n",
+ "iteration: 150, error: 0.4205333333333334, fairness violation: 0.004934040000000001, violated group size: 0.217\n",
+ "iteration: 151, error: 0.4203973509933776, fairness violation: 0.004944476821192053, violated group size: 0.217\n",
+ "iteration: 152, error: 0.4202631578947368, fairness violation: 0.0049547763157894754, violated group size: 0.283\n",
+ "iteration: 153, error: 0.4201307189542483, fairness violation: 0.00496494117647059, violated group size: 0.283\n",
+ "iteration: 154, error: 0.42, fairness violation: 0.004974974025974027, violated group size: 0.283\n",
+ "iteration: 155, error: 0.4198709677419355, fairness violation: 0.0049848774193548395, violated group size: 0.217\n",
+ "iteration: 156, error: 0.4197435897435898, fairness violation: 0.004994653846153847, violated group size: 0.217\n",
+ "iteration: 157, error: 0.4196178343949045, fairness violation: 0.0050043057324840766, violated group size: 0.217\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "iteration: 158, error: 0.4194936708860761, fairness violation: 0.005013835443037975, violated group size: 0.217\n",
+ "iteration: 159, error: 0.41937106918239, fairness violation: 0.005023245283018869, violated group size: 0.283\n",
+ "iteration: 160, error: 0.41925000000000007, fairness violation: 0.0050325375, violated group size: 0.217\n",
+ "iteration: 161, error: 0.4191304347826087, fairness violation: 0.005041714285714285, violated group size: 0.217\n",
+ "iteration: 162, error: 0.41901234567901235, fairness violation: 0.005050777777777778, violated group size: 0.283\n",
+ "iteration: 163, error: 0.41889570552147243, fairness violation: 0.005059730061349694, violated group size: 0.283\n",
+ "iteration: 164, error: 0.4191402439024391, fairness violation: 0.005039463414634148, violated group size: 0.283\n",
+ "iteration: 165, error: 0.41938181818181824, fairness violation: 0.005019442424242424, violated group size: 0.217\n",
+ "iteration: 166, error: 0.4192650602409639, fairness violation: 0.005028421686746988, violated group size: 0.217\n",
+ "iteration: 167, error: 0.41950299401197605, fairness violation: 0.005008706586826348, violated group size: 0.217\n",
+ "iteration: 168, error: 0.41973809523809513, fairness violation: 0.004989226190476189, violated group size: 0.217\n",
+ "iteration: 169, error: 0.41997041420118336, fairness violation: 0.0049699763313609474, violated group size: 0.283\n",
+ "iteration: 170, error: 0.4202, fairness violation: 0.00495095294117647, violated group size: 0.283\n",
+ "iteration: 171, error: 0.4204269005847953, fairness violation: 0.004932152046783625, violated group size: 0.217\n",
+ "iteration: 172, error: 0.42065116279069764, fairness violation: 0.00491356976744186, violated group size: 0.217\n",
+ "iteration: 173, error: 0.4208728323699421, fairness violation: 0.004895202312138728, violated group size: 0.217\n",
+ "iteration: 174, error: 0.42109195402298855, fairness violation: 0.004877045977011494, violated group size: 0.217\n",
+ "iteration: 175, error: 0.4213085714285715, fairness violation: 0.004859097142857142, violated group size: 0.217\n",
+ "iteration: 176, error: 0.42152272727272727, fairness violation: 0.0048413522727272715, violated group size: 0.217\n",
+ "iteration: 177, error: 0.42173446327683617, fairness violation: 0.00482380790960452, violated group size: 0.217\n",
+ "iteration: 178, error: 0.42161235955056187, fairness violation: 0.004833280898876404, violated group size: 0.217\n",
+ "iteration: 179, error: 0.42182122905027924, fairness violation: 0.004815977653631285, violated group size: 0.217\n",
+ "iteration: 180, error: 0.4220277777777778, fairness violation: 0.004798866666666665, violated group size: 0.217\n",
+ "iteration: 181, error: 0.42223204419889504, fairness violation: 0.004781944751381214, violated group size: 0.283\n",
+ "iteration: 182, error: 0.4224340659340659, fairness violation: 0.004765208791208789, violated group size: 0.217\n",
+ "iteration: 183, error: 0.4226338797814208, fairness violation: 0.004748655737704917, violated group size: 0.217\n",
+ "iteration: 184, error: 0.4228315217391304, fairness violation: 0.004732282608695651, violated group size: 0.217\n",
+ "iteration: 185, error: 0.4230270270270271, fairness violation: 0.004716086486486487, violated group size: 0.217\n",
+ "iteration: 186, error: 0.4229032258064515, fairness violation: 0.0047257311827957, violated group size: 0.283\n",
+ "iteration: 187, error: 0.4230962566844919, fairness violation: 0.0047097433155080205, violated group size: 0.217\n",
+ "iteration: 188, error: 0.4229734042553191, fairness violation: 0.00471931914893617, violated group size: 0.283\n",
+ "iteration: 189, error: 0.4231640211640213, fairness violation: 0.004703534391534391, violated group size: 0.217\n",
+ "iteration: 190, error: 0.42304210526315794, fairness violation: 0.004713042105263158, violated group size: 0.217\n",
+ "iteration: 191, error: 0.4232303664921467, fairness violation: 0.004697455497382198, violated group size: 0.217\n",
+ "iteration: 192, error: 0.42310937499999995, fairness violation: 0.004706895833333333, violated group size: 0.217\n",
+ "iteration: 193, error: 0.42329533678756476, fairness violation: 0.004691502590673575, violated group size: 0.283\n",
+ "iteration: 194, error: 0.4231752577319588, fairness violation: 0.004700876288659792, violated group size: 0.217\n",
+ "iteration: 195, error: 0.4230564102564103, fairness violation: 0.004710153846153845, violated group size: 0.217\n",
+ "iteration: 196, error: 0.4229387755102041, fairness violation: 0.004719336734693878, violated group size: 0.283\n",
+ "iteration: 197, error: 0.4228223350253807, fairness violation: 0.004728426395939086, violated group size: 0.283\n",
+ "iteration: 198, error: 0.4227070707070707, fairness violation: 0.004737424242424242, violated group size: 0.217\n",
+ "iteration: 199, error: 0.4228894472361809, fairness violation: 0.004722341708542713, violated group size: 0.217\n",
+ "iteration: 200, error: 0.42277499999999996, fairness violation: 0.004731279999999999, violated group size: 0.217\n",
+ "iteration: 201, error: 0.4226616915422886, fairness violation: 0.004740129353233829, violated group size: 0.217\n",
+ "iteration: 202, error: 0.4225495049504951, fairness violation: 0.00474889108910891, violated group size: 0.217\n",
+ "iteration: 203, error: 0.42243842364532025, fairness violation: 0.004757566502463053, violated group size: 0.283\n",
+ "iteration: 204, error: 0.42232843137254905, fairness violation: 0.004766156862745097, violated group size: 0.283\n",
+ "iteration: 205, error: 0.422219512195122, fairness violation: 0.004774663414634145, violated group size: 0.217\n",
+ "iteration: 206, error: 0.422111650485437, fairness violation: 0.004783087378640775, violated group size: 0.217\n",
+ "iteration: 207, error: 0.4220048309178744, fairness violation: 0.00479142995169082, violated group size: 0.217\n",
+ "iteration: 208, error: 0.4218990384615385, fairness violation: 0.004799692307692306, violated group size: 0.217\n",
+ "iteration: 209, error: 0.42179425837320567, fairness violation: 0.004807875598086124, violated group size: 0.217\n",
+ "iteration: 210, error: 0.4216904761904762, fairness violation: 0.004815980952380952, violated group size: 0.217\n",
+ "iteration: 211, error: 0.42158767772511846, fairness violation: 0.0048240094786729856, violated group size: 0.217\n",
+ "iteration: 212, error: 0.42148584905660386, fairness violation: 0.004831962264150944, violated group size: 0.217\n",
+ "iteration: 213, error: 0.4213849765258215, fairness violation: 0.004839840375586855, violated group size: 0.283\n",
+ "iteration: 214, error: 0.421285046728972, fairness violation: 0.0048476448598130835, violated group size: 0.217\n",
+ "iteration: 215, error: 0.42118604651162794, fairness violation: 0.004855376744186045, violated group size: 0.217\n",
+ "iteration: 216, error: 0.4210879629629629, fairness violation: 0.004863037037037037, violated group size: 0.283\n",
+ "iteration: 217, error: 0.42099078341013824, fairness violation: 0.004870626728110601, violated group size: 0.283\n",
+ "iteration: 218, error: 0.42089449541284396, fairness violation: 0.004878146788990825, violated group size: 0.217\n",
+ "iteration: 219, error: 0.42079908675799094, fairness violation: 0.004885598173515983, violated group size: 0.283\n",
+ "iteration: 220, error: 0.4207045454545455, fairness violation: 0.004892981818181818, violated group size: 0.283\n",
+ "iteration: 221, error: 0.4206108597285068, fairness violation: 0.004900298642533936, violated group size: 0.283\n",
+ "iteration: 222, error: 0.4205180180180179, fairness violation: 0.004907549549549549, violated group size: 0.217\n",
+ "iteration: 223, error: 0.4204260089686098, fairness violation: 0.004914735426008968, violated group size: 0.217\n",
+ "iteration: 224, error: 0.4203348214285714, fairness violation: 0.004921857142857143, violated group size: 0.283\n",
+ "iteration: 225, error: 0.4202444444444444, fairness violation: 0.004928915555555555, violated group size: 0.217\n",
+ "iteration: 226, error: 0.42015486725663725, fairness violation: 0.004935911504424777, violated group size: 0.217\n",
+ "iteration: 227, error: 0.4200660792951542, fairness violation: 0.004942845814977973, violated group size: 0.217\n",
+ "iteration: 228, error: 0.4199780701754386, fairness violation: 0.004949719298245614, violated group size: 0.217\n",
+ "iteration: 229, error: 0.4198908296943231, fairness violation: 0.004956532751091703, violated group size: 0.283\n",
+ "iteration: 230, error: 0.419804347826087, fairness violation: 0.004963286956521739, violated group size: 0.283\n",
+ "iteration: 231, error: 0.4197186147186147, fairness violation: 0.004969982683982686, violated group size: 0.283\n",
+ "iteration: 232, error: 0.4196336206896552, fairness violation: 0.004976620689655175, violated group size: 0.283\n",
+ "iteration: 233, error: 0.419549356223176, fairness violation: 0.004983201716738197, violated group size: 0.283\n",
+ "iteration: 234, error: 0.419465811965812, fairness violation: 0.0049897264957264945, violated group size: 0.217\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "iteration: 235, error: 0.4193829787234044, fairness violation: 0.00499619574468085, violated group size: 0.217\n",
+ "iteration: 236, error: 0.41930084745762713, fairness violation: 0.005002610169491525, violated group size: 0.283\n",
+ "iteration: 237, error: 0.4192194092827004, fairness violation: 0.005008970464135021, violated group size: 0.283\n",
+ "iteration: 238, error: 0.41913865546218493, fairness violation: 0.00501527731092437, violated group size: 0.283\n",
+ "iteration: 239, error: 0.4190585774058578, fairness violation: 0.005021531380753138, violated group size: 0.283\n",
+ "iteration: 240, error: 0.4189791666666667, fairness violation: 0.005027733333333333, violated group size: 0.217\n",
+ "iteration: 241, error: 0.41890041493775937, fairness violation: 0.005033883817427385, violated group size: 0.217\n",
+ "iteration: 242, error: 0.4188223140495868, fairness violation: 0.0050399834710743805, violated group size: 0.217\n",
+ "iteration: 243, error: 0.4187448559670781, fairness violation: 0.005046032921810699, violated group size: 0.217\n",
+ "iteration: 244, error: 0.41890983606557386, fairness violation: 0.005032467213114753, violated group size: 0.217\n",
+ "iteration: 245, error: 0.419073469387755, fairness violation: 0.005019012244897959, violated group size: 0.217\n",
+ "iteration: 246, error: 0.41923577235772364, fairness violation: 0.005005666666666667, violated group size: 0.283\n",
+ "iteration: 247, error: 0.4191578947368422, fairness violation: 0.005011757085020243, violated group size: 0.217\n",
+ "iteration: 248, error: 0.41908064516129034, fairness violation: 0.005017798387096774, violated group size: 0.283\n",
+ "iteration: 249, error: 0.4192409638554217, fairness violation: 0.005004618473895581, violated group size: 0.217\n",
+ "iteration: 250, error: 0.4194000000000001, fairness violation: 0.004991544, violated group size: 0.217\n",
+ "iteration: 251, error: 0.4195577689243028, fairness violation: 0.004978573705179282, violated group size: 0.283\n",
+ "iteration: 252, error: 0.4197142857142857, fairness violation: 0.004965706349206349, violated group size: 0.217\n",
+ "iteration: 253, error: 0.4198695652173912, fairness violation: 0.00495294071146245, violated group size: 0.217\n",
+ "iteration: 254, error: 0.42002362204724414, fairness violation: 0.00494027559055118, violated group size: 0.217\n",
+ "iteration: 255, error: 0.4201764705882353, fairness violation: 0.004927709803921568, violated group size: 0.217\n",
+ "iteration: 256, error: 0.420328125, fairness violation: 0.004915242187499999, violated group size: 0.217\n",
+ "iteration: 257, error: 0.42047859922178993, fairness violation: 0.004902871595330739, violated group size: 0.217\n",
+ "iteration: 258, error: 0.42062790697674424, fairness violation: 0.004890596899224807, violated group size: 0.217\n",
+ "iteration: 259, error: 0.4207760617760617, fairness violation: 0.004878416988416988, violated group size: 0.283\n",
+ "iteration: 260, error: 0.42069615384615383, fairness violation: 0.004884692307692307, violated group size: 0.217\n",
+ "iteration: 261, error: 0.42061685823754785, fairness violation: 0.004890919540229886, violated group size: 0.283\n",
+ "iteration: 262, error: 0.42076335877862603, fairness violation: 0.004878877862595419, violated group size: 0.217\n",
+ "iteration: 263, error: 0.4209087452471483, fairness violation: 0.004866927756653992, violated group size: 0.217\n",
+ "iteration: 264, error: 0.4208295454545454, fairness violation: 0.004873151515151516, violated group size: 0.283\n",
+ "iteration: 265, error: 0.4209735849056603, fairness violation: 0.004861313207547171, violated group size: 0.283\n",
+ "iteration: 266, error: 0.4211165413533834, fairness violation: 0.004849563909774436, violated group size: 0.217\n",
+ "iteration: 267, error: 0.42125842696629223, fairness violation: 0.0048379026217228475, violated group size: 0.217\n",
+ "iteration: 268, error: 0.42139925373134324, fairness violation: 0.004826328358208956, violated group size: 0.283\n",
+ "iteration: 269, error: 0.42131970260223045, fairness violation: 0.0048325873605947955, violated group size: 0.217\n",
+ "iteration: 270, error: 0.42145925925925937, fairness violation: 0.00482111851851852, violated group size: 0.283\n",
+ "iteration: 271, error: 0.42159778597785963, fairness violation: 0.004809734317343174, violated group size: 0.217\n",
+ "iteration: 272, error: 0.42173529411764704, fairness violation: 0.004798433823529413, violated group size: 0.283\n",
+ "iteration: 273, error: 0.4216556776556777, fairness violation: 0.004804703296703296, violated group size: 0.217\n",
+ "iteration: 274, error: 0.42157664233576647, fairness violation: 0.00481092700729927, violated group size: 0.217\n",
+ "iteration: 275, error: 0.42171272727272724, fairness violation: 0.004799745454545455, violated group size: 0.217\n",
+ "iteration: 276, error: 0.42184782608695653, fairness violation: 0.004788644927536233, violated group size: 0.217\n",
+ "iteration: 277, error: 0.4219819494584837, fairness violation: 0.004777624548736462, violated group size: 0.217\n",
+ "iteration: 278, error: 0.42190287769784157, fairness violation: 0.004783856115107913, violated group size: 0.217\n",
+ "iteration: 279, error: 0.42182437275985674, fairness violation: 0.004790043010752689, violated group size: 0.217\n",
+ "iteration: 280, error: 0.42174642857142863, fairness violation: 0.004796185714285715, violated group size: 0.283\n",
+ "iteration: 281, error: 0.42166903914590753, fairness violation: 0.0048022846975088965, violated group size: 0.283\n",
+ "iteration: 282, error: 0.4218014184397163, fairness violation: 0.00479141134751773, violated group size: 0.217\n",
+ "iteration: 283, error: 0.42172438162544174, fairness violation: 0.0047974840989399295, violated group size: 0.217\n",
+ "iteration: 284, error: 0.4216478873239437, fairness violation: 0.004803514084507042, violated group size: 0.217\n",
+ "iteration: 285, error: 0.42157192982456126, fairness violation: 0.004809501754385964, violated group size: 0.217\n",
+ "iteration: 286, error: 0.4217027972027972, fairness violation: 0.004798755244755245, violated group size: 0.217\n",
+ "iteration: 287, error: 0.4218327526132404, fairness violation: 0.004788083623693379, violated group size: 0.283\n",
+ "iteration: 288, error: 0.4219618055555556, fairness violation: 0.004777486111111113, violated group size: 0.283\n",
+ "iteration: 289, error: 0.4218858131487888, fairness violation: 0.004783480968858131, violated group size: 0.217\n",
+ "iteration: 290, error: 0.4218103448275861, fairness violation: 0.004789434482758621, violated group size: 0.217\n",
+ "iteration: 291, error: 0.42193814432989696, fairness violation: 0.004778941580756014, violated group size: 0.283\n",
+ "iteration: 292, error: 0.42186301369863016, fairness violation: 0.0047848698630136985, violated group size: 0.217\n",
+ "iteration: 293, error: 0.4217883959044368, fairness violation: 0.004790757679180888, violated group size: 0.217\n",
+ "iteration: 294, error: 0.42171428571428576, fairness violation: 0.004796605442176871, violated group size: 0.217\n",
+ "iteration: 295, error: 0.4216406779661017, fairness violation: 0.004802413559322035, violated group size: 0.217\n",
+ "iteration: 296, error: 0.4215675675675675, fairness violation: 0.004808182432432432, violated group size: 0.217\n",
+ "iteration: 297, error: 0.4214949494949495, fairness violation: 0.00481391245791246, violated group size: 0.283\n",
+ "iteration: 298, error: 0.4214228187919464, fairness violation: 0.0048196040268456385, violated group size: 0.217\n",
+ "iteration: 299, error: 0.42135117056856186, fairness violation: 0.0048252575250836115, violated group size: 0.217\n",
+ "iteration: 300, error: 0.42128, fairness violation: 0.004830873333333335, violated group size: 0.283\n",
+ "iteration: 301, error: 0.42120930232558146, fairness violation: 0.004836451827242525, violated group size: 0.217\n",
+ "iteration: 302, error: 0.42113907284768215, fairness violation: 0.004841993377483444, violated group size: 0.217\n",
+ "iteration: 303, error: 0.42106930693069305, fairness violation: 0.004847498349834984, violated group size: 0.217\n",
+ "iteration: 304, error: 0.4211940789473684, fairness violation: 0.004837263157894738, violated group size: 0.283\n",
+ "iteration: 305, error: 0.4211245901639345, fairness violation: 0.004842747540983607, violated group size: 0.283\n",
+ "iteration: 306, error: 0.4210555555555555, fairness violation: 0.004848196078431373, violated group size: 0.217\n",
+ "iteration: 307, error: 0.42098697068403895, fairness violation: 0.004853609120521175, violated group size: 0.283\n",
+ "iteration: 308, error: 0.4209188311688312, fairness violation: 0.004858987012987015, violated group size: 0.283\n",
+ "iteration: 309, error: 0.42085113268608415, fairness violation: 0.00486433009708738, violated group size: 0.283\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "iteration: 310, error: 0.4207838709677419, fairness violation: 0.00486963870967742, violated group size: 0.283\n",
+ "iteration: 311, error: 0.420717041800643, fairness violation: 0.004874913183279744, violated group size: 0.217\n",
+ "iteration: 312, error: 0.42065064102564104, fairness violation: 0.0048801538461538466, violated group size: 0.217\n",
+ "iteration: 313, error: 0.42058466453674115, fairness violation: 0.0048853610223642185, violated group size: 0.217\n",
+ "iteration: 314, error: 0.42051910828025474, fairness violation: 0.004890535031847135, violated group size: 0.217\n",
+ "iteration: 315, error: 0.4204539682539683, fairness violation: 0.004895676190476191, violated group size: 0.217\n",
+ "iteration: 316, error: 0.4203892405063292, fairness violation: 0.004900784810126583, violated group size: 0.217\n",
+ "iteration: 317, error: 0.4203249211356468, fairness violation: 0.004905861198738172, violated group size: 0.283\n",
+ "iteration: 318, error: 0.4202610062893082, fairness violation: 0.00491090566037736, violated group size: 0.217\n",
+ "iteration: 319, error: 0.4201974921630094, fairness violation: 0.004915918495297806, violated group size: 0.217\n",
+ "iteration: 320, error: 0.4201343749999999, fairness violation: 0.004920900000000002, violated group size: 0.217\n",
+ "iteration: 321, error: 0.4200716510903427, fairness violation: 0.004925850467289721, violated group size: 0.217\n",
+ "iteration: 322, error: 0.4200093167701862, fairness violation: 0.0049307701863354056, violated group size: 0.283\n",
+ "iteration: 323, error: 0.4199473684210526, fairness violation: 0.00493565944272446, violated group size: 0.283\n",
+ "iteration: 324, error: 0.41988580246913576, fairness violation: 0.004940518518518519, violated group size: 0.217\n",
+ "iteration: 325, error: 0.41982461538461535, fairness violation: 0.004945347692307694, violated group size: 0.217\n",
+ "iteration: 326, error: 0.4197638036809816, fairness violation: 0.004950147239263805, violated group size: 0.283\n",
+ "iteration: 327, error: 0.4197033639143731, fairness violation: 0.004954917431192661, violated group size: 0.283\n",
+ "iteration: 328, error: 0.4196432926829268, fairness violation: 0.004959658536585366, violated group size: 0.217\n",
+ "iteration: 329, error: 0.41958358662613987, fairness violation: 0.004964370820668694, violated group size: 0.283\n",
+ "iteration: 330, error: 0.41952424242424236, fairness violation: 0.004969054545454545, violated group size: 0.217\n",
+ "iteration: 331, error: 0.41946525679758306, fairness violation: 0.00497370996978852, violated group size: 0.217\n",
+ "iteration: 332, error: 0.41940662650602417, fairness violation: 0.004978337349397591, violated group size: 0.217\n",
+ "iteration: 333, error: 0.4193483483483482, fairness violation: 0.004982936936936937, violated group size: 0.217\n",
+ "iteration: 334, error: 0.4192904191616766, fairness violation: 0.004987508982035928, violated group size: 0.217\n",
+ "iteration: 335, error: 0.4192328358208956, fairness violation: 0.004992053731343284, violated group size: 0.283\n",
+ "iteration: 336, error: 0.4191755952380953, fairness violation: 0.00499657142857143, violated group size: 0.283\n",
+ "iteration: 337, error: 0.4191186943620178, fairness violation: 0.0050010623145400595, violated group size: 0.217\n",
+ "iteration: 338, error: 0.41906213017751476, fairness violation: 0.005005526627218935, violated group size: 0.217\n",
+ "iteration: 339, error: 0.4190058997050148, fairness violation: 0.005009964601769911, violated group size: 0.217\n",
+ "iteration: 340, error: 0.41894999999999993, fairness violation: 0.005014376470588236, violated group size: 0.283\n",
+ "iteration: 341, error: 0.41889442815249267, fairness violation: 0.005018762463343108, violated group size: 0.217\n",
+ "iteration: 342, error: 0.41883918128654973, fairness violation: 0.005023122807017544, violated group size: 0.217\n",
+ "iteration: 343, error: 0.41878425655976675, fairness violation: 0.0050274577259475225, violated group size: 0.283\n",
+ "iteration: 344, error: 0.4187296511627907, fairness violation: 0.005031767441860465, violated group size: 0.217\n",
+ "iteration: 345, error: 0.4186753623188406, fairness violation: 0.005036052173913045, violated group size: 0.283\n",
+ "iteration: 346, error: 0.4186213872832369, fairness violation: 0.005040312138728323, violated group size: 0.217\n",
+ "iteration: 347, error: 0.41856772334293946, fairness violation: 0.005044547550432276, violated group size: 0.283\n",
+ "iteration: 348, error: 0.41851436781609197, fairness violation: 0.005048758620689655, violated group size: 0.217\n",
+ "iteration: 349, error: 0.418461318051576, fairness violation: 0.005052945558739255, violated group size: 0.283\n",
+ "iteration: 350, error: 0.4185771428571428, fairness violation: 0.005043468571428572, violated group size: 0.283\n",
+ "iteration: 351, error: 0.4186923076923077, fairness violation: 0.005034045584045584, violated group size: 0.217\n",
+ "iteration: 352, error: 0.4188068181818182, fairness violation: 0.005024676136363637, violated group size: 0.283\n",
+ "iteration: 353, error: 0.4189206798866855, fairness violation: 0.005015359773371105, violated group size: 0.217\n",
+ "iteration: 354, error: 0.41903389830508475, fairness violation: 0.005006096045197741, violated group size: 0.283\n",
+ "iteration: 355, error: 0.41914647887323936, fairness violation: 0.004996884507042254, violated group size: 0.283\n",
+ "iteration: 356, error: 0.4192584269662922, fairness violation: 0.004987724719101122, violated group size: 0.217\n",
+ "iteration: 357, error: 0.41936974789915965, fairness violation: 0.0049786162464986, violated group size: 0.217\n",
+ "iteration: 358, error: 0.41948044692737424, fairness violation: 0.004969558659217878, violated group size: 0.217\n",
+ "iteration: 359, error: 0.41959052924791085, fairness violation: 0.004960551532033426, violated group size: 0.283\n",
+ "iteration: 360, error: 0.4195361111111111, fairness violation: 0.004964855555555557, violated group size: 0.283\n",
+ "iteration: 361, error: 0.4196454293628808, fairness violation: 0.004955911357340723, violated group size: 0.283\n",
+ "iteration: 362, error: 0.4197541436464089, fairness violation: 0.004947016574585636, violated group size: 0.217\n",
+ "iteration: 363, error: 0.4198622589531681, fairness violation: 0.004938170798898072, violated group size: 0.283\n",
+ "iteration: 364, error: 0.41996978021978026, fairness violation: 0.004929373626373626, violated group size: 0.217\n",
+ "iteration: 365, error: 0.42007671232876714, fairness violation: 0.004920624657534246, violated group size: 0.217\n",
+ "iteration: 366, error: 0.42018306010928963, fairness violation: 0.004911923497267759, violated group size: 0.217\n",
+ "iteration: 367, error: 0.4202888283378746, fairness violation: 0.004903269754768393, violated group size: 0.217\n",
+ "iteration: 368, error: 0.42039402173913043, fairness violation: 0.00489466304347826, violated group size: 0.217\n",
+ "iteration: 369, error: 0.4204986449864499, fairness violation: 0.00488610298102981, violated group size: 0.283\n",
+ "iteration: 370, error: 0.4206027027027027, fairness violation: 0.0048775891891891885, violated group size: 0.217\n",
+ "iteration: 371, error: 0.4207061994609164, fairness violation: 0.004869121293800538, violated group size: 0.217\n",
+ "iteration: 372, error: 0.4208091397849463, fairness violation: 0.004860698924731182, violated group size: 0.217\n",
+ "iteration: 373, error: 0.420911528150134, fairness violation: 0.004852321715817694, violated group size: 0.217\n",
+ "iteration: 374, error: 0.420855614973262, fairness violation: 0.004856754010695187, violated group size: 0.217\n",
+ "iteration: 375, error: 0.4209573333333334, fairness violation: 0.004848432, violated group size: 0.217\n",
+ "iteration: 376, error: 0.42105851063829786, fairness violation: 0.004840154255319148, violated group size: 0.217\n",
+ "iteration: 377, error: 0.4211591511936339, fairness violation: 0.004831920424403182, violated group size: 0.217\n",
+ "iteration: 378, error: 0.4211031746031746, fairness violation: 0.004836359788359788, violated group size: 0.217\n",
+ "iteration: 379, error: 0.42120316622691284, fairness violation: 0.004828179419525066, violated group size: 0.217\n",
+ "iteration: 380, error: 0.42130263157894726, fairness violation: 0.004820042105263157, violated group size: 0.217\n",
+ "iteration: 381, error: 0.42124671916010503, fairness violation: 0.004824477690288715, violated group size: 0.283\n",
+ "iteration: 382, error: 0.42134554973821986, fairness violation: 0.004816392670157068, violated group size: 0.283\n",
+ "iteration: 383, error: 0.42144386422976504, fairness violation: 0.004808349869451696, violated group size: 0.217\n",
+ "iteration: 384, error: 0.42154166666666676, fairness violation: 0.004800348958333333, violated group size: 0.283\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "iteration: 385, error: 0.4214857142857143, fairness violation: 0.00480478961038961, violated group size: 0.217\n",
+ "iteration: 386, error: 0.4215829015544041, fairness violation: 0.0047968393782383415, violated group size: 0.217\n",
+ "iteration: 387, error: 0.42152713178294576, fairness violation: 0.004801266149870801, violated group size: 0.283\n",
+ "iteration: 388, error: 0.4216237113402061, fairness violation: 0.004793365979381444, violated group size: 0.283\n",
+ "iteration: 389, error: 0.42171979434447304, fairness violation: 0.004785506426735219, violated group size: 0.217\n",
+ "iteration: 390, error: 0.4218153846153847, fairness violation: 0.00477768717948718, violated group size: 0.217\n",
+ "iteration: 391, error: 0.4219104859335038, fairness violation: 0.004769907928388747, violated group size: 0.217\n",
+ "iteration: 392, error: 0.4218545918367348, fairness violation: 0.00477434693877551, violated group size: 0.217\n",
+ "iteration: 393, error: 0.42194910941475816, fairness violation: 0.004766615776081424, violated group size: 0.217\n",
+ "iteration: 394, error: 0.42204314720812186, fairness violation: 0.004758923857868021, violated group size: 0.283\n",
+ "iteration: 395, error: 0.4221367088607595, fairness violation: 0.00475127088607595, violated group size: 0.217\n",
+ "iteration: 396, error: 0.42208080808080806, fairness violation: 0.004755712121212122, violated group size: 0.283\n",
+ "iteration: 397, error: 0.42202518891687657, fairness violation: 0.004760130982367758, violated group size: 0.217\n",
+ "iteration: 398, error: 0.42196984924623115, fairness violation: 0.004764527638190955, violated group size: 0.283\n",
+ "iteration: 399, error: 0.42191478696741846, fairness violation: 0.004768902255639098, violated group size: 0.217\n",
+ "iteration: 400, error: 0.42186, fairness violation: 0.004773255, violated group size: 0.217\n",
+ "iteration: 401, error: 0.4218054862842893, fairness violation: 0.004777586034912718, violated group size: 0.283\n",
+ "iteration: 402, error: 0.4217512437810945, fairness violation: 0.00478189552238806, violated group size: 0.217\n",
+ "iteration: 403, error: 0.4218436724565757, fairness violation: 0.0047743374689826305, violated group size: 0.283\n",
+ "iteration: 404, error: 0.42178960396039605, fairness violation: 0.004778633663366336, violated group size: 0.217\n",
+ "iteration: 405, error: 0.4217358024691357, fairness violation: 0.004782908641975308, violated group size: 0.217\n",
+ "iteration: 406, error: 0.4216822660098523, fairness violation: 0.004787162561576355, violated group size: 0.217\n",
+ "iteration: 407, error: 0.4216289926289926, fairness violation: 0.004791395577395577, violated group size: 0.283\n",
+ "iteration: 408, error: 0.421575980392157, fairness violation: 0.004795607843137254, violated group size: 0.217\n",
+ "iteration: 409, error: 0.4215232273838631, fairness violation: 0.004799799511002444, violated group size: 0.217\n",
+ "iteration: 410, error: 0.42147073170731714, fairness violation: 0.004803970731707317, violated group size: 0.283\n",
+ "iteration: 411, error: 0.4214184914841849, fairness violation: 0.0048081216545012165, violated group size: 0.217\n",
+ "iteration: 412, error: 0.4213665048543689, fairness violation: 0.004812252427184466, violated group size: 0.283\n",
+ "iteration: 413, error: 0.42131476997578693, fairness violation: 0.004816363196125908, violated group size: 0.217\n",
+ "iteration: 414, error: 0.42126328502415455, fairness violation: 0.004820454106280194, violated group size: 0.217\n",
+ "iteration: 415, error: 0.4212120481927711, fairness violation: 0.004824525301204821, violated group size: 0.283\n",
+ "iteration: 416, error: 0.42116105769230766, fairness violation: 0.004828576923076923, violated group size: 0.217\n",
+ "iteration: 417, error: 0.4211103117505996, fairness violation: 0.004832609112709832, violated group size: 0.283\n",
+ "iteration: 418, error: 0.42105980861244025, fairness violation: 0.004836622009569378, violated group size: 0.283\n",
+ "iteration: 419, error: 0.42100954653937933, fairness violation: 0.004840615751789977, violated group size: 0.217\n",
+ "iteration: 420, error: 0.42110000000000003, fairness violation: 0.0048332238095238084, violated group size: 0.217\n",
+ "iteration: 421, error: 0.42104988123515436, fairness violation: 0.004837206650831354, violated group size: 0.217\n",
+ "iteration: 422, error: 0.42100000000000004, fairness violation: 0.004841170616113744, violated group size: 0.217\n",
+ "iteration: 423, error: 0.420950354609929, fairness violation: 0.004845115839243499, violated group size: 0.217\n",
+ "iteration: 424, error: 0.42104009433962253, fairness violation: 0.004837783018867924, violated group size: 0.217\n",
+ "iteration: 425, error: 0.4209905882352941, fairness violation: 0.004841717647058822, violated group size: 0.217\n",
+ "iteration: 426, error: 0.42094131455399053, fairness violation: 0.004845633802816901, violated group size: 0.217\n",
+ "iteration: 427, error: 0.42089227166276344, fairness violation: 0.004849531615925057, violated group size: 0.217\n",
+ "iteration: 428, error: 0.4208434579439252, fairness violation: 0.004853411214953271, violated group size: 0.217\n",
+ "iteration: 429, error: 0.4207948717948717, fairness violation: 0.0048572727272727274, violated group size: 0.283\n",
+ "iteration: 430, error: 0.42074651162790694, fairness violation: 0.004861116279069767, violated group size: 0.217\n",
+ "iteration: 431, error: 0.4206983758700697, fairness violation: 0.004864941995359629, violated group size: 0.283\n",
+ "iteration: 432, error: 0.420650462962963, fairness violation: 0.00486875, violated group size: 0.217\n",
+ "iteration: 433, error: 0.42060277136258656, fairness violation: 0.0048725404157043874, violated group size: 0.217\n",
+ "iteration: 434, error: 0.42055529953917054, fairness violation: 0.0048763133640553, violated group size: 0.283\n",
+ "iteration: 435, error: 0.42050804597701147, fairness violation: 0.004880068965517241, violated group size: 0.217\n",
+ "iteration: 436, error: 0.4204610091743119, fairness violation: 0.004883807339449542, violated group size: 0.217\n",
+ "iteration: 437, error: 0.4204141876430207, fairness violation: 0.004887528604118992, violated group size: 0.217\n",
+ "iteration: 438, error: 0.42036757990867574, fairness violation: 0.004891232876712329, violated group size: 0.217\n",
+ "iteration: 439, error: 0.4203211845102506, fairness violation: 0.0048949202733485206, violated group size: 0.283\n",
+ "iteration: 440, error: 0.42027499999999995, fairness violation: 0.00489859090909091, violated group size: 0.283\n",
+ "iteration: 441, error: 0.42022902494331066, fairness violation: 0.004902244897959184, violated group size: 0.283\n",
+ "iteration: 442, error: 0.42018325791855204, fairness violation: 0.004905882352941177, violated group size: 0.217\n",
+ "iteration: 443, error: 0.42013769751693003, fairness violation: 0.004909503386004516, violated group size: 0.283\n",
+ "iteration: 444, error: 0.42009234234234244, fairness violation: 0.004913108108108108, violated group size: 0.217\n",
+ "iteration: 445, error: 0.420047191011236, fairness violation: 0.004916696629213483, violated group size: 0.217\n",
+ "iteration: 446, error: 0.42000224215246645, fairness violation: 0.004920269058295964, violated group size: 0.217\n",
+ "iteration: 447, error: 0.4199574944071588, fairness violation: 0.004923825503355704, violated group size: 0.217\n",
+ "iteration: 448, error: 0.41991294642857147, fairness violation: 0.004927366071428571, violated group size: 0.217\n",
+ "iteration: 449, error: 0.41986859688195993, fairness violation: 0.004930890868596881, violated group size: 0.217\n",
+ "iteration: 450, error: 0.41982444444444433, fairness violation: 0.004934399999999999, violated group size: 0.217\n",
+ "iteration: 451, error: 0.41978048780487814, fairness violation: 0.004937893569844789, violated group size: 0.217\n",
+ "iteration: 452, error: 0.41973672566371684, fairness violation: 0.004941371681415928, violated group size: 0.217\n",
+ "iteration: 453, error: 0.41969315673289187, fairness violation: 0.0049448344370860925, violated group size: 0.217\n",
+ "iteration: 454, error: 0.41964977973568285, fairness violation: 0.004948281938325991, violated group size: 0.283\n",
+ "iteration: 455, error: 0.41960659340659345, fairness violation: 0.004951714285714286, violated group size: 0.283\n",
+ "iteration: 456, error: 0.41956359649122804, fairness violation: 0.004955131578947367, violated group size: 0.217\n",
+ "iteration: 457, error: 0.41952078774617063, fairness violation: 0.0049585339168490145, violated group size: 0.217\n",
+ "iteration: 458, error: 0.41947816593886456, fairness violation: 0.004961921397379911, violated group size: 0.217\n",
+ "iteration: 459, error: 0.4194357298474945, fairness violation: 0.00496529411764706, violated group size: 0.283\n",
+ "iteration: 460, error: 0.4193934782608696, fairness violation: 0.004968652173913044, violated group size: 0.283\n",
+ "iteration: 461, error: 0.41935140997830805, fairness violation: 0.004971995661605205, violated group size: 0.283\n",
+ "iteration: 462, error: 0.41930952380952374, fairness violation: 0.0049753246753246735, violated group size: 0.217\n",
+ "iteration: 463, error: 0.41926781857451406, fairness violation: 0.004978639308855291, violated group size: 0.217\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "iteration: 464, error: 0.41922629310344817, fairness violation: 0.004981939655172413, violated group size: 0.217\n",
+ "iteration: 465, error: 0.41918494623655916, fairness violation: 0.004985225806451612, violated group size: 0.217\n",
+ "iteration: 466, error: 0.4191437768240344, fairness violation: 0.004988497854077254, violated group size: 0.283\n",
+ "iteration: 467, error: 0.41910278372591, fairness violation: 0.004991755888650964, violated group size: 0.283\n",
+ "iteration: 468, error: 0.4190619658119658, fairness violation: 0.004994999999999998, violated group size: 0.217\n",
+ "iteration: 469, error: 0.4190213219616205, fairness violation: 0.0049982302771855, violated group size: 0.217\n",
+ "iteration: 470, error: 0.41898085106382993, fairness violation: 0.005001446808510636, violated group size: 0.283\n",
+ "iteration: 471, error: 0.41894055201698516, fairness violation: 0.005004649681528661, violated group size: 0.217\n",
+ "iteration: 472, error: 0.41890042372881364, fairness violation: 0.005007838983050847, violated group size: 0.283\n",
+ "iteration: 473, error: 0.4188604651162791, fairness violation: 0.005011014799154333, violated group size: 0.217\n",
+ "iteration: 474, error: 0.41882067510548526, fairness violation: 0.005014177215189871, violated group size: 0.217\n",
+ "iteration: 475, error: 0.41878105263157905, fairness violation: 0.0050173263157894735, violated group size: 0.283\n",
+ "iteration: 476, error: 0.41874159663865557, fairness violation: 0.0050204621848739485, violated group size: 0.217\n",
+ "iteration: 477, error: 0.4188259958071279, fairness violation: 0.005013576519916141, violated group size: 0.217\n",
+ "iteration: 478, error: 0.41878661087866115, fairness violation: 0.005016707112970709, violated group size: 0.217\n",
+ "iteration: 479, error: 0.4188705636743216, fairness violation: 0.005009858037578285, violated group size: 0.217\n",
+ "iteration: 480, error: 0.41883125000000004, fairness violation: 0.005012983333333334, violated group size: 0.283\n",
+ "iteration: 481, error: 0.4187920997920998, fairness violation: 0.005016095634095634, violated group size: 0.283\n",
+ "iteration: 482, error: 0.4187531120331951, fairness violation: 0.0050191950207468874, violated group size: 0.283\n",
+ "iteration: 483, error: 0.4188364389233955, fairness violation: 0.00501239751552795, violated group size: 0.217\n",
+ "iteration: 484, error: 0.41891942148760325, fairness violation: 0.005005628099173555, violated group size: 0.283\n",
+ "iteration: 485, error: 0.4190020618556701, fairness violation: 0.004998886597938144, violated group size: 0.283\n",
+ "iteration: 486, error: 0.41896296296296304, fairness violation: 0.005001995884773661, violated group size: 0.217\n",
+ "iteration: 487, error: 0.4190451745379877, fairness violation: 0.004995289527720739, violated group size: 0.283\n",
+ "iteration: 488, error: 0.4191270491803279, fairness violation: 0.004988610655737704, violated group size: 0.283\n",
+ "iteration: 489, error: 0.4192085889570552, fairness violation: 0.004981959100204497, violated group size: 0.217\n",
+ "iteration: 490, error: 0.41916938775510204, fairness violation: 0.004985077551020407, violated group size: 0.283\n",
+ "iteration: 491, error: 0.4192505091649695, fairness violation: 0.004978460285132381, violated group size: 0.217\n",
+ "iteration: 492, error: 0.4192113821138212, fairness violation: 0.004981573170731706, violated group size: 0.217\n",
+ "iteration: 493, error: 0.41917241379310355, fairness violation: 0.004984673427991887, violated group size: 0.283\n",
+ "iteration: 494, error: 0.41913360323886634, fairness violation: 0.004987761133603237, violated group size: 0.217\n",
+ "iteration: 495, error: 0.4192141414141415, fairness violation: 0.004981191919191918, violated group size: 0.217\n",
+ "iteration: 496, error: 0.4192943548387097, fairness violation: 0.004974649193548386, violated group size: 0.217\n",
+ "iteration: 497, error: 0.419374245472837, fairness violation: 0.004968132796780683, violated group size: 0.217\n",
+ "iteration: 498, error: 0.4194538152610441, fairness violation: 0.004961642570281124, violated group size: 0.217\n",
+ "iteration: 499, error: 0.41953306613226454, fairness violation: 0.0049551783567134255, violated group size: 0.217\n"
+ ]
+ }
+ ],
+ "source": [
+ "C = 100\n",
+ "print_flag = True\n",
+ "gamma = .005\n",
+ "\n",
+ "\n",
+ "fair_model = GerryFairClassifier(C=C, printflag=print_flag, gamma=gamma, fairness_def='FP',\n",
+ " max_iters=max_iterations, heatmapflag=False)\n",
+ "\n",
+ "# fit method\n",
+ "fair_model.fit(data_set, early_termination=True)\n",
+ "\n",
+ "# predict method. If threshold in (0, 1) produces binary predictions\n",
+ "\n",
+ "dataset_yhat = fair_model.predict(data_set, threshold=False)\n",
+ "\n",
+ "\n"
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "iteration: 310, error: 0.4207838709677419, fairness violation: 0.00486963870967742, violated group size: 0.283\n",
- "iteration: 311, error: 0.420717041800643, fairness violation: 0.004874913183279744, violated group size: 0.217\n",
- "iteration: 312, error: 0.42065064102564104, fairness violation: 0.0048801538461538466, violated group size: 0.217\n",
- "iteration: 313, error: 0.42058466453674115, fairness violation: 0.0048853610223642185, violated group size: 0.217\n",
- "iteration: 314, error: 0.42051910828025474, fairness violation: 0.004890535031847135, violated group size: 0.217\n",
- "iteration: 315, error: 0.4204539682539683, fairness violation: 0.004895676190476191, violated group size: 0.217\n",
- "iteration: 316, error: 0.4203892405063292, fairness violation: 0.004900784810126583, violated group size: 0.217\n",
- "iteration: 317, error: 0.4203249211356468, fairness violation: 0.004905861198738172, violated group size: 0.283\n",
- "iteration: 318, error: 0.4202610062893082, fairness violation: 0.00491090566037736, violated group size: 0.217\n",
- "iteration: 319, error: 0.4201974921630094, fairness violation: 0.004915918495297806, violated group size: 0.217\n",
- "iteration: 320, error: 0.4201343749999999, fairness violation: 0.004920900000000002, violated group size: 0.217\n",
- "iteration: 321, error: 0.4200716510903427, fairness violation: 0.004925850467289721, violated group size: 0.217\n",
- "iteration: 322, error: 0.4200093167701862, fairness violation: 0.0049307701863354056, violated group size: 0.283\n",
- "iteration: 323, error: 0.4199473684210526, fairness violation: 0.00493565944272446, violated group size: 0.283\n",
- "iteration: 324, error: 0.41988580246913576, fairness violation: 0.004940518518518519, violated group size: 0.217\n",
- "iteration: 325, error: 0.41982461538461535, fairness violation: 0.004945347692307694, violated group size: 0.217\n",
- "iteration: 326, error: 0.4197638036809816, fairness violation: 0.004950147239263805, violated group size: 0.283\n",
- "iteration: 327, error: 0.4197033639143731, fairness violation: 0.004954917431192661, violated group size: 0.283\n",
- "iteration: 328, error: 0.4196432926829268, fairness violation: 0.004959658536585366, violated group size: 0.217\n",
- "iteration: 329, error: 0.41958358662613987, fairness violation: 0.004964370820668694, violated group size: 0.283\n",
- "iteration: 330, error: 0.41952424242424236, fairness violation: 0.004969054545454545, violated group size: 0.217\n",
- "iteration: 331, error: 0.41946525679758306, fairness violation: 0.00497370996978852, violated group size: 0.217\n",
- "iteration: 332, error: 0.41940662650602417, fairness violation: 0.004978337349397591, violated group size: 0.217\n",
- "iteration: 333, error: 0.4193483483483482, fairness violation: 0.004982936936936937, violated group size: 0.217\n",
- "iteration: 334, error: 0.4192904191616766, fairness violation: 0.004987508982035928, violated group size: 0.217\n",
- "iteration: 335, error: 0.4192328358208956, fairness violation: 0.004992053731343284, violated group size: 0.283\n",
- "iteration: 336, error: 0.4191755952380953, fairness violation: 0.00499657142857143, violated group size: 0.283\n",
- "iteration: 337, error: 0.4191186943620178, fairness violation: 0.0050010623145400595, violated group size: 0.217\n",
- "iteration: 338, error: 0.41906213017751476, fairness violation: 0.005005526627218935, violated group size: 0.217\n",
- "iteration: 339, error: 0.4190058997050148, fairness violation: 0.005009964601769911, violated group size: 0.217\n",
- "iteration: 340, error: 0.41894999999999993, fairness violation: 0.005014376470588236, violated group size: 0.283\n",
- "iteration: 341, error: 0.41889442815249267, fairness violation: 0.005018762463343108, violated group size: 0.217\n",
- "iteration: 342, error: 0.41883918128654973, fairness violation: 0.005023122807017544, violated group size: 0.217\n",
- "iteration: 343, error: 0.41878425655976675, fairness violation: 0.0050274577259475225, violated group size: 0.283\n",
- "iteration: 344, error: 0.4187296511627907, fairness violation: 0.005031767441860465, violated group size: 0.217\n",
- "iteration: 345, error: 0.4186753623188406, fairness violation: 0.005036052173913045, violated group size: 0.283\n",
- "iteration: 346, error: 0.4186213872832369, fairness violation: 0.005040312138728323, violated group size: 0.217\n",
- "iteration: 347, error: 0.41856772334293946, fairness violation: 0.005044547550432276, violated group size: 0.283\n",
- "iteration: 348, error: 0.41851436781609197, fairness violation: 0.005048758620689655, violated group size: 0.217\n",
- "iteration: 349, error: 0.418461318051576, fairness violation: 0.005052945558739255, violated group size: 0.283\n",
- "iteration: 350, error: 0.4185771428571428, fairness violation: 0.005043468571428572, violated group size: 0.283\n",
- "iteration: 351, error: 0.4186923076923077, fairness violation: 0.005034045584045584, violated group size: 0.217\n",
- "iteration: 352, error: 0.4188068181818182, fairness violation: 0.005024676136363637, violated group size: 0.283\n",
- "iteration: 353, error: 0.4189206798866855, fairness violation: 0.005015359773371105, violated group size: 0.217\n",
- "iteration: 354, error: 0.41903389830508475, fairness violation: 0.005006096045197741, violated group size: 0.283\n",
- "iteration: 355, error: 0.41914647887323936, fairness violation: 0.004996884507042254, violated group size: 0.283\n",
- "iteration: 356, error: 0.4192584269662922, fairness violation: 0.004987724719101122, violated group size: 0.217\n",
- "iteration: 357, error: 0.41936974789915965, fairness violation: 0.0049786162464986, violated group size: 0.217\n",
- "iteration: 358, error: 0.41948044692737424, fairness violation: 0.004969558659217878, violated group size: 0.217\n",
- "iteration: 359, error: 0.41959052924791085, fairness violation: 0.004960551532033426, violated group size: 0.283\n",
- "iteration: 360, error: 0.4195361111111111, fairness violation: 0.004964855555555557, violated group size: 0.283\n",
- "iteration: 361, error: 0.4196454293628808, fairness violation: 0.004955911357340723, violated group size: 0.283\n",
- "iteration: 362, error: 0.4197541436464089, fairness violation: 0.004947016574585636, violated group size: 0.217\n",
- "iteration: 363, error: 0.4198622589531681, fairness violation: 0.004938170798898072, violated group size: 0.283\n",
- "iteration: 364, error: 0.41996978021978026, fairness violation: 0.004929373626373626, violated group size: 0.217\n",
- "iteration: 365, error: 0.42007671232876714, fairness violation: 0.004920624657534246, violated group size: 0.217\n",
- "iteration: 366, error: 0.42018306010928963, fairness violation: 0.004911923497267759, violated group size: 0.217\n",
- "iteration: 367, error: 0.4202888283378746, fairness violation: 0.004903269754768393, violated group size: 0.217\n",
- "iteration: 368, error: 0.42039402173913043, fairness violation: 0.00489466304347826, violated group size: 0.217\n",
- "iteration: 369, error: 0.4204986449864499, fairness violation: 0.00488610298102981, violated group size: 0.283\n",
- "iteration: 370, error: 0.4206027027027027, fairness violation: 0.0048775891891891885, violated group size: 0.217\n",
- "iteration: 371, error: 0.4207061994609164, fairness violation: 0.004869121293800538, violated group size: 0.217\n",
- "iteration: 372, error: 0.4208091397849463, fairness violation: 0.004860698924731182, violated group size: 0.217\n",
- "iteration: 373, error: 0.420911528150134, fairness violation: 0.004852321715817694, violated group size: 0.217\n",
- "iteration: 374, error: 0.420855614973262, fairness violation: 0.004856754010695187, violated group size: 0.217\n",
- "iteration: 375, error: 0.4209573333333334, fairness violation: 0.004848432, violated group size: 0.217\n",
- "iteration: 376, error: 0.42105851063829786, fairness violation: 0.004840154255319148, violated group size: 0.217\n",
- "iteration: 377, error: 0.4211591511936339, fairness violation: 0.004831920424403182, violated group size: 0.217\n",
- "iteration: 378, error: 0.4211031746031746, fairness violation: 0.004836359788359788, violated group size: 0.217\n",
- "iteration: 379, error: 0.42120316622691284, fairness violation: 0.004828179419525066, violated group size: 0.217\n",
- "iteration: 380, error: 0.42130263157894726, fairness violation: 0.004820042105263157, violated group size: 0.217\n",
- "iteration: 381, error: 0.42124671916010503, fairness violation: 0.004824477690288715, violated group size: 0.283\n",
- "iteration: 382, error: 0.42134554973821986, fairness violation: 0.004816392670157068, violated group size: 0.283\n",
- "iteration: 383, error: 0.42144386422976504, fairness violation: 0.004808349869451696, violated group size: 0.217\n",
- "iteration: 384, error: 0.42154166666666676, fairness violation: 0.004800348958333333, violated group size: 0.283\n"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "ak4FlXHmhI5L"
+ },
+ "source": [
+ "**3-d heatmaps**\n",
+ "\n",
+ "We now show to generate a 3d-heatmap of unfairness using the `generate_heatmap` method. The $X-Y$ axes in the plot represent the coefficients of the linear threshold function that defines a protected subgroup with respect to the first two sensitive attributes. Which $2$ attributes are considered sensitive can be overwritten with the `col_index` argument. The $Z$-axes is the $\\gamma$-disparity (FP) of the corresponding subgroup defined by the linear threshold function. This is important because it allows us to (1) visualize convergence as the heatmap flattens and (2) brute force check the fairness in low-dimensions without relying on a heuristic auditor. See the [the rich subgroup fairness empirical paper](https://arxiv.org/abs/1808.08166) for a discussion of these plots. Note that in the below plot no group has a $\\gamma$-disparity of greater than $.005$, which we would expect since the set of linear threshold functions on two attributes is a subset of the set of linear threshold functions on all protected attributes, and the final model is $\\gamma$-fair."
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "iteration: 385, error: 0.4214857142857143, fairness violation: 0.00480478961038961, violated group size: 0.217\n",
- "iteration: 386, error: 0.4215829015544041, fairness violation: 0.0047968393782383415, violated group size: 0.217\n",
- "iteration: 387, error: 0.42152713178294576, fairness violation: 0.004801266149870801, violated group size: 0.283\n",
- "iteration: 388, error: 0.4216237113402061, fairness violation: 0.004793365979381444, violated group size: 0.283\n",
- "iteration: 389, error: 0.42171979434447304, fairness violation: 0.004785506426735219, violated group size: 0.217\n",
- "iteration: 390, error: 0.4218153846153847, fairness violation: 0.00477768717948718, violated group size: 0.217\n",
- "iteration: 391, error: 0.4219104859335038, fairness violation: 0.004769907928388747, violated group size: 0.217\n",
- "iteration: 392, error: 0.4218545918367348, fairness violation: 0.00477434693877551, violated group size: 0.217\n",
- "iteration: 393, error: 0.42194910941475816, fairness violation: 0.004766615776081424, violated group size: 0.217\n",
- "iteration: 394, error: 0.42204314720812186, fairness violation: 0.004758923857868021, violated group size: 0.283\n",
- "iteration: 395, error: 0.4221367088607595, fairness violation: 0.00475127088607595, violated group size: 0.217\n",
- "iteration: 396, error: 0.42208080808080806, fairness violation: 0.004755712121212122, violated group size: 0.283\n",
- "iteration: 397, error: 0.42202518891687657, fairness violation: 0.004760130982367758, violated group size: 0.217\n",
- "iteration: 398, error: 0.42196984924623115, fairness violation: 0.004764527638190955, violated group size: 0.283\n",
- "iteration: 399, error: 0.42191478696741846, fairness violation: 0.004768902255639098, violated group size: 0.217\n",
- "iteration: 400, error: 0.42186, fairness violation: 0.004773255, violated group size: 0.217\n",
- "iteration: 401, error: 0.4218054862842893, fairness violation: 0.004777586034912718, violated group size: 0.283\n",
- "iteration: 402, error: 0.4217512437810945, fairness violation: 0.00478189552238806, violated group size: 0.217\n",
- "iteration: 403, error: 0.4218436724565757, fairness violation: 0.0047743374689826305, violated group size: 0.283\n",
- "iteration: 404, error: 0.42178960396039605, fairness violation: 0.004778633663366336, violated group size: 0.217\n",
- "iteration: 405, error: 0.4217358024691357, fairness violation: 0.004782908641975308, violated group size: 0.217\n",
- "iteration: 406, error: 0.4216822660098523, fairness violation: 0.004787162561576355, violated group size: 0.217\n",
- "iteration: 407, error: 0.4216289926289926, fairness violation: 0.004791395577395577, violated group size: 0.283\n",
- "iteration: 408, error: 0.421575980392157, fairness violation: 0.004795607843137254, violated group size: 0.217\n",
- "iteration: 409, error: 0.4215232273838631, fairness violation: 0.004799799511002444, violated group size: 0.217\n",
- "iteration: 410, error: 0.42147073170731714, fairness violation: 0.004803970731707317, violated group size: 0.283\n",
- "iteration: 411, error: 0.4214184914841849, fairness violation: 0.0048081216545012165, violated group size: 0.217\n",
- "iteration: 412, error: 0.4213665048543689, fairness violation: 0.004812252427184466, violated group size: 0.283\n",
- "iteration: 413, error: 0.42131476997578693, fairness violation: 0.004816363196125908, violated group size: 0.217\n",
- "iteration: 414, error: 0.42126328502415455, fairness violation: 0.004820454106280194, violated group size: 0.217\n",
- "iteration: 415, error: 0.4212120481927711, fairness violation: 0.004824525301204821, violated group size: 0.283\n",
- "iteration: 416, error: 0.42116105769230766, fairness violation: 0.004828576923076923, violated group size: 0.217\n",
- "iteration: 417, error: 0.4211103117505996, fairness violation: 0.004832609112709832, violated group size: 0.283\n",
- "iteration: 418, error: 0.42105980861244025, fairness violation: 0.004836622009569378, violated group size: 0.283\n",
- "iteration: 419, error: 0.42100954653937933, fairness violation: 0.004840615751789977, violated group size: 0.217\n",
- "iteration: 420, error: 0.42110000000000003, fairness violation: 0.0048332238095238084, violated group size: 0.217\n",
- "iteration: 421, error: 0.42104988123515436, fairness violation: 0.004837206650831354, violated group size: 0.217\n",
- "iteration: 422, error: 0.42100000000000004, fairness violation: 0.004841170616113744, violated group size: 0.217\n",
- "iteration: 423, error: 0.420950354609929, fairness violation: 0.004845115839243499, violated group size: 0.217\n",
- "iteration: 424, error: 0.42104009433962253, fairness violation: 0.004837783018867924, violated group size: 0.217\n",
- "iteration: 425, error: 0.4209905882352941, fairness violation: 0.004841717647058822, violated group size: 0.217\n",
- "iteration: 426, error: 0.42094131455399053, fairness violation: 0.004845633802816901, violated group size: 0.217\n",
- "iteration: 427, error: 0.42089227166276344, fairness violation: 0.004849531615925057, violated group size: 0.217\n",
- "iteration: 428, error: 0.4208434579439252, fairness violation: 0.004853411214953271, violated group size: 0.217\n",
- "iteration: 429, error: 0.4207948717948717, fairness violation: 0.0048572727272727274, violated group size: 0.283\n",
- "iteration: 430, error: 0.42074651162790694, fairness violation: 0.004861116279069767, violated group size: 0.217\n",
- "iteration: 431, error: 0.4206983758700697, fairness violation: 0.004864941995359629, violated group size: 0.283\n",
- "iteration: 432, error: 0.420650462962963, fairness violation: 0.00486875, violated group size: 0.217\n",
- "iteration: 433, error: 0.42060277136258656, fairness violation: 0.0048725404157043874, violated group size: 0.217\n",
- "iteration: 434, error: 0.42055529953917054, fairness violation: 0.0048763133640553, violated group size: 0.283\n",
- "iteration: 435, error: 0.42050804597701147, fairness violation: 0.004880068965517241, violated group size: 0.217\n",
- "iteration: 436, error: 0.4204610091743119, fairness violation: 0.004883807339449542, violated group size: 0.217\n",
- "iteration: 437, error: 0.4204141876430207, fairness violation: 0.004887528604118992, violated group size: 0.217\n",
- "iteration: 438, error: 0.42036757990867574, fairness violation: 0.004891232876712329, violated group size: 0.217\n",
- "iteration: 439, error: 0.4203211845102506, fairness violation: 0.0048949202733485206, violated group size: 0.283\n",
- "iteration: 440, error: 0.42027499999999995, fairness violation: 0.00489859090909091, violated group size: 0.283\n",
- "iteration: 441, error: 0.42022902494331066, fairness violation: 0.004902244897959184, violated group size: 0.283\n",
- "iteration: 442, error: 0.42018325791855204, fairness violation: 0.004905882352941177, violated group size: 0.217\n",
- "iteration: 443, error: 0.42013769751693003, fairness violation: 0.004909503386004516, violated group size: 0.283\n",
- "iteration: 444, error: 0.42009234234234244, fairness violation: 0.004913108108108108, violated group size: 0.217\n",
- "iteration: 445, error: 0.420047191011236, fairness violation: 0.004916696629213483, violated group size: 0.217\n",
- "iteration: 446, error: 0.42000224215246645, fairness violation: 0.004920269058295964, violated group size: 0.217\n",
- "iteration: 447, error: 0.4199574944071588, fairness violation: 0.004923825503355704, violated group size: 0.217\n",
- "iteration: 448, error: 0.41991294642857147, fairness violation: 0.004927366071428571, violated group size: 0.217\n",
- "iteration: 449, error: 0.41986859688195993, fairness violation: 0.004930890868596881, violated group size: 0.217\n",
- "iteration: 450, error: 0.41982444444444433, fairness violation: 0.004934399999999999, violated group size: 0.217\n",
- "iteration: 451, error: 0.41978048780487814, fairness violation: 0.004937893569844789, violated group size: 0.217\n",
- "iteration: 452, error: 0.41973672566371684, fairness violation: 0.004941371681415928, violated group size: 0.217\n",
- "iteration: 453, error: 0.41969315673289187, fairness violation: 0.0049448344370860925, violated group size: 0.217\n",
- "iteration: 454, error: 0.41964977973568285, fairness violation: 0.004948281938325991, violated group size: 0.283\n",
- "iteration: 455, error: 0.41960659340659345, fairness violation: 0.004951714285714286, violated group size: 0.283\n",
- "iteration: 456, error: 0.41956359649122804, fairness violation: 0.004955131578947367, violated group size: 0.217\n",
- "iteration: 457, error: 0.41952078774617063, fairness violation: 0.0049585339168490145, violated group size: 0.217\n",
- "iteration: 458, error: 0.41947816593886456, fairness violation: 0.004961921397379911, violated group size: 0.217\n",
- "iteration: 459, error: 0.4194357298474945, fairness violation: 0.00496529411764706, violated group size: 0.283\n",
- "iteration: 460, error: 0.4193934782608696, fairness violation: 0.004968652173913044, violated group size: 0.283\n",
- "iteration: 461, error: 0.41935140997830805, fairness violation: 0.004971995661605205, violated group size: 0.283\n",
- "iteration: 462, error: 0.41930952380952374, fairness violation: 0.0049753246753246735, violated group size: 0.217\n",
- "iteration: 463, error: 0.41926781857451406, fairness violation: 0.004978639308855291, violated group size: 0.217\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "is_executing": true
+ },
+ "id": "u364ULCthI5L",
+ "outputId": "24fa9897-8b4a-4d42-afb3-2832bd76bb99"
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbAAAAEgCAYAAADVKCZpAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nOy9eZQcdbn//67epvfpWTNrZs8kE7LNZJn4Q+CqCMEQ9OJFvChyEPRyBEXOVbgsAiqGq3i+iqC4ACLnXBHwahS9EfCKfCUhgZAA2Wd6enp6Znq2np7epveq3x/5firV3dXdVb1M9ySf1zk5SXqqq6p7uutdz/N5nvfDcBzHgUKhUCiUZYai1CdAoVAoFEouUAGjUCgUyrKEChiFQqFQliVUwCgUCoWyLKECRqFQKJRlCRUwCoVCoSxLqIBRKBQKZVlCBYxCoVAoyxIqYBQKhUJZllABo1AoFMqyhAoYhUKhUJYlVMAoFAqFsiyhAkahUCiUZQkVMAqFQqEsS6iAUSgUCmVZQgWMQqFQKMsSKmAUCoVCWZZQAaNQKBTKsoQKGIVCoVCWJVTAKBQKhbIsoQJGoVAolGUJFTAKhUKhLEuogFEoFAplWUIFjEKhUCjLEipgFAqFQlmWUAGjUCgUyrKEChiFQqFQliVUwCgUCoWyLKECRqFQKJRlCRUwCoVCoSxLqIBRKBQKZVlCBYxCoVAoyxIqYBQKhUJZllABo1AoFMqyhAoYhUKhUJYlVMAoFAqFsiyhAkahUCiUZQkVMAqFQqEsS6iAUSgUCmVZQgWMQqFQKMsSKmAUCoVCWZZQAaNQKBTKsoQKGIVCoVCWJVTAKBQKhbIsoQJGoVAolGUJFTAKhUKhLEuogFEoFAplWaIq9QlQKKWA4zhwHIdwOAwAUCqVUCgUUCgUYBgGDMOU+AwpFEo2qIBRzis4jgPLsojFYmBZFtFoFPF4PEGwGIaBQqGAUqmESqWiwkahlCkMx3FcqU+CQik2ycJFhCj5/2Rb8odgt9vR1tYGpVJJhY1CKRNoBEY5p+E4DvF4HLFYDBzH8WKTSXDEfu5yudDR0QGWZRGPxxGJRBJ+ToSNpCKVSiUVNgqlyFABo5yTcByHaDQKl8sFi8XCpwXzRWwfJLqLx+O8SAq3J9Fa8jobhULJDypglHMKjuMQi8UQj8cRjUYxNDSEbdu2FfWY6SItkoaMRCIJwjY7O4uamhpUVFQkRG1U2CgUeVABo5wTEOGKxWIAzoiKUqlEoZZ4GYZJia6kPEds+5mZGZjNZigUCkQikZSILTkdSYWNQhGHChhlWUNSd0LhIhd7IjqZCIfDYBgGarU667EKWe9EBFZs/7FYDNFoNOFnVNgolFSogFGWJaSiMB6PAxCPdjIJmN/vx8jICPx+PxiGQTweh1qthtFohF6v5/8mwlZIoUi3L6HwCkkWNmEkSIpJSEQnLCChUM51qIBRlg1kTSkajYJlWQDp03TkZ8l4PB6MjIwgFouhs7MTJpOJF4hoNIpAIIBAIICpqSkEAgHEYjGo1WoEg0FMTk7CZDLBYDCkjdj8D3wRxgd+WqBXnPg6xITN4/EgFApBo9GkPCddxEbFjXKuQAWMUvYIe7hOnjyJ1tZW6PV6WRfi+fl5jIyMQKFQoLOzExaLBQD4KI5hGGg0Gmg0GlRVVSU8NxKJ4MiRI2BZFtPT0wnCZjAY+D+qH9wJ4IyIiVEsYSNRF4EIsjC1KnwObdKmnCtQAaOULWLNx5FIJKXxONPzY7EYDh48CI1Gg97eXphMJtnnodFooFar0dTUlBB5RSIRPmKbnp5Gc5b9EGHrABAH4P9/j+cjbGKFJekiNrK9WC8bEbZ4PA6dTsdHbVTYKOUMFTBK2ZGp+VihUPDpw0zPn56exujoKKLRKAYGBmAwGPI6J7H1tOSIzS/2RAn4H/gi3nn0UMrjF82/neMe05Op5J9lWRw6dAj9/f0J2whTkVTYKOUEFTBK2SAmXMmNw5kEjGVZTE1NYXR0FBaLBRs2bMDhw4ezipeU8ngpFY25ICZchNerN4s+XkxhYxgGKtXZy4KUJm3hGhutjKQsJVTAKCVH2HycTrgIYkLCsizGx8fhcDhQW1uLgYEBVFRUFPQcswnY/PQkNGl/mkom4cqGmLDZUBhhS36Ncpu0AVryT1k6qIBRSgbHcfD7/WBZFhqNRpLdkzACi8ViGB8fx8TEBFasWIGtW7dK6ufK53zzJR/hykYpIrZkhJWioVAIp06dQl9fHwAqbJTCQwWMsuQIm4/Hx8eh1WrR1NQk6bnEvcJqtcLpdKK5uRnbtm1LSHsVg3wvssUUrmzIEbZ8X6dQ2Mi4GqEjCm3SphQSKmCUJUOs+VipVGYtyiCEw2HMz89jYmICnZ2d2L59e4qbRbEo1hpYKUkWtkJHahzH8RF1Lk3a5PMhLPenTdoUIVTAKEUlW/OxlKrCUCgEm80Gt9sNvV6PlpYWNDdnK1pPPAc5zc6Z9iPG/PQkND95MONz+788IPp4KSOzZLgn7we37mMF2x/LsllTwtmETdjLFo/H4XQ60draSpu0KQCogFGKRHIPFyC+bqJUKlNmaxECgQBsNht8Ph86OjqwevVq2O12WZFQLia86fZTDMpB2D74vbOitbi4iLfffjuhQVuv10Or1cp+D6QIWDrEhC0ej8PtdmPlypUZm7STKyOpsJ27UAGjFBSx5uNMFxCxCMzn82FkZAShUAidnZ1Yu3ZtQsQmR8DI/vOdBbbUKcT+Lw8UXcSEwkUwGAzYtGkTFhcX4ff7sbCwgImJCYRCISgUCuj1+gRxyyRshbhxEBKPx9MKEvndsCyLcDic8DPqPnLuQgWMUhBID9f09DQ0Gg1MJpOkCwRxfwDO+BRarVawLIvOzk5UVVWlPF+4vRQyCY/b7YbVakU4HIbBYIDRaOQvzBUVFQnHXkoBK4VwETa/9xKYLVtgMplSXEvi8TgWFxcRCATg8XgwOTmZUdgKceMghGXZtGueubiPAGdMnaurq2mT9jKFChglL5Kbjz0eDwwGA8xms6TnMwyDQCCAt956C0qlEl1dXaisrEy7vUKhSKliy7b/ZOGZn5+H1WqFSqVCd3c31Go1QqEQAoEA3G43xsfHEQ6HoVQqeWf6cDgs2vNUSIopXJlESypKpVKWsLEsC47jMDo6yrv763S6nN+/XAUxU8n/qVOnMDAwkPJ7FVtjo5WR5QcVMEpOpGs+ViqVkiIkjuMwNzeH06dPg2VZbNy4UZJPoZSiD7HtOY6Dy+XCyMgIKioqsHr1aphMJn5ys9iFORaL8am0cDgMm80Gq9UKpVLJR2t6df4RRikjrkKQTthcLhecTie0Wi28Xi+cTieCwSAYhkmJ2KQIW6EjOiJsYnPZMrmP0JL/8oEKGEUWYpOPhReVbAJDfAptNhtMJhO6u7sxNzcn2WRXroABwNzcHCYmJqDT6dDX1wej0SjpeSqVCmazGWazGT6fDw0NDaisrEQsFkMgEIDfn6v74dJQbOHKBsMw0Gq1aGhoSHicZVk+YvP5fJiampIkbPF4fEnaJqQ0adNJ2uUBFTCKJDJNPhaiVCpFU3wsy8LpdMJut6OqqgqbNm2CVquFz+eTJUhS16I4jsPs7CwWFhagUCiwbt066PV6ycfJdFyVSoXKykpUVlZifnoy530SxCoR84nK8hUu7sn7wXw+c1uAFNJFTAqFAkajMeVGQkzYQqEQAPC/O5ZlEQgEoNPp8o7GpE41IGQSNuBMxG6z2VBRUYG6ujoAVNiKDRUwSkakTD4WolQq+YsOcOaueWJiAg6HA3V1ddi8eXPC8EW5RRlyIjyz2QyLxYKenp604lWIPrBsPWC5ILW8/oPf+xj+79f+xP+7nJArEJmEjQwU9fv9sNlsCAaDAACdTpcSsUkVtkJFdMICkng8zpfxZ3MfSVfyT5EOFTBKCsJUyYkTJ9DT0yPZAYEITCwWg8PhwMTEBBobG9P6FOa6piV2zlNTU7DZbLBYLHyE995778lOOYoh9toLEX3JhQibofVsWq7chIsgdOLIB4VCwVeJarVatLa2AjgrbGQm28zMjCxhy1TVmCuxWIzfp9QmbbLO9pOf/ATr16/Hxz5Wnr/PcoQKGIVHrPmYmO1K9RokxRLT09NoaWnB4OBgxufKsZICUgVMOEKluroa/f390Gq1CdsXovy9XKykhMJVTAqRRixGGX3yeisRqOTtMgkbqSyVYh4tFxKBZSKdsE1OTmJgQDzypohDBYySsflYalVhOBzG6OgopqenodVq8YEPfEDSxSHXCIxlWUxOTsJut2ccocIwTEEiMKAwbvS5slTCVUiKIWBSbqQyCRtplwgEAlhYWIDP58PBgwdTIja9Xp/TucdisZyNpb1eLywWS07PPV+hAnYek2nyMSGbgAWDQdhsNiwsLKC9vR0NDQ0YGxuT/OWXuwZGRrDs378fdXV12LJlS8Kamtj+CxWBlYLlKFyEQqUQCfF4PK85b6ThWq/Xo66uDm63G3Nzc+ju7k6I2Obm5rC4uAiO42QLmzCFKBePx8NP96ZIgwrYeYiUyceEdAIWCAQwMjKCQCCAjo4OrFmzhm9KLpRThpB4PI7x8XGMjY0BAAYHByXN/ipU6i95P0ux/lVq8co3jSgn9Sx1f8Vw9iDl+0TYCBzHJQiby+XC4uIiWJblU5FCYSPflVxfs8fjoRGYTKiAnUfImXxMSBYwr9eLkZERRCIRdHZ2oqamJqUfRm5ZfCbi8TgcDgfGx8fR2NiITZs24dSpU5IHVxYqhZiLEEZuub8oFYrLhWKvgeUL8VZMRyZhE6Yi5+fneWEjGYlkYZOC1+vN6EJDSYUK2HkAES6Xy8Wn+qReCIiALSwswGq1guM4dHV1pU11SF0zy4awirG5uZkvBolEIrLXzLIJT7mWLisGLwH75mslPYdDhw6l+ERmStkKkVtGn41CNzLnGi0xDAOdTgedTofa2lr+cY7jcODAAVRWVqYIm1arTUlFJr+WeDxe1Ini5yJUwM5hkpuPAfCOB1LgOA7hcBinTp2CwWBAd3d31jvEfAUsFovBbrfD6XSipaUlZWhlLhFeuu1jsRhGR0exsLDAX1iIZ1+ywJdLFeJS03/kD/B+8g4EAgHMzs5idHQU0WgUKpUqQdQMBkPKxbfQa2BLHYHJhWQ0amtrU4QtFArxTdputxuBQIAXNpfLhZMnT0KhUCAQCKR1itm7dy++8pWvIB6P46abbsJdd92V8PNwOIzrr78ehw4dQk1NDX7zm9+gvb0dALB79248+eSTUCqVePTRR3HZZZchFArhoosuQjgcRiwWwyc/+Uk8+ODyyhhQATsHSdd8rFKpUmYoiUFcLGw2G+LxOBobG9HV1SXp2LlYPQFANBqF3W7ny+/TTVvOZZxK8vZEJKemptDc3Iyuri5+rYOsc3Acx69xGI3GhNYC4frX+ZAiJK4jQqLRKG+nNT09jUAggFgsBrVazQtbMBgsaEqsGAImNZqUQjqjZ2HEVlNTk7B9OBzG6dOnMTs7C5fLhcsuuwyBQAA33XQTbr311oRz/dKXvoRXXnkFLS0t2LJlC3bt2oW+vj5+myeffBJVVVUYHh7Gc889hzvvvBO/+c1vcPz4cTz33HM4duwYJicn8ZGPfASnT59GRUUF/vd//xdGoxHRaBQXXnghduzYgcHBwZTXUOj3vlBQATtHyDb5GMgeHZFm4NHRUZjNZqxbtw4ul0v2AEk5RCIRhEIhHDx4ECtXrsT27duzrkvkaj0lFK7W1lZs374dwJmLscFgSLhrFvYS+Xw+uFwuzMzMYHx8HJ2tTdlf1y33Sz7HTJRDGlEMtVoNi8WSUnQQiUTg9/t5cRsZGeENlIXRmsFgkJ0OLHTEVIz9yXlNxCty/fr16OrqwhtvvIE33niDj9iEHDx4EN3d3ejs7AQAXHvttdizZ0+CgO3ZswcPPPAAAOCTn/wkbr31VnAchz179uDaa69FRUUFOjo60N3djYMHD2L79u18tBeNRhGNRkWbrhmGwVNPPYXa2loMDg6meFuWEipgyxypk48BpI3ASE/V2NgYqqureRcLAFhYWEgZEFgISN/Y3NwcFAoFBgcHJX355QokwzCIRqOwWq2Ymprim6vJsdKJs7CXqL6+HiqVCmq1GvX19fDMTUs6dqboLFuBh+lfrpV0jHJDo9Gguroa1dXVCAQCaGpqgslkShC2iYmJhBRatrUhQqGdMwq9ppZPD9jCwgIfrZKITcjExATvQAIALS0tOHDgQNptiF+ny+XCxMREQlTV0tKCiYkJAGfeg4GBAQwPD+NLX/oStm3blrBP8n2bmZnBCy+8gNdffx1XXXUVNm3aJHlkUjGhArZMIaXw8Xhc0uRj4MyHWhiBkdL08fFx1NfXp/gUkucsLi4W7LxDoRBsNhvcbjfa29vR09ODAwcOFKWQIhaLYX5+Hh6PB52dnZJFUgxyfkvhhl5u5FpOT9JODMOgoqICFRUVKSm0dNV8Yv1XxUghlosglqoCUalU4siRI1hYWMAnPvEJHD16FBdccEHKdnfffTfuvvtuvPjii7j99tthNBpxyy234FOf+lRJvxPll9SkZIRUFIZCIRw5ciRtA7IYJP0Wi8UwMjKC/fv3Ix6PY9u2bejp6RFdDyhUVWEoFMLx48fxzjvvwGKxYPv27WhqauLdugvllgGAf30HDhyAQqFAe3s72tra8vqilbKIQzF4SUmOmy/ZBIdEGrW1tWhra0NfXx82b96MLVu2oKurCyaTCcFgEHa7HYcOHYLX68WJEydgs9kwMzPDR3G5Uk4RmMfjyShgzc3NcDgc/P/Hx8fR3NycdptYLAaPx4OamhpJz7VYLPinf/on7N27V/T4kUgEp06dQm9vL77xjW9gfn4eu3fvxurVq/GHP/xB9ustFDQCWyaINR/7fD5ZkUs0GkUoFMKBAwcyFkoIyVfAFhcXYbPZ4PV6ExqehRA3jnybXmOxGMbGxvgKxsHBQUxNTcma4JwJjuMkNTBLWf86H4o/cp1ena7/6sCBA+js7BT1OMxlQGYxBCzX/S0sLGRsYt6yZQuGhoZgs9nQ3NyM5557Dv/1X/+VsM2uXbvwzDPPYPv27XjxxRfxoQ99CAzDYNeuXfjXf/1X3HHHHZicnMTQ0BC2bt2K2dlZfi0zGAzilVdewZ133plybJZl8eMf/xgnTpzA+++/jw996EP485//jLa2NtjtdlxyySXYtWtXTq87X6iAlTnZBkhKIRQKYXR0FC6XCwqFImuhhBClUimpclEIwzD82ItAIIDOzk709fWlvaDkWrlIEBMuoSN4uVlJLccG51zSiMWYoJzO41BsQCaxjhIKm1arLdqAzGK6cKhUKjz22GO47LLLEI/HceONN2Lt2rX4xje+gc2bN2PXrl34/Oc/j89+9rPo7u5GdXU1nnvuOQDA2rVrcc0116Cvrw8qlQqPP/44lEolnE4nPve5z/HLENdccw127tyZcmyv14vXX38d9913HzZt2sQ/znEcGhsb8R//8R85veZCQAWsTBETLrkXUWH0097ejt7eXuzfv1/WRUVuBOb3+xEMBnH06FF0dXWhtrY263nnKmBC4SLNzskXpHzFkXC2+rE0Tc/lWo2YiWIImBjp5ojF43Fe2DweDyYnJxEKhfgCncXFRXg8HphMJmg0mrxvUvJNIWazkbriiitwxRVXJDz2zW9+k/+3VqvFCy+8IPrce+65B/fcc0/CY+vXr8fhw4eznpter8e2bdsSxMvr9eKpp57C7bffjptuuinrPooFFbAyQ+rkY4JYmoaULy8uLqZEP+RCLNeJIxs+nw8jIyMIh8PQarXYuHFjwliTbMeQIzIcx2FkZCSjcBGK5YUIFC8NGLnlfmDuVFH2vZQU2olDLkqlEiaTCSaTKeHxeDzONxQvLCxgYmIC4XAYSqUyIVozGo2y+sTyjcCS16VKTSgUwvvvv4+//vWvePbZZ3H11VcjGAyiubkZ77//Pv7whz/g9ttvL+nvmApYmSB38jFwtiyeOCB4vV5YrVbEYjF0dnaiuro6ZR+kErFQAiY8ZldXF6qrq3H48OGCDKlMhkRcgUBAcul9oSIwADBozr8KRCFy04iFduIoFEqlEmazGWq1Gt3d3fzjsVgswbjXbrfzriNCURNzHSHPl3rTlkw5jlIJh8M4duwY/vKXv2Bubg73338/fD4fotEoFhYWcPHFFwMozmBQqVABKyGk+TgSicBqtaKzs1NWqpAIGIm4AKCzszPjSAaypiXVcy2dgHk8HlitVrAsm+KNKDftmG2kCvFFnJycRHNzM4xGI1auXCnp4ljYNbDs+yEFHOmiMykFHjVpoq/llkbMtYgj3b6KDemdSuc6QgpH/H4//x0SilokEslrlEq5GflWVlbihhtuwNq1azExMYGPf/zjmJqaQjgcRlVVFd8HVsoyeipgJUCs+XhmZibhblDKPmKxGI4cOQKdToeenh5JjYVyxSX5AuR2uzEyMgKGYdDV1SX6pct1SGUyQkNfYXHG9PS05DRoId3oKfIp1PtWSiujTK4jRNicTifm5uYwPz8PnU6X4hOZ7SJfjqNU/v73v4PjOKxZswbHjx/H3r17E5rUdTpdyc2HqYAtIbk0H4vtY2ZmBjabDdFoFF1dXWhqym5tRJDqh5jM/Pw8rFYrVCpVVrHMJQITikxyxJWPoW8m70SWZTE+Pg6n05ngeyiWImIYRkoAljflvv7FPXk/rBd/NsH4eCnuwAstYIWI6DQaDTQaDZ99OHr0KDo6OqBSqXgrLaHrSEVFRYKwCd87r9ebMXNSaCNfh8OB66+/HtPT02AYBl/4whfwla98RfR9stvt+NGPfgSGYeDxeBAOh+FwOHDPPffgW9/6VsGrOeVABWwJiUajiMfjOQkXy7K8T6HFYsGGDRvgcDhk3wHJEReO4zA/P49AIICxsTGsXr06ZUFcDLlTlkkRR7qIS2z/UgVMLIVIrLPsdjvq6+uxevVq3upIaEwrvOBomMI1WudT/FEOacSqqioEAgE4HI4U5wzyfiVbIeVLOblmZNqnWq2GRqNBRUUFqqur+Z8R415ip0VcR5xOJ376059iYWEBr7zyCjZt2oRVq1YlFI8Uw8hXpVLh+9//Pvr7++Hz+TAwMIBLL72U3yfHcfwa17Fjx7B///601xqaQjxPyNTDla46kGVZTExMYGxsDLW1tRgYGODHqidbQ0lBSgTGcRzm5uYwMjICnU7HVxVKJRdnjenpaVitVtGIKxm5ERjZluM4OJ1OjI6Oora2Flu2bIFarUYkEoFOp0tIhyZfcDTa7DcKhTLwLXdqampSLKGI8bHf7+cbjAOBAI4dO8aLmtFoREVFRU5pxXK3kQIyNzIT416tVptgGh2Px9Hd3Y2bbroJY2Nj2Lt3LxobG/HII4/w2xTLyLexsREAYDKZsGbNGkxMTPD7ZBgGkUgEGo0GDz/8MAYHB/GlL32poO9XIaACtoRkuvCq1WrEYjH+zisWi/E+hQ0NDdiyZYuoT6HcdGCmCIyMURkZGYHBYMAFF1wAg8GA/fv3y1qQlxrlxeNxjI2NwW63w2KxyDL0lROBsSwLp9MJm82G6urqBM/HdKmk5AuOFAeObJyrApfOOePgwYNoa2uD3+9P6MMSlqvzEW6WcvXlIGC5nKNSqcTq1auhUChw9913i37HimXkSxgdHcXhw4dTjHzJ+9PR0YF3330Xb7zxBlasWMGnP8uh6IQKWJmgUqn4cQbJzbnpekvIhGK5x0kWPbKuNjIyApPJhPXr10Ov1/M/l2v1lC1CIsI1OTmJpqYmdHV1geM4yRcUqTPBSAp0bm4OGo0G/f39OZc5J1OMHjCpAlfqNKLUcnqGYUQbjIXl6sIhmcJZYuQP+cwthxQikF/RSikKhfx+P66++mr84Ac/SFnXJmJcX1+PF154AceOHeOjx+npafzsZz/D+vXrC1ptKhcqYGUCwzAYGRmB1+vlZ1Vl+4Ll4hSvVCr58SjC+V+VlZXYuHGj6NoFiaikCli6CIwI18TEBJqbm7Ft2zaoVCo4nc6U+UeZyCaQJAVqtVr51OCaNWvSbk8rDJeWdOXqpKrP7/fD6XQiEAggHo9Dq9VCqVQiGo3C7/eLTsyWSykLD5IJhUL8soAYcox8W1paJBv5RqNRXH311bjuuuvwz//8zynHJd+La6+9FjfffDP/+1lcXMTCwgJfNU0bmc8TxH7RZLyIy+VCU1NT0X0KVSoV/H4/JicnMTo6iqqqqoT5X+mOI7coQxgZxuNxvjijqakpJaosVNk9x3FwuVywWq3Q6/VYv349GIbBiRMnJO9bDLkGvvlEZ+l6wJYbubQuJFf1AWdHrkxNTcHtdsNut/M3bcnl6lIMfAmFFrB8qhqz9YAVw8iX4zh8/vOfx5o1a3DHHXdkPL/a2lq8/vrrUKlU/Eicjo6OhCxNqaACViIWFxcxMjICn8+Hjo4OPn0i585S7hoYy7Jwu928kAgLQjKRa1k8ES5y10ciLrHt8ym7B86U+Q8PD0Or1fJrd8CZ8uJCjmrJlXNt/StbGrFQLhxk5Ar5fXZ0dABInZgtNPAVW19LFrZCC1g+a3SlMPL9xz/+gWeffRbr1q3jC7S+853vpHgtAsAPf/hD7Nu3Dy+88ALq6+sxMzMDAJibm0uotCwFVMCWEDICZWRkBKFQCB0dHVi7di2/7iV37IdUARNWMprNZtTU1GD16tWyjiO3+Xl+fh7T09OiEVcy+URgbrcbw8PDUKvV6OvrS1lvkeLEUcocPkGuwJV6HSwbhS66SLYrSp6YTSAGvn6/H/Pz8xgbG0MkEuHtoArhmiFGMWeBAYU38r3wwgslR40//vGPcfz4cTgcDuzbtw9vvPEGXnrpJVrEcb4RjUZx+vRptLe3p/gUqlQqfm1KKtkEjDTqOhwO1NfXY8uWLbxtlRzkVBU6HA7Y7XZUVFSkjbiSkStgDMPwvWkMw2TsTyukF2IhWG5jVHKl0D6IUgUxnYEvsYMivX7z8/NgWRbz8/MJEVuujdn5RHTZZoGVEuLvqFAoEAgEMDU1hf7+ftx8883YvXt3qU+PCthSotFoMExcnn0AACAASURBVDAwIPoztVqNQCAga3/pBEy45tTQ0ICtW7fyTYikYVgO2QQseY1r3bp1mJyclFX0IVVkSLpIqVRi7dq1We8CpXohpovCCjXA8lwkUxqx0E70+ab8ku2gxsbGoFarUV1dzff6ZWvMziSg+URgXq+3LKIZMaLRKK688koEg0FcddVVePDBB2E2m9HQ0FDqUwNABaxsIGX0cki+8AudLJqamkQjoFyan9MViwiFq7GxkT+e3+/Pe00rGb/fj+HhYUSjUdTU1MBisUj60mcTMOKIUkyz2GIJXDmnEYuRQpQz2iQbsVgMer0eFRUVfGECIV1jNnBmNpZYY3Y+AlbOEZhOp8O3v/1tAMDdd9+NJ554AiqVCnfffXeJz+wMVMCWmHQXy1w9CoGzY0aId2CmNadcKheTI7B0wkXIZU0rneAFAgEMDw8jHA7zC9R2uz0vKylK8SmGgC3V/tI1ZpPJz6QxWzhHTKlUgmEYuN1uSY3ZQrxerywj76XGbrfjb3/7G1iWxbp167Bhw4ayiRipgJUJarVadgQWjUYRDodx4MABtLS0SOodyyWtQ3pwsgmXcPt8I7DFxUVYrVYsLi7ywkXOXa4XYjpisRjsdjsmJyf5ogDSeJtpmGGh17GWcwoyXRqx0IUxcmbYSd2f3Igp3eRncgO5uLiYtjGbrK+JHbMcnegJo6OjuPfeezE+Po7e3l48++yz8Pv9eOKJJ9IuhywlVMCWmEJEYJFIBHa7HTMzM2AYRrIFU64wDMPb0mQSLkI+AhYMBvn2gq6uLtTW1qZcCPMdkSIU4tbWVmzZsoVPGwmr11Y21mffWRJL3QNWDmnE48ePJ1yoNRpN0asQ86WQgqhSqaDRaKDVahMmQwgbsycnJxMas0nhiNPphNvtzihghXaiB4Abb7wRL730Eurr63H06NGUY5Lf35tvvgmv14vXXnuN/9mzzz6Lb3/72/jd735X8oZwKmBlgpSLfiQSgc1mw9zcHNra2rB9+3a89dZbRUuRxeNxjI+PY3R0FDqdrmhVhSStefz4cXg8HnR2dqKvry/tHbxCoZAdrQKJ7QSNjY288EciEXAcl3J3XYgCjuUcXUmltbUVfr8fbrcbDocjoYl9cnKSF7Z8LnTl7oVI1tSEZGrMDgQC8Hq92L17N44ePYqDBw9i/fr1+MAHPoDbbrst4TwL7USvVCpxww034NZbb8X1118v+nrId89isaC+vh6Tk5PQaDSwWCwIhUJoa2tL2K5UUAFbYtL9wjN9EMLhMGw2G+bn59HW1oaenh7+y0wiN7npkEwpHiJc4+PjaGxsRF9fH+bm5mR5IUoV1UgkktDQvWbNmqxfCrkCyXEc7zxSV1eXUJVJyR+xsvXp6WnMzs4iHo+n2EKRGwViCivlIliMFGIp+sBIY7ZOp0NtbS2ef/55XH311fj5z3/Oj1cRUiwn+osuugijo6Npz5NcHxQKBd555x3ccsstuOSSS3D48GGMj4/j8ssvx/PPP4++vj5ccMEF8t+wAkEFrIwhNlNutxvt7e3o7e1N+bLnUvxBBCD5CywUroaGBj7i8ng8sisXsxGJRDA6Ooq5uTm0t7fDYDBILs2VKmDEpJjc7Qpd6EuBWHqxEBFaqdOIYutgSqUSer0+wUWdRB9+v5+v7ltcXIRCoeCr+9K5ZxQjhVgu+/N4PKirq4Ner0/x7Cy2E306yM1CS0sLbrnlFgSDQXi9XvT19aG5uRl2ux1vvvkmbrjhBipglLOQLzkx9u3o6MDq1avT3qXmMxOMfOHSCRdB7ppWJqLRKEZHRzEzM4O2tjYMDg5CoVBkvBtMRoqZL/FEJIvncpxHKPkjlvITRh/C6j6he4bL5eLdM9RqNZ9+DIfDBU2VFzolmU8ZfTgcLvgA0EJRVVWFz3zmM9DpdPzacVNTU9ZxREsFFbAlJlO6hGEYvP/++1hcXERHR0fGdSBCPjPBsglX8vb5QCr+pqameLf9XC8gmUrjibVURUUF1q1bB71ej3379sneH13/kkfIkWiYbAZglhiQpHPPEBZBhEIhHD9+PKXJ2Gg0yjLxFVLoKslcBCybABTLiT4bJKL83ve+h8HBQVxzzTW444478Mwzz2Dnzp145JFH0NDQQNfAKGd6nUjJeGtrK9atWyf5g5FrA7TD4cDc3FxG4RJun6uACUeotLa2FqRiUiwC83q9GBoagkKhyGgtda5SqjRi+KPXZvx5srARtK3px9sQhEUQ09PT6O/vB8MwKU3GJA0ptITK1AZRDDJNY5ZCuu97MZzo5XDkyBHcfPPNOHLkCAKBAObn53HdddfhyJEjuPzyy0vuI0oFbIkR/rL9fj+sVivC4TC/SGs2m2V9IOREYMQbcXZ2FitWrJBcVZhLmpLjOIyOjvJ3fVJ61KQiFLBAIIChoSHEYjH09PQUvcGykD1gyzlCyyZc2RATtkyiRi6U6ZqMk9OQdrtdtBfLYDAUpew71zUwkipNRzGc6AHg05/+NF577TXMzc2hpaUFDz74ID7/+c+nHL+pqQmvvvoqXnvtNVx11VVQKBRwuVwJziWlhApYCfD5fLBarYhGo+js7OSbdGdnZ3Oa75UtAhOa+jY0NKC5uRk1NTWSUx5y+q7IsRYXFxGLxbI60eeCQqFAJBLh0609PT1ZxzosxZ3i+TAHLF/hykS2aC3T709KGnJiYgKBQAAsy/IFUkTYck1DCsnl+aVwogeAX//61xmPSYTua1/7Gn70ox9BpVJh586dAM7YaZHJzDSFeJ4RDAZx6tQpdHV1JfSHALm5cahUqrTTjJOFi5SPW61W2eNRssGyLCYnJ2G327FixQqYzWasXLlSlnhJERnSUuB2u7F+/XrRRmex85cjYMt1/atYacRiipYUQo4TWNdgTBA4KSlIQLwXKxaL4Z133oHRaOTd6YWzxJKbsotJuRr5kqnpPT09ePzxx1FRUYFgMIiFhQU8/fTTKdeuUkEFbInR6/XYvHmz6M9yKcgQe0464crnOOlI7rHasmULNBoNvF6v7CnOmUqlo9Eo38Td3NyMWCyWkELKBEk5FrLqjFJa8llbI8bAdXV1KWnIQCCAQCAgmoYUTn8uVBpSSgRWCp5++mncc889ePDBB6FQKHj3EGKHddNNN5XF94kKWBmRawRGxEgoXCtWrEjbsFuIqkKO4zA1NQWbzYaamhpeuHI9BjH0Tb4wCKsXSdl9JBLB3Nyc5H1nM/T1er3gOA4GgwGeuWnJ+5VDcnpxOax/lTryygUpwpZuvUqpVMJsNsNsNic8HolE+JErwjSkTqfj2zQ4jsspTV2uTvQ7d+4EwzBYv349/9oDgQBmZ2cRCoXKQrwAKmBLTqYPuEql4sc2SIUY7Y6NjWUVLuFx5A7PBM6W/M7MzGBkZAQWiwUDAwOoqKhI2TafKctAol8hMSomX5pcBmCKCZjH4+ErF5vqquEJ+iXvs1wpRBpxOQpXNoTCxgDosUi/9Gk0GlRXVyesswq9M71eL8LhMN566y3ZaUiPx1M26TgCy7JgWRaHDh3CpZdeCrPZDI7joNFoSup7KAYVsBKQydBXTgTGsixmZmYwOzsLo9Eo2SIplwiMYRheuMxmMzZt2gStVluwYxBRSvYrFKuUzFcchZWLLStqJe+HsByip1w5F8UrHfmkIYXVkCaTCcFgEOvWrePTkOmqIckfMvk5WwqxGEa+2fbp9Xrx2GOPoaqqCl6vF06nEz09PWfes1AIq1atwle/+tWs79FSQAWsjFCr1ZLWpoQX+bq6OhiNRlnzhOSsgRFXCzJOfMOGDSmmpWLkImDT09NwOp1Z/QpzjcBCoRCsViv8fj/amlZIfr5clpvAnU+iJQW5wiZ04ciWhvT7/RgfH0cgEMBDDz0Ej8eDlStXorW1FevXr0dnZyefpSmGkS+ArPvUarW45ZZbYLfb8eSTT6KrqwsbN27E2NgYfv/73/OCW+oeMIAKWEnIdaSKULhIqlClUslaDwKki8v8/DyGh4eh1WphNpuxatUqyZY3cvwKZ2dn+deQLiUpJJchlVarFV6vF52tTagxG2Q9l1CoHrByEjgqXtJJ17smxUZKLA35wgsv4D/+4z9gsVjw7rvv4te//jV+/etf82m6Yhj5Asi6T61Wy48Y0mg0eOyxx/ifffzjH8f995/5/FIBoySQrohDKFz19fV5u6lnE0pix6RWq9HX1wej0Yh3331XdlVhtu1dLheGh4dhMBhQV1eH1tbWrOIFSO89icfjsNvtmJ+fR0dHBxzsRgDDkp4rl3LqAZOyDkaFqzCEHCcQ19XltDakUqnAsiyuuOIKXHTRRSk/L5aRb7Z9kopdEjW+/vrraG1thU6nw2uvvcZfe0rtgwhQASsrkoWl0MJFSCcupKhBqVSm2DHlWlUoxsLCAoaGhqDRaHDBBRfAYDDg5MmTeQ2pFCJ835qamhCtvxynA2d+dtidmmrdVCVd1MopesoFKlyFRdu6BgtTUzk365fjNGZSLNXf348rr7wS3/ve97Bx40acPHkSwWAQ9957b8J2pYQKWAnINhMsF+GSE84nC6XX68Xw8DA4jktrxyRXwEhflxCfz4ehoSEASBFIuetaYnAch+npaYyMjKCurg5e08Xw+rI/T0zUAHnCRihngaPiVTiklOVLIVMVYrGMfKUa/Or1etx+++3YsWMHDh8+jB07dmDbtm38ay11+hCgAlZWsCyLSCSC/fv3y4q40s33yra93+/H0NAQ4vE4uru7M94J5iJgpFSfVP1Fo1H09PSIHkdM8OQwNzeH4eFhmM1meIwXwSOvG0EUImxEyHIVJ2F6cakEjopV8Ugu5ojFYhkrcjORScCKYeTLcVzWfSbT29uL3t7enF5fsaECVgKS71yENkwsy2Lz5s2S1oIIyfO9srG4uIjFxUUcP36cNwDNRi4pxHA4jKNHjyIQCKC7uzujAWimlGMmPB4PTp8+jYqKCrj1F8JdGIMRALlFYOVA0CxtMChFHlKqEOUSDAbTVvUWy8hXbJ/p4DiO/14qFAreULlcoAJWQoTCVV9fjy1btuDw4cOy0xFEwLKJ3uLiIj+2RaPRYMuWLZI/jHIELBwOY3JyEh6PB2vXrkVdXV3W48hNIcbjcRw+fBgsy2KuYrvk50mBChdFSLa+sFxTiKQIItN3oxhGvmL7TAfDMAU34y4k5Xtm5zAcx2F8fDxBuEi3PhEjOR+abFWFwWCQ73/q6upCbW0t9u/fL+tOSoqACf0K6+rqoNVqUV9fL2n/UgUsFApheHj4zGj6yn8CCmwMkI94lWr9K1m43BUNqApPleRczjWkNDXnE4EB5bGWlI6XX34ZgUAAWq0WNTU1qKqqQkdHR9mIWnmcxXmG1+tFMBhM8Q8EpDczC0knYKFQCCMjI/B4POjq6sLatWsTvixyCj+Ea1rJiPkVer1evmxX6v4jkUjan0ciEYyMjMDtdmPB8EFgxUbJ+5bCUkRdhRa4TBGXuyL1Z43zRxEyyHceOR85Oh0Ax3HQeY4mOGhUVFSkfGdyjcDyHYJZLMh14ac//SneffddPPXUU1ixYgXcbjf8fj/eeustDAwM0D6w8xWLxQKDQbyZNpcJy8kDJ8nIkfn5eXR2dmLNmjUpHzQielLL8lUqFRYXFxMeY1mWn7ac7FeYq5VUMkJxbG9vhy1ygeR9yiG5YGOpyLUH7FT1hQCAlbHE8x1TdcMEcU9HZ7X4e0ejtbOQiGtz65nPN/E79Hg8mJiYQDgchkqlShC1aDSaU0Ti9XpTHDvKAaGAvfLKKxgdHcUf//hHzM3N4fvf/z7a2toAlEfkSAWsBGQz9M01AotEIrDZbHC5XGhvb0dvb2/aYxGBkSpgwiIL4dpdQ0ODqF9hvn1jQmf9lpYWeE0X4z2X5N3lTC59YoWc0iyXMZV0C7F0JEdrrfa/I2aqOe+iteR0ITHmNRgMWLHirPVYNBrl/Q4nJyfh9Xpx+PBh3p2e/NFqtRm/6+XYAwacvT5xHAedTodgMIh//OMfuPjii/GnP/0Jd9xxR4nP8CxUwMqMXEaqMAyDyclJ2Gw2tLW1oaenJ2uToVyhVCqViMVicDqdsNlsqK2tzVjmPzoXR0VNN46PJTZi9a00iW5PIjCO4+B0OjE6Oor6+np4TRfjuEfyaRYFKX1i5dL/ZVLm56jfav87AEDlc8HoS71j8DeUZzl1PkgdjklQq9WwWCy8+Ph8PmzevJmP1nw+H6amphAMBqFUKhOc6Y1GI3+zJ2eUyvz8PD71qU9hdHQU7e3teP7550XL75955hl8+9vfBgDce++9+NznPgcAOHToEG644QYEg0FcccUV+OEPfwiGYfDCCy/ggQcewIkTJ3Dw4EFs3ryZF7B/+Zd/QTgcxtVXX42nn34af/7zn1FbW1tW88sYrhz8QM5D0q0nORwOcByHlStXZt0HSa85HA6YzWZs3LhRcnf80aNH0draKunDyHEcxsbGMDw8jKamJnR2dqateEwWLCn0rTTxzt3hcBgWiwWj0XWy97PUSEk3kugsncDlkkIk6UMxchUwIly5sJxFTa54ifHWW29hy5Ytoj+LxWJ8tEb+xONxvPLKK7DZbAgGg3jkkUfQ1dWVcT3s61//Oqqrq3HXXXfh4Ycfhtvtxn/+538mbDM/P4/Nmzfj7bffBsMwGBgYwKFDh1BVVYWtW7fi0UcfxbZt23DFFVfgy1/+Mnbs2IETJ05AoVDgi1/8Ih555JG0w3b/9Kc/weFw4LOf/Wza5Y9SQCOwEpHOkFatViMQCGR8biwWw9jYGCYnJ9Ha2oq+vj4sLCzIsnaRGoERv8KKigpUVlZizRrxL3wuwpX4XA20tT3QAuAAtMEN+0J5zUkilLLMfi+zEx1YKNj+8hEugnFKXITLWdgKIVxSIP6EwhtFjuNQW1uLZ599Fu+88w7uvfdeDA8P48UXX+RNdpPZs2cPXnvtNQDA5z73OVxyySUpAvaXv/wFl156Kd/Xeemll2Lv3r245JJL4PV6eW/E66+/Hr///e+xY8eOtN9nAJiamsKrr74KhmHQ0dGBrq4uzMzMoKOjI5+3pKBQASszMgkLGfJI7F+2b98OpVKJhYUF2etm2daokv0KVSoVjh49mrJdPsKVjTaLO+WxUopaqfvD9jI7C7avQghXNsSErdSipm1dcyZias2+rRRySWAxDIPOzk50dHSgublZ0myt6elpNDY2AgAaGhowPZ06OVzM/HdiYoIvskp+PBMejwd33nknPB4P3G43XC4X5ufnUVNTg/fff78sKhABKmBlh1gZvbCgobGxEYODgwlFE/kUfiSTzq8wFoslbF9M4cpEKUSt1MIFLD/xSoeYqC1VwYi2dU3BHdTz6QFbWFhI8CH8yEc+gqmp1IrQhx56KOH/S+GGYbfb8eabb+LUKfHouhzEC6ACVjKkTGVOnv8lVu1HnpNvBBYIBDA8PIxIJCLqV0i8CkslXJkQEzVP2ICFYPpR7lIphHjlu/51rohXOopdMCJMFxY6csjHyNfr9SbYOL366qtpt12xYgWcTicaGxvhdDpFDQKam5v5NCNwxqj3kksuQXNzM8bHxxMeT2fgS2hoaMBNN92EAwcOoLq6GjqdDhqNhp8mXS5QASsziIBNTExgdHQ063Ri8pxcIrBwOMy7dGTzKzzh8KOyuTg9WMWgsiKAyorUtUS50ZqwArEUkVghxQsAHG0XpzxWjqIGANrAmSGnuUZnYutc+QiOGPlEYHLK6Ilp71133YVnnnkGV111Vco2l112Ge6++2643Wdu6F5++WXs3r0b1dXVMJvNePPNN7Ft2zb86le/wm233ZbxeAzDYGJiAl/4whfwsY99DBzHIRKJoL+/H9dddx1NIZ7viP3yOY7D3Nwc3G43zGazqFOHGKTEXQ4sy2JqagpTU1Po6upK61dYjhFXPuSTgizk2BUpyBWvXCsQy03UYqbEm6hchOw9pw9a93sJfVk6na7gAhaPx5dEwO666y5cc801ePLJJ9HW1obnn38eAPD222/jiSeewC9+8QtUV1fjvvvu4ysiv/GNb/AFHT/+8Y/5MvodO3Zgx44dAIDf/e53uO222zA7O4uPfexj2LBhA15++WX85S9/wZtvvolvfetb8Pl8CIfDcLvdkoy/lxJaRl8iYrEYn8Ijc6xsNhssFgtcLhcuvDB9qbQY+/btwwc+8IGs2xG/wqmpKej1egwMDJwXwiWXfNfViKhpfvJgzilEMQHrqEpfgZhvD1g2lkLUksVLjExCRqIujuPO+GUKyteDwTMzduLxOFauXMn3Z+Xj6+dyubCwsICuri7Zz7322mvx+OOPo729PefjFxoSWf3ud7+Dw+HAl7/85VKfUkZoBFZCOI7D7OwsrFYrKisrsWnTJmi1Wuzbt6/gxyKl906nEytXrsQFF1wAp9OZIl7nu3AR8llXK0SDc6FTh/lSbPGKbfl44gMn/2/abbWBuRQRS04XMgwDnU4HnU6Huro6/nG32833WjqdTgQCAcTjcdkuGvx555lCTDcLrFSQuYIzMzP4yU9+gsnJSfT398NisaCyshK9vb1l5R5CBaxEeL1evP/++zAajdi4cSN0Ol1RjsOyLBwOB2/JNDg4CKVSCb/fn5J2pOKVmWzraoVKJeYiXr/6HzW+VATNW3LhIqz+IP9PvlFYIGrCtKLcni6dTpdQVs5xXIKLhtPpRCgUSvE8NBgMKenHfFKSfr8fRqMxp+cWC9JL2tzcjHXr1uHEiRPYt28fgsEgTpw4gR/84Ae46aabCp6KzRUqYCVCrVZj/fr1ohU9DMOAZVlZjcnJz0n2K0wuvRdWIVLhyg8Src1zZ9Nf1Uxuxo3lEnktSbownXilQyBqBLlzkMUuvAzDQK/XQ6/XJ1T3RaNRPv04MTGBQOCMQ71er+dFLRQK5XTzSVZu5HzHlwKGYRCPx7Fz507s3Jn+s1gO4gVQASsZBoMhrech8UPMZSqzWq3G1NRUVr9ClUoFbW0PFa8i8ZarBwCwpWZI8nPOF/GSLVwFRE7koFarUVVVlZDmY1kWi4uL8Pv9cLvdmJmZAXDGtYKImslkgl6vlyRO5VDJJ4TjOCiVSrz77rvYs2cPAoEAdDod9Ho9otEorr/++oRm6VJDBawMkTphWYhSqcT09DTGx8dhsVgwMDBQUL9CijT+fIhc7Dj0djBphSy5gON8EK9SChch39SXQqHghQo4awtlMBj4aM1ut/Ojh4QmviaTib+ZlJNhKZaR79e+9jX88Y9/hEajQVdXF55++mmYTCYolUp85zvfgVarxQUXXIBYLAa/34+pqSnZ1c7FhgpYiSjkSBVSCQUg43oaFa7icVa4xMkUkUkVr3QViL/6H2kjcaTgaLu4KCI2230xjNGo5PE9xSKfsncxSBGHRqNBdXV1Qpk5y7K8kS8xq45Gozh27BgOHToEhmFw8uRJ9PT0ZBTVhx9+GB/+8Id5I9+HH35Y1Mj3wQcfTDDy3bVrF6qqqnDLLbfg5z//OW/ku3fvXuzYsQOXXnopdu/eDZVKhTvvvBO7d+/G7t27+f395je/Kbuy+WSogJUhUkeqeDwenD59Gmq1GrW1tWhpaREVLypcxSObcCVDhOxynInAChV5fWlnatWkkMdfqsq6DUGsLwzILTpzr/7wmchkbg42mw2xWAxarZaPSLJV/BW6yycej8vKbEjZXzrxUSgUMJlMvB0bcOb1tLS0IBaL4fDhw7j//vsxNDSEhx9+GB/96EdF91MsI1/h8QYHB/Hiiy/yUeFXv/pV3H///RgYGEBjYyMsFgtMJpPocNxSQgWsDMkWgRG/Qo7j0NvbC7PZjKGhIVpVuMRkE69TtjNpxHQsZdpQqnhlQk7D8+ma9ejs7IQJSLmAC/uzxCr+TCYTDAYDP+S00I3HpXTiYBgGTU1N2Lp1K44cOYJf/epXWZ+zFEa+Tz31FD71qU/x/z906BAOHjzIX2dCoRDcbjfefPNNaiVFyZxCTBeBCf0Ku7u7E/LgQtGjwlVc5EZdYpTLmle+iImaElEo/t8aUDLp+rOEFX8Oh4MfKaTVahEOhzE/P5+whpQrpRYwgsfjSRixUkoj34ceeggqlQrXXXcd/9jjjz+OkZGRBLGKRCKSnIGWEipgJSSToa9w4GUoFMLw8DD8fj/vV5j8IVapVFiImbBAxatoFEK4Ck0h17/ypbH+zPszNTUluzw8XcWf2+2G1WqFy+XC6OhoQgqSRGtSm46BwgsYx3E5lcIn20iVysj3l7/8JV566SX89a9/5d/DSCSCSy65BO+99x4/vFalUhV07bBQlN8ZUfihlpFIBFarFW63G11dXVi7dm0G26fy6Y4/18hHuLKlEZcCOetfuUCEiyC3hzEdCoUCWq0WBoMBPT1n1g45jkM4HIbP54Pf78f09DSCwSCUSiW/pkbK2MWEqlwacMvByHfv3r347ne/i7///e8JkVYoFEIgEMDXv/51XHjhhdDr9VCr1airq8ONN95YgFdfOKiAlZB0ERgAzM7O8tNPV69enfYOk6YLi0s5Rl3lRLJ4AYUdWZIshgzDQKvVQqvVpk1Bjo+PJzQdC4WtnARMLJISo1hGvrfeeivC4TAuvfRSAGcKOZ544gkolUp8+tOfhtFoxOzsLBYXF+FyuXhxLBcneoCa+ZaUaDQKlmX5/xO/wvHxcajVamzbtk3SnSwVscJTSOEqRASWrYQ+U4RVjAhMTLgIDocDKpWKLzzIB4/Hg6mpKfT2yp8NRpqOSbTm8/ng9XphsVhgNpsTXOpzuSBzHIe3336bFw057N69G/39/fjkJz8p+7nFRChODocDwJnlCZPJBI1GQ9fAKKkQv0KSnx4YGMDJkyclp2H6Vprg8/kwOjqKdevWAaCilivFiLiKlUaUIl6FJpNwEQqVQgTyS/klNx0DwMGDB9Hb24tAIACfzyeagkzne1jIstB9JwAAIABJREFUcytHI1/gTIQ7NTWFF198EW+88QZmZ2dhsVgwMTGBnTt34p577ino7zdfqICVEI7jMD4+zvsVkonLsVgspwnLwuf0rTxbuhyPx2G32xFS1Yk9lYJzN1X4+EtL/7qKmULMF2EVZG3tWUd74jbh8/lSfA+FwiaMQPIVsHJydQfOvtf/+Mc/cODAAfT19eHw4cO47bbb8MMf/rCsLKQIVMBKiN1uRzAYTBlcKTTalYpY7xjLspiYmMDY2BiamprQ25y4sG21WhFWS8vDn6ucq8IlpFAR2uMvVeErV85mbT4upOgs1d2+SqWCxWJJEBVhClLopFFRUQGj0ciX9Oci2OUagQHA2NgYNm3ahPXr12N8fBwXX3wxRkZGcPDgQVx//fUFby7PBypgJaSzs1M00srl7lUoYGRA5sjICOrq6tIa+iqVSlQqvQlltcD5k35cSvEqh2rEdEhdI/vKlbMJ40bUanWCowYxsC2XFGK+iKUgSRWk3+/H3NwcAoEA3nrrLSiVyoT3IlsKshwjMHLdqa2tBcdx0Gq1WFxcxG9/+1u8/vrrOQ3tLDZUwM4RFAoFOI7D3NwchoeHYTabMxr6AukdP4TpR8K5JGrnQtRViv6vurq6hMq/SCTCp91cLhcWFxf5cRzE1dxoNOYlQOW03gIkVkEqFAqoVCp0d3fzKUix0StiKUi/3w+z2Zz1eMUy8r3vvvuwZ88eKBQK1NfX45e//CWampoAnCnbj8fjqKmpgdVqxbe+9S1cccUV+Ld/+zcA5TNKBaBViCWFZdm0nof79u3D9u3bJUdjHo8HBw8eRH19PXp6eiTZvTidTgSDQXR2dmbdluM4OBwOnDp1Cl1dXWhra8OpCXG3hXKl1MKVawQmVoEoFLB00RNZ/ypUClFKAUc8HsexY8eg1+v5i7pYObtUR41CVjSyLIt33nkHmzdvzntfADAzM4PFxUW0t7enPR4ZvUIqIa1WK372s5/B4/HggQcewKZNm9DV1ZVWpL/+9a+jurqaN/J1u92iRr6bN29OMPI9dOgQqqqqsHXrVjz66KO8ke+Xv/xl7NixA16vlxfQRx99FMePH8cTTzwB4OzSBvFYraysBMdxOTdtFxMagZUpwvlemfD7/RgaGgLLstBqtdiwYYPsY2Rjbm4OQ0NDqKyshMFg4AVvuURqpRYuQjmmEaWmDx9/qQrfltDDqlQqoVarsWLFCt4DMXktSeioIRS1ioqKlBu2QprvxuPxgl6As6U3hSnIhoYGAMCGDRuwadMmfOYzn8GpU6fwwgsv4OKLL8att94quo9iGfkKo79AIJAwEPfll1/GM888g66uLlRXV2N6ehqtra1Yu3YtLr/8csn9a0sBFbAyhfghphMwYi8VCATQ09OD6upq7Nu3T9aCcrZiEeJ2r9FosGHDBuh0Ouzfvz/jPpOrH99++22YGvsknU8xKBfxOhcg2QLix8cwjKggJKf90q0lhUIh+Hw++Hw+TE5OIhwOQ61WpzQeF7IgpBijVOSgUCjQ0dEBlUqF++67L+v2xTTyveeee/CrX/0KlZWV+Nvf/sY/fuzYMXR3d+PCCy+E0WjE3r178e677+Lw4cN49dVX8d3vfpcX5FJDBayE5DITLBKJYGRkBPPz8+ju7kZdXR2/H6lRW7ZjLC4uYmhoCNFolHe7zwXiJi4UNY7jMDMzA1e4uI7W57JwyUkfFhK1Wg2WZfk/APgbIKGoSbmJEpazC+/oI5EIn26bm5uD2+3G3NwcXC5XglN9LuswsVisoBEYiSLl4vf7Exz6S2Xk+9BDD+Ghhx7C7t278dhjj+H+++8HAOzfvx+vvvoqf47XXnstPvrRj+K///u/8YlPfAJOp5MKGCUzKpUqYX0sFovBbrdjamoK7e3t6O3tFTX0lSNgyREY8V5cWFhAT09PQp9MLiSfn8fjwalTp6DX69Hd3Z3y5S9E+rHchWup04iFWP8iYqhUKhOEg2VZcByX8Dep0OM4DtFoFAqFgv8cSBEPjUaDmpoa1NTUAABOnTqFuro6KJXKjD1aUtbVWJYti9EsCwsLCU70pTLyJVx33XW44oor8MADDwAAuru78d3vfhdXXXUVjEYj/H4/ZmZm+BuXchpySQWshGQbqRKLxcCyLMbHx+FwONDc3Izt27envRDIneRMto/H4xgdHcXU1FRW78VcEEZ0a9asSbj7BM4OLWwyx3H06FGoVCqYzWaYzWZ42UqxXaZQ7sJVjsiJ0nbfnHqhJp9DpVLJu8k4nU6sWrUKOp2OX/gXRmukEIA8N5uosSzLpxWFF33hutrc3FzKuhoRNuG6WiwWK8tRKpkolpHv0NAQb5C8Z8+ehO/8Pffcg0ceeQTPPPMMKioqcOjQIdx8882oqKjA6tWr+ZuLcoAKWIlJZ+irVCr5kvgVK1bwLh2ZkCtgDMNgcXERb775ZlZxFCJ1nS0ajSIUCuHdd99NG9GRCxzLsjAajRgcHEQsFoPX64XX60XYd8ZMVKlUwmw2w2Qywced7Z+hwlV6FhYW+Ehpy5YtKSJBBEwYqZHIXywFKfwMpiujF66rkTUi4bqax+PBxMREwrqa8BwKcYOWawQmR8CKZeR711134dSpU1AoFGhra+MrEAGgr68PP/vZzzAyMoJgMIj77ruPP9/HH39c9ustJlTAygzSyzU2NgatVpvi0pEJqQLGcRxmZ2cxPDyMWCyG7du3S047SlnjYFkWY2NjmJiYgEKhwODgYMr2QuEi+yXbqNXqhDQScEYMFxYWMD4+joWFU1CpVHjb/RFJ51xuyEkjJpfQS+n/khNZ5ZNijEQiGBoaQiQSwbp169K2bqSLtsRSkEBipBaLxSSLTbZ1tampKfj9/oTG43zW1fKJwKQ2MdfU1OCvf/1ryuObN2/GL37xC/7/N954o+iok82bN+Po0aMpj//2t7/NeFyVSoVVq1ZJOsdSQgWsjHC73RgaGoJOp0NnZyfC4bAs92cpAkaOodfr0d/fj3feeUfWlFtyDLHzIg4gVqsVDQ0NGBwcxIEDBxIuQJmEKx0cx/Hl1w0NDVi/fj2USiUGo3489j/GjM+l5AcRw/379/NrTiaTCYFAAE6nE52dnaivr88pohGmIAlEzOLxOCYnJxEKhaBQKBLWg8m6mtSCDLKuFolEYDab0dramrbx2GAwJDhqZPpuLIWAUTJDBazEMAwDr9eLoaEhAODXiFwuF/x+v6x9ZRIw0i/GcRz6+voSSprlkK703u124/Tp0zCZTNi8eTPfu0P6S8jfZF1EakUVKeU3Go3o7+9PEM58x8ufz8itUhwcHEQwGMTMzAxOnDgBhmGgVCoxOTkJn88Hk8kEs9ksazqyGAqFAj6fDydPnoTFYsG2bdv4qF/4+RGmIYnrR7ZiEWHKL533YSAQgN/vx+zsLGw2G2KxGHQ6HS9qZKyIsG9KLlTACgcVsBIzNDQEl8uFnp6eBIsYUsQhB5VKhXA4nPBYKBSC1WqF3+/n+8XyIVnAAoEATp8+DY7jsHbt2hRhJKX0wtSjlC99MBjkU5xr1qzJWXDLlVyqEZPTh0s5RiUej8PhcMDn86G/vx9Go5H3BfR6vbxHYjAYhEaj4S/2ZrMZer1ekqjF43FYrVZ4PB6sXr06pdgnOVIjf2dKQQrTl/F4PONNj0Kh4M9buK4WDAbh9/tT1tXC4TBmZmZ4H0ipwu31etHR0SFpW0pmqICVmPb2dnR2dqYtiZeDSqVCIBAAcCa9YbPZMDs7i87OTvT19aX9guXS/ByJRDA8PAyv14tVq1aJCiPHcVCpVBgaGuKHCGbrmyHnTfrcslU8fXVXGP/nD4VxajgXEEZWhRS4t956C21tbVi1ahX/WRH6AoqtOXm9Xn6iL5m3JawQFN7IzM7Owmq1oqWlBT09PVk/j5nW1dIVi4RCIWi1Wv6GSsqNFMMw0Ov10Ov1Ca8xHA7jnXfeQTAYTHiNwvRj8msk0AiscFABKzEajUY0JUecOORAesfsdjvGx8fR2tqKwcHBjF9UIkhSc/kMw8DhcMDj8aCzsxNr1qzJWKCxZs0aeDwe3m0hFAqhoqKCL5M3m82oqKgAx3GYnJyEw+FAa2srtm7dWjZjy881pKYPyXaf/f9Ooadns+SUbXIvF3DmxoS4boyNjcHv9/NFF4FAAGq1GuvXr5fk4ZkJYdRFCIfDGB4eRigUQltbm2iklslZJN1rVKlUCT6IyTPFyBKAwWDgRY0sGUipQiyWkS/h+9//Pv793/8ds7Ozefd8lgoqYGWK3AiM4zgsLCzA6XSira1NUtm98DjZtiUCMz09jfr6etGSe7ECjYqKCtTX1/N3r8K0k9fr5b/osVgMRqMR7e3tZTsnqdCUozeiGH19+VuBqVQqVFVV8b9bjuMwNjaG8fFx1NTUgGVZHD16FCzLwmAw8C0Tcox/kyGuLyMjI2hvb0dDQwN/Ac9UAUnIVCwitv6VaV2NTH/+4he/iKmpKUxNTeGDH/wgLrroIlx00UWi5//www/jwx/+MG/k+/DDD4sa+T744IMJRr67du1CVVUVbrnlFvz85z/njXz37t3Ll9E7HA68/PLLWLlyZQ7vbPlABazEpIsy5EQfLpeLryy0WCzo7u6W/FwpwzNdLhdOnz4Ni8WClpYWmM3mhC9v8gJ7pgINYdpJr9fzd6MrV65EJBKB1+uF0+lEOBzmm1KFkZrYfs/VNKKwhL4Y41OWcg1NiLBIY3BwMGVti1zwSVoxFovxFZBE2LIZ/IZCIZw8eRIqlQoDAwMpVbOZKiClFItEo1FJZffCdbWmpib8/e9/x5VXXolvfvObGBsbw+TkZNrnFsvIFwC++tWv8m4byxkqYMsYn8+H06dPQ6lUYt26dVCr1Xjvvfdk7SNb5eKpU6egVCqxYcMG6PV6jI6OJggeKXmWU6BBLKuIEbEwnZIuUhsfH+dFjQiayWTKyYvuXKWQ/ofF8FKMxWIYGRmBx+NJW5gjvOATOI7jXTfcbjfsdjsikUjCDY7wszA+Po6JiQn09PTIco1IJ2rkb6HAzc/PQ6lUIhqNJnzupXz+fT4fNm3ahG3btmXcrlhGvnv27EFzc7OsyRXlChWwEiOlByp5m2AwiKGhIYTDYaxatYoXAJZlZRd+iEVg4XAYQ0NDCAQC6O3tTUiJkO1z6eeKx+MYGxvD9PR0VssqsQKBZKcFh8PBixowKOt1lwtLnUbMRZiOHz/O3zSkK0zIBmmcb21tlVSkIYRhGBgMBhgMBt5ENrkCcnJyEoFAAJFIBHq9Hq2trdBqtXm7biQLUzQaxenTpxGJRNDb2wulUsnfxAHZnUXINiQtutRGvouLi/jOd76Dl19+Oe99lQNUwMqY5AKLaDSa4ERfW1ub8KEm49zlIIzAYrEYRkdHMTMzg66uLqxduzblS6NQKBAIBPg7T6mNyNPT07DZbGhsbMTWrVtzugiKOS0QUWtqGsN/HVje+fxypampCT6fDw6Hgy9MMBqNCetU6dJpoVCItyzq7+8v2Gwv4Q1ObW0tbDYbIpEIVq9eDeBMqbrVak2pgDSbzTAYDDl9/mZmZmC1WtHR0YEVK1akfO6FEVqyY7+wWERoHbfURr5WqxU2m42Pvsb///bOO6zJs/vj3zBlI6iAICJ7yZDh6FLUUq2iVquob8VZ29pqrQuLWrRVrB12aJ21Wluk1dbxVsQ96mApbtlD9giQAIGQcf/+4Pc8bwIBAgQBvT/XxdUSniTPE5N87/uc7zknLw9DhgxBfHx8t+kw3xaogHUxyoxU4XA4bLzcxsZGzsrcUdTV1SEWi5Gbm4unT5/CyspKoXOR+WAaGBigsLAQ8fHxCt2Ejc+rsrISaWlpMDAwUJiL6ChMQS2XywXwfAqYovxX4/xVZ4T8GBobEyQSCeu2KygoQHV1NdvLkhEJfX19FBYWoqCgoM2hvLbA9GE0MzODr68v+75t3IasuroafD4fOTk5rANS1tbfkgjX19cjOTmZNUk09x5uLa8mkUiwd+9eFBcXK/X57YxGvoMHD0ZJSQl7fxsbGyQmJlIXIkX1aGhoID8/H0VFRejfv3+ThHdHYYo0c3NzYW5uDn9//yaOr8ZJbV1dXXh7ewMAG85j3IRMnQ1T78V8UDqrEJnpuch00Ue6yp/imdAZYcSOGDRkxVBRF3p1dXUYGRk16Q5fU1MDPp+P3NxclJWVQV1dHcbGxqiqahiTY2hoqLLuKWKxGGlpaaitrW2xDyPQUJIi64AE/ifCfD5foQgztnemhRkze6+tMKKWlZWFpUuXYsiQIcjMzFTqvp3VyPd5gkMUtUKnPDMIIaivr29yW1lZGR48eABTU1O4uLgovXO5efMmhg8f3uoKj8/nIyUlBRKJBKampuxohcbnIWvQUCZUWFNTg/T0dPB4PLa+S0dHR26n1tFdGNOMODMzE2ZmZrC2tmaFvae6ERsLWGsOxNZ2YIoETJljGh+nSMCaQywWIyMjA1VVVXB2doaenh7rKGRyVSKRCLq6umz4sT3vByaUN3DgQFhYWKgsGiHrgKyoqEBJSQk4HA6MjY1hZGTECltbwqASiQS7du1CVFQUfvzxR7z00ksqOVdKA3QH1sUoGvqYmpoKbW1tmJubo2/fvm36gLdWmNzYAMLsomRpj0FDKpUiPz8feXl5sLa2hqenJxvvr6urA5/Pl3OQMV9izI+yK/OqqiqkpaVBW1sb3t7eKsupvGio2kLPiIq1tbVciFvRyJPmHIWy7lJF4WihUIjk5GSoqal1SjiaGdHCFN57eHjAxMREaQdk4/NNTU3F0qVL4e/vjxs3bkBHR0el50uhAtYt4HA4bE9BiUTC9oHLyspqVzcORYXJjIW5rKyMnc3F1LPI1rm0VbgAoKysDBkZGTA1NYWfn5/cc8saL8zMzNjnEQgE4PP5KCsrQ2ZmJsRiMVvAyvzIPo6s9d7R0RGGhoZtel26O82FEVU9PkXVj9VavVVjmnMUMoscWXcpk2Nlut8XFRXB0dGx0/I1AoEAT548gb6+vtxcs+bOl+kswoTPtbS08PTpU5SUlKCgoAAXL17Ejh07MGLEiE45XwoVsG5BcnIyKioqmiS729sPUfY+zKRcZmfU2KAhO5W5rcJVXV2N1NRUaGlpwdPTU+maLNkvMdmVOZNDYVbzEokEenp6bL7C1ta21WnRz2tRc0dRtclDdgJzR00azS1yhEIhSktLkZqaytrRc3JyUF5ezgpbW5roNgfTFaSwsBDOzs6t9ilsae5YTk4O/vrrL5SXl0NLSwuhoaHYtWsX3NzcOnSOFMVQAesGWFpaKnQWampqss15lYURJKaNTkZGBvr27auwtRRj7a2oqEB+fj7rHmvtC0EoFLIW5caFyO2Fw+Gw4ab+/fuzea60tDTo6enBxMQEeXl5ePr0KWvhZr7EVGlsochTW1vbJDzG4/GQkpLC7rg74/VnWpeVlpbCw8ODfY8x3VqY1ky1tbVy07qZ7vfK2uSrq6vx5MkT9O7du0PXIhaL8dNPP+Hvv//Gzp072SJlJhdM6RyogHUDDA0NFdZvtXcHVllZidTUVHZopaKdEWPQ0NHRgbOzM/h8PrKysiAQCKChoSEXytPR0QGHw2lSiKyoka8qkN3Z+fj4yJ2/VCqVc48x+TtZUQOa1sv0BJR1Iz7LFlDJyclsA2bGlCESieDq6tpk3Imq4PF4SE5ORr9+/eDn5ycnRlpaWujTp49cGFEkErFGkaysLNTU1LRa+yWVSpGdnY2ysjI4Ozt3KCT95MkTfPTRR3jttddw/fp1uferKhZ3lOahAtaNaWtHeoFAgNLSUqipqWHw4MEKv2AU5bkaW4xFIhHbwqm4uBgCgQBAw86rb9++8PDwYEVNlYhEItbFJtthRBY1NTUZoWpA1hKdl5eHEWbJuFmsuEFqT6K9+a/2Clzjx2pwIHqzu6HMzEx2oOPDhw+hpaUltxPu6HtCLBYjPT0dNTU1cHd3h56enlL309TUhImJidxIH9nu90ztF2PS0NTURGlpaZPasfac7/fff49Tp07hp59+Yq3slGcHFbBuQHMfemV3YPX19cjMzERFRQVbwNhYvNpi0NDU1GTHYTCFyLq6uqwjKzk5Wa4voZGRUYfs8VKplO1fZ2NjAycnpzZ9ESqqS7p5ql2n0i2QtdCrgo7kv2pra5GSkgINDQ0MHTpU7t+4pWGWbc1RMa2mrK2t2/zvr4jG3e+Bhs9JSkoKysvLYWBggLKyMpSVlckVYCsbkn78+DGWLl2KgIAAXL9+nYYJuwgqYN2Y1gRMKpUiJyeH7dDh5OSEgoICuV1be52FjN1eIpHA1dW1yWq4JXs8I2jKjMJgHIx9+vSBv7+/yvIpPdXMkZJFMKiTp8kou0PLyclBYWFhswNLtbW10bdvX7kC38Y5KoFAAE1NzSY5KuY9KBQKkZKSAgAqbTXVmMrKSiQnJ8PCwgLu7u5yY1WY3XthYSFSU1NbHOkiEonw3Xff4fTp09i1axd8fHw65XwpykEFrBvQ0k5IUQiREIKioiJkZmbC3NxcrkOHhoYG6urq2jTiRBaRSITs7GxUVFTAzs6uWXdZa/Z4ZhSGRCJRaLpgygY0NDTa5GCkPDvEYnGbjQ2KclSyE5pLSkrYPCvTV3PQoEGwsrLqlHyqbFhS0cBMRSFp2YJmxgh1/Phx3L17FwUFBfD398fJkydZBy2l66AC1o1RU1OTa/wJABUVFUhNTYWBgQH8/PyahO2YqcxtHXHSuBDZ3t6+zV8oiuzxsi2GCgsL2fAjAFhYWMDc3FzlBakA/t+92fN2YAzdof7Lzs5OJY/deEKzQCDAo0ePoK6uDktLS9YF29hNqKen1yFRY+bkWVlZtSks2XiGl0gkwoULFyCVSvHOO++gsrISs2bNws6dO1Uy7JPSfqiAdQOU+WAxOxYAzSa4CSHQ0tJiQzdMXsjQ0LDZVTQhBFwulw3jNS5E7ijMl4Genh6kUikqKythZ2cHfX19tsN5VVUVexxzvu398hKLxcjKykJFRQXmveqIX66ZqexaeiKd2eS3rTAh75KSkiZjegB5N2FmZqZcJ3lG2JTpJC878sTLy6tDu/sHDx5g6dKlGDduHC5cuNApiy1K+6EC1s2RSqV4/Pgxqqqq4ODgoDAXIZvn6tWrF0aMGMGG8oqKipCWlgZCCPT19VmB0NfXR01NDdLS0tpciNxWuFwu0tPTm3TqkP0CY1xjja3QTHjHyMioRZcbIQSFhYXIycnBgAED2rWD7A6UFAta3H111RTljsLn85GcnMwukhSJUHNuQianlp2djZqaGnaxw7w3ZEWNMYPY2NjA3Ny83e+B+vp6fP3117hw4QL27NkDLy+v9l04pVOhAtZNkUgkyM7OhkAggK2trcKaq5YMGrJFwUCDEDICkZ2djfLyckilUvTp0wcmJiZyIUdVIRAIkJqaCjU1NdZ63xyKXGOydn4md8JYtxkh1tbWBp/PR2pqKgwNDeHr66uyjufPkpJiQZuOb25n1R6BU/RYs4c/wdOnHS8Wl0gkSE9PR1VVFdzc3JS2xjNoaGg0a5GXHY/C3K6hoQFHR0f07t273e/le/fuYdmyZZgwYQKuXbtGd13dGCpg3QDZDxpTc5OdnY3+/fujd+/eMDU1bXJMWw0aTA1MeXk5BAIBXF1d0bt3b/aLID09XU4gGJFQ1FS1NUQiEbKyslBZWQkHBwc5UWoLsnZ+Bsa6zYzt4PP54HA4MDMzg6mpaZOcYXd3I7ZVuNpCR8KHAwcObOLMa2sHlLKyMqSnp8PKykqlM+xkFzvMsNTMzExYWlpCXV0dhYWFSEtLY9/zsm7ClsKPQqEQX331FS5fvoz9+/fDw8NDJecbExODZcuWQSKRYOHChQgNDZX7+/Lly3H58mUADYu+kpISVFY2lFKoq6tj8ODBAABra2ucOtWD60M6ASpg3Qgul4vU1FS2rY2WlhZ4PB7EYjFrL2484kQZgwbjWmREUXYicuPVbX19PXg8Hjvji6n3YnY8RkZGze5wCCHIz89Hbm4urK2t2zw6Xhm0tbVhamoKgUAAkUgEFxcXGBoaNukWznS7b6gN655usc4Ur47CfOFbWloC+J8Zh8fjtSpq9fX1bGPqjuagWoJpJKypqQk/P78m70uJRMIWM+fl5bE7NdkOHbq6utDU1MTdu3exbNkyTJ48GdeuXVPZLl4ikWDJkiU4f/48rKys4Ofnh6CgIDnzx/bt29n///HHH5GUlMT+rqOjg7t376rkXJ5HqIB1AwghuH37NtTV1eHp6Sln9ZXtbdieeq62TkTW0tKSq+1h6r14PB7Ky8uRnZ0NkUgEPT09VtQMDQ3B4/GQnp7Oiq8qjSCyMM4yps0QswvQ1dVt1s7f3QSsK4WrvTk0WWceg6K2XvX19RCLxTAzM4OVlVWnhHOZKEVubm6LjYSZgZqKpkkzXVvWrVuH9PR0VFdXY+HChRg9erRKezvGx8fD3t4etra2AIDg4GCcPHmyWffikSNHsHHjRpU9//MOFbBugJqaGlxcXBROldXQ0IBQKGQLmpUVLoFAgPT0dEilUoWFyMoiW+8lO06CWY3n5eWhrKwMQMNuTkdHBwKBAPr6+u1u0dPc9TD5NE9PzxbzaY3t/E5O3TuM2Fa6cnyKLLI1VLW1tewoEgsLCwgEAlbUCCFypgt9ff12iwTzPLq6uvD19W3zQkm2a8udO3fA5XIxd+5cjB07Fvfu3cPvv//ONuJVBfn5+RgwYAD7u5WVFeLi4hQem5OTg6ysLAQEBLC31dXVsdcZGhqKyZMnq+zcngeogHUT9PT05PI3zI7LyMgIaWlprCOP+fApGqAHyOef7O3tFboWOwqHw0GvXr3Y9kGenp7s6Hg+n4+nT5+yvedkDRftGX0hkUiQlZUFLpfbrAuzJ3Hm9+vwGTOk05/nWdnnZceqODk5sflO2X8n2Z3owaRXAAAgAElEQVRafn6+wgbMrYkaIQS5ubkoKCiQe572UFdXh61bt+LmzZs4dOgQO+pk2LBh7X5MVRAVFYVp06bJvQ45OTmwtLREZmYmAgICMHjwYJXV5z0PUAHrZjDGDMak0bdvX/Tr14915PF4PBQVFbFjLhiBMDAwQElJCfLy8jBw4MBOyT8x51dQUICnT582sasz4sqsOBkLtCKTiKyLUNF5Msn5rKwsNnegyh1dV3Dm9+sAgNsX7jT527MQNVVTVVWFJ0+ewNTUVC6v2pjmul0wC57WRK2mpgaPHz+GsbFxh8e3JCYmYvny5ZgxYwauXLnSaaFuBktLS+Tm5rK/5+XlsXnFxkRFRWHnzp1N7g8Atra2GDlyJJKSkqiAycAhjW1blC6B6Z4ha9BoSYCYgX+VlZUoLi4Gl8uFuro6evfuzQqJqmdlVVRUIC0tDcbGxhg0aFC78huyLkIejwehUAgdHR05Uaurq0NKSgr09PRgZ2enMhtzV4URGeFqK41FjclfKWOh78gxDV3om0cikSAzMxOVlZVwcXGBvr5+i8cri2x+is/no7q6GkKhEFKpFJaWlujXr1+rTsLmqKurw5YtWxAfH489e/bAxcVFJefcGmKxGI6Ojrh48SIsLS3h5+eHyMjIJgMuk5OT8cYbbyArK4v93FdUVEBXVxfa2tooKyvD8OHDW8yfvYjQHVg3gBCCHTt2wMPDQ+lRJRwOByKRCAUFBdDS0sKIESOgra0t5xRjmqQyHS6MjIza1eGCaewrlUrbVcsjS+MGsLImkZKSEjx8+BASiQTGxsbQ1dVlC5p74tDK9goXg+xOzWfMkG7RVYMx0VhaWsLX11elu3zZ/BSzu+vfvz9MTU1RXV3dxEmorD0+Pj4eK1aswMyZM3Hp0iWV7bpas8cfPHgQq1atgoGBAZydnWFkZIRly5bBzc0NkyZNQkJCAvT09LBu3TpkZWUhODhY7vV88uQJFi9eDDU1NUilUoSGhlLxagQVsG6AWCyGrq4uIiMjsWbNGqipqcHLyws+Pj7w9fWFo6Oj3Bc4MxG5trYWDg4OcqEZpoCZCT0wVmLZDhfMwEpmx9NcPk0sFiM7OxtcLhf29vYdGhvfHBwOB9ra2hCJRKisrISTkxP69esHgUDAhkuZTiKNjQDdOaTYUfFiaE9osaNCd/v2bbmBpr169YJIJEJaWppK2jO1hFQqZUcDubq6srs72ZxXYydhY1HT0dFhc8qbN2/G7du38fvvv8PZ2Vll56mMPR4AZsyYgR07dsjdVl5ejgcPHuDhw4fgcDjw8fHB7du3m+T1RowYgQcPHqjsnJ9HqIB1AzQ1NbF48WIsXrwYhBBUV1fj9u3biIuLQ0REBFJTU9GnTx94eHiguLgYBgYGWLt2Lfr169fqCliRlZgZecHj8VBQUIC6ujq21osJPZaVlSEnJ6fT80/l5eVIS0tj8yiMUCsS4urqavB4PNYkwvTJU8YkUlNTg9es7uBq3vBOuQ4GVQkXoPq8mLIW+sGDB7NhvIKCAlRXV0MkEsHU1JTt7KLqri1AQ8lHSkoKzM3NW9zdKZr/JrtQu3nzJtatWwc+nw87OzuEhISofAffVnu8LGfPnsXYsWNZo8vYsWMRExODmTNnqvQcXwSogHUzOBwODAwMMHLkSIwcORJAw5fFvn37EBERAWdnZ5SUlCAoKAj29vbsLs3b2xv6+vpKfak0HnkhG8bLz89HWVkZ1NTUYGJiAkIIqqqqOmR9VoRsWHLw4MEKSwhkUfSlxZhEmPCjIpOIpqYmu6J3dHTE1TyVXYIc3Vm4FNHSLo15f+jp6YHH48HExAQDBw5EbW0tK2rMokd2p9beWV5Mu6nq6mql3guKYBZqWlpauHnzJiwtLXH06FEIBALcvn0bjx8/hoODQ7vOTxHK2uP/+usvXLt2DY6Ojti+fTsGDBig8L75+fkqO7cXCSpgPQAOhwMbGxvcvn2bXbVJJBKkpqYiNjYWJ0+exGeffQaRSAQPDw/4+PjAz88PLi4uShktGMNIaWkpJBIJhg4dCh0dHTafxrjEGHHtSMd4iUTCdiRvqQhVGRT1yWNMIjweD5mZmaipqWGLnCUSSbufqyW6MlzYGcha1mWHWerr68vlLmVf69zcXDlDDvPTmgGnvLwcqampKmk3devWLaxatQohISH49ttv2QXXiBEj2v2YHWHixImYOXMmtLW1sWfPHoSEhODSpUvtfryqqqomk9ZfdKiA9RBef/11ud/V1dXh4uICFxcXzJs3D0DDrubu3buIjY3FDz/8gMePH8PAwABDhgyBn58ffHx8MGDAALlwINM0uKysDHZ2dnKDCBt3XpBIJGxoiRl3wUzblc2nKYIQgtLSUmRmZsLCwqJF23VH0NbWhq6uLnJzc2FgYABvb29IpVLweDxwuVy80j8L/xa8pJLn6o67LlUYPRITE1u1rDO1gL169UK/fv0AtDylW1bUmEGtaWlpEAqFHc6p1dTUYNOmTXj48CH++OMPle60mkMZe7zs4mzhwoVYvXo1e98rV67I3ZeJtihCJBIhNjYWtbW1GDVqFG7dugUfH58OmameF6iN/jmGmfWVkJCA2NhYJCQksH0KfXx8UFNTg6KiIqxfvx5WVlbtEhTZ3omytnjZNlNCoRCpqanQ1taGg4NDp3X3lp0F5uTkJBdulKWjdnpVCpci2itmSyZUtChgyljo3/K6A2dnZ5Wt9GXbejE/dXV1EIvF6NOnD/r37w8jI6N2OQMJIbhx4wbWrFmD+fPn44MPPlBJmLs1d+G3336Lffv2ISMjA76+vjh06BCmTZuGyMhIeHh4sM13+/Xrh3PnzgEAjh8/ji+//BKxsbEoLy+Hj48P7txpcJkOGTJELrqiiPPnz+O7775DWloapkyZgoiIiG5tYnpWUAF7wZBKpTh16hRCQ0NhYGAAHR0d8Pl8uLq6sqHHwYMHtzufQQhBbW0teDweKisrUVpaCrFYDGNjY/Tt25e1Pqvywydb9DxgwABYWlq2Gopqr4h1tngpQhlBa61GTNljtixU67Q5avX19UhJSYFUKsWAAQPY3Rqfz4dUKoWenp7cTq0lMaqpqUF4eDiSk5Oxd+9elRX3SiQSODo6yrkLjxw5ImfOuHz5MoYOHYorV65g3rx5qKmpwdq1axEWFgYtLS0cO3YMQUFBWLt2LU6dOsWGunft2sU6IQ8cOIAtW7YAAMLCwtgoCgPztcz8W6SkpGDcuHEYMmQI9u3bh969e0Mqlb7wIkYF7AXk7NmzsLKyYosp6+vr8eDBA8TGxiI+Ph4PHjyAlpYWvL29WVGzs7NT+sMi25V+4MCBMDMzQ01NDbtLYyYwd7TNFABUV1cjJSUFOjo6sLe3V3p311YB6wrhaglFRc6thQ+VEbDWipjbg+wCw9bWlm26LAvT7Z4RtKqqKrbbvWx4WktLC9evX8eaNWuwaNEivP/++yr9Er916xbCw8Nx9uxZAEBERAQAYO3atQqPT0pKwocffogbN24AaMgTMrb+9iKRSFjxvn79OsRiMTw9PVFRUYEDBw7A3Nwcc+bMkSufeVGhObAXkMDAQLnftbS04OPjAx8fHyxZsgSEEPD5fCQmJiI2NhafffYZMjIyYG5uzroefX190bdv3yaiU1lZKTcShgkNMatqKysrAM23mZId29LSLlAsFiMzMxM8Hg+Ojo7Nhgs7SncTLoa2Fjl31SRnoVCI5ORkaGhotDhsVLbbvewIF9keikuXLkVhYSHq6+vxwQcfYNiwYSrfLbal+S4A/Pzzzxg3bhz7uyqa7zLi9csvv2Djxo14+eWXIRaLsW/fPgwbNgwxMTG4f/8+Xn75ZaSlpfXY6eOqgAoYpQkcDgdGRkYYPXo0Ro8eDeB/u6q4uDjExsZix44dKC8vh6OjI3x8fGBjY4Pjx4/j/fffh7u7e6sJ5tYchHl5eRAKhdDV1ZXLp6mrq7OzzQYMGNDuno/KDLrsruLF0BlFzo8ePZKb79Xe3Y1sz0wHBwc5c5CyMLt0AwMD1jT0ySefwM/PD3fu3MHhw4fh4+PTrvNTBb/99hsSExNx9epV9rb2Nt9ldl2MUWrixInw9fXFo0ePIJVKsXHjRmzYsAHbt2/Ho0ePsHfvXsyePRurVq2Cvb19Z15mt4YKGEUpOBwOrKysYGVlhalTpwJo2AXdu3cPW7Zswfbt2+Hi4oLVq1fD09OT3ak5OTkpnaBX1GaKMQCUlJQgJSUFtbW10NbWhqWlJQwNDTuloPZ5FC5lsbKykutwwZROMKKmp6fXqqjJjjzp6Gy4qqoqrF+/HtnZ2Thx4gRsbGwAdF7neGWb7164cAGbN2/G1atX5SIFbW2+y2RwmF2XmpoaevfuDR8fH1y/fp197EWLFmHFihU4ffo01qxZg3PnzuHtt9/GxIkTO37RPRgqYJR2w4x29/f3R2RkJLS0tNjC0fj4eGzbto0NJzIhSn9/f1hYWCglOsxcLy0tLfD5fGhoaGDIkCHgcDhyY1uYUTNM6FGZXpLN8SKLF4BmO1zweDxkZ2ezvSllzRZM/pIQgry8POTn53d45AkhBFevXsXatWuxZMkS7N69+5kYFvz8/JCWloasrCxYWloiKioKkZGRcsckJSVh8eLFiImJYUsIgKbNd2/cuMFa55uDeZ+eOXMG33zzDYYPHw43Nzd8//338Pb2xtGjRzFz5kzY2tpi5syZ+Oyzz/Dyyy83Kat5UaEmDkqnQghBSUkJ4uPjWSt/YWEhBg0axObShgwZAgMDgyaiQwhBYWEhcnJyYG1tjf79+zc7A435kuXz+RAIBNDW1pbLpzVn7mDCiM+7cKnSwMGM9mF+BAIB1NXVUVdXBwMDAzg4OLSryJ2Bz+dj3bp1yMvLw969e2Ftbd2ux2lMa/Z4oVCIOXPm4Nq1a6ioqICZmRneffddhIWFISAgAE+ePGEFu6CgABYWDZO+ra2tcerUKdy8eVOu+e7HH3+MBQsWNDmPS5cuQSqVYsyYMQCAf//9F+vXr8ePP/6I6OhoREZG4uzZs0hKSsKqVatw8eJFmJmZsYs2d3d3lbwezwNUwCjPHKlUirS0NMTFxSEuLg537tyBUCiEm5sbK2o1NTV48OABRo0aBTs7uzaPbmEs2oyo1dfXszZtRtjU1dVBCEFgcGInXalqUMWuSxmXYnsciFKpFE+fPkVhYSH69+/PmnPa02qKEILLly8jLCwMH330EebPn6+yXZcy9viffvoJ9+/fx+7duxEVFYXjx4/jjz/+wOPHjzFz5kzEx8ejoKAAY8aMQWpqartrzq5duwYXFxd2YXX48GEIhUIYGxvj888/x7p16/D2228DAObOnYuysjL8888/KnkdnjdoCJHyzFFTU4OTkxOcnJwwZ84cAA2r37t37+Ly5ctYsGAB+Hw+nJyckJWVxYqajY2N0l9oirpENO5wLxaLIRKJELFaH3Z2djA0NGQf//UZCZ1z8e2AcRx2l1ZTDLIDLYcOHSr3b8O0mmLqAZ8+fdpsVw4A4PF4WLduHQoLC/HPP//IOQFVgTLNd0+ePInw8HAAwLRp0/Dhhx+CEIKTJ08iODgY2traGDRoEOzt7REfH4/hw5VvDC2VStmWba+++iqio6Nx6tQp7NixA2ZmZnj77bfx+uuvIzY2Fjo6OsjNzUVVVRX279+P69e7d3SgK6EC1gUcPXoU4eHhePLkCeLj4+Hr66vwuOZCHszsIC6XCx8fHxw+fLjTuls8K7S1tTF06FCcOXMGn376Kf7zn/+gsrISCQkJiIuLw7Fjx1iHFyNoPj4+MDExaVM+TU9PD/369UNGRgZ4PB5sbGwgFotZ0wKT3/n1+wEwMjKSGzXT1aLW3knOqrbQS6VSZGVloby8XG7kiSyyraaYui/ZIveysjJkZmZi+/btKC8vR2ZmJt555x189913ndLvTxl7vOwxGhoaMDIyApfLRX5+vpxpRNnmu4zBiBDCivvNmzeho6MDHx8ffPXVV/jnn3/g4uKCkJAQmJiYQEdHBxcuXMDKlSuxcuVKuLq6tthm6kWHClgX4O7ujr///huLFy9u9piW5g2tWbMGy5cvR3BwMN577z38/PPPeP/995/hFXQezAoYAExMTBAYGMjWrTHhqtjYWFy7dg3ffPMN+Hw+nJ2dWVHz9PRssR8jU1BrbW2tsHksk99hdmq1tbVsKCxy56AmDWp7iqipahgmj8dDcnIyzMzM4OPj06YQH4fDga6uLnR1dWFhYYHKykoYGRlBLBZjxYoVSE9PR2BgIE6cOCFnjuipVFRUsAss5vN8584dbNu2Dd7e3li0aBEOHjyIiIgIvP/++5g/fz6Sk5ORnJyMTZs2ISgoqKsvodtDBawLUGaceXMhDxcXF1y6dIl1RoWEhCA8PPy5EbCWUFNTg42NDWxsbBAcHAygQXAePnyIuLg4/Prrr7h//z7U1dXZLiK+vr5wcHDA48ePweVy0a9fP/j4+DS7Y9XU1ISpqSnbiFU2FFZRUYHs7GyIxWI2n/bnHgcYGBiw+ZCuFjSgcyY5SyQSZGRkgM/nK1Xn1xKEEJw7dw6fffYZVqxYgXfeeafTHYbK2OOZY6ysrCAWi8Hj8WBqaqq0tZ5BKBQiMTER0dHR2Lx5MwDgxIkT4HK5iI+PZ4+bNWsWrly5gsOHD2Pz5s24fPkyKioqYGpq2uMjKs8KKmDdlOZCHlwuF8bGxmxtzYs+S0hTUxPe3t7w9vbGe++9xw4ETUxMRFxcHDZu3IjY2Fj06tULkyZNwssvvwwTExOYmZkpHXpUFApjRs0UFhYiJSUFAKCnp4cvVjSEjdzc3Ni5Vl0hau3Jl3G5XLm8FAMz8sTS0rLdheMMFRUVWLt2LSorKxETE8MOyOxslLHHBwUF4dChQxg+fDiOHTuGgIAAcDgcBAUFYdasWfjkk09QUFCAtLQ0+Pv7K3yeAwcO4Mcff8Tly5cxYsQInDt3Dq+//jqqqqrY+sb6+npwOBxoamoiPDwcb7zxBl566SW8+eabrLORohxUwDqJMWPGoKioqMntmzdvxqRJk1T6XGKxGGPHjkV2djZsbGzw559/NqnBuXz5MpYvX87+npycjKioKEyePBlz587F1atX2fqfgwcPwsvLS6Xn+KxgCm9HjRqFkSNH4o033kBoaCimTp3KTrnevXs3ysrKmgwEVdb6zeFw5CZGM/VPOTk56N27NyQSCe7du8eOmjn8gzXby+9Z5NPaI17LJpairKxhhppEIoGenh4MDAxQWVkJiUQCT09P6OjotPucCCGIiYnBxo0bsXr1asyaNeuZNqLV0NDAjh07EBgYCIlEgvnz58PNzQ0bNmyAr68vgoKCsGDBArzzzjuwt7eHiYkJoqKiAABubm6YPn06XF1doaGhgZ07dzZxIDK5vZs3b2Lz5s0ghCA9PR2zZ8/G9evXYW5uDrFYjNLSUlbIrl27hmHDhuHQoUPw8PB4Zq/F8wS10XchI0eOxNdff63QxNFcU9HQ0FD07dsXRUVF0NDQwK1btzBr1iwsXrwYoaGh2Lp1KyoqKvDll182+7zl5eWwt7dHXl4edHV1MXfuXEyYMAHTpk3rnAvtQsRiscJOEBKJBCkpKWwD46SkJIjF4iYDQVvrIlFTU4Pk5GTo6zc4GWWPr6+vl7PyM9Zypli48W6no6LWEZeirIVeKpUiPz8fWVlZ0NXVZQeBMh05jIyMlOrIwVBeXo7Q0FBUV1dj165dnbbLKC8vx4wZM1pcyN29exfvv/8++Hw+1NXVERYWhhkzZgBAuxdyv/zyC86fP4+wsDB89dVXKCoqglQqxfHjx/HDDz+wQ2fnzp0LHR0dBAQE4Nq1a0hJScHevXvZ7iKUtkN3YN2U5kIeHA4Ho0aNwrFjxxAcHIxDhw5BIBAgJCQEQENObOTIkS0K2LFjxzBu3Lh2jW7vaTQnQOrq6nB1dYWrqyvmz58PABAIBEhKSkJcXBy+++47PHnyBAYGBuwuzc/PD5aWllBTU0NNTQ3y8vLA4/GanT2mpaWFPn36sH0AmYGPzHBN2d2OkZERju51hL6+frvyaaqy2ItEIqSkpLCTuZnaLaYjB5/PR05OjlIdUAghiI6Oxueff47Q0FAEBwd36q5r69atGD16NLuQ27p1a5PPga6uLn799Vc4ODigoKAAPj4+CAwMhLGxMQDgq6++atNCrqKiArt378aPP/4IJycnqKmpIT09Hfv374eenh4++eQTBAUFYc+ePdi9ezf++OMPnD17FoaGhoiOju5Qmy0K3YF1CcePH8dHH32E0tJSGBsbw8vLC2fPnkVBQQEWLlyI6OhoAEB0dDQ+/vhjNuQRFhYGAMjMzERwcDDKy8vh7e2N8+fPo7KyEkDDl0bv3r3Z3xUREBCATz75BBMmTADQsPK8desWtLW1MXr0aGzdurXd88CeJwghKCsrQ3x8POLi4pCQkIC8vDzo6+ujqKgIy5Ytw9tvvw1jY+N254WYMSLMLq2qqooNgxoZGUEikbBjafr379+k6FpVwhWxSB3FxcXIzMxsduRJYxR15NDW1sbVq1dhaGiIixcvQl1dHTt37oS5ublKzrMlnJyccOXKFVhYWKCwsBAjR45k85PN4enpiWPHjsHBwaHNkQjGJr98+XJcvnwZ5ubm+Pnnn7F//34QQjBr1iw4OjoiISEBc+fOxW+//QZvb2/U1tZ2KBxL+R9UwHoILeXUQkJC5ASrd+/eqKhQXPtTWFgIDw8PFBQUsOGrwsJCmJubo76+HiEhIUhISGAdf4rCMEDDDoaZPMu00gGezxo1BkII5s+fj/z8fEyYMAHp6elITExEbW2t3EBQd3f3Di0AJBIJuFwuMjIyIBKJoKGhAW1tbbkuIrKlAmv3STp8bcH+D6Gurg5HR8cO/XvV1dVh+/btiImJgVAoBIfDga2tLQ4dOtTp86uMjY3btJCLj49HSEgIHj16BDU1NaUXcrLzuqqrq/H666+jqKgIW7ZsQXBwMNLT07Fx40aMHj0aU6dOhYGBAT777DOYmppi6dKlnXPxLyhUwJ4D2rLy/P7779lxDIoIDg7G3bt3kZyc3GI+rbnBfdOnT8dbb73F1qh5eno+Vxb/5ORkdqouQ319Pe7fv8/m0x4+fAhtbW25gaC2trZKhc9kx5DY29vLOdeYXRqPx4NQKISOjo7cqBnZfFpbRW3FlIp2jTyRpaysDKtWrYJUKmU7TBBCkJGRATs7O5VMDVDlQm7kyJE4dOgQW6Qsu5B79913YWdnhw0bNii8f3V1Ne7cuQMXFxfo6+vj4sWL2L9/P3744QdYW1sjKioKZ8+exaxZszB27NhOmZpAoQL2XLBq1SqYmpqysf/y8nJs27ZN4bHDhg1DREQERo0axd5WWFgICwsLEEJgYmKC2bNnY8eOHS2KoSIBI4Q0MZjIGlFeFAgh4PF47EDQhIQEZGZmwsLCQm4gaJ8+feS+1AQCAZ48eQI9PT3Y29u3mB+R7WrBhPAkEoncBGPZeV6tCVpHJjETQnDq1Cls2bIF69atw/Tp07vky1rZhRyfz8fIkSPx6aefNhsuvHLlCr7++muFPQhPnDiB8PBwTJo0CX/++Sd+/fVXODs7Y+PGjZBIJNi+fTsAYNmyZRg7diwbqqeoHipgzwFcLhfTp0/H06dPMXDgQPz5558wMTFBYmIidu/ejf379wMAsrOz8dJLLyE3N1duNxAQEIDS0lIQQpCamory8nLo6+u3GIbR0NCAl5eX3OTZsrIyDBs2DOnp6QCA3NxcjBs3Dg8fPnw2L0Q3hhCC3NxctoFxQkICKisr4ejoCC8vL2RkZKBv375YunRpu8eQyE4w5vF4qKqqYodCyhot8vPzkZ+fj5MP/AB0TLxKS0uxcuVKqKur44cffujSDhrKLOTq6+sxbtw4TJw4ER9//LHc32QXcsuXL0evXr2wZcsWuc+KWCzGkiVLsGLFCujq6sLLywvh4eH48MMPkZSUhE2bNmHy5MkICQlhXaeUzoMK2AuIKsIw+fn5cpNnL168CCMjIzkBe/DgAYYPHw4LC4tnamvuKYjFYhw9ehRhYWGwtLREXV0dAMDLy0tuIGh7u54zz8Hs0LhcLng8HjQ1NWFmZgZjY2MYGRm1K19HCMGJEyfw5ZdfYsOGDZg6dWqn7LqUscYDDTlZFxcX5OTkgBCCYcOG4c8//wSPx8OECRPYxdS4ceOwaNEiuLm5sfdl3leyCzkvLy/s3LmTfe/V19dDS0sLT58+RXh4OIyNjfHvv//i008/xZQpUyAUCqGtrc0aOBYtWqTy14LSFCpgFDna4+Ri3FtTp06VCyH+5z//QXx8PFJTU5vNp6WmpoLD4cjZmp88eQJjY+Pnuj6N4fDhw/Dz84OzszPb4YMpuE5ISGAHgjLNi/38/JQeCMpACEFOTg6Ki4vh7OyMXr16yeXTmC7xsvm0lsKXJSUlWLFiBbS1tfHDDz90OHfWEqtXr4aJiUmrNY6dlZPl8XhYvnw5CCFwdnbGmjVrMGHCBPB4PPz7778AGnJ/W7duxbp16+QmGlA6HypgFDmUCcM0njw7fPhwdjTF22+/jalTpyI4OBjGxsZYs2YN1q5d+0xszc8jTANi2YGgRUVFsLW1lRsIqq+vr1DUqqur8eTJE5iYmGDQoEEKv1yZUTOyRddSqVSucFlbWxtaWlr466+/8NVXX2Hjxo2YMmVKp+e6lF1QqSonK5VKoaamBkIIqqqq8NZbb2HYsGGYOHEiNm/ejIEDB2LlypV45ZVX8Pnnn0MoFGLnzp0ICgrC559/rvLrp7QMFTCKHMrk01qaPCtbo/b06VNUVVVBW1u7U23NLxpSqRSpqalsPi0pKQlCoRDu7u6sqNnZ2eG3336Dj48PXFxc2jyiRCqVsoXLhYWFWLRoEUQiEXr16oXly5dj7NixbKPpzkRZa3xHc6yzSHQAABVdSURBVLKyLkFGCMViMZYtW4aff/6Z3ZFaWlri7NmzKC0txZ07d3Dnzh289957eOWVVzrl+iktQwWM0iG6i635RUcoFLJdRM6cOYO4uDi4u7vLdeUfOHBgm8NbUqkUx44dw7fffovQ0FCYmZkhISGhRadrW3lWOVllTEW//vor9u7di7CwMPj7+8PPzw8nT55kax43bNiA/v3747333uvoZVNUAaFQOglHR0dSUFBACCGkoKCAODo6KjyOx+MRb29vcvToUYV/P3PmDBkwYADR1dUlERERTf5eV1dHpk+fTuzs7Ii/vz/Jyspi/7ZlyxZiZ2dHHB0dSUxMTMcvqpuTnp5ORo0aRZKTk0lZWRk5c+YMCQ8PJ+PHjydubm7kjTfeIOvXrycnTpwgubm5pLq6mtTU1Cj8ycjIIEFBQeSdd94hXC63S65H2feQLCEhIeTo0aNEKpUSU1NTIhKJCCGE3Lx5k7z++uvN3u/UqVNkxIgR5Pbt2+xtW7duJV5eXqSoqIjweDzyxhtvkOjo6A5eFUVVUAF7wZFIJEQqlXbKY69cuZIVnIiICLJq1aomxwiFQhIQEEC2b9/e5G8FBQVELBYTW1tbMnfuXLJy5Uri4eFBHj16JHfczp07yeLFiwkhhBw5coRMnz6dEELIo0ePiIeHB6mrqyOZmZnE1taWiMViVV9mt6O5f0+JREIyMjJIZGQk+fjjj8lLL71EPD09yYwZM8hXX31FLl++TLhcLqmqqiI///wzGTx4MDl16lSnvT+UQZn3UHl5OamrqyOEEFJaWkrs7e3Z98i0adPIkSNHCCGELF68mOzcubPZ5woLCyObNm0ihDS8LxkWLlxI5s2bR9zd3UloaKhqLoyiEqiAUTqNsrIyEhAQQOzt7cno0aPZVXxCQgJZsGABIYSQw4cPEw0NDeLp6cn+JCUlEUIIGTVqFLG1tSV6enpk9uzZpKqqimzZsoVs2bJF7nlef/11cvPmTUIIISKRiJiamhKpVNrkWNnjKA3U19eT27dvk59++omEhISQIUOGEDMzMxIcHEzKy8s75Tm5XC4ZM2YMsbe3J2PGjFH4PJcuXSKenp7Ezc2N6OvrEw6HQzw8PAiXyyUhISHEwsKCmJiYEE9PT/LLL78Qd3d34uHhQdzd3cn+/fvZx8nIyCB+fn7Ezs6OTJs2jQgEgmbP67fffiNLly6VO5+amhpCCCHV1dUkNTVVha8CRRVQAXuBOX/+PDlw4AC5c+dOs8d05eqbEEKOHj3Kih0hhPz6669kyZIlcse4ubmR3Nxc9ndbW1tSWlpKlixZQg4fPszePn/+/GbDlJQGpFIpyc3NJRKJpNOeY9WqVXK7qtWrV7d4PJfLJb1792bFhAkRthXZa6qtrW3y90uXLpFVq1axu7CjR4+ScePGUeHqxtCChReU+/fv4/fff0diYiI++OADbN68GTU1NQAaZpFlZ2cDaBjeyMyDkkUqlT7L01UpMTExcHJygr29PbZu3drk799++y1cXV3h4eGB0aNHIycnh/2buro6vLy84OXlhaCgoGd52s8EDocDKyurTq1lOnnypNz4nxMnTrR4vKrG/6ipqaG4uBgzZ87E+vXrER0dLfc+HjlyJCZPnoyrV69iypQpiIiIwMaNG+Hg4NCh56V0HnQYzQtKTU0NEhMTcf78eZibm6O8vBx6enrYsmULuFwukpKSIJVK8fvvv8PS0hKxsbHw9fVl7cTPqljT0tISubm57O95eXmwtLRUeIyVlRXEYjF4PB5MTU0V3tfc3BwhISE4f/48rKys4Ofnh6CgILi6urLHeXt7IzExEbq6uti1axdWr16NP/74AwCgo6ODu3fvdvJVP98UFxezQy3Nzc1RXFzc4vFRUVH45JNP5G4LCwvDpk2bWi2vIDL2+MePH2PZsmVYtGgRNDU1MW/ePPz555947bXX2ONGjBiB//73vygqKsKgQYNUcLWUzoTuwF5Q3N3dMXXqVERERODs2bMwMTHBjRs3sG3bNgQHB+P06dNYsGABjh49CkIIwsPD8d///hcAcPbsWZw9e7bFXZiiXVt7kB3sWV9fj6ioqCY7n6CgIBw6dAhAw2o9ICAAHA4HQUFBiIqKglAoRFZWFtLS0gAA9vb2sLW1hZaWFoKDg3Hy5Em5xxs1ahS72h82bBjy8vJUci0vEmPGjIG7u3uTn8avNYfDabEYurCwEA8ePEBgYCB7W0REBJKTk1k7v6LOHMx7k8PhsC26hEIh3njjDQwYMAA7d+7E7Nmz8dprr7HHMejo6FDx6il0cQiT0gXIOqwiIyNJQEAAuXjxItmxYwfx8fEhs2bNIt7e3sTR0ZEEBwcTQhqMF35+fiQ2NpZ4eHiQ48ePK3xs2VxUY8RicbtyaqdPnyYODg7E1taWfPHFF4QQQtavX09OnjxJCGnIZ0ybNo3Y2dkRPz8/kpGRwd73iy++ILa2tsTR0ZFER0crlVOTZcmSJeTzzz9nf1dXVyc+Pj5k6NChzb4GlJZpizX+u+++I4sWLWr275cvXyZvvvlms3/fv38/ee211wiXyyX//PMPGTFiBBk+fDg5f/48IaThs/Dw4cN2Xgmlq6EC9gJSWFhI3n33XRIeHk6OHTtG5syZQ65fv05WrlxJvvzyS/a4goICtqYqLy+PvPLKKyQwMJBcunSJECIvSCUlJeSbb74hw4YNI4MHDyZz5sxhk+7MfxUhFos71TDQmLYI2OHDh8nQoUNZizYhDa8DIQ3utoEDB5L09HRy5swZ4ujoSOzs7BTWqf3yyy+kT58+rMty37597N8OHjxI7O3tib29PTl48KCqLrNbo4w1nmHo0KHs+42BET+pVEqWLVtG1qxZo/C+oaGhZPTo0SQuLo4Q0uCKHT9+PPs6i8ViMnHiRPLhhx8+0/cgRXVQAXsBEYvF5MqVKyQ8PJxMmjSJxMTEEKlUSqKjo8ncuXPJlStXSH19PeHxeKxAbd++nfTt25d89NFH7GMQ8j+X4rZt28i4ceNIVVUVqa6uJrGxsaSuro78+++/JCgoiDg7O5Ply5cTPp/f4rlJJBJy8+ZNcuDAAZKTk6Pya29czKrIlk9Ig0PT2dmZFBcXN/tYISEhJCoqitja2pKMjAwiFAoV1qn98ssvCkWSy+WSQYMGES6XS8rLy8mgQYM6zbrenVCmvIIQQrKyskj//v2biMuoUaOIu7s7cXNzY8srGh8jEAjIf/7zH1bsmEXI6dOnyZIlS0hgYCDx8vIiGzZs6MxLpXQyVMAocmzdupWMHTuWODk5kS+//JIIhUKyevVqMnnyZJKYmEjGjh1LsrOz2eOZL44333yTREZGyj3WjRs3yKxZs8jevXuJVColc+bMYWt0UlNTyerVq8nMmTNJZGQku0vLysoi48ePJ++99x7Ztm0b4fF4ZPbs2WTDhg0kOzu7w1/wIpGIDBo0iGRmZrKC0ziEdOfOHWJra9vEPq2oYPb3339vVRCbE7DIyEjy7rvvsr+/++67TV7DnsKff/5JXF1dCYfDIQkJCc0e19xuNTMzk/j7+xM7Ozsyffp0uTB3S8iGpIuLi8m///7L/j58+HCyZ88eQsj/FlzMf5OSkkh6erryF0jpllATB0WONWvW4Ny5c0hOTsbixYshFArB5/Nx5MgR+Pj4wMnJCVFRUezxTPI7MzMT9vb2ABqS5QBw7do1eHt7Y+LEieBwODAyMkJZWRkyMjLw008/wd/fH8uXL0dMTAzu3r2Lu3fv4q233kJGRgbWrVuHBQsW4MCBA1BTU0NAQADu3buHb775BlwuF0CDw4y0sZWnhoYGduzYgcDAQLi4uGD69Olwc3PDhg0bcOrUKQANHfmrq6vx9ttvy9nlnzx5Al9fX3h6emLUqFEIDQ2FlpYWBgwYwD6+lZUV8vPzmzzvX3/9BQ8PD0ybNo11Rubn5yt1356Au7s7/v77b7z66qvNHiORSLBkyRKcOXMGjx8/xpEjR/D48WMADe+75cuXIz09Hb1798bPP/+s1PMy77+9e/fi1Vdfxb59+7BkyRIUFBRg/fr1OHbsGNLS0qCurs46SoGGmWt2dnYdvGpKV0Nt9JRmYYb57dq1ixWKsLAw3Lp1iz2Gw+GAEIIZM2bgwoULcHV1hZ6eHgBAU1MTAoEApqamABpGXlhYWOC3337DuXPncO/ePQQHB6OgoAAxMTHYtGkTTExMYGZmhmPHjiEuLg7R0dGws7ODra0tDA0NWTFknluWqqoqpKSkYNCgQexzKmL8+PEYP3683G2bNm1i///ChQsK7zdixAg8ePBA7rZjx441/wL+PxMnTsTMmTOhra2NPXv2ICQkBJcuXWr1fj0JFxeXVo+Jj49nHaAAWAeoi4sLLl26hMjISAANtWHh4eHNzu2SSCTskM+6ujpcu3YNN2/exL1793Du3DmEhIRgwIABmDFjBl566SXMmTMHpqamEAqFKmtATOke0B0YRSkYsTA3N8eUKVPkdj4cDgcLFy5EYmIi/P39MWPGDFRXV8PHxwc3b96EpqYmKisrcfLkSTg5OUEkEmHZsmX44osvwOPxYG1tDSsrKwANwwF/+uknLFu2DBERERg+fDj75b9gwQJ8//33ABpGXZw7dw5JSUly51JSUoKMjAwADVZqiUTSqUXXytSpmZqasnVKCxcuxO3bt1u8b2uF1suXL2eLqR0dHWFsbMz+rTsXWje34+RyuTA2NmZrDFvbiTLi9dtvv2HmzJnw9/fHjh078N133+HLL7/E559/jhs3buDevXv47LPP8Pfff2P58uU4f/48vL29O/ciKc8UugOjtIvGux9LS0v89ddfAICCggLo6+vD29sbdnZ2sLOzg5OTE2bNmoWhQ4fi0aNHOH36NN59912MGDGCfQwulws+nw9TU1NIpVJkZmZCS0sLK1euBNBQfM2MtWA6OeTn56OyshJnzpwBIQQODg5sKLO5YmupVNpq/ZGyyNapWVpaIioqit1JMBQWFrKFu6dOnWJ3K4GBgfj000/Z8SDnzp3DF198gWHDhrVYaL19+3b2/3/88UckJSWxv3dmoXVLY08mTZrUKc+piPr6eixZsgRVVVXYsmULjI2NweVycePGDZw/fx46OjqIjIzEwYMHYWtrCw8PD/b1pzxfUAGjqJz+/fsDaAhB7tixA0KhEIWFhewuKzAwEGfPnsWECRPg5OSEV199FZMmTUJiYiIGDhwIQ0ND1NfXIysrC7179wbQMMtJJBLB0tISJ0+eRHZ2Nm7cuAEAKC0tRd++fXHgwAHExMTg0KFD6NWrFzZu3AgOhwM/Pz8EBgayK3dZYSMynRpqamrY8KeyyObUJBIJ5s+fz+bUfH19ERQUhB9++AGnTp2ChoYGTExMcPDgQQCAiYkJ1q9fDz8/PwANs6bS09MVhtlkBUyWI0eOYOPGjW065/bSXGhVWZrbcZqamqKyshJisRgaGhoKd7GyaGlpQV1dHfHx8XBxcQEhBMbGxuDz+fjmm2/g7OwMPT09LFiwAB4eHh06Z0r3hoYQKZ2OtrY2bGxsoKGhAUIILC0tsW/fPnzwwQfQ0dFhJ+DGxMSwX1wCgQDp6elwcnICANy9exf9+vWDjo4O0tLS8NZbbwFoMIz07dsXAoEAfD4fHh4e4HA4WLRoEXx9fWFtbY3IyEhcvXoVQEPO6urVq7h//z5EIpHcLmz79u0oKytr8/WNHz8eqampyMjIQFhYGICGnBoTwouIiMCjR49w7949XL58Gc7Ozux958+fj/T0dKSnp2PevHltMnbk5OQgKysLAQEB7G11dXXw9fXFsGHDWu0x+KxprqsKh8PBqFGj2HzioUOHWt3Rbdu2DSYmJti/fz84HA7U1NSwbds23L59G/v27cO3336LiRMnPovLonQlXWV/pFAaU1RUxFr0i4uLyTvvvMN2HV+1ahX5+OOPCSENdvPG9Tu5ublk1apV5K+//iK3bt0iWlpaZM6cOeT48ePk66+/JiNHjiSEEDJ79mwyatQoMmnSJOLk5EQuXLhAJBIJ2bt3L7GxsXmGV6uYthRab926lXz44YdytykqtH4W/P3338TS0pJoaWmRfv36saUF+fn5ZNy4cexxirqqMOcrO/ZEtni8Oc6dO0e8vb3lur/weDwVXhWlu0NDiJRug5mZGfv//fr1w6FDhyAWiwE0hJ+Y0OT48eNx7NgxnDt3DoMGDYKDgwNKS0tRVVUFDw8P/PLLL5gzZw7mzZuHqKgoJCQkYNy4cSgsLAQhBJMnT8bSpUuxZ88e7NixA6NHj0Z0dDQqKyvxyiuvYN68eZg/f36XvAbKmEIYoqKisHPnzib3BwBbW1uMHDkSSUlJz8QuPmXKFEyZMqXJ7f3790d0dDT7uyIHKNBwvvHx8W16zrFjxyImJgYhISG4ePEiAMDQ0LCNZ07pyVABo3RbOBwONDU1AQDLli1jb3/zzTfx9OlT7N69G8XFxTh9+jQKCwtRWloKY2NjmJqaQiwWY8SIEXImkf/+97+wsLBga5VycnJYUfTy8sLgwYOxadMmFBQUPMOrlEcZUwgAJCcno6KiAsOHD2dvq6iogK6uLrS1tVFWVoYbN26wdU/PK2FhYTh79mxXnwali6A5MEqPQ0NDAx999BH+/vtv3LhxA8bGxnB3d8esWbNgamqK9957DxkZGZg7dy727duH48ePA2hwLOrp6bGidf/+fQwbNgwAkJCQAE9PTwD/M6F0BcoUWgMNu6/g4GC5HJ6iQuvmzB/PCyYmJpg5c2ZXnwali+AQ0sZWBhRKDyA7Oxvnz59HXFwcrK2tsWHDBnzyyScwNjbGhg0bAABDhgzBwYMH4eDgAF9fX1y6dAlmZmZyzkQKhdJ9oQJGeWHIzs6GQCCAq6srsrOzERgYiKSkJPB4PLzyyitITk5mi2kpFEr3h4YQKS8MNjY2cHV1BSEENjY2SEhIgK6uLnr16oUxY8ZgxIgRbI0WhULp/tAdGIUiQ3uKmSkUStdABYxCoVAoPRIaQqRQKBRKj4QKGIVCoVB6JFTAKBQKhdIjoQJGoVAolB4JFTAKhUKh9EiogFEoFAqlR0IFjEKhUCg9EipgFAqFQumRUAGjUCgUSo+EChiFQqFQeiRUwCgUCoXSI6ECRqFQKJQeCRUwCoVCofRIqIBRKBQKpUdCBYxCoVAoPRIqYBQKhULpkVABo1AoFEqPhAoYhUKhUHokVMAoFAqF0iOhAkahUCiUHgkVMAqFQqH0SKiAUSgUCqVHQgWMQqFQKD0SKmAUCoVC6ZFQAaNQKBRKj4QKGIVCoVB6JP8H7BuVmhx77EMAAAAASUVORK5CYII=\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "\n",
+ "# output heatmap (brute force)\n",
+ "# replace None with the relative path if you want to save the plot\n",
+ "fair_model.heatmapflag = True\n",
+ "fair_model.heatmap_path = 'heatmap'\n",
+ "fair_model.generate_heatmap(data_set, dataset_yhat.labels)\n",
+ "Image(filename='{}.png'.format(fair_model.heatmap_path))\n",
+ "\n",
+ "\n",
+ "\n"
+ ]
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "iteration: 464, error: 0.41922629310344817, fairness violation: 0.004981939655172413, violated group size: 0.217\n",
- "iteration: 465, error: 0.41918494623655916, fairness violation: 0.004985225806451612, violated group size: 0.217\n",
- "iteration: 466, error: 0.4191437768240344, fairness violation: 0.004988497854077254, violated group size: 0.283\n",
- "iteration: 467, error: 0.41910278372591, fairness violation: 0.004991755888650964, violated group size: 0.283\n",
- "iteration: 468, error: 0.4190619658119658, fairness violation: 0.004994999999999998, violated group size: 0.217\n",
- "iteration: 469, error: 0.4190213219616205, fairness violation: 0.0049982302771855, violated group size: 0.217\n",
- "iteration: 470, error: 0.41898085106382993, fairness violation: 0.005001446808510636, violated group size: 0.283\n",
- "iteration: 471, error: 0.41894055201698516, fairness violation: 0.005004649681528661, violated group size: 0.217\n",
- "iteration: 472, error: 0.41890042372881364, fairness violation: 0.005007838983050847, violated group size: 0.283\n",
- "iteration: 473, error: 0.4188604651162791, fairness violation: 0.005011014799154333, violated group size: 0.217\n",
- "iteration: 474, error: 0.41882067510548526, fairness violation: 0.005014177215189871, violated group size: 0.217\n",
- "iteration: 475, error: 0.41878105263157905, fairness violation: 0.0050173263157894735, violated group size: 0.283\n",
- "iteration: 476, error: 0.41874159663865557, fairness violation: 0.0050204621848739485, violated group size: 0.217\n",
- "iteration: 477, error: 0.4188259958071279, fairness violation: 0.005013576519916141, violated group size: 0.217\n",
- "iteration: 478, error: 0.41878661087866115, fairness violation: 0.005016707112970709, violated group size: 0.217\n",
- "iteration: 479, error: 0.4188705636743216, fairness violation: 0.005009858037578285, violated group size: 0.217\n",
- "iteration: 480, error: 0.41883125000000004, fairness violation: 0.005012983333333334, violated group size: 0.283\n",
- "iteration: 481, error: 0.4187920997920998, fairness violation: 0.005016095634095634, violated group size: 0.283\n",
- "iteration: 482, error: 0.4187531120331951, fairness violation: 0.0050191950207468874, violated group size: 0.283\n",
- "iteration: 483, error: 0.4188364389233955, fairness violation: 0.00501239751552795, violated group size: 0.217\n",
- "iteration: 484, error: 0.41891942148760325, fairness violation: 0.005005628099173555, violated group size: 0.283\n",
- "iteration: 485, error: 0.4190020618556701, fairness violation: 0.004998886597938144, violated group size: 0.283\n",
- "iteration: 486, error: 0.41896296296296304, fairness violation: 0.005001995884773661, violated group size: 0.217\n",
- "iteration: 487, error: 0.4190451745379877, fairness violation: 0.004995289527720739, violated group size: 0.283\n",
- "iteration: 488, error: 0.4191270491803279, fairness violation: 0.004988610655737704, violated group size: 0.283\n",
- "iteration: 489, error: 0.4192085889570552, fairness violation: 0.004981959100204497, violated group size: 0.217\n",
- "iteration: 490, error: 0.41916938775510204, fairness violation: 0.004985077551020407, violated group size: 0.283\n",
- "iteration: 491, error: 0.4192505091649695, fairness violation: 0.004978460285132381, violated group size: 0.217\n",
- "iteration: 492, error: 0.4192113821138212, fairness violation: 0.004981573170731706, violated group size: 0.217\n",
- "iteration: 493, error: 0.41917241379310355, fairness violation: 0.004984673427991887, violated group size: 0.283\n",
- "iteration: 494, error: 0.41913360323886634, fairness violation: 0.004987761133603237, violated group size: 0.217\n",
- "iteration: 495, error: 0.4192141414141415, fairness violation: 0.004981191919191918, violated group size: 0.217\n",
- "iteration: 496, error: 0.4192943548387097, fairness violation: 0.004974649193548386, violated group size: 0.217\n",
- "iteration: 497, error: 0.419374245472837, fairness violation: 0.004968132796780683, violated group size: 0.217\n",
- "iteration: 498, error: 0.4194538152610441, fairness violation: 0.004961642570281124, violated group size: 0.217\n",
- "iteration: 499, error: 0.41953306613226454, fairness violation: 0.0049551783567134255, violated group size: 0.217\n"
- ]
- }
- ],
- "source": [
- "C = 100\n",
- "print_flag = True\n",
- "gamma = .005\n",
- "\n",
- "\n",
- "fair_model = GerryFairClassifier(C=C, printflag=print_flag, gamma=gamma, fairness_def='FP',\n",
- " max_iters=max_iterations, heatmapflag=False)\n",
- "\n",
- "# fit method\n",
- "fair_model.fit(data_set, early_termination=True)\n",
- "\n",
- "# predict method. If threshold in (0, 1) produces binary predictions\n",
- "\n",
- "dataset_yhat = fair_model.predict(data_set, threshold=False)\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**3-d heatmaps**\n",
- "\n",
- "We now show to generate a 3d-heatmap of unfairness using the `generate_heatmap` method. The $X-Y$ axes in the plot represent the coefficients of the linear threshold function that defines a protected subgroup with respect to the first two sensitive attributes. Which $2$ attributes are considered sensitive can be overwritten with the `col_index` argument. The $Z$-axes is the $\\gamma$-disparity (FP) of the corresponding subgroup defined by the linear threshold function. This is important because it allows us to (1) visualize convergence as the heatmap flattens and (2) brute force check the fairness in low-dimensions without relying on a heuristic auditor. See the [the rich subgroup fairness empirical paper](https://arxiv.org/abs/1808.08166) for a discussion of these plots. Note that in the below plot no group has a $\\gamma$-disparity of greater than $.005$, which we would expect since the set of linear threshold functions on two attributes is a subset of the set of linear threshold functions on all protected attributes, and the final model is $\\gamma$-fair. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "pycharm": {
- "is_executing": true
- }
- },
- "outputs": [
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- ""
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "SwMw4smGhI5M"
+ },
+ "source": [
+ "**black-box auditing**\n",
+ "\n",
+ "We now show to audit any black box classifier with respect to rich subgroup fairness under either FP or FN rate. Note the below auditing procedure would work for any set of (soft) predictions $\\hat{y}$, and need make no assumptions about the structure of the predictor. We note that as expected the disparity of the group found is the same as the disparity printed out in the last iteration of the `fit` method.\n",
+ " "
]
- },
- "execution_count": 3,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "\n",
- "# output heatmap (brute force)\n",
- "# replace None with the relative path if you want to save the plot\n",
- "fair_model.heatmapflag = True\n",
- "fair_model.heatmap_path = 'heatmap'\n",
- "fair_model.generate_heatmap(data_set, dataset_yhat.labels)\n",
- "Image(filename='{}.png'.format(fair_model.heatmap_path)) \n",
- "\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**black-box auditing**\n",
- "\n",
- "We now show to audit any black box classifier with respect to rich subgroup fairness under either FP or FN rate. Note the below auditing procedure would work for any set of (soft) predictions $\\hat{y}$, and need make no assumptions about the structure of the predictor. We note that as expected the disparity of the group found is the same as the disparity printed out in the last iteration of the `fit` method.\n",
- " "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "pycharm": {
- "is_executing": true
- }
- },
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "0.004955178356713431\n"
- ]
- }
- ],
- "source": [
- "\n",
- "\n",
- "gerry_metric = BinaryLabelDatasetMetric(data_set)\n",
- "gamma_disparity = gerry_metric.rich_subgroup(array_to_tuple(dataset_yhat.labels), 'FP')\n",
- "print(gamma_disparity)\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "**pareto curves**\n",
- "\n",
- "The `FairFictPlay` algorithm implemented in the `fit` method converges given access to perfect oracles for solving cost-sensitive classification (CSC) problems. A cost-sensitive classification problem over a hypothesis class $\\mathcal{H}$ is $$\\min_{h}\\sum_{i = 1}^{n}(1-h(x_i))c_0 + h(x_i)c_1$$\n",
- "By default in this package, and in the companion [empirical](https://arxiv.org/abs/1808.08166) and [theory](https://arxiv.org/pdf/1711.05144.pdf) papers, the hypothesis class of the learner and the of the subgroups are hyperplanes. The corresponding heuristic oracle for solving the CSC problem first forms two regression problems $(x_i, c_0)$ and $(x_i, c_1)$. Then in the case of hyperplanes, trains two regressions $r_i: \\mathcal{X} \\to R$ which predict the costs of classifying a given point $x$ $0,1$ respectively. Finally the binary classifier output by the oracle is defined as $\\hat{r}(x) = \\arg\\min_{j \\in \\{0,1\\}}r_j(x)$. But of course if we are interesting in different hypothesis classes for the learner, we simply need different regressors. In this package in addition to linear regression, we've added support for regression trees, kernelized ridge regression, and support vector regression. Below we trace out Pareto curves of $\\gamma$-disparity vs. error for each of these different heuristic oracles. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {
- "pycharm": {
- "is_executing": true
- }
- },
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "is_executing": true
+ },
+ "id": "YsozvR3hhI5M",
+ "outputId": "6ef397ab-120c-49a0-a767-5201a047ee3d"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0.004955178356713431\n"
+ ]
+ }
+ ],
+ "source": [
+ "\n",
+ "\n",
+ "gerry_metric = BinaryLabelDatasetMetric(data_set)\n",
+ "gamma_disparity = gerry_metric.rich_subgroup(array_to_tuple(dataset_yhat.labels), 'FP')\n",
+ "print(gamma_disparity)\n"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Curr Predictor: Linear\n",
- "Curr Predictor: SVR\n",
- "Curr Predictor: Tree\n",
- "Curr Predictor: Kernel\n"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "1qK4jg_PhI5M"
+ },
+ "source": [
+ "**pareto curves**\n",
+ "\n",
+ "The `FairFictPlay` algorithm implemented in the `fit` method converges given access to perfect oracles for solving cost-sensitive classification (CSC) problems. A cost-sensitive classification problem over a hypothesis class $\\mathcal{H}$ is $$\\min_{h}\\sum_{i = 1}^{n}(1-h(x_i))c_0 + h(x_i)c_1$$\n",
+ "By default in this package, and in the companion [empirical](https://arxiv.org/abs/1808.08166) and [theory](https://arxiv.org/pdf/1711.05144.pdf) papers, the hypothesis class of the learner and the of the subgroups are hyperplanes. The corresponding heuristic oracle for solving the CSC problem first forms two regression problems $(x_i, c_0)$ and $(x_i, c_1)$. Then in the case of hyperplanes, trains two regressions $r_i: \\mathcal{X} \\to R$ which predict the costs of classifying a given point $x$ $0,1$ respectively. Finally the binary classifier output by the oracle is defined as $\\hat{r}(x) = \\arg\\min_{j \\in \\{0,1\\}}r_j(x)$. But of course if we are interesting in different hypothesis classes for the learner, we simply need different regressors. In this package in addition to linear regression, we've added support for regression trees, kernelized ridge regression, and support vector regression. Below we trace out Pareto curves of $\\gamma$-disparity vs. error for each of these different heuristic oracles."
+ ]
},
{
- "data": {
- "image/png": "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\n",
- "text/plain": [
- ""
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "is_executing": true
+ },
+ "id": "WJspqrmEhI5M",
+ "outputId": "d1478011-d530-45e8-a32f-3862db281721"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Curr Predictor: Linear\n",
+ "Curr Predictor: SVR\n",
+ "Curr Predictor: Tree\n",
+ "Curr Predictor: Kernel\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# set to 50 iterations for fast running of notebook - set >= 1000 when running real experiments\n",
+ "pareto_iters = 50\n",
+ "def multiple_classifiers_pareto(dataset, gamma_list=[0.002, 0.005, 0.01, 0.02, 0.05, 0.1], save_results=False, iters=pareto_iters):\n",
+ "\n",
+ " ln_predictor = linear_model.LinearRegression()\n",
+ " svm_predictor = svm.LinearSVR()\n",
+ " tree_predictor = tree.DecisionTreeRegressor(max_depth=3)\n",
+ " kernel_predictor = KernelRidge(alpha=1.0, gamma=1.0, kernel='rbf')\n",
+ " predictor_dict = {'Linear': {'predictor': ln_predictor, 'iters': iters},\n",
+ " 'SVR': {'predictor': svm_predictor, 'iters': iters},\n",
+ " 'Tree': {'predictor': tree_predictor, 'iters': iters},\n",
+ " 'Kernel': {'predictor': kernel_predictor, 'iters': iters}}\n",
+ "\n",
+ " results_dict = {}\n",
+ "\n",
+ " for pred in predictor_dict:\n",
+ " print('Curr Predictor: {}'.format(pred))\n",
+ " predictor = predictor_dict[pred]['predictor']\n",
+ " max_iters = predictor_dict[pred]['iters']\n",
+ " fair_clf = GerryFairClassifier(C=100, printflag=True, gamma=1, predictor=predictor, max_iters=max_iters)\n",
+ " fair_clf.printflag = False\n",
+ " fair_clf.max_iters=max_iters\n",
+ " errors, fp_violations, fn_violations = fair_clf.pareto(dataset, gamma_list)\n",
+ " results_dict[pred] = {'errors': errors, 'fp_violations': fp_violations, 'fn_violations': fn_violations}\n",
+ " plt.plot(errors, fp_violations, label=pred)\n",
+ "\n",
+ " if save_results:\n",
+ " pickle.dump(results_dict, open('results_dict_' + str(gamma_list) + '_gammas' + str(gamma_list) + '.pkl', 'wb'))\n",
+ "\n",
+ " plt.xlabel('Error')\n",
+ " plt.ylabel('Unfairness')\n",
+ " plt.legend()\n",
+ " plt.title('Error vs. Unfairness\\n(Adult Dataset)')\n",
+ " plt.savefig('gerryfair_pareto.png')\n",
+ " plt.close()\n",
+ "multiple_classifiers_pareto(data_set)\n",
+ "Image(filename='gerryfair_pareto.png')"
]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# set to 50 iterations for fast running of notebook - set >= 1000 when running real experiments\n",
- "pareto_iters = 50\n",
- "def multiple_classifiers_pareto(dataset, gamma_list=[0.002, 0.005, 0.01, 0.02, 0.05, 0.1], save_results=False, iters=pareto_iters):\n",
- "\n",
- " ln_predictor = linear_model.LinearRegression()\n",
- " svm_predictor = svm.LinearSVR()\n",
- " tree_predictor = tree.DecisionTreeRegressor(max_depth=3)\n",
- " kernel_predictor = KernelRidge(alpha=1.0, gamma=1.0, kernel='rbf')\n",
- " predictor_dict = {'Linear': {'predictor': ln_predictor, 'iters': iters},\n",
- " 'SVR': {'predictor': svm_predictor, 'iters': iters},\n",
- " 'Tree': {'predictor': tree_predictor, 'iters': iters},\n",
- " 'Kernel': {'predictor': kernel_predictor, 'iters': iters}}\n",
- "\n",
- " results_dict = {}\n",
- "\n",
- " for pred in predictor_dict:\n",
- " print('Curr Predictor: {}'.format(pred))\n",
- " predictor = predictor_dict[pred]['predictor']\n",
- " max_iters = predictor_dict[pred]['iters']\n",
- " fair_clf = GerryFairClassifier(C=100, printflag=True, gamma=1, predictor=predictor, max_iters=max_iters)\n",
- " fair_clf.printflag = False\n",
- " fair_clf.max_iters=max_iters\n",
- " errors, fp_violations, fn_violations = fair_clf.pareto(dataset, gamma_list)\n",
- " results_dict[pred] = {'errors': errors, 'fp_violations': fp_violations, 'fn_violations': fn_violations}\n",
- " plt.plot(errors, fp_violations, label=pred)\n",
- "\n",
- " if save_results:\n",
- " pickle.dump(results_dict, open('results_dict_' + str(gamma_list) + '_gammas' + str(gamma_list) + '.pkl', 'wb'))\n",
- "\n",
- " plt.xlabel('Error')\n",
- " plt.ylabel('Unfairness')\n",
- " plt.legend()\n",
- " plt.title('Error vs. Unfairness\\n(Adult Dataset)')\n",
- " plt.savefig('gerryfair_pareto.png')\n",
- " plt.close()\n",
- "multiple_classifiers_pareto(data_set)\n",
- "Image(filename='gerryfair_pareto.png') "
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "A natural question one might ask is, suppose we fix a statistical fairness definition for rich subgroup fairness like equality of false positive rates, `FP`. Does learning a classifier that is fair with respect to `FP` increase or decrease fairness with respect to false negative rates `FN`? One could see this relationship going in either direction - and indeed we submit that it is dataset dependent. In some cases, if enforcing `FP` fairness pushes the classifier towards the constant classifier, then it will also satisify `FN` rate fairness, since the constant classifier is perfectly fair. However, if the hypothesis class is sufficiently rich, then one would expect that ceteris paribus since we are optimizing for error in addition to `FP` rate fairness, the algorithm would increase `FN` rate unfairness in order to decrease error. Below we trace the FN vs. FP rate tradeoff across a range of input $\\gamma$, where the classifier is optimized only for `FP` rate fairness. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "pycharm": {
- "is_executing": true
- }
- },
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "gamma: 0.001 gamma: 0.002 gamma: 0.003 gamma: 0.004 gamma: 0.005 gamma: 0.0075 gamma: 0.01 gamma: 0.02 gamma: 0.03 gamma: 0.05 "
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "N_ghZDh6hI5N"
+ },
+ "source": [
+ "A natural question one might ask is, suppose we fix a statistical fairness definition for rich subgroup fairness like equality of false positive rates, `FP`. Does learning a classifier that is fair with respect to `FP` increase or decrease fairness with respect to false negative rates `FN`? One could see this relationship going in either direction - and indeed we submit that it is dataset dependent. In some cases, if enforcing `FP` fairness pushes the classifier towards the constant classifier, then it will also satisify `FN` rate fairness, since the constant classifier is perfectly fair. However, if the hypothesis class is sufficiently rich, then one would expect that ceteris paribus since we are optimizing for error in addition to `FP` rate fairness, the algorithm would increase `FN` rate unfairness in order to decrease error. Below we trace the FN vs. FP rate tradeoff across a range of input $\\gamma$, where the classifier is optimized only for `FP` rate fairness."
+ ]
},
{
- "data": {
- "image/png": "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\n",
- "text/plain": [
- ""
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "is_executing": true
+ },
+ "id": "0xLIr5QdhI5N",
+ "outputId": "33fb403b-09c0-44a7-9c55-260319380020"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "gamma: 0.001 gamma: 0.002 gamma: 0.003 gamma: 0.004 gamma: 0.005 gamma: 0.0075 gamma: 0.01 gamma: 0.02 gamma: 0.03 gamma: 0.05 "
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "def fp_vs_fn(dataset, gamma_list, iters):\n",
+ " fp_auditor = Auditor(dataset, 'FP')\n",
+ " fn_auditor = Auditor(dataset, 'FN')\n",
+ " fp_violations = []\n",
+ " fn_violations = []\n",
+ " for g in gamma_list:\n",
+ " print('gamma: {} '.format(g), end =\" \")\n",
+ " fair_model = GerryFairClassifier(C=100, printflag=False, gamma=g, max_iters=iters)\n",
+ " fair_model.gamma=g\n",
+ " fair_model.fit(dataset)\n",
+ " predictions = array_to_tuple((fair_model.predict(dataset)).labels)\n",
+ " _, fp_diff = fp_auditor.audit(predictions)\n",
+ " _, fn_diff = fn_auditor.audit(predictions)\n",
+ " fp_violations.append(fp_diff)\n",
+ " fn_violations.append(fn_diff)\n",
+ "\n",
+ " plt.plot(fp_violations, fn_violations, label='adult')\n",
+ " plt.xlabel('False Positive Disparity')\n",
+ " plt.ylabel('False Negative Disparity')\n",
+ " plt.legend()\n",
+ " plt.title('FP vs FN Unfairness')\n",
+ " plt.savefig('gerryfair_fp_fn.png')\n",
+ " plt.close()\n",
+ "\n",
+ "gamma_list = [0.001, 0.002, 0.003, 0.004, 0.005, 0.0075, 0.01, 0.02, 0.03, 0.05]\n",
+ "fp_vs_fn(data_set, gamma_list, pareto_iters)\n",
+ "Image(filename='gerryfair_fp_fn.png')"
]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "pycharm": {
+ "is_executing": true
+ },
+ "id": "wn6BQLiFhI5N"
+ },
+ "outputs": [],
+ "source": []
}
- ],
- "source": [
- "def fp_vs_fn(dataset, gamma_list, iters):\n",
- " fp_auditor = Auditor(dataset, 'FP')\n",
- " fn_auditor = Auditor(dataset, 'FN')\n",
- " fp_violations = []\n",
- " fn_violations = []\n",
- " for g in gamma_list:\n",
- " print('gamma: {} '.format(g), end =\" \")\n",
- " fair_model = GerryFairClassifier(C=100, printflag=False, gamma=g, max_iters=iters)\n",
- " fair_model.gamma=g\n",
- " fair_model.fit(dataset)\n",
- " predictions = array_to_tuple((fair_model.predict(dataset)).labels)\n",
- " _, fp_diff = fp_auditor.audit(predictions)\n",
- " _, fn_diff = fn_auditor.audit(predictions)\n",
- " fp_violations.append(fp_diff)\n",
- " fn_violations.append(fn_diff)\n",
- "\n",
- " plt.plot(fp_violations, fn_violations, label='adult')\n",
- " plt.xlabel('False Positive Disparity')\n",
- " plt.ylabel('False Negative Disparity')\n",
- " plt.legend()\n",
- " plt.title('FP vs FN Unfairness')\n",
- " plt.savefig('gerryfair_fp_fn.png')\n",
- " plt.close()\n",
- "\n",
- "gamma_list = [0.001, 0.002, 0.003, 0.004, 0.005, 0.0075, 0.01, 0.02, 0.03, 0.05]\n",
- "fp_vs_fn(data_set, gamma_list, pareto_iters)\n",
- "Image(filename='gerryfair_fp_fn.png')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.9"
+ },
"pycharm": {
- "is_executing": true
+ "stem_cell": {
+ "cell_type": "raw",
+ "metadata": {
+ "collapsed": false
+ },
+ "source": []
+ }
+ },
+ "colab": {
+ "provenance": []
}
- },
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
},
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.9"
- },
- "pycharm": {
- "stem_cell": {
- "cell_type": "raw",
- "metadata": {
- "collapsed": false
- },
- "source": []
- }
- }
- },
- "nbformat": 4,
- "nbformat_minor": 1
-}
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
\ No newline at end of file
diff --git a/examples/demo_json_explainers.ipynb b/examples/demo_json_explainers.ipynb
index da23274c..fe8e8ddb 100644
--- a/examples/demo_json_explainers.ipynb
+++ b/examples/demo_json_explainers.ipynb
@@ -1,263 +1,309 @@
{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "# Load all necessary packages\n",
- "import sys\n",
- "sys.path.append(\"../\")\n",
- "from collections import OrderedDict\n",
- "import json\n",
- "from pprint import pprint\n",
- "from aif360.datasets import GermanDataset\n",
- "from aif360.metrics import BinaryLabelDatasetMetric\n",
- "from aif360.explainers import MetricTextExplainer, MetricJSONExplainer\n",
- "from IPython.display import JSON, display_json"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "##### Load dataset"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "gd = GermanDataset()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "##### Create metrics"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "priv = [{'sex': 1}]\n",
- "unpriv = [{'sex': 0}]\n",
- "bldm = BinaryLabelDatasetMetric(gd, unprivileged_groups=unpriv, privileged_groups=priv)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "##### Create explainers"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "text_expl = MetricTextExplainer(bldm)\n",
- "json_expl = MetricJSONExplainer(bldm)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "##### Text explanations"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
+ "cells": [
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Number of positive-outcome instances: 700.0\n"
- ]
- }
- ],
- "source": [
- "print(text_expl.num_positives())"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "source": [
+ "[](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/main/examples/demo_json_explainers.ipynb)\n"
+ ],
+ "metadata": {
+ "id": "PXISC-e0l2JJ"
+ }
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Mean difference (mean label value on privileged instances - mean label value on unprivileged instances): -0.0748013090229\n"
- ]
- }
- ],
- "source": [
- "print(text_expl.mean_difference())"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true,
+ "id": "s-b2u2Rvl013"
+ },
+ "outputs": [],
+ "source": [
+ "# Load all necessary packages\n",
+ "import sys\n",
+ "sys.path.append(\"../\")\n",
+ "from collections import OrderedDict\n",
+ "import json\n",
+ "from pprint import pprint\n",
+ "from aif360.datasets import GermanDataset\n",
+ "from aif360.metrics import BinaryLabelDatasetMetric\n",
+ "from aif360.explainers import MetricTextExplainer, MetricJSONExplainer\n",
+ "from IPython.display import JSON, display_json"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Disparate impact (probability of favorable outcome for unprivileged instances / probability of favorable outcome for privileged instances): 0.896567328205\n"
- ]
- }
- ],
- "source": [
- "print(text_expl.disparate_impact())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "##### JSON Explanations"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "def format_json(json_str):\n",
- " return json.dumps(json.loads(json_str, object_pairs_hook=OrderedDict), indent=2)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "3HohcQsBl016"
+ },
+ "source": [
+ "##### Load dataset"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{\n",
- " \"metric\": \"num_positives\", \n",
- " \"message\": \"Number of positive-outcome instances: 700.0\", \n",
- " \"numPositives\": 700.0, \n",
- " \"description\": \"Computed as the number of positive instances for the given (privileged or unprivileged) group.\", \n",
- " \"ideal\": \"The ideal value of this metric lies in the total number of positive instances made available\"\n",
- "}\n"
- ]
- }
- ],
- "source": [
- "print(format_json(json_expl.num_positives()))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true,
+ "id": "vHsL60ZSl017"
+ },
+ "outputs": [],
+ "source": [
+ "gd = GermanDataset()"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{\n",
- " \"metric\": \"mean_difference\", \n",
- " \"message\": \"Mean difference (mean label value on privileged instances - mean label value on unprivileged instances): -0.0748013090229\", \n",
- " \"numPositivesUnprivileged\": 201.0, \n",
- " \"numInstancesUnprivileged\": 310.0, \n",
- " \"numPositivesPrivileged\": 499.0, \n",
- " \"numInstancesPrivileged\": 690.0, \n",
- " \"description\": \"Computed as the difference of the rate of favorable outcomes received by the unprivileged group to the privileged group.\", \n",
- " \"ideal\": \"The ideal value of this metric is 0.0\"\n",
- "}\n"
- ]
- }
- ],
- "source": [
- "print(format_json(json_expl.mean_difference()))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Fy7C2Lttl017"
+ },
+ "source": [
+ "##### Create metrics"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true,
+ "id": "BWIQjCUdl018"
+ },
+ "outputs": [],
+ "source": [
+ "priv = [{'sex': 1}]\n",
+ "unpriv = [{'sex': 0}]\n",
+ "bldm = BinaryLabelDatasetMetric(gd, unprivileged_groups=unpriv, privileged_groups=priv)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "eUsxBOxVl018"
+ },
+ "source": [
+ "##### Create explainers"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true,
+ "id": "7agLzgCCl018"
+ },
+ "outputs": [],
+ "source": [
+ "text_expl = MetricTextExplainer(bldm)\n",
+ "json_expl = MetricJSONExplainer(bldm)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "hEKcHC3Fl019"
+ },
+ "source": [
+ "##### Text explanations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "4yfddrtAl019",
+ "outputId": "680af6c9-53c0-4b51-d4b6-d09207f00ccd"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Number of positive-outcome instances: 700.0\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(text_expl.num_positives())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Xg91poCjl01-",
+ "outputId": "e310a8ce-3730-4e08-dafd-6f9ecd26945a"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Mean difference (mean label value on privileged instances - mean label value on unprivileged instances): -0.0748013090229\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(text_expl.mean_difference())"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "{\n",
- " \"metric\": \"disparate_impact\", \n",
- " \"message\": \"Disparate impact (probability of favorable outcome for unprivileged instances / probability of favorable outcome for privileged instances): 0.896567328205\", \n",
- " \"numPositivePredictionsUnprivileged\": 201.0, \n",
- " \"numUnprivileged\": 310.0, \n",
- " \"numPositivePredictionsPrivileged\": 499.0, \n",
- " \"numPrivileged\": 690.0, \n",
- " \"description\": \"Computed as the ratio of likelihood of favorable outcome for the unprivileged group to that of the privileged group.\", \n",
- " \"ideal\": \"The ideal value of this metric is 1.0\"\n",
- "}\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "W9HdX1wUl01-",
+ "outputId": "aff81d6b-f850-4a9c-e25a-60e77c69b093"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Disparate impact (probability of favorable outcome for unprivileged instances / probability of favorable outcome for privileged instances): 0.896567328205\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(text_expl.disparate_impact())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "LA2uVnjPl01_"
+ },
+ "source": [
+ "##### JSON Explanations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true,
+ "id": "JCosLnQbl01_"
+ },
+ "outputs": [],
+ "source": [
+ "def format_json(json_str):\n",
+ " return json.dumps(json.loads(json_str, object_pairs_hook=OrderedDict), indent=2)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "F1nemIIel01_",
+ "outputId": "7c03e9aa-4aee-4862-ddd5-e7abd20d5941"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{\n",
+ " \"metric\": \"num_positives\", \n",
+ " \"message\": \"Number of positive-outcome instances: 700.0\", \n",
+ " \"numPositives\": 700.0, \n",
+ " \"description\": \"Computed as the number of positive instances for the given (privileged or unprivileged) group.\", \n",
+ " \"ideal\": \"The ideal value of this metric lies in the total number of positive instances made available\"\n",
+ "}\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(format_json(json_expl.num_positives()))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "vaBapB4xl01_",
+ "outputId": "381346be-c9e9-4f41-dbaf-2c18cdd06ff6"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{\n",
+ " \"metric\": \"mean_difference\", \n",
+ " \"message\": \"Mean difference (mean label value on privileged instances - mean label value on unprivileged instances): -0.0748013090229\", \n",
+ " \"numPositivesUnprivileged\": 201.0, \n",
+ " \"numInstancesUnprivileged\": 310.0, \n",
+ " \"numPositivesPrivileged\": 499.0, \n",
+ " \"numInstancesPrivileged\": 690.0, \n",
+ " \"description\": \"Computed as the difference of the rate of favorable outcomes received by the unprivileged group to the privileged group.\", \n",
+ " \"ideal\": \"The ideal value of this metric is 0.0\"\n",
+ "}\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(format_json(json_expl.mean_difference()))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "qExRlmw8l02A",
+ "outputId": "11b63f8e-4cc6-4192-e600-de0231ae33cc"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{\n",
+ " \"metric\": \"disparate_impact\", \n",
+ " \"message\": \"Disparate impact (probability of favorable outcome for unprivileged instances / probability of favorable outcome for privileged instances): 0.896567328205\", \n",
+ " \"numPositivePredictionsUnprivileged\": 201.0, \n",
+ " \"numUnprivileged\": 310.0, \n",
+ " \"numPositivePredictionsPrivileged\": 499.0, \n",
+ " \"numPrivileged\": 690.0, \n",
+ " \"description\": \"Computed as the ratio of likelihood of favorable outcome for the unprivileged group to that of the privileged group.\", \n",
+ " \"ideal\": \"The ideal value of this metric is 1.0\"\n",
+ "}\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(format_json(json_expl.disparate_impact()))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "collapsed": true,
+ "id": "mu6tkv_xl02A"
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 2",
+ "language": "python",
+ "name": "python2"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.11"
+ },
+ "colab": {
+ "provenance": []
}
- ],
- "source": [
- "print(format_json(json_expl.disparate_impact()))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 2",
- "language": "python",
- "name": "python2"
},
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 2
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython2",
- "version": "2.7.11"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
\ No newline at end of file
diff --git a/examples/demo_lfr.ipynb b/examples/demo_lfr.ipynb
index 134a2729..9ed91c43 100644
--- a/examples/demo_lfr.ipynb
+++ b/examples/demo_lfr.ipynb
@@ -1,473 +1,528 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "#### This notebook demonstrates the use of the learning fair representations algorithm for bias mitigation\n",
- "Learning fair representations [1] is a pre-processing technique that finds a latent representation which encodes the data well but obfuscates information about protected attributes. We will see how to use this algorithm for learning representations that encourage individual fairness and apply them on the Adult dataset.\n",
- "\n",
- "References:\n",
- "\n",
- "[1] R. Zemel, Y. Wu, K. Swersky, T. Pitassi, and C. Dwork, \"Learning Fair Representations.\" \n",
- "International Conference on Machine Learning, 2013."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "%matplotlib inline\n",
- "# Load all necessary packages\n",
- "import sys\n",
- "sys.path.append(\"../\")\n",
- "from aif360.datasets import BinaryLabelDataset\n",
- "from aif360.datasets import AdultDataset\n",
- "from aif360.metrics import BinaryLabelDatasetMetric\n",
- "from aif360.metrics import ClassificationMetric\n",
- "from aif360.metrics.utils import compute_boolean_conditioning_vector\n",
- "\n",
- "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_adult\n",
- "from aif360.algorithms.preprocessing.lfr import LFR\n",
- "\n",
- "from sklearn.linear_model import LogisticRegression\n",
- "from sklearn.preprocessing import StandardScaler\n",
- "from sklearn.metrics import accuracy_score\n",
- "from sklearn.metrics import classification_report\n",
- "\n",
- "from IPython.display import Markdown, display\n",
- "import matplotlib.pyplot as plt\n",
- "import numpy as np\n",
- "\n",
- "from common_utils import compute_metrics"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "#### Load dataset and set options"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Get the dataset and split into train and test\n",
- "dataset_orig = load_preproc_data_adult()\n",
- "dataset_orig_train, dataset_orig_test = dataset_orig.split([0.7], shuffle=True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "#### Clean up training data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
+ "cells": [
{
- "data": {
- "text/markdown": [
- "#### Training Dataset shape"
+ "cell_type": "markdown",
+ "source": [
+ "[](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/main/examples/demo_lfr.ipynb)\n"
],
- "text/plain": [
- ""
+ "metadata": {
+ "id": "tVXbNnjsmVIz"
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "BKhA67dOmMcG"
+ },
+ "source": [
+ "#### This notebook demonstrates the use of the learning fair representations algorithm for bias mitigation\n",
+ "Learning fair representations [1] is a pre-processing technique that finds a latent representation which encodes the data well but obfuscates information about protected attributes. We will see how to use this algorithm for learning representations that encourage individual fairness and apply them on the Adult dataset.\n",
+ "\n",
+ "References:\n",
+ "\n",
+ "[1] R. Zemel, Y. Wu, K. Swersky, T. Pitassi, and C. Dwork, \"Learning Fair Representations.\"\n",
+ "International Conference on Machine Learning, 2013."
]
- },
- "metadata": {},
- "output_type": "display_data"
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "(34189, 18)\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "wG_aksDfmMcJ"
+ },
+ "outputs": [],
+ "source": [
+ "%matplotlib inline\n",
+ "# Load all necessary packages\n",
+ "import sys\n",
+ "sys.path.append(\"../\")\n",
+ "from aif360.datasets import BinaryLabelDataset\n",
+ "from aif360.datasets import AdultDataset\n",
+ "from aif360.metrics import BinaryLabelDatasetMetric\n",
+ "from aif360.metrics import ClassificationMetric\n",
+ "from aif360.metrics.utils import compute_boolean_conditioning_vector\n",
+ "\n",
+ "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_adult\n",
+ "from aif360.algorithms.preprocessing.lfr import LFR\n",
+ "\n",
+ "from sklearn.linear_model import LogisticRegression\n",
+ "from sklearn.preprocessing import StandardScaler\n",
+ "from sklearn.metrics import accuracy_score\n",
+ "from sklearn.metrics import classification_report\n",
+ "\n",
+ "from IPython.display import Markdown, display\n",
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "\n",
+ "from common_utils import compute_metrics"
+ ]
},
{
- "data": {
- "text/markdown": [
- "#### Favorable and unfavorable labels"
- ],
- "text/plain": [
- ""
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "AtUDRcFzmMcK"
+ },
+ "source": [
+ "#### Load dataset and set options"
]
- },
- "metadata": {},
- "output_type": "display_data"
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "1.0 0.0\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "UeELy5k1mMcL"
+ },
+ "outputs": [],
+ "source": [
+ "# Get the dataset and split into train and test\n",
+ "dataset_orig = load_preproc_data_adult()\n",
+ "dataset_orig_train, dataset_orig_test = dataset_orig.split([0.7], shuffle=True)"
+ ]
},
{
- "data": {
- "text/markdown": [
- "#### Protected attribute names"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "rKE1eMYemMcL"
+ },
+ "source": [
+ "#### Clean up training data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "1rBcLsI-mMcL",
+ "outputId": "027ce352-06e6-478c-ceb6-1b810401643d"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/markdown": [
+ "#### Training Dataset shape"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "(34189, 18)\n"
+ ]
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "#### Favorable and unfavorable labels"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1.0 0.0\n"
+ ]
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "#### Protected attribute names"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "['sex', 'race']\n"
+ ]
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "#### Privileged and unprivileged protected attribute values"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[array([1.]), array([1.])] [array([0.]), array([0.])]\n"
+ ]
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "#### Dataset feature names"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "['race', 'sex', 'Age (decade)=10', 'Age (decade)=20', 'Age (decade)=30', 'Age (decade)=40', 'Age (decade)=50', 'Age (decade)=60', 'Age (decade)=>=70', 'Education Years=6', 'Education Years=7', 'Education Years=8', 'Education Years=9', 'Education Years=10', 'Education Years=11', 'Education Years=12', 'Education Years=<6', 'Education Years=>12']\n"
+ ]
+ }
],
- "text/plain": [
- ""
+ "source": [
+ "# print out some labels, names, etc.\n",
+ "display(Markdown(\"#### Training Dataset shape\"))\n",
+ "print(dataset_orig_train.features.shape)\n",
+ "display(Markdown(\"#### Favorable and unfavorable labels\"))\n",
+ "print(dataset_orig_train.favorable_label, dataset_orig_train.unfavorable_label)\n",
+ "display(Markdown(\"#### Protected attribute names\"))\n",
+ "print(dataset_orig_train.protected_attribute_names)\n",
+ "display(Markdown(\"#### Privileged and unprivileged protected attribute values\"))\n",
+ "print(dataset_orig_train.privileged_protected_attributes,\n",
+ " dataset_orig_train.unprivileged_protected_attributes)\n",
+ "display(Markdown(\"#### Dataset feature names\"))\n",
+ "print(dataset_orig_train.feature_names)"
]
- },
- "metadata": {},
- "output_type": "display_data"
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "['sex', 'race']\n"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "4pY8KI0QmMcM"
+ },
+ "source": [
+ "#### Metric for original training data"
+ ]
},
{
- "data": {
- "text/markdown": [
- "#### Privileged and unprivileged protected attribute values"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "n5qIXMbemMcM",
+ "outputId": "ad97d8eb-3381-4388-982f-5c1f070d9504"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/markdown": [
+ "#### Original training dataset"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Difference in mean outcomes between unprivileged and privileged groups = -0.193139\n"
+ ]
+ },
+ {
+ "data": {
+ "text/markdown": [
+ "#### Original test dataset"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Difference in mean outcomes between unprivileged and privileged groups = -0.197697\n"
+ ]
+ }
],
- "text/plain": [
- ""
+ "source": [
+ "# Metric for the original dataset\n",
+ "privileged_groups = [{'sex': 1.0}]\n",
+ "unprivileged_groups = [{'sex': 0.0}]\n",
+ "\n",
+ "metric_orig_train = BinaryLabelDatasetMetric(dataset_orig_train,\n",
+ " unprivileged_groups=unprivileged_groups,\n",
+ " privileged_groups=privileged_groups)\n",
+ "display(Markdown(\"#### Original training dataset\"))\n",
+ "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_orig_train.mean_difference())\n",
+ "metric_orig_test = BinaryLabelDatasetMetric(dataset_orig_test,\n",
+ " unprivileged_groups=unprivileged_groups,\n",
+ " privileged_groups=privileged_groups)\n",
+ "display(Markdown(\"#### Original test dataset\"))\n",
+ "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_orig_test.mean_difference())\n"
]
- },
- "metadata": {},
- "output_type": "display_data"
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "[array([1.]), array([1.])] [array([0.]), array([0.])]\n"
- ]
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xvap7BOvmMcN"
+ },
+ "source": [
+ "#### Train with and transform the original training data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "HelNehJ4mMcN"
+ },
+ "outputs": [],
+ "source": [
+ "scale_orig = StandardScaler()\n",
+ "dataset_orig_train.features = scale_orig.fit_transform(dataset_orig_train.features)\n",
+ "dataset_orig_test.features = scale_orig.transform(dataset_orig_test.features)"
+ ]
},
{
- "data": {
- "text/markdown": [
- "#### Dataset feature names"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "V3qCK0q-mMcN",
+ "outputId": "76a156cf-7de7-40ef-87a1-c89add05e108"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "step: 0, loss: 1.0939550595829053, L_x: 2.531834521858599, L_y: 0.8200826015334493, L_z: 0.010344502931797964\n",
+ "step: 250, loss: 0.9162820270109503, L_x: 2.529109218043187, L_y: 0.6432961063010657, L_z: 0.010037499452782905\n",
+ "step: 500, loss: 0.8207071510514392, L_x: 2.5204911168067197, L_y: 0.5500397646035967, L_z: 0.00930913738358528\n",
+ "step: 750, loss: 0.8102771268166408, L_x: 2.511873834704061, L_y: 0.5427956868742799, L_z: 0.008147028235977415\n",
+ "step: 1000, loss: 0.7996570283329768, L_x: 2.480828451323288, L_y: 0.5399446552800813, L_z: 0.00581476396028337\n",
+ "step: 1250, loss: 0.7844631169970814, L_x: 2.4242508289183613, L_y: 0.5304307199052671, L_z: 0.005803657099989009\n",
+ "step: 1500, loss: 0.7653305722023572, L_x: 2.3297047767431986, L_y: 0.5176248867874912, L_z: 0.007367603870273078\n",
+ "step: 1750, loss: 0.7154304631442515, L_x: 2.085955877234543, L_y: 0.48081670080967953, L_z: 0.013009087305558827\n",
+ "step: 2000, loss: 0.6906420918886886, L_x: 1.896344106091722, L_y: 0.4646651544564373, L_z: 0.018171263411539594\n",
+ "step: 2250, loss: 0.6783680937630076, L_x: 1.7895665853948028, L_y: 0.4587714378849466, L_z: 0.020319998669290275\n",
+ "step: 2500, loss: 0.6725576747654705, L_x: 1.742061633693402, L_y: 0.4577729094336143, L_z: 0.020289300981257967\n",
+ "step: 2750, loss: 0.6694103860159343, L_x: 1.7548885984309939, L_y: 0.4545867175857845, L_z: 0.019667404293525217\n",
+ "step: 3000, loss: 0.6658207636894926, L_x: 1.7515234617350093, L_y: 0.4539151313299769, L_z: 0.018376643093007367\n",
+ "step: 3250, loss: 0.6481415219979564, L_x: 1.7252276686316934, L_y: 0.4491717858033674, L_z: 0.013223484665709846\n",
+ "step: 3500, loss: 0.645366243737316, L_x: 1.7196207136719521, L_y: 0.4482843307446003, L_z: 0.012559920812760247\n",
+ "step: 3750, loss: 0.6425278186287126, L_x: 1.7117758355776211, L_y: 0.4473063883366716, L_z: 0.012021923367139413\n",
+ "step: 4000, loss: 0.6419409673076768, L_x: 1.7092609385556714, L_y: 0.44744616781598634, L_z: 0.011784352818061686\n",
+ "step: 4250, loss: 0.6377801462539607, L_x: 1.6917081956472533, L_y: 0.4496335370425122, L_z: 0.009487894823361622\n"
+ ]
+ }
],
- "text/plain": [
- ""
+ "source": [
+ "# Input recontruction quality - Ax\n",
+ "# Fairness constraint - Az\n",
+ "# Output prediction error - Ay\n",
+ "\n",
+ "privileged_groups = [{'sex': 1}]\n",
+ "unprivileged_groups = [{'sex': 0}]\n",
+ "\n",
+ "TR = LFR(unprivileged_groups=unprivileged_groups,\n",
+ " privileged_groups=privileged_groups,\n",
+ " k=10, Ax=0.1, Ay=1.0, Az=2.0,\n",
+ " verbose=1\n",
+ " )\n",
+ "TR = TR.fit(dataset_orig_train, maxiter=5000, maxfun=5000)"
]
- },
- "metadata": {},
- "output_type": "display_data"
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "['race', 'sex', 'Age (decade)=10', 'Age (decade)=20', 'Age (decade)=30', 'Age (decade)=40', 'Age (decade)=50', 'Age (decade)=60', 'Age (decade)=>=70', 'Education Years=6', 'Education Years=7', 'Education Years=8', 'Education Years=9', 'Education Years=10', 'Education Years=11', 'Education Years=12', 'Education Years=<6', 'Education Years=>12']\n"
- ]
- }
- ],
- "source": [
- "# print out some labels, names, etc.\n",
- "display(Markdown(\"#### Training Dataset shape\"))\n",
- "print(dataset_orig_train.features.shape)\n",
- "display(Markdown(\"#### Favorable and unfavorable labels\"))\n",
- "print(dataset_orig_train.favorable_label, dataset_orig_train.unfavorable_label)\n",
- "display(Markdown(\"#### Protected attribute names\"))\n",
- "print(dataset_orig_train.protected_attribute_names)\n",
- "display(Markdown(\"#### Privileged and unprivileged protected attribute values\"))\n",
- "print(dataset_orig_train.privileged_protected_attributes, \n",
- " dataset_orig_train.unprivileged_protected_attributes)\n",
- "display(Markdown(\"#### Dataset feature names\"))\n",
- "print(dataset_orig_train.feature_names)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "#### Metric for original training data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "sO4LI_hbmMcO"
+ },
+ "outputs": [],
+ "source": [
+ "# Transform training data and align features\n",
+ "dataset_transf_train = TR.transform(dataset_orig_train)\n",
+ "dataset_transf_test = TR.transform(dataset_orig_test)"
+ ]
+ },
{
- "data": {
- "text/markdown": [
- "#### Original training dataset"
- ],
- "text/plain": [
- ""
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "ZNXZ4AlUmMcO"
+ },
+ "outputs": [],
+ "source": [
+ "print(classification_report(dataset_orig_test.labels, dataset_transf_test.labels))"
]
- },
- "metadata": {},
- "output_type": "display_data"
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Difference in mean outcomes between unprivileged and privileged groups = -0.193139\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "UA_fxUSemMcO"
+ },
+ "outputs": [],
+ "source": [
+ "metric_transf_train = BinaryLabelDatasetMetric(dataset_transf_train,\n",
+ " unprivileged_groups=unprivileged_groups,\n",
+ " privileged_groups=privileged_groups)\n",
+ "display(Markdown(\"#### Transformed training dataset\"))\n",
+ "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_transf_train.mean_difference())\n",
+ "metric_transf_test = BinaryLabelDatasetMetric(dataset_transf_test,\n",
+ " unprivileged_groups=unprivileged_groups,\n",
+ " privileged_groups=privileged_groups)\n",
+ "display(Markdown(\"#### Transformed test dataset\"))\n",
+ "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_transf_test.mean_difference())\n"
+ ]
},
{
- "data": {
- "text/markdown": [
- "#### Original test dataset"
- ],
- "text/plain": [
- ""
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "PRATChL3mMcO"
+ },
+ "outputs": [],
+ "source": [
+ "from common_utils import compute_metrics\n",
+ "\n",
+ "display(Markdown(\"#### Predictions from transformed testing data\"))\n",
+ "bal_acc_arr_transf = []\n",
+ "disp_imp_arr_transf = []\n",
+ "\n",
+ "class_thresh_arr = np.linspace(0.01, 0.99, 100)\n",
+ "\n",
+ "dataset_transf_test_new = dataset_orig_test.copy(deepcopy=True)\n",
+ "dataset_transf_test_new.scores = dataset_transf_test.scores\n",
+ "\n",
+ "\n",
+ "for thresh in class_thresh_arr:\n",
+ "\n",
+ " fav_inds = dataset_transf_test_new.scores > thresh\n",
+ " dataset_transf_test_new.labels[fav_inds] = 1.0\n",
+ " dataset_transf_test_new.labels[~fav_inds] = 0.0\n",
+ "\n",
+ " metric_test_aft = compute_metrics(dataset_orig_test, dataset_transf_test_new,\n",
+ " unprivileged_groups, privileged_groups,\n",
+ " disp = False)\n",
+ "\n",
+ " bal_acc_arr_transf.append(metric_test_aft[\"Balanced accuracy\"])\n",
+ " disp_imp_arr_transf.append(metric_test_aft[\"Disparate impact\"])"
]
- },
- "metadata": {},
- "output_type": "display_data"
},
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Difference in mean outcomes between unprivileged and privileged groups = -0.197697\n"
- ]
- }
- ],
- "source": [
- "# Metric for the original dataset\n",
- "privileged_groups = [{'sex': 1.0}]\n",
- "unprivileged_groups = [{'sex': 0.0}]\n",
- "\n",
- "metric_orig_train = BinaryLabelDatasetMetric(dataset_orig_train, \n",
- " unprivileged_groups=unprivileged_groups,\n",
- " privileged_groups=privileged_groups)\n",
- "display(Markdown(\"#### Original training dataset\"))\n",
- "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_orig_train.mean_difference())\n",
- "metric_orig_test = BinaryLabelDatasetMetric(dataset_orig_test, \n",
- " unprivileged_groups=unprivileged_groups,\n",
- " privileged_groups=privileged_groups)\n",
- "display(Markdown(\"#### Original test dataset\"))\n",
- "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_orig_test.mean_difference())\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "#### Train with and transform the original training data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "scale_orig = StandardScaler()\n",
- "dataset_orig_train.features = scale_orig.fit_transform(dataset_orig_train.features)\n",
- "dataset_orig_test.features = scale_orig.transform(dataset_orig_test.features)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "HGsipNVamMcO"
+ },
+ "outputs": [],
+ "source": [
+ "fig, ax1 = plt.subplots(figsize=(10,7))\n",
+ "ax1.plot(class_thresh_arr, bal_acc_arr_transf)\n",
+ "ax1.set_xlabel('Classification Thresholds', fontsize=16, fontweight='bold')\n",
+ "ax1.set_ylabel('Balanced Accuracy', color='b', fontsize=16, fontweight='bold')\n",
+ "ax1.xaxis.set_tick_params(labelsize=14)\n",
+ "ax1.yaxis.set_tick_params(labelsize=14)\n",
+ "\n",
+ "\n",
+ "ax2 = ax1.twinx()\n",
+ "ax2.plot(class_thresh_arr, np.abs(1.0-np.array(disp_imp_arr_transf)), color='r')\n",
+ "ax2.set_ylabel('abs(1-disparate impact)', color='r', fontsize=16, fontweight='bold')\n",
+ "ax2.yaxis.set_tick_params(labelsize=14)\n",
+ "ax2.grid(True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "biB1DXjKmMcP"
+ },
+ "source": [
+ "abs(1-disparate impact) must be small (close to 0) for classifier predictions to be fair."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "DxtBqemwmMcP"
+ },
+ "outputs": [],
+ "source": [
+ "display(Markdown(\"#### Individual fairness metrics\"))\n",
+ "print(\"Consistency of labels in transformed training dataset= %f\" %metric_transf_train.consistency())\n",
+ "print(\"Consistency of labels in original training dataset= %f\" %metric_orig_train.consistency())\n",
+ "print(\"Consistency of labels in transformed test dataset= %f\" %metric_transf_test.consistency())\n",
+ "print(\"Consistency of labels in original test dataset= %f\" %metric_orig_test.consistency())"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "90gI7jtwmMcP"
+ },
+ "outputs": [],
+ "source": [
+ "def check_algorithm_success():\n",
+ " \"\"\"Transformed dataset consistency should be greater than original dataset.\"\"\"\n",
+ " assert metric_transf_test.consistency() > metric_orig_test.consistency(), \"Transformed dataset consistency should be greater than original dataset.\"\n",
+ "\n",
+ "check_algorithm_success()"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "step: 0, loss: 1.0939550595829053, L_x: 2.531834521858599, L_y: 0.8200826015334493, L_z: 0.010344502931797964\n",
- "step: 250, loss: 0.9162820270109503, L_x: 2.529109218043187, L_y: 0.6432961063010657, L_z: 0.010037499452782905\n",
- "step: 500, loss: 0.8207071510514392, L_x: 2.5204911168067197, L_y: 0.5500397646035967, L_z: 0.00930913738358528\n",
- "step: 750, loss: 0.8102771268166408, L_x: 2.511873834704061, L_y: 0.5427956868742799, L_z: 0.008147028235977415\n",
- "step: 1000, loss: 0.7996570283329768, L_x: 2.480828451323288, L_y: 0.5399446552800813, L_z: 0.00581476396028337\n",
- "step: 1250, loss: 0.7844631169970814, L_x: 2.4242508289183613, L_y: 0.5304307199052671, L_z: 0.005803657099989009\n",
- "step: 1500, loss: 0.7653305722023572, L_x: 2.3297047767431986, L_y: 0.5176248867874912, L_z: 0.007367603870273078\n",
- "step: 1750, loss: 0.7154304631442515, L_x: 2.085955877234543, L_y: 0.48081670080967953, L_z: 0.013009087305558827\n",
- "step: 2000, loss: 0.6906420918886886, L_x: 1.896344106091722, L_y: 0.4646651544564373, L_z: 0.018171263411539594\n",
- "step: 2250, loss: 0.6783680937630076, L_x: 1.7895665853948028, L_y: 0.4587714378849466, L_z: 0.020319998669290275\n",
- "step: 2500, loss: 0.6725576747654705, L_x: 1.742061633693402, L_y: 0.4577729094336143, L_z: 0.020289300981257967\n",
- "step: 2750, loss: 0.6694103860159343, L_x: 1.7548885984309939, L_y: 0.4545867175857845, L_z: 0.019667404293525217\n",
- "step: 3000, loss: 0.6658207636894926, L_x: 1.7515234617350093, L_y: 0.4539151313299769, L_z: 0.018376643093007367\n",
- "step: 3250, loss: 0.6481415219979564, L_x: 1.7252276686316934, L_y: 0.4491717858033674, L_z: 0.013223484665709846\n",
- "step: 3500, loss: 0.645366243737316, L_x: 1.7196207136719521, L_y: 0.4482843307446003, L_z: 0.012559920812760247\n",
- "step: 3750, loss: 0.6425278186287126, L_x: 1.7117758355776211, L_y: 0.4473063883366716, L_z: 0.012021923367139413\n",
- "step: 4000, loss: 0.6419409673076768, L_x: 1.7092609385556714, L_y: 0.44744616781598634, L_z: 0.011784352818061686\n",
- "step: 4250, loss: 0.6377801462539607, L_x: 1.6917081956472533, L_y: 0.4496335370425122, L_z: 0.009487894823361622\n"
- ]
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "GkkBIqXgmMcP"
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.10"
+ },
+ "colab": {
+ "provenance": []
}
- ],
- "source": [
- "# Input recontruction quality - Ax\n",
- "# Fairness constraint - Az\n",
- "# Output prediction error - Ay\n",
- "\n",
- "privileged_groups = [{'sex': 1}]\n",
- "unprivileged_groups = [{'sex': 0}]\n",
- " \n",
- "TR = LFR(unprivileged_groups=unprivileged_groups,\n",
- " privileged_groups=privileged_groups,\n",
- " k=10, Ax=0.1, Ay=1.0, Az=2.0,\n",
- " verbose=1\n",
- " )\n",
- "TR = TR.fit(dataset_orig_train, maxiter=5000, maxfun=5000)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Transform training data and align features\n",
- "dataset_transf_train = TR.transform(dataset_orig_train)\n",
- "dataset_transf_test = TR.transform(dataset_orig_test)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(classification_report(dataset_orig_test.labels, dataset_transf_test.labels))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "metric_transf_train = BinaryLabelDatasetMetric(dataset_transf_train, \n",
- " unprivileged_groups=unprivileged_groups,\n",
- " privileged_groups=privileged_groups)\n",
- "display(Markdown(\"#### Transformed training dataset\"))\n",
- "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_transf_train.mean_difference())\n",
- "metric_transf_test = BinaryLabelDatasetMetric(dataset_transf_test, \n",
- " unprivileged_groups=unprivileged_groups,\n",
- " privileged_groups=privileged_groups)\n",
- "display(Markdown(\"#### Transformed test dataset\"))\n",
- "print(\"Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_transf_test.mean_difference())\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "from common_utils import compute_metrics\n",
- "\n",
- "display(Markdown(\"#### Predictions from transformed testing data\"))\n",
- "bal_acc_arr_transf = []\n",
- "disp_imp_arr_transf = []\n",
- "\n",
- "class_thresh_arr = np.linspace(0.01, 0.99, 100)\n",
- "\n",
- "dataset_transf_test_new = dataset_orig_test.copy(deepcopy=True)\n",
- "dataset_transf_test_new.scores = dataset_transf_test.scores\n",
- "\n",
- "\n",
- "for thresh in class_thresh_arr:\n",
- " \n",
- " fav_inds = dataset_transf_test_new.scores > thresh\n",
- " dataset_transf_test_new.labels[fav_inds] = 1.0\n",
- " dataset_transf_test_new.labels[~fav_inds] = 0.0\n",
- " \n",
- " metric_test_aft = compute_metrics(dataset_orig_test, dataset_transf_test_new, \n",
- " unprivileged_groups, privileged_groups,\n",
- " disp = False)\n",
- "\n",
- " bal_acc_arr_transf.append(metric_test_aft[\"Balanced accuracy\"])\n",
- " disp_imp_arr_transf.append(metric_test_aft[\"Disparate impact\"])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "fig, ax1 = plt.subplots(figsize=(10,7))\n",
- "ax1.plot(class_thresh_arr, bal_acc_arr_transf)\n",
- "ax1.set_xlabel('Classification Thresholds', fontsize=16, fontweight='bold')\n",
- "ax1.set_ylabel('Balanced Accuracy', color='b', fontsize=16, fontweight='bold')\n",
- "ax1.xaxis.set_tick_params(labelsize=14)\n",
- "ax1.yaxis.set_tick_params(labelsize=14)\n",
- "\n",
- "\n",
- "ax2 = ax1.twinx()\n",
- "ax2.plot(class_thresh_arr, np.abs(1.0-np.array(disp_imp_arr_transf)), color='r')\n",
- "ax2.set_ylabel('abs(1-disparate impact)', color='r', fontsize=16, fontweight='bold')\n",
- "ax2.yaxis.set_tick_params(labelsize=14)\n",
- "ax2.grid(True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "abs(1-disparate impact) must be small (close to 0) for classifier predictions to be fair."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "display(Markdown(\"#### Individual fairness metrics\"))\n",
- "print(\"Consistency of labels in transformed training dataset= %f\" %metric_transf_train.consistency())\n",
- "print(\"Consistency of labels in original training dataset= %f\" %metric_orig_train.consistency())\n",
- "print(\"Consistency of labels in transformed test dataset= %f\" %metric_transf_test.consistency())\n",
- "print(\"Consistency of labels in original test dataset= %f\" %metric_orig_test.consistency())"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "def check_algorithm_success():\n",
- " \"\"\"Transformed dataset consistency should be greater than original dataset.\"\"\"\n",
- " assert metric_transf_test.consistency() > metric_orig_test.consistency(), \"Transformed dataset consistency should be greater than original dataset.\"\n",
- "\n",
- "check_algorithm_success() "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
},
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.10"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
\ No newline at end of file
diff --git a/examples/demo_mdss_classifier_metric.ipynb b/examples/demo_mdss_classifier_metric.ipynb
index 004d2956..13f94437 100644
--- a/examples/demo_mdss_classifier_metric.ipynb
+++ b/examples/demo_mdss_classifier_metric.ipynb
@@ -1,1230 +1,1381 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Bias scan using Multi-Dimensional Subset Scan (MDSS)\n",
- "\n",
- "\"Identifying Significant Predictive Bias in Classifiers\" https://arxiv.org/abs/1611.08292\n",
- "\n",
- "The goal of bias scan is to identify a subgroup(s) that has significantly more predictive bias than would be expected from an unbiased classifier. There are $\\prod_{m=1}^{M}\\left(2^{|X_{m}|}-1\\right)$ unique subgroups from a dataset with $M$ features, with each feature having $|X_{m}|$ discretized values, where a subgroup is any $M$-dimension\n",
- "Cartesian set product, between subsets of feature-values from each feature --- excluding the empty set. Bias scan mitigates this computational hurdle by approximately identifing the most statistically biased subgroup in linear time (rather than exponential).\n",
- "\n",
- "\n",
- "We define the statistical measure of predictive bias function, $score_{bias}(S)$ as a likelihood ratio score and a function of a given subgroup $S$. The null hypothesis is that the given prediction's odds are correct for all subgroups in $\\mathcal{D}$:\n",
- "\n",
- "$$H_{0}:odds(y_{i})=\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}}\\ \\forall i\\in\\mathcal{D}.$$\n",
- "\n",
- "The alternative hypothesis assumes some constant multiplicative bias in the odds for some given subgroup $S$:\n",
- "\n",
- "$$H_{1}:\\ odds(y_{i})=q\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}},\\ \\text{where}\\ q>1\\ \\forall i\\in S\\ \\mathrm{and}\\ q=1\\ \\forall i\\notin S.$$\n",
- "\n",
- "In the classification setting, each observation's likelihood is Bernoulli distributed and assumed independent. This results in the following scoring function for a subgroup $S$:\n",
- "\n",
- "\\begin{align*}\n",
- "score_{bias}(S)= & \\max_{q}\\log\\prod_{i\\in S}\\frac{Bernoulli(\\frac{q\\hat{p}_{i}}{1-\\hat{p}_{i}+q\\hat{p}_{i}})}{Bernoulli(\\hat{p}_{i})}\\\\\n",
- "= & \\max_{q}\\log(q)\\sum_{i\\in S}y_{i}-\\sum_{i\\in S}\\log(1-\\hat{p}_{i}+q\\hat{p}_{i}).\n",
- "\\end{align*}\n",
- "Our bias scan is thus represented as: $S^{*}=FSS(\\mathcal{D},\\mathcal{E},F_{score})=MDSS(\\mathcal{D},\\hat{p},score_{bias})$.\n",
- "\n",
- "where $S^{*}$ is the detected most anomalous subgroup, $FSS$ is one of several subset scan algorithms for different problem settings, $\\mathcal{D}$ is a dataset with outcomes $Y$ and discretized features $\\mathcal{X}$, $\\mathcal{E}$ are a set of expectations or 'normal' values for $Y$, and $F_{score}$ is an expectation-based scoring statistic that measures the amount of anomalousness between subgroup observations and their expectations.\n",
- "\n",
- "Predictive bias emphasizes comparable predictions for a subgroup and its observations and Bias scan provides a more general method that can detect and characterize such bias, or poor classifier fit, in the larger space of all possible subgroups, without a priori specification."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "import itertools\n",
- "\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "\n",
- "from aif360.metrics import BinaryLabelDatasetMetric, MDSSClassificationMetric\n",
- "from aif360.detectors import bias_scan\n",
- "\n",
- "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_compas"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We'll demonstrate scoring a subset and finding the most anomalous subset with bias scan using the compas dataset.\n",
- "\n",
- "We can specify subgroups to be scored or scan for the most anomalous subgroup. Bias scan allows us to decide if we aim to identify bias as `higher` than expected probabilities or `lower` than expected probabilities. Depending on the favourable label, the corresponding subgroup may be categorized as priviledged or unprivileged."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "dataset_orig = load_preproc_data_compas()\n",
- "\n",
- "female_group = [{'sex': 1}]\n",
- "male_group = [{'sex': 0}]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The dataset has the categorical features one-hot encoded so we'll modify the dataset to convert them back \n",
- "to the categorical featues because scanning one-hot encoded features may find subgroups that are not meaningful e.g., a subgroup with 2 race values. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "dataset_orig_df = pd.DataFrame(dataset_orig.features, columns=dataset_orig.feature_names)\n",
- "\n",
- "age_cat = np.argmax(dataset_orig_df[['age_cat=Less than 25', 'age_cat=25 to 45',\n",
- " 'age_cat=Greater than 45']].values, axis=1).reshape(-1, 1)\n",
- "priors_count = np.argmax(dataset_orig_df[['priors_count=0', 'priors_count=1 to 3',\n",
- " 'priors_count=More than 3']].values, axis=1).reshape(-1, 1)\n",
- "c_charge_degree = np.argmax(dataset_orig_df[['c_charge_degree=M', 'c_charge_degree=F']].values, axis=1).reshape(-1, 1)\n",
- "\n",
- "features = np.concatenate((dataset_orig_df[['sex', 'race']].values, age_cat, priors_count,\n",
- " c_charge_degree, dataset_orig.labels), axis=1)\n",
- "feature_names = ['sex', 'race', 'age_cat', 'priors_count', 'c_charge_degree']"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {
- "scrolled": true
- },
- "outputs": [
+ "cells": [
{
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
sex
\n",
- "
race
\n",
- "
age_cat
\n",
- "
priors_count
\n",
- "
c_charge_degree
\n",
- "
two_year_recid
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
1
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
2.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
2
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
2.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
3
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
\n",
- "
\n",
- "
4
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ "cell_type": "markdown",
+ "source": [
+ "[](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/main/examples/demo_mdss_classifier_metric.ipynb)"
],
- "text/plain": [
- " sex race age_cat priors_count c_charge_degree two_year_recid\n",
- "0 0.0 0.0 1.0 0.0 1.0 1.0\n",
- "1 0.0 0.0 0.0 2.0 1.0 1.0\n",
- "2 0.0 1.0 1.0 2.0 1.0 1.0\n",
- "3 1.0 1.0 1.0 0.0 0.0 0.0\n",
- "4 0.0 1.0 1.0 0.0 1.0 0.0"
+ "metadata": {
+ "id": "zu6QM0J8CN0q"
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "kci10l16B90B"
+ },
+ "source": [
+ "## Bias scan using Multi-Dimensional Subset Scan (MDSS)\n",
+ "\n",
+ "\"Identifying Significant Predictive Bias in Classifiers\" https://arxiv.org/abs/1611.08292\n",
+ "\n",
+ "The goal of bias scan is to identify a subgroup(s) that has significantly more predictive bias than would be expected from an unbiased classifier. There are $\\prod_{m=1}^{M}\\left(2^{|X_{m}|}-1\\right)$ unique subgroups from a dataset with $M$ features, with each feature having $|X_{m}|$ discretized values, where a subgroup is any $M$-dimension\n",
+ "Cartesian set product, between subsets of feature-values from each feature --- excluding the empty set. Bias scan mitigates this computational hurdle by approximately identifing the most statistically biased subgroup in linear time (rather than exponential).\n",
+ "\n",
+ "\n",
+ "We define the statistical measure of predictive bias function, $score_{bias}(S)$ as a likelihood ratio score and a function of a given subgroup $S$. The null hypothesis is that the given prediction's odds are correct for all subgroups in $\\mathcal{D}$:\n",
+ "\n",
+ "$$H_{0}:odds(y_{i})=\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}}\\ \\forall i\\in\\mathcal{D}.$$\n",
+ "\n",
+ "The alternative hypothesis assumes some constant multiplicative bias in the odds for some given subgroup $S$:\n",
+ "\n",
+ "$$H_{1}:\\ odds(y_{i})=q\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}},\\ \\text{where}\\ q>1\\ \\forall i\\in S\\ \\mathrm{and}\\ q=1\\ \\forall i\\notin S.$$\n",
+ "\n",
+ "In the classification setting, each observation's likelihood is Bernoulli distributed and assumed independent. This results in the following scoring function for a subgroup $S$:\n",
+ "\n",
+ "\\begin{align*}\n",
+ "score_{bias}(S)= & \\max_{q}\\log\\prod_{i\\in S}\\frac{Bernoulli(\\frac{q\\hat{p}_{i}}{1-\\hat{p}_{i}+q\\hat{p}_{i}})}{Bernoulli(\\hat{p}_{i})}\\\\\n",
+ "= & \\max_{q}\\log(q)\\sum_{i\\in S}y_{i}-\\sum_{i\\in S}\\log(1-\\hat{p}_{i}+q\\hat{p}_{i}).\n",
+ "\\end{align*}\n",
+ "Our bias scan is thus represented as: $S^{*}=FSS(\\mathcal{D},\\mathcal{E},F_{score})=MDSS(\\mathcal{D},\\hat{p},score_{bias})$.\n",
+ "\n",
+ "where $S^{*}$ is the detected most anomalous subgroup, $FSS$ is one of several subset scan algorithms for different problem settings, $\\mathcal{D}$ is a dataset with outcomes $Y$ and discretized features $\\mathcal{X}$, $\\mathcal{E}$ are a set of expectations or 'normal' values for $Y$, and $F_{score}$ is an expectation-based scoring statistic that measures the amount of anomalousness between subgroup observations and their expectations.\n",
+ "\n",
+ "Predictive bias emphasizes comparable predictions for a subgroup and its observations and Bias scan provides a more general method that can detect and characterize such bias, or poor classifier fit, in the larger space of all possible subgroups, without a priori specification."
]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df = pd.DataFrame(features, columns=feature_names + ['two_year_recid'])\n",
- "df.head()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Training\n",
- "We'll create a structured dataset and then train a simple classifier to predict the probability of the outcome"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "from aif360.datasets import StandardDataset\n",
- "dataset = StandardDataset(df, label_name='two_year_recid', favorable_classes=[0],\n",
- " protected_attribute_names=['sex', 'race'],\n",
- " privileged_classes=[[1], [1]],\n",
- " instance_weights_name=None)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "dataset_orig_train, dataset_orig_test = dataset.split([0.7], shuffle=True, seed=0)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Train set: Difference in mean outcomes between unprivileged and privileged groups = -0.124496\n",
- "Test set: Difference in mean outcomes between unprivileged and privileged groups = -0.159410\n"
- ]
- }
- ],
- "source": [
- "metric_train = BinaryLabelDatasetMetric(dataset_orig_train,\n",
- " unprivileged_groups=male_group,\n",
- " privileged_groups=female_group)\n",
- "\n",
- "print(\"Train set: Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_train.mean_difference())\n",
- "metric_test = BinaryLabelDatasetMetric(dataset_orig_test,\n",
- " unprivileged_groups=male_group,\n",
- " privileged_groups=female_group)\n",
- "print(\"Test set: Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_test.mean_difference())\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "It shows that overall Females in the dataset have a lower observed recidivism them Males."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "If we train a classifier, the model is likely to pick up this bias in the dataset"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "2DYOd2UsB90E"
+ },
+ "outputs": [],
+ "source": [
+ "import itertools\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "\n",
+ "from aif360.metrics import BinaryLabelDatasetMetric, MDSSClassificationMetric\n",
+ "from aif360.detectors import bias_scan\n",
+ "\n",
+ "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_compas"
+ ]
+ },
{
- "data": {
- "text/plain": [
- "LogisticRegression(random_state=0)"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "SqG1-54_B90F"
+ },
+ "source": [
+ "We'll demonstrate scoring a subset and finding the most anomalous subset with bias scan using the compas dataset.\n",
+ "\n",
+ "We can specify subgroups to be scored or scan for the most anomalous subgroup. Bias scan allows us to decide if we aim to identify bias as `higher` than expected probabilities or `lower` than expected probabilities. Depending on the favourable label, the corresponding subgroup may be categorized as priviledged or unprivileged."
]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from sklearn.linear_model import LogisticRegression\n",
- "clf = LogisticRegression(solver='lbfgs', C=1.0, penalty='l2', random_state=0)\n",
- "clf.fit(dataset_orig_train.features, dataset_orig_train.labels.flatten())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Note that the probability scores we use are the probabilities of the favorable label, which is 0 in this case."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "array([0., 1.])"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "CeaDWPaEB90G"
+ },
+ "outputs": [],
+ "source": [
+ "dataset_orig = load_preproc_data_compas()\n",
+ "\n",
+ "female_group = [{'sex': 1}]\n",
+ "male_group = [{'sex': 0}]"
]
- },
- "execution_count": 9,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "clf.classes_"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "predictions should reflect the probability of a favorable outcome (i.e. no recidivism)."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "dataset_bias_test_prob = clf.predict_proba(dataset_orig_test.features)[:, 0]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
sex
\n",
- "
race
\n",
- "
age_cat
\n",
- "
priors_count
\n",
- "
c_charge_degree
\n",
- "
observed
\n",
- "
probabilities
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
2.0
\n",
- "
2.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.552951
\n",
- "
\n",
- "
\n",
- "
1
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.740959
\n",
- "
\n",
- "
\n",
- "
2
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.374728
\n",
- "
\n",
- "
\n",
- "
3
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
2.0
\n",
- "
2.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.444487
\n",
- "
\n",
- "
\n",
- "
4
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.584908
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "UbcjVVhAB90G"
+ },
+ "source": [
+ "The dataset has the categorical features one-hot encoded so we'll modify the dataset to convert them back\n",
+ "to the categorical featues because scanning one-hot encoded features may find subgroups that are not meaningful e.g., a subgroup with 2 race values."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "NzbzKqmfB90G"
+ },
+ "outputs": [],
+ "source": [
+ "dataset_orig_df = pd.DataFrame(dataset_orig.features, columns=dataset_orig.feature_names)\n",
+ "\n",
+ "age_cat = np.argmax(dataset_orig_df[['age_cat=Less than 25', 'age_cat=25 to 45',\n",
+ " 'age_cat=Greater than 45']].values, axis=1).reshape(-1, 1)\n",
+ "priors_count = np.argmax(dataset_orig_df[['priors_count=0', 'priors_count=1 to 3',\n",
+ " 'priors_count=More than 3']].values, axis=1).reshape(-1, 1)\n",
+ "c_charge_degree = np.argmax(dataset_orig_df[['c_charge_degree=M', 'c_charge_degree=F']].values, axis=1).reshape(-1, 1)\n",
+ "\n",
+ "features = np.concatenate((dataset_orig_df[['sex', 'race']].values, age_cat, priors_count,\n",
+ " c_charge_degree, dataset_orig.labels), axis=1)\n",
+ "feature_names = ['sex', 'race', 'age_cat', 'priors_count', 'c_charge_degree']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": true,
+ "id": "QxeSBzMVB90H",
+ "outputId": "a64c16ec-f5e1-40fc-81f9-1c0a4e96d521"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
sex
\n",
+ "
race
\n",
+ "
age_cat
\n",
+ "
priors_count
\n",
+ "
c_charge_degree
\n",
+ "
two_year_recid
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
2.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
2.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sex race age_cat priors_count c_charge_degree two_year_recid\n",
+ "0 0.0 0.0 1.0 0.0 1.0 1.0\n",
+ "1 0.0 0.0 0.0 2.0 1.0 1.0\n",
+ "2 0.0 1.0 1.0 2.0 1.0 1.0\n",
+ "3 1.0 1.0 1.0 0.0 0.0 0.0\n",
+ "4 0.0 1.0 1.0 0.0 1.0 0.0"
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
],
- "text/plain": [
- " sex race age_cat priors_count c_charge_degree observed probabilities\n",
- "0 1.0 1.0 2.0 2.0 1.0 1.0 0.552951\n",
- "1 1.0 0.0 1.0 0.0 1.0 0.0 0.740959\n",
- "2 0.0 1.0 0.0 1.0 1.0 0.0 0.374728\n",
- "3 0.0 0.0 2.0 2.0 1.0 1.0 0.444487\n",
- "4 0.0 1.0 1.0 1.0 0.0 1.0 0.584908"
+ "source": [
+ "df = pd.DataFrame(features, columns=feature_names + ['two_year_recid'])\n",
+ "df.head()"
]
- },
- "execution_count": 11,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df = pd.DataFrame(dataset_orig_test.features, columns=dataset_orig_test.feature_names)\n",
- "df['observed'] = pd.Series(dataset_orig_test.labels.flatten(), index=df.index)\n",
- "df['probabilities'] = pd.Series(dataset_bias_test_prob, index=df.index)\n",
- "df.head()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We'll the create another structured dataset as the classified dataset by assigning the predicted probabilities to the scores attribute"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [],
- "source": [
- "dataset_bias_test = dataset_orig_test.copy()\n",
- "dataset_bias_test.scores = dataset_bias_test_prob\n",
- "dataset_bias_test.labels = dataset_orig_test.labels"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Bias scoring"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "First, we try to observe the difference between the model prediction and the actual observations of the favorable label, which in this case is 0. We create a new test_df for this computation. \n",
- "\n",
- "If the model's average prediction of the favorable label is higher than the actual observations average, then the group is said to be privileged. In the converse case, the group is said to be unprivileged.\n",
- "\n",
- "We would check for whether the male and female groups are privileged or not using mdss score"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "9MBbYHoyB90I"
+ },
+ "source": [
+ "### Training\n",
+ "We'll create a structured dataset and then train a simple classifier to predict the probability of the outcome"
+ ]
+ },
{
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
sex
\n",
- "
race
\n",
- "
age_cat
\n",
- "
priors_count
\n",
- "
c_charge_degree
\n",
- "
two_year_recid
\n",
- "
model_not_recid
\n",
- "
observed_not_recid
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
2479
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
2.0
\n",
- "
2.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.552951
\n",
- "
0.0
\n",
- "
\n",
- "
\n",
- "
3574
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.740959
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
513
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.374728
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
1725
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
2.0
\n",
- "
2.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.444487
\n",
- "
0.0
\n",
- "
\n",
- "
\n",
- "
96
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.584908
\n",
- "
0.0
\n",
- "
\n",
- "
\n",
- "
...
\n",
- "
...
\n",
- "
...
\n",
- "
...
\n",
- "
...
\n",
- "
...
\n",
- "
...
\n",
- "
...
\n",
- "
...
\n",
- "
\n",
- "
\n",
- "
4931
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.374728
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
3264
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.535753
\n",
- "
0.0
\n",
- "
\n",
- "
\n",
- "
1653
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.490037
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
2607
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.769140
\n",
- "
0.0
\n",
- "
\n",
- "
\n",
- "
2732
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
2.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.251726
\n",
- "
0.0
\n",
- "
\n",
- " \n",
- "
\n",
- "
1584 rows × 8 columns
\n",
- "
"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Mr3tEubCB90I"
+ },
+ "outputs": [],
+ "source": [
+ "from aif360.datasets import StandardDataset\n",
+ "dataset = StandardDataset(df, label_name='two_year_recid', favorable_classes=[0],\n",
+ " protected_attribute_names=['sex', 'race'],\n",
+ " privileged_classes=[[1], [1]],\n",
+ " instance_weights_name=None)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "ub0RsE6_B90I"
+ },
+ "outputs": [],
+ "source": [
+ "dataset_orig_train, dataset_orig_test = dataset.split([0.7], shuffle=True, seed=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "ozr0_L3AB90I",
+ "outputId": "b4a747cc-8121-452b-dc23-effcd43b89d7"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Train set: Difference in mean outcomes between unprivileged and privileged groups = -0.124496\n",
+ "Test set: Difference in mean outcomes between unprivileged and privileged groups = -0.159410\n"
+ ]
+ }
],
- "text/plain": [
- " sex race age_cat priors_count c_charge_degree two_year_recid \\\n",
- "2479 1.0 1.0 2.0 2.0 1.0 1.0 \n",
- "3574 1.0 0.0 1.0 0.0 1.0 0.0 \n",
- "513 0.0 1.0 0.0 1.0 1.0 0.0 \n",
- "1725 0.0 0.0 2.0 2.0 1.0 1.0 \n",
- "96 0.0 1.0 1.0 1.0 0.0 1.0 \n",
- "... ... ... ... ... ... ... \n",
- "4931 0.0 1.0 0.0 1.0 1.0 0.0 \n",
- "3264 0.0 0.0 0.0 0.0 1.0 1.0 \n",
- "1653 0.0 0.0 1.0 1.0 1.0 0.0 \n",
- "2607 1.0 1.0 1.0 0.0 1.0 1.0 \n",
- "2732 0.0 1.0 0.0 2.0 0.0 1.0 \n",
- "\n",
- " model_not_recid observed_not_recid \n",
- "2479 0.552951 0.0 \n",
- "3574 0.740959 1.0 \n",
- "513 0.374728 1.0 \n",
- "1725 0.444487 0.0 \n",
- "96 0.584908 0.0 \n",
- "... ... ... \n",
- "4931 0.374728 1.0 \n",
- "3264 0.535753 0.0 \n",
- "1653 0.490037 1.0 \n",
- "2607 0.769140 0.0 \n",
- "2732 0.251726 0.0 \n",
- "\n",
- "[1584 rows x 8 columns]"
+ "source": [
+ "metric_train = BinaryLabelDatasetMetric(dataset_orig_train,\n",
+ " unprivileged_groups=male_group,\n",
+ " privileged_groups=female_group)\n",
+ "\n",
+ "print(\"Train set: Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_train.mean_difference())\n",
+ "metric_test = BinaryLabelDatasetMetric(dataset_orig_test,\n",
+ " unprivileged_groups=male_group,\n",
+ " privileged_groups=female_group)\n",
+ "print(\"Test set: Difference in mean outcomes between unprivileged and privileged groups = %f\" % metric_test.mean_difference())\n"
]
- },
- "execution_count": 13,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "test_df = dataset_bias_test.convert_to_dataframe()[0]\n",
- "test_df['model_not_recid'] = dataset_bias_test.scores.flatten()\n",
- "test_df['observed_not_recid'] = 1 - test_df['two_year_recid']\n",
- "test_df"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "model_not_recid 0.617561\n",
- "observed_not_recid 0.657051\n",
- "dtype: float64"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "uhDQDm_5B90J"
+ },
+ "source": [
+ "It shows that overall Females in the dataset have a lower observed recidivism them Males."
]
- },
- "execution_count": 14,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# Females actual vs predicted rates of positive label\n",
- "test_df[test_df.sex == 1][['model_not_recid','observed_not_recid']].mean()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Since model average predictions for the positive label is lower than the observed average by a substantial amount (about 4%), the female group is most likely unprivileged."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "model_not_recid 0.512443\n",
- "observed_not_recid 0.497642\n",
- "dtype: float64"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "VNo-Ix97B90J"
+ },
+ "source": [
+ "If we train a classifier, the model is likely to pick up this bias in the dataset"
]
- },
- "execution_count": 15,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# Males actual vs predicted rates of positive label\n",
- "test_df[test_df.sex == 0][['model_not_recid','observed_not_recid']].mean()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Since model average predictions for the positive label is greater than the observed average by a small amount (about 1.5%), the male group could be privileged."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now, we'll create an instance of the MDSS Classification Metric and assess the apriori defined privileged and unprivileged groups; females and males respectively. \n",
- "\n",
- "By apriori defining the male group as unprivileged, we are saying we expect that the model's predictions is systematically lower than the actual observation.\n",
- "\n",
- "By apriori defining the female group as privileged, we are saying we expect that the model's predictions is systematically higher than the actual observation.\n",
- "\n",
- "From our mini-analysis above, we know that these hypothesis are unlikely to be true "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "mdss_classified = MDSSClassificationMetric(dataset_orig_test, dataset_bias_test,\n",
- " unprivileged_groups=male_group,\n",
- " privileged_groups=female_group)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "-0.0"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Hj1KWwaOB90J",
+ "outputId": "abf6f0c4-ec8c-4033-a88a-bbb11fa47900"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "LogisticRegression(random_state=0)"
+ ]
+ },
+ "execution_count": 8,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "from sklearn.linear_model import LogisticRegression\n",
+ "clf = LogisticRegression(solver='lbfgs', C=1.0, penalty='l2', random_state=0)\n",
+ "clf.fit(dataset_orig_train.features, dataset_orig_train.labels.flatten())"
]
- },
- "execution_count": 17,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# We are asking the question:\n",
- "# Is there evidence that the hypothesized privileged group is actually privileged?\n",
- "\n",
- "female_privileged_score = mdss_classified.score_groups(privileged=True)\n",
- "female_privileged_score"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "By having a score very close to zero, mdss bias score is informing us that there is no evidence from the data that our hypothesis of the female group being privileged is true."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "-0.0"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "8bC5aDp4B90K"
+ },
+ "source": [
+ "Note that the probability scores we use are the probabilities of the favorable label, which is 0 in this case."
]
- },
- "execution_count": 18,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "# We are asking the question:\n",
- "# Is there evidence that the hypothesized unprivileged group is actually unprivileged?\n",
- "\n",
- "male_unprivileged_score = mdss_classified.score_groups(privileged=False)\n",
- "male_unprivileged_score"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "By having a score very close zero, mdss bias score is informing us that there is no evidence from the data to support our hypothesis of the male group being unprivileged is true."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can flip our initial hypothesis and check if the male group is privileged or the female group is unprivileged."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "mdss_classified = MDSSClassificationMetric(dataset_orig_test, dataset_bias_test,\n",
- " unprivileged_groups=female_group,\n",
- " privileged_groups=male_group)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "0.63"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "tQU_CQtOB90K",
+ "outputId": "f9beec78-31e0-48e7-bfcf-7f276b09af16"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "array([0., 1.])"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "clf.classes_"
]
- },
- "execution_count": 20,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "male_privileged_score = mdss_classified.score_groups(privileged=True)\n",
- "male_privileged_score"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "By having a positive score, mdss bias score is informing us that there is evidence from the data that our hypothesis of the male group being privileged is true."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "1.1769"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "UyhqLqhNB90K"
+ },
+ "source": [
+ "predictions should reflect the probability of a favorable outcome (i.e. no recidivism)."
]
- },
- "execution_count": 21,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "female_unprivileged_score = mdss_classified.score_groups(privileged=False)\n",
- "female_unprivileged_score"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "By having a positive score, mdss bias score is informing us that there is evidence from the data to support our hypothesis of the female group being unprivileged is true."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "By taking into account the size of the group and the magnitude of the deviation, mdss bias core has been able to tell us the following about the male and female groups:\n",
- "- There is no evidence that the female group is privileged.\n",
- "- There is no evidence that the male group is unprivileged.\n",
- "- There is evidence that the male group is privileged.\n",
- "- There is evidence that the female is unprivileged."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Bias scan\n",
- "We get the bias score for the apriori defined subgroup but assuming we had no prior knowledge \n",
- "about the predictive bias and wanted to find the subgroups with the most bias, we can apply bias scan to identify the priviledged and unpriviledged groups. The privileged argument is not a reference to a group but the direction for which to scan for bias."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [],
- "source": [
- "privileged_subset = bias_scan(df.iloc[:, :-2], df.observed, df.probabilities,\n",
- " favorable_value=dataset_orig_test.favorable_label,\n",
- " penalty=0.5, overpredicted=True)\n",
- "unprivileged_subset = bias_scan(df.iloc[:, :-2], df.observed, df.probabilities,\n",
- " favorable_value=dataset_orig_test.favorable_label,\n",
- " penalty=0.5, overpredicted=False)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {},
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "({'race': [0.0], 'age_cat': [0.0], 'sex': [0.0]}, 3.1526)\n",
- "({'sex': [1.0], 'race': [0.0]}, 3.3036)\n"
- ]
- }
- ],
- "source": [
- "print(privileged_subset)\n",
- "print(unprivileged_subset)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert privileged_subset[0]\n",
- "assert unprivileged_subset[0]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can observe that the bias score is higher than the score of the prior groups. These subgroups are guaranteed to be the highest scoring subgroup among the exponentially many subgroups.\n",
- "\n",
- "For the purposes of this example, the logistic regression model systematically underestimates the recidivism risk of individuals in the `Non-caucasian`, `less than 25`, `Male` subgroup whereas individuals belonging to the `Non-caucasian`, `Female` are assigned a higher risk than is actually observed. We refer to these subgroups as the `detected privileged group` and `detected unprivileged group` respectively."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We can create another srtuctured dataset using the new groups to compute other dataset metrics. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "protected_attr_names = set(privileged_subset[0].keys()).union(set(unprivileged_subset[0].keys()))\n",
- "dataset_orig_test.protected_attribute_names = list(protected_attr_names)\n",
- "dataset_bias_test.protected_attribute_names = list(protected_attr_names)\n",
- "\n",
- "protected_attr = np.where(np.isin(dataset_orig_test.feature_names, list(protected_attr_names)))[0]\n",
- "\n",
- "dataset_orig_test.protected_attributes = dataset_orig_test.features[:, protected_attr]\n",
- "dataset_bias_test.protected_attributes = dataset_bias_test.features[:, protected_attr]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "# converts from dictionary of lists to list of dictionaries\n",
- "a = list(privileged_subset[0].values())\n",
- "subset_values = list(itertools.product(*a))\n",
- "\n",
- "detected_privileged_groups = []\n",
- "for vals in subset_values:\n",
- " detected_privileged_groups.append((dict(zip(privileged_subset[0].keys(), vals))))\n",
- "\n",
- "a = list(unprivileged_subset[0].values())\n",
- "subset_values = list(itertools.product(*a))\n",
- "\n",
- "detected_unprivileged_groups = []\n",
- "for vals in subset_values:\n",
- " detected_unprivileged_groups.append((dict(zip(unprivileged_subset[0].keys(), vals))))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "20HUpT98B90K"
+ },
+ "outputs": [],
+ "source": [
+ "dataset_bias_test_prob = clf.predict_proba(dataset_orig_test.features)[:, 0]"
+ ]
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Test set: Difference in mean outcomes between unprivileged and privileged groups = 0.275836\n"
- ]
- }
- ],
- "source": [
- "metric_bias_test = BinaryLabelDatasetMetric(dataset_bias_test,\n",
- " unprivileged_groups=detected_unprivileged_groups,\n",
- " privileged_groups=detected_privileged_groups)\n",
- "\n",
- "print(\"Test set: Difference in mean outcomes between unprivileged and privileged groups = %f\"\n",
- " % metric_bias_test.mean_difference())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "It appears the detected privileged group have a higher risk of recidivism than the unprivileged group."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As noted in the paper, predictive bias is different from predictive fairness so there's no the emphasis in the subgroups having comparable predictions between them. \n",
- "We can investigate the difference in what the model predicts vs what we actually observed as well as the multiplicative difference in the odds of the subgroups."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [],
- "source": [
- "to_choose = df[privileged_subset[0].keys()].isin(privileged_subset[0]).all(axis=1)\n",
- "temp_df = df.loc[to_choose]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "ryABk3w5B90K",
+ "outputId": "234f8d7b-f69a-42ad-9544-bc151e367245"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
sex
\n",
+ "
race
\n",
+ "
age_cat
\n",
+ "
priors_count
\n",
+ "
c_charge_degree
\n",
+ "
observed
\n",
+ "
probabilities
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
2.0
\n",
+ "
2.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.552951
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.740959
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.374728
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
2.0
\n",
+ "
2.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.444487
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.584908
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sex race age_cat priors_count c_charge_degree observed probabilities\n",
+ "0 1.0 1.0 2.0 2.0 1.0 1.0 0.552951\n",
+ "1 1.0 0.0 1.0 0.0 1.0 0.0 0.740959\n",
+ "2 0.0 1.0 0.0 1.0 1.0 0.0 0.374728\n",
+ "3 0.0 0.0 2.0 2.0 1.0 1.0 0.444487\n",
+ "4 0.0 1.0 1.0 1.0 0.0 1.0 0.584908"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df = pd.DataFrame(dataset_orig_test.features, columns=dataset_orig_test.feature_names)\n",
+ "df['observed'] = pd.Series(dataset_orig_test.labels.flatten(), index=df.index)\n",
+ "df['probabilities'] = pd.Series(dataset_bias_test_prob, index=df.index)\n",
+ "df.head()"
+ ]
+ },
{
- "data": {
- "text/plain": [
- "'Our detected priviledged group has a size of 192, we observe 67.71% as the average risk of recidivism, but our model predicts 57.30%'"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "8cH_NmUuB90L"
+ },
+ "source": [
+ "We'll the create another structured dataset as the classified dataset by assigning the predicted probabilities to the scores attribute"
]
- },
- "execution_count": 29,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "group_obs = temp_df['observed'].mean()\n",
- "group_prob = 1-temp_df['probabilities'].mean()\n",
- "\n",
- "\"Our detected priviledged group has a size of {}, we observe {:.2%} as the average risk of recidivism, but our model predicts {:.2%}\"\\\n",
- ".format(len(temp_df), group_obs, group_prob)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "'This is a multiplicative increase in the odds by 1.562'"
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "kctU9DX1B90L"
+ },
+ "outputs": [],
+ "source": [
+ "dataset_bias_test = dataset_orig_test.copy()\n",
+ "dataset_bias_test.scores = dataset_bias_test_prob\n",
+ "dataset_bias_test.labels = dataset_orig_test.labels"
]
- },
- "execution_count": 30,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "odds_mul = (group_obs / (1 - group_obs)) / (group_prob /(1 - group_prob))\n",
- "\"This is a multiplicative increase in the odds by {:.3f}\".format(odds_mul)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert odds_mul > 1"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "to_choose = df[unprivileged_subset[0].keys()].isin(unprivileged_subset[0]).all(axis=1)\n",
- "temp_df = df.loc[to_choose]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "'Our detected unpriviledged group has a size of 169, we observe 33.14% as the average risk of recidivism, but our model predicts 43.65%'"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "OB_6l47PB90L"
+ },
+ "source": [
+ "### Bias scoring"
]
- },
- "execution_count": 33,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "group_obs = temp_df['observed'].mean()\n",
- "group_prob = 1-temp_df['probabilities'].mean()\n",
- "\n",
- "\"Our detected unpriviledged group has a size of {}, we observe {:.2%} as the average risk of recidivism, but our model predicts {:.2%}\"\\\n",
- ".format(len(temp_df), group_obs, group_prob)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/plain": [
- "'This is a multiplicative decrease in the odds by 0.640'"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "ykJRqEA3B90L"
+ },
+ "source": [
+ "First, we try to observe the difference between the model prediction and the actual observations of the favorable label, which in this case is 0. We create a new test_df for this computation.\n",
+ "\n",
+ "If the model's average prediction of the favorable label is higher than the actual observations average, then the group is said to be privileged. In the converse case, the group is said to be unprivileged.\n",
+ "\n",
+ "We would check for whether the male and female groups are privileged or not using mdss score"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "PV5F2zMwB90L",
+ "outputId": "32bd9359-b7a1-4ee5-89fe-7f838dd50e15"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
sex
\n",
+ "
race
\n",
+ "
age_cat
\n",
+ "
priors_count
\n",
+ "
c_charge_degree
\n",
+ "
two_year_recid
\n",
+ "
model_not_recid
\n",
+ "
observed_not_recid
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
2479
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
2.0
\n",
+ "
2.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.552951
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
3574
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.740959
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
513
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.374728
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
1725
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
2.0
\n",
+ "
2.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.444487
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
96
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.584908
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
...
\n",
+ "
\n",
+ "
\n",
+ "
4931
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.374728
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
3264
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.535753
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
1653
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.490037
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
2607
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.769140
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
2732
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
2.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.251726
\n",
+ "
0.0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
1584 rows × 8 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sex race age_cat priors_count c_charge_degree two_year_recid \\\n",
+ "2479 1.0 1.0 2.0 2.0 1.0 1.0 \n",
+ "3574 1.0 0.0 1.0 0.0 1.0 0.0 \n",
+ "513 0.0 1.0 0.0 1.0 1.0 0.0 \n",
+ "1725 0.0 0.0 2.0 2.0 1.0 1.0 \n",
+ "96 0.0 1.0 1.0 1.0 0.0 1.0 \n",
+ "... ... ... ... ... ... ... \n",
+ "4931 0.0 1.0 0.0 1.0 1.0 0.0 \n",
+ "3264 0.0 0.0 0.0 0.0 1.0 1.0 \n",
+ "1653 0.0 0.0 1.0 1.0 1.0 0.0 \n",
+ "2607 1.0 1.0 1.0 0.0 1.0 1.0 \n",
+ "2732 0.0 1.0 0.0 2.0 0.0 1.0 \n",
+ "\n",
+ " model_not_recid observed_not_recid \n",
+ "2479 0.552951 0.0 \n",
+ "3574 0.740959 1.0 \n",
+ "513 0.374728 1.0 \n",
+ "1725 0.444487 0.0 \n",
+ "96 0.584908 0.0 \n",
+ "... ... ... \n",
+ "4931 0.374728 1.0 \n",
+ "3264 0.535753 0.0 \n",
+ "1653 0.490037 1.0 \n",
+ "2607 0.769140 0.0 \n",
+ "2732 0.251726 0.0 \n",
+ "\n",
+ "[1584 rows x 8 columns]"
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "test_df = dataset_bias_test.convert_to_dataframe()[0]\n",
+ "test_df['model_not_recid'] = dataset_bias_test.scores.flatten()\n",
+ "test_df['observed_not_recid'] = 1 - test_df['two_year_recid']\n",
+ "test_df"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "qMjfpXO4B90M",
+ "outputId": "78697715-5600-4c49-ee4d-a747ded2606d"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "model_not_recid 0.617561\n",
+ "observed_not_recid 0.657051\n",
+ "dtype: float64"
+ ]
+ },
+ "execution_count": 14,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Females actual vs predicted rates of positive label\n",
+ "test_df[test_df.sex == 1][['model_not_recid','observed_not_recid']].mean()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HKaZKOgNB90M"
+ },
+ "source": [
+ "Since model average predictions for the positive label is lower than the observed average by a substantial amount (about 4%), the female group is most likely unprivileged."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "_tKQJ0FUB90M",
+ "outputId": "1b6bbce7-e1b7-44c9-8803-59241e5cdc27"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "model_not_recid 0.512443\n",
+ "observed_not_recid 0.497642\n",
+ "dtype: float64"
+ ]
+ },
+ "execution_count": 15,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Males actual vs predicted rates of positive label\n",
+ "test_df[test_df.sex == 0][['model_not_recid','observed_not_recid']].mean()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "y-wvPZH3B90M"
+ },
+ "source": [
+ "Since model average predictions for the positive label is greater than the observed average by a small amount (about 1.5%), the male group could be privileged."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "6fQGJP-2B90M"
+ },
+ "source": [
+ "Now, we'll create an instance of the MDSS Classification Metric and assess the apriori defined privileged and unprivileged groups; females and males respectively.\n",
+ "\n",
+ "By apriori defining the male group as unprivileged, we are saying we expect that the model's predictions is systematically lower than the actual observation.\n",
+ "\n",
+ "By apriori defining the female group as privileged, we are saying we expect that the model's predictions is systematically higher than the actual observation.\n",
+ "\n",
+ "From our mini-analysis above, we know that these hypothesis are unlikely to be true"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "yIqKBRPbB90M"
+ },
+ "outputs": [],
+ "source": [
+ "mdss_classified = MDSSClassificationMetric(dataset_orig_test, dataset_bias_test,\n",
+ " unprivileged_groups=male_group,\n",
+ " privileged_groups=female_group)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "SI_TOyu0B90N",
+ "outputId": "9c118292-8368-4e0b-fdee-5ef094b6e9a1"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-0.0"
+ ]
+ },
+ "execution_count": 17,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# We are asking the question:\n",
+ "# Is there evidence that the hypothesized privileged group is actually privileged?\n",
+ "\n",
+ "female_privileged_score = mdss_classified.score_groups(privileged=True)\n",
+ "female_privileged_score"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "OOKIOvxUB90N"
+ },
+ "source": [
+ "By having a score very close to zero, mdss bias score is informing us that there is no evidence from the data that our hypothesis of the female group being privileged is true."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "B8dsK90PB90N",
+ "outputId": "4cf06af8-6ca3-49d6-a9d7-895e7ce69478"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "-0.0"
+ ]
+ },
+ "execution_count": 18,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# We are asking the question:\n",
+ "# Is there evidence that the hypothesized unprivileged group is actually unprivileged?\n",
+ "\n",
+ "male_unprivileged_score = mdss_classified.score_groups(privileged=False)\n",
+ "male_unprivileged_score"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Anh6JvDAB90N"
+ },
+ "source": [
+ "By having a score very close zero, mdss bias score is informing us that there is no evidence from the data to support our hypothesis of the male group being unprivileged is true."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "2QuN7nxrB90N"
+ },
+ "source": [
+ "We can flip our initial hypothesis and check if the male group is privileged or the female group is unprivileged."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Om4DTlYtB90O"
+ },
+ "outputs": [],
+ "source": [
+ "mdss_classified = MDSSClassificationMetric(dataset_orig_test, dataset_bias_test,\n",
+ " unprivileged_groups=female_group,\n",
+ " privileged_groups=male_group)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "baP4caqSB90O",
+ "outputId": "a892284c-1b54-411b-c839-45925cf17bc6"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "0.63"
+ ]
+ },
+ "execution_count": 20,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "male_privileged_score = mdss_classified.score_groups(privileged=True)\n",
+ "male_privileged_score"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "vZWkojHvB90O"
+ },
+ "source": [
+ "By having a positive score, mdss bias score is informing us that there is evidence from the data that our hypothesis of the male group being privileged is true."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "bp8kBr9nB90O",
+ "outputId": "d689519f-775d-4a5f-e372-43de18e122ae"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "1.1769"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "female_unprivileged_score = mdss_classified.score_groups(privileged=False)\n",
+ "female_unprivileged_score"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "v7LHafDqB90P"
+ },
+ "source": [
+ "By having a positive score, mdss bias score is informing us that there is evidence from the data to support our hypothesis of the female group being unprivileged is true."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "fIO7ig11B90P"
+ },
+ "source": [
+ "By taking into account the size of the group and the magnitude of the deviation, mdss bias core has been able to tell us the following about the male and female groups:\n",
+ "- There is no evidence that the female group is privileged.\n",
+ "- There is no evidence that the male group is unprivileged.\n",
+ "- There is evidence that the male group is privileged.\n",
+ "- There is evidence that the female is unprivileged."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "XlYgT7krB90Q"
+ },
+ "source": [
+ "### Bias scan\n",
+ "We get the bias score for the apriori defined subgroup but assuming we had no prior knowledge\n",
+ "about the predictive bias and wanted to find the subgroups with the most bias, we can apply bias scan to identify the priviledged and unpriviledged groups. The privileged argument is not a reference to a group but the direction for which to scan for bias."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "JDyYYrMsB90Q"
+ },
+ "outputs": [],
+ "source": [
+ "privileged_subset = bias_scan(df.iloc[:, :-2], df.observed, df.probabilities,\n",
+ " favorable_value=dataset_orig_test.favorable_label,\n",
+ " penalty=0.5, overpredicted=True)\n",
+ "unprivileged_subset = bias_scan(df.iloc[:, :-2], df.observed, df.probabilities,\n",
+ " favorable_value=dataset_orig_test.favorable_label,\n",
+ " penalty=0.5, overpredicted=False)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "SmDnrJb_B90Q",
+ "outputId": "325ae712-7d15-4a19-9732-7afe72ab3e41"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "({'race': [0.0], 'age_cat': [0.0], 'sex': [0.0]}, 3.1526)\n",
+ "({'sex': [1.0], 'race': [0.0]}, 3.3036)\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(privileged_subset)\n",
+ "print(unprivileged_subset)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "k3i-WdwLB90Q"
+ },
+ "outputs": [],
+ "source": [
+ "assert privileged_subset[0]\n",
+ "assert unprivileged_subset[0]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "LEkybEddB90Q"
+ },
+ "source": [
+ "We can observe that the bias score is higher than the score of the prior groups. These subgroups are guaranteed to be the highest scoring subgroup among the exponentially many subgroups.\n",
+ "\n",
+ "For the purposes of this example, the logistic regression model systematically underestimates the recidivism risk of individuals in the `Non-caucasian`, `less than 25`, `Male` subgroup whereas individuals belonging to the `Non-caucasian`, `Female` are assigned a higher risk than is actually observed. We refer to these subgroups as the `detected privileged group` and `detected unprivileged group` respectively."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "I_YxruutB90Q"
+ },
+ "source": [
+ "We can create another srtuctured dataset using the new groups to compute other dataset metrics. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "xdVrmsmtB90R"
+ },
+ "outputs": [],
+ "source": [
+ "protected_attr_names = set(privileged_subset[0].keys()).union(set(unprivileged_subset[0].keys()))\n",
+ "dataset_orig_test.protected_attribute_names = list(protected_attr_names)\n",
+ "dataset_bias_test.protected_attribute_names = list(protected_attr_names)\n",
+ "\n",
+ "protected_attr = np.where(np.isin(dataset_orig_test.feature_names, list(protected_attr_names)))[0]\n",
+ "\n",
+ "dataset_orig_test.protected_attributes = dataset_orig_test.features[:, protected_attr]\n",
+ "dataset_bias_test.protected_attributes = dataset_bias_test.features[:, protected_attr]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Q2W84utLB90R"
+ },
+ "outputs": [],
+ "source": [
+ "# converts from dictionary of lists to list of dictionaries\n",
+ "a = list(privileged_subset[0].values())\n",
+ "subset_values = list(itertools.product(*a))\n",
+ "\n",
+ "detected_privileged_groups = []\n",
+ "for vals in subset_values:\n",
+ " detected_privileged_groups.append((dict(zip(privileged_subset[0].keys(), vals))))\n",
+ "\n",
+ "a = list(unprivileged_subset[0].values())\n",
+ "subset_values = list(itertools.product(*a))\n",
+ "\n",
+ "detected_unprivileged_groups = []\n",
+ "for vals in subset_values:\n",
+ " detected_unprivileged_groups.append((dict(zip(unprivileged_subset[0].keys(), vals))))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "5CQLNeLMB90R",
+ "outputId": "fdf5c829-9610-43a1-b46c-6d6e7409f52c"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Test set: Difference in mean outcomes between unprivileged and privileged groups = 0.275836\n"
+ ]
+ }
+ ],
+ "source": [
+ "metric_bias_test = BinaryLabelDatasetMetric(dataset_bias_test,\n",
+ " unprivileged_groups=detected_unprivileged_groups,\n",
+ " privileged_groups=detected_privileged_groups)\n",
+ "\n",
+ "print(\"Test set: Difference in mean outcomes between unprivileged and privileged groups = %f\"\n",
+ " % metric_bias_test.mean_difference())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "FpsKHVQOB90R"
+ },
+ "source": [
+ "It appears the detected privileged group have a higher risk of recidivism than the unprivileged group."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "aM5Xi7wYB90R"
+ },
+ "source": [
+ "As noted in the paper, predictive bias is different from predictive fairness so there's no the emphasis in the subgroups having comparable predictions between them.\n",
+ "We can investigate the difference in what the model predicts vs what we actually observed as well as the multiplicative difference in the odds of the subgroups."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "-S80czQ6B90R"
+ },
+ "outputs": [],
+ "source": [
+ "to_choose = df[privileged_subset[0].keys()].isin(privileged_subset[0]).all(axis=1)\n",
+ "temp_df = df.loc[to_choose]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Tv-pOahHB90S",
+ "outputId": "b4b11f9c-8ec7-4cff-c4fb-fb1dbc17f383"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'Our detected priviledged group has a size of 192, we observe 67.71% as the average risk of recidivism, but our model predicts 57.30%'"
+ ]
+ },
+ "execution_count": 29,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "group_obs = temp_df['observed'].mean()\n",
+ "group_prob = 1-temp_df['probabilities'].mean()\n",
+ "\n",
+ "\"Our detected priviledged group has a size of {}, we observe {:.2%} as the average risk of recidivism, but our model predicts {:.2%}\"\\\n",
+ ".format(len(temp_df), group_obs, group_prob)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "mhzLh_ZWB90S",
+ "outputId": "6507496a-8086-4605-a135-e031dde9de69"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'This is a multiplicative increase in the odds by 1.562'"
+ ]
+ },
+ "execution_count": 30,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "odds_mul = (group_obs / (1 - group_obs)) / (group_prob /(1 - group_prob))\n",
+ "\"This is a multiplicative increase in the odds by {:.3f}\".format(odds_mul)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "4CBlc07aB90S"
+ },
+ "outputs": [],
+ "source": [
+ "assert odds_mul > 1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "u-e4oDAwB90S"
+ },
+ "outputs": [],
+ "source": [
+ "to_choose = df[unprivileged_subset[0].keys()].isin(unprivileged_subset[0]).all(axis=1)\n",
+ "temp_df = df.loc[to_choose]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "hOwgUMAsB90S",
+ "outputId": "d45edf71-6256-4ae8-8c21-60c1a5275de4"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'Our detected unpriviledged group has a size of 169, we observe 33.14% as the average risk of recidivism, but our model predicts 43.65%'"
+ ]
+ },
+ "execution_count": 33,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "group_obs = temp_df['observed'].mean()\n",
+ "group_prob = 1-temp_df['probabilities'].mean()\n",
+ "\n",
+ "\"Our detected unpriviledged group has a size of {}, we observe {:.2%} as the average risk of recidivism, but our model predicts {:.2%}\"\\\n",
+ ".format(len(temp_df), group_obs, group_prob)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "DHMHVUyOB90T",
+ "outputId": "705cf23c-7d1c-41e4-a2a7-cb8b39a5b498"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'This is a multiplicative decrease in the odds by 0.640'"
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "odds_mul = (group_obs / (1 - group_obs)) / (group_prob /(1 - group_prob))\n",
+ "\"This is a multiplicative decrease in the odds by {:.3f}\".format(odds_mul)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "RComzQQWB90T"
+ },
+ "outputs": [],
+ "source": [
+ "assert odds_mul < 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pak55cFNB90T"
+ },
+ "source": [
+ "In summary, this notebook demonstrates the use of bias scan to identify subgroups with significant predictive bias, as quantified by a likelihood ratio score, using subset scanning. This allows consideration of not just subgroups of a priori interest or small dimensions, but the space of all possible subgroups of features.\n",
+ "It also presents opportunity for a kind of bias mitigation technique that uses the multiplicative odds in the over-or-under estimated subgroups to adjust for predictive fairness."
]
- },
- "execution_count": 34,
- "metadata": {},
- "output_type": "execute_result"
}
- ],
- "source": [
- "odds_mul = (group_obs / (1 - group_obs)) / (group_prob /(1 - group_prob))\n",
- "\"This is a multiplicative decrease in the odds by {:.3f}\".format(odds_mul)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [],
- "source": [
- "assert odds_mul < 1"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In summary, this notebook demonstrates the use of bias scan to identify subgroups with significant predictive bias, as quantified by a likelihood ratio score, using subset scanning. This allows consideration of not just subgroups of a priori interest or small dimensions, but the space of all possible subgroups of features.\n",
- "It also presents opportunity for a kind of bias mitigation technique that uses the multiplicative odds in the over-or-under estimated subgroups to adjust for predictive fairness."
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3.9.7 ('aif360')",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.7"
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3.9.7 ('aif360')",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.7"
+ },
+ "vscode": {
+ "interpreter": {
+ "hash": "d0c5ced7753e77a483fec8ff7063075635521cce6e0bd54998c8f174742209dd"
+ }
+ },
+ "colab": {
+ "provenance": []
+ }
},
- "vscode": {
- "interpreter": {
- "hash": "d0c5ced7753e77a483fec8ff7063075635521cce6e0bd54998c8f174742209dd"
- }
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
\ No newline at end of file
diff --git a/examples/demo_mdss_detector.ipynb b/examples/demo_mdss_detector.ipynb
index 6ce0ed8d..1cf24f3e 100644
--- a/examples/demo_mdss_detector.ipynb
+++ b/examples/demo_mdss_detector.ipynb
@@ -1,1542 +1,1730 @@
{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Bias scan using Multi-Dimensional Subset Scan (MDSS)\n",
- "\n",
- "\"Identifying Significant Predictive Bias in Classifiers\" https://arxiv.org/abs/1611.08292\n",
- "\n",
- "The goal of bias scan is to identify a subgroup(s) that has significantly more predictive bias than would be expected from an unbiased classifier. There are $\\prod_{m=1}^{M}\\left(2^{|X_{m}|}-1\\right)$ unique subgroups from a dataset with $M$ features, with each feature having $|X_{m}|$ discretized values, where a subgroup is any $M$-dimension\n",
- "Cartesian set product, between subsets of feature-values from each feature --- excluding the empty set. Bias scan mitigates this computational hurdle by approximately identifing the most statistically biased subgroup in linear time (rather than exponential).\n",
- "\n",
- "\n",
- "We define the statistical measure of predictive bias function, $score_{bias}(S)$ as a likelihood ratio score and a function of a given subgroup $S$. The null hypothesis is that the given prediction's odds are correct for all subgroups in $\\mathcal{D}$:\n",
- "\n",
- "$$H_{0}:odds(y_{i})=\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}}\\ \\forall i\\in\\mathcal{D}.$$\n",
- "\n",
- "The alternative hypothesis assumes some constant multiplicative bias in the odds for some given subgroup $S$:\n",
- "\n",
- "$$H_{1}:\\ odds(y_{i})=q\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}},\\ \\text{where}\\ q>1\\ \\forall i\\in S\\ \\mathrm{and}\\ q=1\\ \\forall i\\notin S.$$\n",
- "\n",
- "In the classification setting, each observation's likelihood is Bernoulli distributed and assumed independent. This results in the following scoring function for a subgroup $S$:\n",
- "\n",
- "\\begin{align*}\n",
- "score_{bias}(S)= & \\max_{q}\\log\\prod_{i\\in S}\\frac{Bernoulli(\\frac{q\\hat{p}_{i}}{1-\\hat{p}_{i}+q\\hat{p}_{i}})}{Bernoulli(\\hat{p}_{i})}\\\\\n",
- "= & \\max_{q}\\log(q)\\sum_{i\\in S}y_{i}-\\sum_{i\\in S}\\log(1-\\hat{p}_{i}+q\\hat{p}_{i}).\n",
- "\\end{align*}\n",
- "Our bias scan is thus represented as: $S^{*}=FSS(\\mathcal{D},\\mathcal{E},F_{score})=MDSS(\\mathcal{D},\\hat{p},score_{bias})$.\n",
- "\n",
- "where $S^{*}$ is the detected most anomalous subgroup, $FSS$ is one of several subset scan algorithms for different problem settings, $\\mathcal{D}$ is a dataset with outcomes $Y$ and discretized features $\\mathcal{X}$, $\\mathcal{E}$ are a set of expectations or 'normal' values for $Y$, and $F_{score}$ is an expectation-based scoring statistic that measures the amount of anomalousness between subgroup observations and their expectations.\n",
- "\n",
- "Predictive bias emphasizes comparable predictions for a subgroup and its observations and Bias scan provides a more general method that can detect and characterize such bias, or poor classifier fit, in the larger space of all possible subgroups, without a priori specification."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Usage\n",
- "\n",
- "MDScan currently supports three scoring functions. These scoring functions usage are described below:\n",
- "- *BerkJones*: Non-parametric scoring function. To be used for all of the four types of outcomes supported - binary, continuous, nominal, ordinal.\n",
- "- *Bernoulli*: Parametric scoring function. To used for two of the four types of outcomes supported - binary and nominal.\n",
- "- *Guassian*: Parametric scoring function. To used for one of the four types of outcomes supported - continuous.\n",
- "- *Poisson*: Parametric scoring function. To be used for three of the four types of outcomes supported - binary, continuous, and ordinal.\n",
- "\n",
- "Note, non-parametric scoring functions can only be used for datasets where the expectations are constant or none.\n",
- "\n",
- "The type of outcomes must be provided using the mode keyword argument. The definition for the four types of outcomes supported are provided below:\n",
- "- Binary: Yes/no outcomes. Outcomes must 0 or 1.\n",
- "- Continuous: Continuous outcomes. Outcomes could be any real number.\n",
- "- Nominal: Multiclass outcomes with no rank or order between them. Outcomes must be a finite set of integers with dimensionality <= 10.\n",
- "- Ordinal: Multiclass outcomes that are ranked in a specific order. Outcomes must be positive integers.\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from aif360.detectors.mdss_detector import bias_scan\n",
- "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_compas\n",
- "\n",
- "import numpy as np\n",
- "import pandas as pd"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We'll demonstrate finding the most anomalous subset with bias scan using the compas dataset. We can specify subgroups to be scored or scan for the most anomalous subgroup. Bias scan allows us to decide if we aim to identify bias as `higher` than expected probabilities or `lower` than expected probabilities."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Compas Dataset\n",
- "This is a binary classification use case where the favorable label is 0 and the scoring function is the default bernoulli."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "np.random.seed(0)\n",
- "\n",
- "dataset_orig = load_preproc_data_compas()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The dataset has the categorical features one-hot encoded so we'll modify the dataset to convert them back \n",
- "to the categorical featues because scanning one-hot encoded features may find subgroups that are not meaningful eg. a subgroup with 2 race values. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "dataset_orig_df = pd.DataFrame(dataset_orig.features, columns=dataset_orig.feature_names)\n",
- "\n",
- "age_cat = np.argmax(dataset_orig_df[['age_cat=Less than 25', 'age_cat=25 to 45', \n",
- " 'age_cat=Greater than 45']].values, axis=1).reshape(-1, 1)\n",
- "priors_count = np.argmax(dataset_orig_df[['priors_count=0', 'priors_count=1 to 3', \n",
- " 'priors_count=More than 3']].values, axis=1).reshape(-1, 1)\n",
- "c_charge_degree = np.argmax(dataset_orig_df[['c_charge_degree=F', 'c_charge_degree=M']].values, axis=1).reshape(-1, 1)\n",
- "\n",
- "features = np.concatenate((dataset_orig_df[['sex', 'race']].values, age_cat, priors_count, \\\n",
- " c_charge_degree, dataset_orig.labels), axis=1)\n",
- "feature_names = ['sex', 'race', 'age_cat', 'priors_count', 'c_charge_degree']"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "df = pd.DataFrame(features, columns=feature_names + ['two_year_recid'])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {
- "scrolled": true
- },
- "outputs": [
- {
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
sex
\n",
- "
race
\n",
- "
age_cat
\n",
- "
priors_count
\n",
- "
c_charge_degree
\n",
- "
two_year_recid
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
1
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
2.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
2
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
2.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
\n",
- "
\n",
- "
3
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
\n",
- "
\n",
- "
4
\n",
- "
0.0
\n",
- "
1.0
\n",
- "
1.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
0.0
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "source": [
+ "[](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/main/examples/demo_mdss_detector.ipynb)"
],
- "text/plain": [
- " sex race age_cat priors_count c_charge_degree two_year_recid\n",
- "0 0.0 0.0 1.0 0.0 0.0 1.0\n",
- "1 0.0 0.0 0.0 2.0 0.0 1.0\n",
- "2 0.0 1.0 1.0 2.0 0.0 1.0\n",
- "3 1.0 1.0 1.0 0.0 1.0 0.0\n",
- "4 0.0 1.0 1.0 0.0 0.0 0.0"
- ]
- },
- "execution_count": 5,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df.head()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### training\n",
- "We'll train a simple classifier to predict the probability of the outcome"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "LogisticRegression()"
- ]
- },
- "execution_count": 6,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "from sklearn.linear_model import LogisticRegression\n",
- "X = df.drop('two_year_recid', axis = 1)\n",
- "y = df['two_year_recid']\n",
- "clf = LogisticRegression(solver='lbfgs', C=1.0, penalty='l2')\n",
- "clf.fit(X, y)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Note that the probability scores we use are the probabilities of the favorable label, which is 0 in this case."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "probs = pd.Series(clf.predict_proba(X)[:,0])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### bias scan\n",
- "We can scan for a privileged and unprivileged subset using bias scan"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [],
- "source": [
- "privileged_subset = bias_scan(data=X,observations=y,expectations=probs,favorable_value=0, overpredicted=True)\n",
- "unprivileged_subset = bias_scan(data=X,observations=y,expectations=probs,favorable_value=0,overpredicted=False)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "({'age_cat': [1.0], 'priors_count': [0.0, 1.0, 2.0], 'sex': [1.0], 'race': [1.0], 'c_charge_degree': [0.0]}, 7.9086)\n",
- "({'race': [0.0], 'age_cat': [1.0, 2.0], 'priors_count': [1.0], 'c_charge_degree': [0.0, 1.0]}, 7.0227)\n"
- ]
- }
- ],
- "source": [
- "print(privileged_subset)\n",
- "print(unprivileged_subset)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "dff = X.copy()\n",
- "dff['observed'] = y \n",
- "dff['probabilities'] = 1 - probs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "to_choose = dff[privileged_subset[0].keys()].isin(privileged_subset[0]).all(axis=1)\n",
- "temp_df = dff.loc[to_choose]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected priviledged group has a size of 147, we observe 0.5374149659863946 as the average risk of recidivism, but our model predicts 0.38278159716895366'"
- ]
- },
- "execution_count": 12,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "\"Our detected priviledged group has a size of {}, we observe {} as the average risk of recidivism, but our model predicts {}\"\\\n",
- ".format(len(temp_df), temp_df['observed'].mean(), temp_df['probabilities'].mean())"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "to_choose = dff[unprivileged_subset[0].keys()].isin(unprivileged_subset[0]).all(axis=1)\n",
- "temp_df = dff.loc[to_choose]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected priviledged group has a size of 732, we observe 0.3770491803278688 as the average risk of recidivism, but our model predicts 0.4447038821779929'"
- ]
- },
- "execution_count": 14,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "\"Our detected priviledged group has a size of {}, we observe {} as the average risk of recidivism, but our model predicts {}\"\\\n",
- ".format(len(temp_df), temp_df['observed'].mean(), temp_df['probabilities'].mean())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Adult Dataset\n",
- "This is a binary classification use case where the favorable label is 1 and the scoring function is the berk jones."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
workclass
\n",
- "
education
\n",
- "
marital_status
\n",
- "
occupation
\n",
- "
relationship
\n",
- "
race
\n",
- "
sex
\n",
- "
native_country
\n",
- "
age_bin
\n",
- "
education_num_bin
\n",
- "
hours_per_week_bin
\n",
- "
capital_gain_bin
\n",
- "
capital_loss_bin
\n",
- "
observed
\n",
- "
expectation
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
0
\n",
- "
Private
\n",
- "
11th
\n",
- "
Never-married
\n",
- "
Machine-op-inspct
\n",
- "
Own-child
\n",
- "
Black
\n",
- "
Male
\n",
- "
United-States
\n",
- "
17-27
\n",
- "
1-8
\n",
- "
40-44
\n",
- "
0
\n",
- "
0
\n",
- "
0
\n",
- "
0.236226
\n",
- "
\n",
- "
\n",
- "
1
\n",
- "
Private
\n",
- "
HS-grad
\n",
- "
Married-civ-spouse
\n",
- "
Farming-fishing
\n",
- "
Husband
\n",
- "
White
\n",
- "
Male
\n",
- "
United-States
\n",
- "
37-47
\n",
- "
9
\n",
- "
45-99
\n",
- "
0
\n",
- "
0
\n",
- "
0
\n",
- "
0.236226
\n",
- "
\n",
- "
\n",
- "
2
\n",
- "
Local-gov
\n",
- "
Assoc-acdm
\n",
- "
Married-civ-spouse
\n",
- "
Protective-serv
\n",
- "
Husband
\n",
- "
White
\n",
- "
Male
\n",
- "
United-States
\n",
- "
28-36
\n",
- "
12-16
\n",
- "
40-44
\n",
- "
0
\n",
- "
0
\n",
- "
1
\n",
- "
0.236226
\n",
- "
\n",
- "
\n",
- "
3
\n",
- "
Private
\n",
- "
Some-college
\n",
- "
Married-civ-spouse
\n",
- "
Machine-op-inspct
\n",
- "
Husband
\n",
- "
Black
\n",
- "
Male
\n",
- "
United-States
\n",
- "
37-47
\n",
- "
10-11
\n",
- "
40-44
\n",
- "
7298-7978
\n",
- "
0
\n",
- "
1
\n",
- "
0.236226
\n",
- "
\n",
- "
\n",
- "
4
\n",
- "
?
\n",
- "
Some-college
\n",
- "
Never-married
\n",
- "
?
\n",
- "
Own-child
\n",
- "
White
\n",
- "
Female
\n",
- "
United-States
\n",
- "
17-27
\n",
- "
10-11
\n",
- "
1-39
\n",
- "
0
\n",
- "
0
\n",
- "
0
\n",
- "
0.236226
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ "metadata": {
+ "id": "cot8Opn7Ck5r"
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xYzhBYSCChiJ"
+ },
+ "source": [
+ "## Bias scan using Multi-Dimensional Subset Scan (MDSS)\n",
+ "\n",
+ "\"Identifying Significant Predictive Bias in Classifiers\" https://arxiv.org/abs/1611.08292\n",
+ "\n",
+ "The goal of bias scan is to identify a subgroup(s) that has significantly more predictive bias than would be expected from an unbiased classifier. There are $\\prod_{m=1}^{M}\\left(2^{|X_{m}|}-1\\right)$ unique subgroups from a dataset with $M$ features, with each feature having $|X_{m}|$ discretized values, where a subgroup is any $M$-dimension\n",
+ "Cartesian set product, between subsets of feature-values from each feature --- excluding the empty set. Bias scan mitigates this computational hurdle by approximately identifing the most statistically biased subgroup in linear time (rather than exponential).\n",
+ "\n",
+ "\n",
+ "We define the statistical measure of predictive bias function, $score_{bias}(S)$ as a likelihood ratio score and a function of a given subgroup $S$. The null hypothesis is that the given prediction's odds are correct for all subgroups in $\\mathcal{D}$:\n",
+ "\n",
+ "$$H_{0}:odds(y_{i})=\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}}\\ \\forall i\\in\\mathcal{D}.$$\n",
+ "\n",
+ "The alternative hypothesis assumes some constant multiplicative bias in the odds for some given subgroup $S$:\n",
+ "\n",
+ "$$H_{1}:\\ odds(y_{i})=q\\frac{\\hat{p}_{i}}{1-\\hat{p}_{i}},\\ \\text{where}\\ q>1\\ \\forall i\\in S\\ \\mathrm{and}\\ q=1\\ \\forall i\\notin S.$$\n",
+ "\n",
+ "In the classification setting, each observation's likelihood is Bernoulli distributed and assumed independent. This results in the following scoring function for a subgroup $S$:\n",
+ "\n",
+ "\\begin{align*}\n",
+ "score_{bias}(S)= & \\max_{q}\\log\\prod_{i\\in S}\\frac{Bernoulli(\\frac{q\\hat{p}_{i}}{1-\\hat{p}_{i}+q\\hat{p}_{i}})}{Bernoulli(\\hat{p}_{i})}\\\\\n",
+ "= & \\max_{q}\\log(q)\\sum_{i\\in S}y_{i}-\\sum_{i\\in S}\\log(1-\\hat{p}_{i}+q\\hat{p}_{i}).\n",
+ "\\end{align*}\n",
+ "Our bias scan is thus represented as: $S^{*}=FSS(\\mathcal{D},\\mathcal{E},F_{score})=MDSS(\\mathcal{D},\\hat{p},score_{bias})$.\n",
+ "\n",
+ "where $S^{*}$ is the detected most anomalous subgroup, $FSS$ is one of several subset scan algorithms for different problem settings, $\\mathcal{D}$ is a dataset with outcomes $Y$ and discretized features $\\mathcal{X}$, $\\mathcal{E}$ are a set of expectations or 'normal' values for $Y$, and $F_{score}$ is an expectation-based scoring statistic that measures the amount of anomalousness between subgroup observations and their expectations.\n",
+ "\n",
+ "Predictive bias emphasizes comparable predictions for a subgroup and its observations and Bias scan provides a more general method that can detect and characterize such bias, or poor classifier fit, in the larger space of all possible subgroups, without a priori specification."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_StPscBKChiM"
+ },
+ "source": [
+ "### Usage\n",
+ "\n",
+ "MDScan currently supports three scoring functions. These scoring functions usage are described below:\n",
+ "- *BerkJones*: Non-parametric scoring function. To be used for all of the four types of outcomes supported - binary, continuous, nominal, ordinal.\n",
+ "- *Bernoulli*: Parametric scoring function. To used for two of the four types of outcomes supported - binary and nominal.\n",
+ "- *Guassian*: Parametric scoring function. To used for one of the four types of outcomes supported - continuous.\n",
+ "- *Poisson*: Parametric scoring function. To be used for three of the four types of outcomes supported - binary, continuous, and ordinal.\n",
+ "\n",
+ "Note, non-parametric scoring functions can only be used for datasets where the expectations are constant or none.\n",
+ "\n",
+ "The type of outcomes must be provided using the mode keyword argument. The definition for the four types of outcomes supported are provided below:\n",
+ "- Binary: Yes/no outcomes. Outcomes must 0 or 1.\n",
+ "- Continuous: Continuous outcomes. Outcomes could be any real number.\n",
+ "- Nominal: Multiclass outcomes with no rank or order between them. Outcomes must be a finite set of integers with dimensionality <= 10.\n",
+ "- Ordinal: Multiclass outcomes that are ranked in a specific order. Outcomes must be positive integers.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "pEw-BVOaChiM"
+ },
+ "outputs": [],
+ "source": [
+ "from aif360.detectors.mdss_detector import bias_scan\n",
+ "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_compas\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "WBaUrHfAChiN"
+ },
+ "source": [
+ "We'll demonstrate finding the most anomalous subset with bias scan using the compas dataset. We can specify subgroups to be scored or scan for the most anomalous subgroup. Bias scan allows us to decide if we aim to identify bias as `higher` than expected probabilities or `lower` than expected probabilities."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "b-4Qb66XChiN"
+ },
+ "source": [
+ "# Compas Dataset\n",
+ "This is a binary classification use case where the favorable label is 0 and the scoring function is the default bernoulli."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Yzhak7pCChiO"
+ },
+ "outputs": [],
+ "source": [
+ "np.random.seed(0)\n",
+ "\n",
+ "dataset_orig = load_preproc_data_compas()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_aaSrX5ZChiO"
+ },
+ "source": [
+ "The dataset has the categorical features one-hot encoded so we'll modify the dataset to convert them back\n",
+ "to the categorical featues because scanning one-hot encoded features may find subgroups that are not meaningful eg. a subgroup with 2 race values."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "EsV3GU1TChiO"
+ },
+ "outputs": [],
+ "source": [
+ "dataset_orig_df = pd.DataFrame(dataset_orig.features, columns=dataset_orig.feature_names)\n",
+ "\n",
+ "age_cat = np.argmax(dataset_orig_df[['age_cat=Less than 25', 'age_cat=25 to 45',\n",
+ " 'age_cat=Greater than 45']].values, axis=1).reshape(-1, 1)\n",
+ "priors_count = np.argmax(dataset_orig_df[['priors_count=0', 'priors_count=1 to 3',\n",
+ " 'priors_count=More than 3']].values, axis=1).reshape(-1, 1)\n",
+ "c_charge_degree = np.argmax(dataset_orig_df[['c_charge_degree=F', 'c_charge_degree=M']].values, axis=1).reshape(-1, 1)\n",
+ "\n",
+ "features = np.concatenate((dataset_orig_df[['sex', 'race']].values, age_cat, priors_count, \\\n",
+ " c_charge_degree, dataset_orig.labels), axis=1)\n",
+ "feature_names = ['sex', 'race', 'age_cat', 'priors_count', 'c_charge_degree']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "jWLqSK0pChiO"
+ },
+ "outputs": [],
+ "source": [
+ "df = pd.DataFrame(features, columns=feature_names + ['two_year_recid'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "scrolled": true,
+ "id": "EfTmpJKMChiP",
+ "outputId": "b3287ae8-bee7-4d60-bfac-a201c384849f"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
sex
\n",
+ "
race
\n",
+ "
age_cat
\n",
+ "
priors_count
\n",
+ "
c_charge_degree
\n",
+ "
two_year_recid
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
2.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
2.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sex race age_cat priors_count c_charge_degree two_year_recid\n",
+ "0 0.0 0.0 1.0 0.0 0.0 1.0\n",
+ "1 0.0 0.0 0.0 2.0 0.0 1.0\n",
+ "2 0.0 1.0 1.0 2.0 0.0 1.0\n",
+ "3 1.0 1.0 1.0 0.0 1.0 0.0\n",
+ "4 0.0 1.0 1.0 0.0 0.0 0.0"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
],
- "text/plain": [
- " workclass education marital_status occupation \\\n",
- "0 Private 11th Never-married Machine-op-inspct \n",
- "1 Private HS-grad Married-civ-spouse Farming-fishing \n",
- "2 Local-gov Assoc-acdm Married-civ-spouse Protective-serv \n",
- "3 Private Some-college Married-civ-spouse Machine-op-inspct \n",
- "4 ? Some-college Never-married ? \n",
- "\n",
- " relationship race sex native_country age_bin education_num_bin \\\n",
- "0 Own-child Black Male United-States 17-27 1-8 \n",
- "1 Husband White Male United-States 37-47 9 \n",
- "2 Husband White Male United-States 28-36 12-16 \n",
- "3 Husband Black Male United-States 37-47 10-11 \n",
- "4 Own-child White Female United-States 17-27 10-11 \n",
- "\n",
- " hours_per_week_bin capital_gain_bin capital_loss_bin observed expectation \n",
- "0 40-44 0 0 0 0.236226 \n",
- "1 45-99 0 0 0 0.236226 \n",
- "2 40-44 0 0 1 0.236226 \n",
- "3 40-44 7298-7978 0 1 0.236226 \n",
- "4 1-39 0 0 0 0.236226 "
- ]
- },
- "execution_count": 15,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "data = pd.read_csv('https://gist.githubusercontent.com/Viktour19/b690679802c431646d36f7e2dd117b9e/raw/d8f17bf25664bd2d9fa010750b9e451c4155dd61/adult_autostrat.csv')\n",
- "data.head()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Note that for the adult dataset, the positive label is 1 and thus the expectations provided is the probability of the earning >50k i.e label 1 and the favorable label is 1 which is the default for binary classification tasks. Since we would be using scoring function BerkJones, we also need to pass in an alpha value. Alpha can be interpreted as what proportion of the data you expect to have the favorable value"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [],
- "source": [
- "X = data.drop(['observed','expectation'], axis = 1)\n",
- "probs = data['expectation']\n",
- "y = data['observed']"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 17,
- "metadata": {},
- "outputs": [],
- "source": [
- "privileged_subset = bias_scan(data=X, observations=y, scoring='BerkJones', expectations=probs, overpredicted=True,penalty=50, alpha = .24)\n",
- "unprivileged_subset = bias_scan(data=X,observations=y, scoring='BerkJones', expectations=probs, overpredicted=False,penalty=50, alpha = .24)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 18,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "({'relationship': [' Not-in-family', ' Other-relative', ' Own-child', ' Unmarried'], 'capital_gain_bin': ['0']}, 932.4812)\n",
- "({'education_num_bin': ['12-16'], 'marital_status': [' Married-civ-spouse']}, 1041.1901)\n"
- ]
- }
- ],
- "source": [
- "print(privileged_subset)\n",
- "print(unprivileged_subset)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 19,
- "metadata": {},
- "outputs": [],
- "source": [
- "dff = X.copy()\n",
- "dff['observed'] = y \n",
- "dff['probabilities'] = probs"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 20,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected privileged group has a size of 8532, we observe 0.0472 as the average probability of earning >50k, but our model predicts 0.2362'"
- ]
- },
- "execution_count": 20,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_choose = dff[privileged_subset[0].keys()].isin(privileged_subset[0]).all(axis=1)\n",
- "temp_df = dff.loc[to_choose]\n",
- "\n",
- "\"Our detected privileged group has a size of {}, we observe {} as the average probability of earning >50k, but our model predicts {}\"\\\n",
- ".format(len(temp_df), np.round(temp_df['observed'].mean(),4), np.round(temp_df['probabilities'].mean(),4))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 21,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected unprivileged group has a size of 2430, we observe 0.6996 as the average probability of earning >50k, but our model predicts 0.2362'"
- ]
- },
- "execution_count": 21,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_choose = dff[unprivileged_subset[0].keys()].isin(unprivileged_subset[0]).all(axis=1)\n",
- "temp_df = dff.loc[to_choose]\n",
- "\n",
- "\"Our detected unprivileged group has a size of {}, we observe {} as the average probability of earning >50k, but our model predicts {}\"\\\n",
- ".format(len(temp_df), np.round(temp_df['observed'].mean(),4), np.round(temp_df['probabilities'].mean(),4))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Insurance Costs\n",
- "This is a regression use case where the favorable value is 0 and the scoring function is Gaussian."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 22,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "(1338, 7)"
- ]
- },
- "execution_count": 22,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "data = pd.read_csv('https://raw.githubusercontent.com/Adebayo-Oshingbesan/data/main/insurance.csv')\n",
- "data.shape"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {},
- "outputs": [],
- "source": [
- "for col in ['bmi','age']:\n",
- " data[col] = pd.qcut(data[col], 10, duplicates='drop')\n",
- " data[col] = data[col].apply(lambda x: str(round(x.left, 2)) + ' - ' + str(round(x.right,2)))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {},
- "outputs": [],
- "source": [
- "features = data.drop('charges', axis = 1)\n",
- "X = features.copy()\n",
- "\n",
- "for feature in X.columns:\n",
- " X[feature] = X[feature].astype('category').cat.codes\n",
- "\n",
- "y = data['charges']"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "from sklearn.linear_model import LinearRegression\n",
- "reg = LinearRegression()\n",
- "reg.fit(X, y)\n",
- "y_pred = pd.Series(reg.predict(X))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [],
- "source": [
- "privileged_subset = bias_scan(data=features, observations=y, expectations=y_pred, scoring = 'Gaussian', \n",
- " overpredicted=True, penalty=1e10, mode ='continuous', favorable_value='low')\n",
- "\n",
- "unprivileged_subset = bias_scan(data=features, observations=y, expectations=y_pred, scoring = 'Gaussian', \n",
- " overpredicted=False, penalty=1e10, mode ='continuous', favorable_value='low')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "({'bmi': ['15.96 - 22.99', '22.99 - 25.33', '25.33 - 27.36'], 'smoker': ['no']}, 2384.5786)\n",
- "({'bmi': ['15.96 - 22.99', '22.99 - 25.33', '25.33 - 27.36', '27.36 - 28.8'], 'smoker': ['yes']}, 3927.8765)\n"
- ]
- }
- ],
- "source": [
- "print(privileged_subset)\n",
- "print(unprivileged_subset)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected privileged group has a size of 321, we observe 7844.8402958566985 as the mean insurance costs, but our model predicts 5420.493262774548'"
- ]
- },
- "execution_count": 28,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_choose = data[privileged_subset[0].keys()].isin(privileged_subset[0]).all(axis=1)\n",
- "temp_df = data.loc[to_choose].copy()\n",
- "temp_y = y_pred.loc[to_choose].copy()\n",
- "\n",
- "\"Our detected privileged group has a size of {}, we observe {} as the mean insurance costs, but our model predicts {}\"\\\n",
- ".format(len(temp_df), temp_df['charges'].mean(), temp_y.mean())"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 29,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected privileged group has a size of 115, we observe 21148.373896173915 as the mean insurance costs, but our model predicts 29694.035319112845'"
- ]
- },
- "execution_count": 29,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_choose = data[unprivileged_subset[0].keys()].isin(unprivileged_subset[0]).all(axis=1)\n",
- "temp_df = data.loc[to_choose].copy()\n",
- "temp_y = y_pred.loc[to_choose].copy()\n",
- "\n",
- "\"Our detected privileged group has a size of {}, we observe {} as the mean insurance costs, but our model predicts {}\"\\\n",
- ".format(len(temp_df), temp_df['charges'].mean(), temp_y.mean())"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Hospitalization Time\n",
- "This is an ordinal, multiclass classification use case where the favorable value is 1 and the scoring function is Poisson."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "(29980, 22)"
- ]
- },
- "execution_count": 30,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "data = pd.read_csv('https://raw.githubusercontent.com/Adebayo-Oshingbesan/data/main/hospital.csv')\n",
- "data = data[data['Length of Stay'] != '120 +'].fillna('Unknown')\n",
- "data.shape"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 31,
- "metadata": {},
- "outputs": [],
- "source": [
- "X = data.drop(['Length of Stay'], axis = 1)\n",
- "y = pd.to_numeric(data['Length of Stay'])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "privileged_subset = bias_scan(data=X, observations=y, scoring = 'Poisson', favorable_value = 'low', overpredicted=True, penalty=50, mode ='ordinal')\n",
- "unprivileged_subset = bias_scan(data=X, observations=y, scoring = 'Poisson', favorable_value = 'low', overpredicted=False, penalty=50, mode ='ordinal')"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "({'APR Severity of Illness Description': ['Extreme']}, 11180.5386)\n",
- "({'Patient Disposition': ['Home or Self Care', 'Left Against Medical Advice', 'Short-term Hospital'], 'APR Severity of Illness Description': ['Minor', 'Moderate'], 'APR MDC Code': [1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 21]}, 9950.881)\n"
- ]
- }
- ],
- "source": [
- "print(privileged_subset)\n",
- "print(unprivileged_subset)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [],
- "source": [
- "dff = X.copy()\n",
- "dff['observed'] = y \n",
- "dff['predicted'] = y.mean()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected privileged group has a size of 1900, we observe 15.2216 as the average number of days spent in the hospital, but our model predicts 5.4231'"
- ]
- },
- "execution_count": 35,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_choose = dff[privileged_subset[0].keys()].isin(privileged_subset[0]).all(axis=1)\n",
- "temp_df = dff.loc[to_choose]\n",
- "\n",
- "\"Our detected privileged group has a size of {}, we observe {} as the average number of days spent in the hospital, but our model predicts {}\"\\\n",
- ".format(len(temp_df), np.round(temp_df['observed'].mean(),4), np.round(temp_df['predicted'].mean(),4))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 36,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "'Our detected unprivileged group has a size of 14620, we observe 2.8301 as the average number of days spent in the hospital, but our model predicts 5.4231'"
- ]
- },
- "execution_count": 36,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_choose = dff[unprivileged_subset[0].keys()].isin(unprivileged_subset[0]).all(axis=1)\n",
- "temp_df = dff.loc[to_choose]\n",
- "\n",
- "\"Our detected unprivileged group has a size of {}, we observe {} as the average number of days spent in the hospital, but our model predicts {}\"\\\n",
- ".format(len(temp_df), np.round(temp_df['observed'].mean(),4), np.round(temp_df['predicted'].mean(),4))"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Temperature Dataset\n",
- "This is a regression use case where the favorable value is the higher temperatures and the scoring function is Berk Jones."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 37,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "
",
+ "image/svg+xml": "\n\n\n\n",
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAHGCAYAAABD3NsdAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8li6FKAAAgAElEQVR4nOzdd3hU1dbA4d+iFxGlSPGCKIICCtIGQRNBxV5QEfDa/cSCYgFBvWIXRFAUO+BVwYqCYlekKpgoVRQQECkqvYgYOtnfH2vmZowpk2RmzpmZ9T7PPJOcOXNmJZkkZ5299trinMMYY4wxxhhjTP5KeR2AMcYYY4wxxvidJU7GGGOMMcYYUwhLnIwxxhhjjDGmEJY4GWOMMcYYY0whLHEyxhhjjDHGmEJY4mSMMcYYY4wxhbDEyRhjjDHGGGMKYYmTMcYYY4wxxhTCEidjjMlFRL4RERd2a+J1TCZyIjIt7GfXwOt44kFEVuZ6z+Z3W+l1rMYYk6gscTLGmDAi0hBon2vz5V7EYowxxhj/sMTJGGP+7rI8tv1bRCTukRSBiFTwOgbjHedcA+echG65HpOwWwOPQjTGmIRniZMxxvzdpcH7ncDbwY8PA9Jz7ygirUTkLRH5XUT2iMgmEZkqIoEi7vO/0rJcx//HdhF5NazsKk1ExonINmBx8PHuIvKliPwqIjtEZLeI/CIiL4pIraJ8DSLyVNhrtcv1vNnB7VtFpGJ+38yixJPra+sgIq8Hj79ZRMaLSO08jn+NiCwTkV0iMk9ETs8vlnziGyci60Xk7DweGysiP4lIuaIcM4/jPBn2dR2f67Fvg9v/EpEqInK4iIwRkdXBr+kPEfkx+L05pCRx5HrdovxcIn5/GmNMUnPO2c1udrOb3ZwDOB5wwdt7wBlhn4/Kte8FwN6wx8NvV0W6T3C/aaHtuV7jH9uBV8OOsSns45XBx1/M5/Uc8BNQLtKvATgC2B/8/JWw5zUK2++5Qr6nRYkn/Gvbmsf+k3Id+6o89tkLbAj7vEEh8QWCcUzJtf2U4PNPjcL7qnlYPMPDtjcM2/5qcNvCAr5fxxThNf/3vCj8XCJ+f9rNbnazWzLfbMTJGGNyhJfpjQMmA38EP+8qIuUBgiMso4AywcfuA2oBNYCLgV8i2ScK8f6JzseqCJwV3PYm0C74OmWDr/lK8LGjQvtFEp9z7hfgo+Dj3UXkoODHPcJiGFVIjBHFk4cVaGLRGE2EAE4RkTrB+EsBA8P2vwI4EOgP1Cwkpv9xzn0HvAEcE9omImWBZ4F3nXOTIj1WAa+xAJgT/LSbiJQOfnxJ2G4vi0h1oGnw86eBykA1oC1wL7CtpLGEKe7PxRhjUlaZwncxxpjkFzxZ7h78dDfwsXNur4h8hDaHOAg4F02oTgCqB/ed5px7OOxQ44LHO7WwfaJggHMuM/jxouD9WjQJOgmojZ4UhzsqeF/o1xD0JHA+mpxdCQwn5/s0xzk3v5AYI40nt/uCiRsi8jVwUXD7YcFjHgXUDW6b55x7LRSviNwO1CskrnCLgJoiUt05txnoA/wL6FyEYxTmZaA1+j3oBEwiJ3H62Tn3VTAZ/AN9r50JbEdLML93zj0SxVig+D8XY4xJWTbiZIwx6gz06jvAXKC+iBwDfB+2T2hEKnwOyCLyFsk+hSns4ta88E9EpCowA42zHv88GQZNgCKOzzk3Pex1rheRY4Fmwc9fKii4IsaT25Kwj7PCPg41wagetu23XM/9vaC48vBT8L6JiPwLHd15yDmX+7j5ElXQ/9Q3gV3Bj/8tIi3IGV16GcA5l40m6b+h5ZD3AK8DP4jIDyJSlGSwoFhL8nMJZxdfjTEpxRInY4xR4WV67YEfgrfHw7afKSLVgPVh2/Jb4ymSfUJ2hz6QYHc8ERHg8EKetzPX552AUAOByUAdpx3WbilhfMPD9nsm+PEONBkoSFHiyW1v2Md5NR/YFPbxv3I9dmgExw+3DJ3L1QQdYVsFPBV6UEQ6ichMEZkrIktFpHdw+0PBBhYfoSND/2he8b8vwLk/gAnBTy9E52cRfN3RYft9DNRHR3zOAx4K7nMMMKCIX1d+ivpzKe770xhjkoolTsaYlCciB6JleIUph5apzQQ2B7d1EpH/iEhNETlYRLqISHqE+4SsCvs4FEdvckrRIrUv7ONdQJaINAseK7eixPcWOYnWScH7d5xzf0YxnqJaCqwJftxSRC4PdqUrapkezrk9wHLgOqArcLNzLjxxewvo7pxrhSY0rwe3t0aTnMucc0c759ZQsJeD91WBm4MffxH+PBF5Bm1M8RfwOTCenMSlflG+rgIU9ecSrfenMcYkNEucjDFGT5ZDpUnvuL+veyPAaWH7Xuac2wn0JOcEdCDawGAL8D5wRCT7hB3zjbCP3xGR7egoT+4RpcLMBDYGPz4bbR7xY147FiW+YGLxfK5DFFimV9R4iipY1nZP2KYxweM/jnbkK6rFQBvgbefc1FyP/QY8KyI9gMrOudDxWwO3OucibdowGVgd/DhU5vZyrn1uBL5Eyw33oKWilYKPfRHh6xSmqD+XaL0/jTEmoVniZIwxfy/TG5PH45PJmTfTQUSOcM69j3Ylexsd+diHJh3TCc4ZimSf4H7TgWvRUZTd6OjHxcB3Rfkigif0Z6LzV3YEX/MBYHA++0cUX9AL5Ix8LHbOzYx2PEXlnHsV+D/0+7UHLa28EFhQjMP9jI6+9M3jsePRLndpwDIRqRqcC1XGOTcvj/3zizcbbbkesgn4MNdug9Hv1wb057EDnXN3CzklkyVSjPdJVN6fxhiT6MQ5W7fOGGNMwUSkKZqQlEZHWZ72OKSoEpGxQD3nXIdc248GljrnskXkUHQE6DDgVOAG59yZ8Y/WGGOMF6wjjjHGmHyJyAXAY2iyUBotW4ukTC/RtAY+y2P77egcsCx0ROoy51yWiLQGZsUzQGOMMd6yxMkYY0xBqqKtsXcBXwO9nHM7vA0puoLtuY9AS+L+xjl3fV7Pcc7dF+u4jDHG+IuV6hljjDHGGGNMIaw5hDHGGGOMMcYUIuVL9UqVKuUqVoxkgXRjjDHGGGNMfnbs2OGcc0k7MJPyiVPFihXJysryOgxjjDHGGGMSmogUuL5bcHH1O9CGPHWBq4NLSxT0nGOBZ4EAumTGCOBh58F8o6TNCI0xxhhjjDG+cgC64PatRLCItogciC4Kvh5oG3xeP6BPDGPMV8qPOBljjDHGGGNizzn3KfApgIi8GsFTLgUqAVc653YCPwbX1+sjIsPiPepkI07GGGOMMcYYP2oPfB1MmkK+QMv8GsQ7mJQfcapWrRrTpk3zOgxjjDHGGGMSXRkRmR32+Ujn3MgSHK82uvB6uPVhj60owbGLLOUTpy1bttCxY0evwzDGGGOMMSbR7XPOtfE6iFixUj1jjDHGGGOMH60DauXaVivssbiyxMkYY4wxxhjjRxlAmohUCNvWGVgDrIx3MJY4GWOMMcYYY2JORA4QkeNE5Dg0D6kf/Lx+8PFHRWRy2FPeBHYAr4rIMSJyIXAXEPeOemCJkzHGGGOMMSY+2gDzgreKwIPBjx8KPl4HaBja2Tm3DR1hqgvMBp4DngCGxS/kHOJBsuYrlStXdllZWV6HYYwxxhhjTEITkR3OucpexxErNuJkjDHGGGOMMYWwxMkYY4wxxhhjCuFJ4iQivURkhYjsEpE5IpJWyP7/FpH5IrJDRNaJyOsiUjvXPheJyCIR2R28vyC2X4UxxhhjjDEmVcQ9cRKR7sBwYBDQEvgG+CzUTSOP/U8AXgNGA82ALkBT4I2wfdoDY4Pbjgvevysi7WL3lRhjjDHGGGNSRdybQ4jIt8AC51zPsG3LgHHOubvz2P8OoLdz7rCwbVcDzzjnDgh+Phao5pzrHLbPJGCjc+6SguLxvDnEjh2waBG0SdpFlo0xxhhjTAz98gtUqAB163obhzWHiCIRKQe0Bibmemgi0CGfp80E6ojIuaJqAD2AT8P2aZ/HMb8o4Jj+MWwYBALw559eR2KMMcYYYxLEihUwZIhee2/YEJ5/3uuIkl+8S/VqAKWB9bm2rwdq/3N3cM5loInSG8AeYCMgwJVhu9UuyjFF5DoRmS0is/ft21fUryG6AgFwDmbP9jYOY4wxxhjjaytXwtCh0LYtHHEE3HknlC6t23r2LPTppoTKeB1AYUSkKfAM8DA6ilQHGAqMAK4ozjGdcyOBkaCletGJtJhCJXrffQcnn+xpKMYYY4wxxl9WroRx4+Cdd2DWLN3Wtq0mS127QoMGXkaXWuKdOG0C9gO1cm2vBazL5zl3A98554YGP18gIlnA1yLyH+fcb8HnFuWY/lGtGjRqpImTMcYYY4xJeatW5SRLoVPENm20NK9rVzj8cG/jS1VxTZycc3tEZA7QGXg37KHOwPh8nlYJTbbChT4PlRpmBI8xNGyfzmjHPv8LBGDqVK+jMMYYY4wxHlm9OidZ+vZb3da6NTz2mCZLRxzhbXzGm1K9YcBrIvId2vjhBqAu8CKAiIwBcM6FyvA+AkaJyI3klOo9Bcx1zq0O7jMc+EpE7gImABcAnYAT4/IVlVS7dvDGG/D773DooV5HY4wxxpTY2rVQsSIcdJDXkRjjX7/+mpMsZWbqtlatYPBgTZYaNvQ2PvN3cU+cnHNjRaQ6MABNgn4EznLOrQruUj/X/q+KSBXgZuAJYBswBbgzbJ9vRKQH8AjwELAc6O6c+zbWX09UBAJ6/913cIGt22uMMSaxbd4MLVrAv/6lvY9KxX3VSGP867ffcpKljAzd1rIlPPqoJktHHultfCZ/cV/HyW88X8cJYNcuOPBA6NtXf2uMMcaYBHbNNfDKK/rxa6/BZZd5G48xXvv995xk6ZvgRJLjjoNu3eDii5MnWUr2dZwscfJD4gTaHuXAA2HyZK8jMcYYY4pt2jTo1An694dJk3T06aefdHFOY1JJKFl6912YOVO3tWiRkyw1auRtfLFgiVOS803idPPNMGYMbN2qDfmNMcaYBLNrl54Y7tsHP/6oJ4udO8MTT0CfPl5HZ0zsrVmTkyzNmKHbmjfPSZYaN/Y2vlhL9sTJqo79IhCA7dthyRKvIzHGGGOKZdAgWLoUXnxRG0OceiqcdhoMHAh//OF1dMbExpo18MwzkJ6u8/puvRW2bYOHH9bR1u+/h3vuSf6kKRVY4uQX4Q0ijDHGmASzaJF2ArvsMh1lCnnsMS2mGDzYu9iMiba1a+HZZ+GkkzRZuuUWfZ8/+CAsXgwLFsCAAXDUUV5HaqLJSvX8UqqXnQ0HHwyXXgrPP+91NMYYY0zEsrP1BHLRIr3CXrPm3x+//HItX1q6FOrV8yZGY0pq3ToYP17L8L76CpyDZs1yyvCaNPE6Qu8le6meJU5+SZxAaxq2boU5c7yOxBhjjInYyJFw/fXw8stw9dX/fHzVKi1TuvRS3ceYRLF+fU6yNH16TrJ08cV6a9rU6wj9xRKnJOerxOmee2DIEPjzTy0ON8ZE1ahRWmv+7LNeR2JM8li3Do4+WtehmTIFRPLe7447YNgw/R089tj4xmhMUaxfD++9l5MsZWfraFJoZKlZM68j9C9LnJKcrxKnDz6ALl20wX/79l5HY0zSadsW5s3Tgd0qVbyOxpjk0KMHTJigczoKmvy+ZQs0bAgdOsAnn8QvPmMisWGDJkvvvJOTLB19tCZL3bpZshSpZE+crDmEn1iDCGNiZudOmD8f9u/PWandGFMyn34KY8dG1jGsWjW4+259zrRpcQnPmAJt3AgjRsApp0CdOnDjjdoh75574IcfdM7egw9a0mRy2IiTn0acQGfNpqXBm296HYkxSWXGDP3VAu109PDD3sZjTKLLytITysqVdSS3XLnCn7NzpyZYderAt9/mX9ZnTKxs3Ajvv68jS1On6shS48Y5I0vHHGPvy5JI9hGnMl4HYHJp185GnIyJgcxMvT/8cO2GZIwpmfvv16YPX38dWdIEOn334Ye1gcS77+qJqjGxtmnT35Ol/fuhUSMdAe3WTefcWbJkImEjTn4bcRoyBO68U3/Lq1f3OhpjksZFF2mpXpcu8Nxzujhh+fJeR2VMYpo7V+cMXnutljoVxf792khixw4thYo06TKmKDZvzkmWpkzR992RR+aMLDVvbslSLCT7iJPNcfKb0DynWbO8jcOYJOKczmtq317L9Xbvtl8xY4pr3z647jpdq6k4i9qWLq2L4i5frm3MjYmWzZvhv/+F00+HWrWgZ09YsQL699dy0qVLYeBAaNHCkiZTPJY4+U3r1vrbbOV6xkTNr7/qKu/t28OJJ+q2r7/2NiZjEtWzz+pyg08/reu2F8cZZ0CnTjrx/s8/oxufSS1btujaYGecAbVr6yjo8uXQr5+OjC5dCoMGwXHHWbJkSs4SJ7+pUkVXU/v2W68jMSZphLroHX881Kihv2I2z8mYolu9WpurnHWWrmdTXCJamb5pEwwdGr34TGrYuhVeeQXOPFNHlv7v/2DZMujbV5P6Zcvg0Ue1JNSSJRNN1hzCj9q1gw8/1Poi+403psQyM3VSevPm+nlaGrz1lta8ly7tbWzGJArn4Kab9P6550r+76lNG+jeXRfF7dVLO+0Zk5+tW3W5y3fegUmTYO9ebfbTp4/OWWrVyk6ZTOzZiJMfBQJ6GW7lSq8jMSYpZGToSVrZsvp5erqWBy1Y4G1cxiSS8ePh44+1K16DBtE55sCBegL8wAPROZ5JPuvXw3nn6cjS1VfD4sVw++06T3X5cp0vF5rlYEysWeLkR7YQrjFRs3u3Tgpu3z5nW2g9JyvXMyYyf/wBvXvrVf1bbonecRs2hBtu0An9P/0UveOa5HHzzTBxItx2m54W/fKLJktt2liyZOLPEic/OuYYqFDB5jkZEwVz58KePTq/KaRePb1ibg0ijInM3XfDhg3aBa9MlIv8770XKlXS1zAm3Ecfwbhx+h4ZMkRb4FuyZLxkiZMflS2r48424mRMiYUaQ4SPOIGOOn31lc7XMMbk75tv4MUX4dZb9V9TtNWsqcsXTpgAM2dG//gmMW3frnPqmjXTDnnG+IElTn4VCOil8r17vY7EmISWmamjS7Vr/317ejps3Kitao0xeduzR9dsql8fHnoodq9z223aHKJfP7uYYdS99+pSEiNH2iLJxj8scfKrQAB27oSFC72OxJiElpHx9zK9EJvnZEzhhg7Vf0PPPQcHHBC716lcWdd0ysjQkSeT2mbNgmeegRtvhA4dvI7GmByWOPmVNYgwpsR++01vucv0ABo3hkMOsXlOxuRn2TLtoHfxxXDOObF/vauvhqOP1rlO+/bF/vWMP+3bp6OctWrpWkzG+IklTn51+OG6Uqc1iDCm2DIz9T6vEScRHXWyxMmYf3JOu91VqADDh8fnNcuUgcGDYckS7bJnUtNTT8H8+TriVLWq19EY83eWOPmViI462YiTMcWWmQnly8Nxx+X9eFqaLpf2669xDcsY3xszBqZM0UQmngvTnncenHCCruuUlRW/1zX+sGIF3HcfnHsuXHih19EY80+WOPlZIKDF5du3ex2JMQkpI0O7gOU3sTg9Xe9t1MmYHJs2Qd++Orfkuuvi+9oiOq9q3ToYNiy+r2285Rz06gWlS+ucOms7bvzIEic/CwT0L8ncuV5HYkzC2bMH5szJe35TSPPmcOCB1iDCmHB9+8Kff2o3s1IenCW0b6+jDUOG6NpRJjW8/TZ8/jk88oiutWeMH1ni5Gdt2+q9zXMypsjmz4fdu/Oe3xRSurSWBdmIkzFq0iQt0+vfX9fP8cqgQdpYNpYt0I1/bNmiLenbtIGbb/Y6GmPyZ4mTn9WoAQ0b2jwnY4ohv4Vvc0tLg0WLtDzJmFS2c6c2hGjUCAYM8DaWo46Cnj1hxAjt7meSW//+sHkzjBqlF7SM8StLnPzOGkQYUyyZmVruceihBe8Xmuc0Y0bsYzJJZNu2pFup9ZFHYPlyePFF7abntfvv1+Yu99zjdSQmlqZP1y6Kffrk38jHGL+wxMnvAgFt+bV2rdeRGJNQ8lv4Nrc2bfTkzMr1TMQ++UQXAWvfPmneOD/8oHOKrrwSTj7Z62hU7do63+rdd+36YbLavRuuvx4aNNBE2Ri/s8TJ70IL4c6a5W0cxiSQtWth1arCy/RAk6Z27axBhInQlClw0UVaz/bbbzpk2aUL/PST15EVW3a2nrwedBA8/rjX0fzdHXdojtq/f9IN8Bl0gdslS+CFF6ByZa+jMfEkIr1EZIWI7BKROSKSVsj+N4nIYhHZKSJLROSKeMUazhInv2vZUlcFtAYRxkSsoIVv85KeDvPmWed/U4hvvtGFhho10vqipUv1zG/qVDjmGJ0gtG6d11EW2YgROkI7bJhOrfWTKlV0JGL6dPj0U6+jMdG0eLH++lxyCZxxhtfRmHgSke7AcGAQ0BL4BvhMROrns/+NwGPAQ0Az4H7gORE5Nz4Rh8XiUvwSTuXKlV2W31fZa90aqlWDL7/0OhJjEsKdd+rq83/+qSNKhZk4EU4/Hb74Ak47LfbxmQQ0d67WsNWsqeV5tWvnPLZpk04Qev55XTTsjjv0dsAB3sUboTVroEkTbeL65Zf+XDtn717t8FeuHHz/vTUPSAbZ2dCxI/z4oyZQtWp5HZGJFhHZ4ZwrcPxQRL4FFjjneoZtWwaMc87dncf+3wDfOuduD9v2BNDOOXdi9KIvnI04JYJAQEv1srO9jsSYhJCRoYO1kSRNoCV9pUsnzXQVE20LF2pGXbUqTJ7896QJdJjmqaf0DPDss+HBB+HII7XLwt693sQcoVtu0TXPXnzRn0kTQNmy2p584UIYPdrraEw0vPyy/r0dOtSSplQjIuWA1sDEXA9NBDrk87TywK5c23YCAREpG90IC5byI0716tVzr732mtdhFGzzZli5UktBIj0TNCZF7dsnnHPOiZx77hpuuml5xM+74YZWlC+fzfDh82MYnUk4u3frJAzQHtmR/A3OytL5T3/9pe3pDj1UJxD5zMyZ1Rkw4FiuvfYXLr10tdfhFMg5uOmmVmzcWJ7XXvuWChXsQmKi2rKlHFde2ZaGDbN48sn5vk3YTfF06tRpD/BD2KaRzrmRoU9EpC7wO3CSc+6rsO33AZc6547KfUwRGQT8H3AOMBtNvD4GagF1nXNx66CW8olTQpTqLVyoSdPo0XCFJ3PhjEkYc+Zop7yxY6Fbt8if17cvPPecdpm26xMGgNWrdaGvrCydZFOUFWGdg48/1rrRxYt1peWhQyPrWBIH27dD06aaz82dq6M6fvfVV3DSSTov5q67vI7GFNcll8B778GCBXotwiSXwkr1ipk4VQSeAy4HBFgPvA70B2o759ZH96vIn5XqJYKjj9YZstaP1ZhChRa+jbQxREhamg4uWANLA2iTh1NP1Ux64sSiJU2gdW/nnqtnhyNH6gJJHTpA166+WNH13nvh9981tERImkCbuJxzDgwerIUYJvF89hm8/bauzWVJU8raBOxHR4vC1QLy7K7jnNvpnLsGqAQ0AOoDK4HtwMZYBZoXS5wSQenSegndEidjCpWZCXXr6uK3RXFicHqpzXMybN4MnTtr54RPP4VWrYp/rDJloGdP+PlneOgh7UDStCncfDNs2BC9mItg1ix45hm48UbfDIBFbPBgHS0bONDrSExRZWXpe+7oo3Ug1qQm59weYA7QOddDndHuegU9d69z7jfn3H6gB/Cxcy6udbuWOCWKQADmz9dL4saYfIUWvi1q3XyNGno+a4lTitu2TVssLlsGH36oo0TRULmyDvP8/DNcd512Y2jYULvxxbFcfN8+fflatbThQqJp1gyuvlrLales8DoaUxT336/r640caeXQhmHAVSJyrYg0EZHhQF3gRQARGSMiY0I7i0hjEblcRBqJSEBE3gaOAf4T78AtcUoUgYB2Z5pvE9eNyc+GDfDLL8W/ip6WBjNnwv790Y3LJIisLO2K9/33MH68th+Ptlq19Kw/1Knv3nt1XaiXXtKsJsaeekr/jTz7rDYJTEQPPqiFGAMGeB2JidTcufDkkzr4mlbgMqcmFTjnxgK3AQOA+cCJwFnOuVXBXeoHbyGlgT7A98CXQAWgg3NuZbxiDrHEKVEEAnpv5XrG5KuoC9/mlp6uaz8tWBC9mEyC2LULunTRIcs339QEKpaOOkqTs5kz4fDD9YyyRQttKBGjpk0rVuhV//POgwsuiMlLxMWhh8Jtt+mPae5cr6MxhQmNctasCY895nU0xi+cc8875xo458o751qHN4pwznV0znUM+3yxc66lc66Sc66qc66Lc26JF3Fb4pQo/vUvnbhhiZMx+crM1CklrVsX7/mhK6FffVXwfibJ7N2rLRgnTdIFZi6+OH6v3aEDzJihbcb27dOGEp06Rb1LiXPQqxeUKqWjTYneAvrOO6F6dZsrkwiefVa7nQ4fDgcf7HU0xpSMJU6JJBCwxMmYAoQWvq1YsXjPr1cPGjSweU4pZf9+uPxy+OgjLaG78sr4xyCiQ0A//gjPP6/tywMB6NFDu/FFwdix8Pnn2lShqI1T/KhqVS3VmzRJmx4af1q9Wn9OZ55ZtOUhjPErS5wSSSAAS5fC1q1eR2KM7+zbp9cVilumF5KWpiNOKb7EXWrIztYSubFjYcgQHZLxUtmy2nbs55/hvvs0mWvSROvSNm0q9mG3boVbb4W2beGmm6IYr8duvFEvdNx5p/4ojb+ERjmd0+sBiT7KaQxY4pRYQvOcbKEZY/7hxx9hx46St1dOS4ONG/UahUlizmlC8sormqT06+d1RDmqVNEOCD//rC3knnlGO/ANHgw7dxb5cP37a4f1kSO1qUKyKF9eR9Dmz9f5TsZfxo2DTz7RLvwNGngdjTHRYYlTImnTRi/ZWLmeMf9Q3IVvc0tP13sr10ty99yjCUmfPvDAA15Hk7c6dWDECPjhB+jYEe6+Gxo3hldfjbj149dfa8O+Pn3guONiGq0nevTQZbYGDND+HsYf/vgDbrlFS6dvvdXraIyJHkucEknVqrpynCVOxvxDZqZ2ei7plc3GjeGQQ6xBRFIbNAgefRSuvx4ef9z/NURNm8IHH8D06dok6Oqr9Yz0888LrCndvfOjptcAACAASURBVFu7mTVooN30klGpUlpluWqVTlEz/nDXXbo8xKhR2rDHmGRhiVOiCTWIsAkYxvxNcRe+zU1Ey/VsxClJDR+uo02XXZZ4Ey/S0/UKwTvvaF3qmWdC58759uQePBh++gleeEHX301Wp5yiaxYPHGhTgP1gxgwdKL311uJ3ODXGryxxSjSBAKxfr61qjDGAzt9Ytqzk85tC0tJg5Ur49dfoHM/4xEsv6bymCy/UuU2lEvBfoIi2S1+0CJ5+Wif4tG6tieCqVf/b7aefdGDtkkvgjDM8jDdOHntMy8MGD/Y6ktS2Z48O5Navr3ObjEk2CfhfI8XZQrjG/ENJF77NzeY5JaG33tK6tTPP1I8TvX6oXDno3Vvbld99ty6m27gx9OtH9uatXH89VKoETz7pdaDx0aKF5o7Dh9sFDy8NGaI5/fPPwwEHeB2NMdFniVOiad5cWwlZ4mTM/2RkaLewNm2ic7zmzeHAA22eU9KYMEHXajrpJE0wypXzOqLoqVpVh5aWLYNLL4UnnuCV+vfz1VcwdNBeatXyOsD4efhhrWK/7z6vI0lNS5fCI4/ogOjZZ3sdjTGxIS7F58pUrlzZZWVleR1G0bRvr//4p0/3OhJjfOHUU3Vuw5w50TvmWWdp5dPChdE7pvHAF1/AeedpM4Uvv9RW30lsw7RFHH1aPY7dO5ep9a+i1KBHtF4vEcsSi+GOO2DYMPj+ezj2WK+jSR3O6VyzuXN1/eY6dbyOyHhFRHY455J2VmVq/CVNNoEAzJ6tK34ak+L274dvv41emV5IWpqWnJRg3VHjta++ggsu0EVkP/ss6ZMmgNtHNSVLqjDi5XKUqlFN69fatIFJk7wOLS7+8x8dhLvrLq8jSS2jR8PUqTrXzJImk8wscUpEgYB2VFq0yOtIjPHcokXw11/RawwRkpam9zNmRPe4Jk6++w7OOQcOOwwmToSDD/Y6opj7/HNdCPbuu+Hoq9vrYulvvAFbtmj3vTPO0KGYJFatmiZPn36qJ/Im9jZuhL594YQToGdPr6MxJrYscUpE7drpvc1zMiZqC9/m1ratTie0BhEJaMECTRJq1NCRlkMO8TqimNuxA3r1gqOO0sQJ0PK8f/8bliyBJ57Q/xktW8JVVyV1B4XevaFePejfH7KzvY4m+fXpA9u3w8iRKVMRalKYvcUTUcOGevXUEidjyMzU8+OGDaN73PLl9RqFNYhIMEuW6OhKpUoweTIceqjXEcXFgw/CihV68lq+fK4Hy5fXs9vly3US0Ntvawe+u+7SHt5JpkIFbRQxeza8+67X0SS3L7+E11/Xt1LTpl5HY0zsWXOIRGwOAXo1dd06XcPDmBTWpAkceSR89FH0j33vvfDoo9p4IgWmxyS+FSu0xnLvXs14jzrK64jiYv58ncZ01VW6VFWhVq3SN/frr+tFuHvvhRtvzCPjSlz79+vgWlaWNitIpkaKfrFjhzbgKFNGK0ArVPA6IuMH1hzC+FMgAD/8oP8VjElRW7boQp/Rnt8UkpamJ2ChckDjY7//ru0Vd+zQy+ApkjTt36/LU1WvrmvoROSww2DMGG1D2aoV3H67XoEYOzZpattKl9ZGBb/8AiNGeB1NcnrooZzvryVNJlVY4pSoAgH9Bzd3rteRGOOZULVqrBKn9u31BMzmOfncxo2aNG3YoB0Smjf3OqK4ef557QHx1FPaGKFIQi3av/hCh1R79NDJgtOmxSLUuDvjDDj5ZD3B//NPr6NJLgsWwOOPw9VXQ8eOXkdjTPx4kjiJSC8RWSEiu0RkjoikFbDvqyLi8rhlhe3TMZ99jo7PV+SBQEDvbZ6TSWEZGToZuW3b2By/ShU9t7R5Tj62dSucdpqWn33ySc7fxhTw66/aQe700zXnKbbTTtOLcKNHawl4p05w7rkJv4iZiI7CbdpUhNE4U6j9+7V7XrVqMHSo19EYE19xT5xEpDswHBgEtAS+AT4Tkfr5POVWoE6u2y/AO3ns2yzXfsuiGryfHHIINGhgiZNJaZmZWmN/wAGxe420NF0navfu2L2GKabt23Wl4kWL4P33IT3d64jiqndvPYl94QVNEkqkdGm44gptrvHYYzrM2ry5niGvWROVeL3QurUmlcOGJfSX4SsvvKCnHk8+qSWixqQSL0ac+gCvOudGOecWO+d6A2uBG/Pa2Tm3zTm3LnQDGgJHAKPy2H1D+L7Ouf0x+yr8IBCwxMmkrOzs2Cx8m1t6uiZNs2fH9nVMEe3cCeedp3VqY8fqsEsKef99+OAD7aZ3+OFRPHDFitrHe/lyuPVWHYU68khtIJGg9W4DB+p68Q884HUkie+337Td/Wmnaad7Y1JNXBMnESkHtAYm5npoItAhwsP0BBY6577J47HZIrJWRCaLSKcShJoYAgFYuVLr+o1JMT/9BNu2xW5+U8iJJ+q9lev5yO7dcOGFMH26Njno0sXriOJq2za4+WZo0QJuuy1GL1K9ug7T/PSTfn8feUQTqOee066FCeSII7Rp4H//qx32TPFFdZTTmAQU7xGnGkBpYH2u7euB2oU9WUSqAt3452hTaMTqIuBCYAkwOb+5UyJynYjMFpHZ+/btK9pX4Ce2EK5JYbFa+Da3GjV0fRJrEOET+/bppe7PP9dFi1Lwsvc998DatTBqFJQtG+MXO+IIePNNHdlr1kwztmbNYPx4SKDlTAYMgMqVwxYHNkX2/vswYYKO3B1xhNfRGOONROuqdxka82vhG51zS5xzLzrn5jjnMpxzvYDPgX55HcQ5N9I518Y516ZMmTKxjzpWWrbUunRLnEwKyszUJWgaN479a6WlwcyZeqXVeCg7W9t4vfeeTrC49lqvI4q7zEztpNe7d+yaouSpTRuYMkUbcJQrB127QocOMGNGHIMovpo14c47tbxx5kyvo0k8f/6pOXPz5tq93phUFe/EaROwH6iVa3stYF0Ez+8JjHfObYlg32+BRkULL8FUrgzHHGOJk0lJGRk62hSPcpH0dD1xWLAg9q9l8uEc9Oqli7Y+8kgMa9T8a+9e7dVw6KH6LYg7EW3G8f33utLu6tV6VaFLFy3p87nbb4e6daFfv4QaLPOF//wnjqOcxvhYXBMn59weYA7QOddDndHuevkSkQDQgrybQuTlOLSEL7mFGkTYfwGTQrZt00ZqsZ7fFJIWLPq1eU4ecQ7uuENX2rzrLj2LS0FPPAE//qjTjKpU8TCQ0qXh//4Pli3TzgtTpuhFvBtv1HbmPlWpkjbTyMjQsjMTmdAo5803p1S3f2Py5EWp3jDgKhG5VkSaiMhwoC7wIoCIjBGRMXk87zpgmXNuWu4HROQ2EekiIo1EpJmIPAp0AZ6N3ZfhE4GArmPy889eR2JM3ISuFcQrcapXDw47zOY5eebBB7VRwc03w6BBKTkrffly/TZceKE2E/SFSpU0iV2+XEcDX3pJG0g8+CD89ZfX0eXpqqugSROd65RgPS48ET7KOXCg19EY4724J07OubHAbcAAYD5wInCWc25VcJf6wdv/iEgVoAfwUj6HLQcMBRYAXwePebZz7r2ofwF+Yw0iTArKyNBz53he/UxP18TJBnfjbOhQPRG/+moYPjwlkybndDCnbFl4+mmvo8lDzZoa2OLFWsr3wAOaQI0Yoc08fKRMGRg8GJYu1S57pmChUc5nn/V4lNMYnxCX4mcBlStXdllZWV6HUXz790PVqlo2MXy419EYExdnnQW//go//BC/1xw1Cq67TqdyHHVU/F43pT3/PNx0E3TvDm+8oSViKej11+Hyy7VEr1cvr6OJQGamTiSaMQOuucZ3GYpzeiFk2TIt1ojlAtqJbPlyrcA86yxtomhMJERkh3OustdxxEqiddUzuZUurUuj24iTSRHZ2XpeFus25Lmlp+u9levFyejRmjSdey689lrKJk2bN2tTg+OPhxtu8DqaCB1/vE4IvPpqePtt8NnFSREYMgTWr9cKUPNPzun7zbejnMZ4xBKnZBAIwLx5sGeP15EYE3PLlum0vnjNbwpp3BgOOcQaRMTFuHE6UnHKKfDOOyndxqtfP/jjD12yqlQi/ccWgSuugB07tIW5z7Rvr/PFhg61NeTz8vrrMGmSljUeeqjX0RjjH4n0Z9jkp1072L3beiWblBCvhW9zE9HuejbiFGOffAKXXKJnth98ABUqeB2RZ6ZOhVde0eTp2GO9jqYY0tKgdm0YO9brSPL06KOwcyc89JDXkfjLpk3Qp0+CjXIaEyeWOCWD0Ax5K9czKSAjAw46CI4+Ov6vnZYGK1fq/CoTA1OmwEUXQYsWmkBVTtoy+ULt2gXXXw8NG8K993odTTGVLq0L5X76KWzf7nU0/9C4sc5bHDFCR7KNSthRTmPiwH4lkkG9elCrliVOJiVkZuogqxf/0EPrOdmoUwx884322W7UCL74QpvepLBBg/Rk/sUXoWJFr6Mpge7dNQv86COvI8nT/fdD+fIpuzTYP0yZAq++msCjnMbEmCVOySDUl/nbb72OxJiY2r5dW+PGu0wvpEULbclriVOUzZ2rrbvq1IEvv4Tq1b2OyFOLFunckssvh1NP9TqaEurQQSfJ+LRcr1YtXVt53Dj7F7pzZxKMchoTY5Y4JYtAQPskb9vmdSTGxMysWdpVL96NIUJKl4YTTrAGEVG1cCGcdpqOME2erHNiUlh2tpaPHXigrqGT8EqVgosvhs8/9+3/p759tfFL//6pvU7bwIHanj3hRzmNiSFLnJJFaCHc2bO9jcOYGAo1hojnwre5pafriMCmTd7FkDR+/hk6d9aueZMnQ/36hT8nyb30EsycCY8/ruvKJoXu3bXr6wcfeB1JnqpU0ZK9r77yZQPAuPjxR3jssSQZ5TQmhixxShZt2ui9zXMySSwzE5o0gYMP9i6G0DynGTO8iyEprF6t7cb37NG+x0ce6XVEnlu7Vkc9OnWCK6/0OpooatdOk+J33vE6knz17KnT6+66S9eVTyXZ2VqiV7VqkoxyGhNDljgli4MP1hZBqV6kbZKWc94sfJtb27Y6mdzmOZXAunV6WfuPP2DiRGjWzOuIfOG227SPwogROnU1aYhAt276s9661eto8lS2rLYnX7hQ115OJSNHam+WJ55IolFOY2LEEqdkEmoQkcpF2iZpLV+u5XFezW8KKV9eL6DbPKdi2rxZy/N+/x0++wxatfI6Il/45BMdkBkwQEc+kk63brB3L0yY4HUk+brwQr0wc++9um5vKlizBu68E04+WdcrNsYUzBKnZNKunV7J/f13ryMxJupC85u8TpxA5znNm+fLpWn8bds2OP107bP94Yfacc3w11/Qqxc0baqlekmpTRs4/HDfdtcDHRgbMkSTieHDvY4mPm69FXbv1oYQSTXKaUyMWOKUTGwhXJPEMjJ0EneTJl5HovOc9u/PSeZMBLKy4Jxz4PvvtffzKad4HZFv3H+/TvkaORLKlfM6mhgJletNmuTrzippaXDuudoO3sdhRsVHH+mv4n33JekopzExYIlTMmnRQgu1LXEySSi08G3p0l5HoqNepUrZPKeI7doFF1ygEyneeEMTKAPoElZPPaWT8084wetoYqx7d73i8P77XkdSoMGDdRRw4ECvI4md7dvhppt0euEdd3gdjTGJwxKnZFK+PBx3nDWIMEknKwsWLPC+MURIlSo6NccSpwjs3asjDV9+CS+/rB8bAPbt025uhxyiJ+tJ77jjdGjDx+V6oCWTV18Nzz0HK1Z4HU1s3Hsv/PYbjBqVxKOcxsSAJU7JJhDQtZxSrZ+qSWqht7Qf5jeFpKXpKNju3V5H4mP79+vCMB99pGehSdVju+SeeUZHnJ5+Gg46yOto4iBUrjd1KmzY4HU0BXrwQShTRpt1JJtZs/S9d8MN/vqbakwisMQp2bRrpzUGP/3kdSTGRE1oLlFonWc/SE/XpMnWnM5HdrYOp4wdqzPue/XyOiJfWbVKr/qffTZ07ep1NHHUvbu+N8aP9zqSAh16KNx+O7z5pia3yWLfPrjuOqhVS9uvG+MVEeklIitEZJeIzBGRtEL2/7eIzBeRHSKyTkReF5Ha8Yo3xBKnZGMNIkwSyszUZcqqV/c6khwnnqj31pY8D87pokSvvKIzz/v18zoiX3FO55c4pwNxKdXN7Jhj4Oijfb0Ybkj//vo3p3//5Fnl46mnYP58HXGqWtXraEyqEpHuwHBgENAS+Ab4TETq57P/CcBrwGigGdAFaAq8EZeAw1jilGwaNdK/hjbPySQJ53TEyS/zm0Jq1NC5EDbPKQ/33KNnZn36wAMPeB2N74wbp+s2PfIIHHaY19HEmYiOOk2fDmvXeh1NgapW1VHByZN17d5Et2KFXsc47zxds8oYD/UBXnXOjXLOLXbO9QbWAjfms3974Dfn3JPOuRXOuUzgGSDudShl4v2CflOtWjWmTZvmdRjRNXSojscn29dlUtKaNRXYsOF4qldfyrRpa7wO528aNmzMlCmHMHnyDF90+/OFdev0Mv3o0VC/vp4gm//5668y3HBDWxo12kPz5nOZNi1JhjKK4vjj9f/U119rZwwfa9JEqFMnwE037WfkyNmUStDLzc7BXXcdC1Tl0ktnMX26Tc40MVNGRMKL2Ec650aGPhGRckBr4PFcz5sI5Le430xgkIicC3wMVAd6AJ9GLeoIpXzitGXLFjp27Oh1GNE1aZK2aNq+HSpW9DoaY0rkzTf1/sorG9OiRWNvg8nl99+170G1ah1p2dLraHxg+HAt0bvsMk2cEvUsM4ZuuAH++AO+/LI8rVqd5HU43unXTztiJMCQ7RNPwL//Db//3pHLL/c6muJ56y2t4B8+HLp1s44QJqb2OefaFPB4DaA0sD7X9vXAqXk9wTmXISI90NK8imj+8iUQ945D9l8tGQUC2s1q3jyvIzGmxDIyoHJlXW/Eb9LT9T4Bzv1i76WXNGm64AKd22RJ0z/MnAkjRui3qVUrr6PxWPfuMGOG9sT2ue7doXVr7bC3a5fX0RTdli36nmvbVufWGZNoRKQpWpr3MDpadQZQGxgR71jsP1syattW761BhEkCmZl6LaCMD8fH69XTOSop3yDirbe0VdcZZ+jHfvxheWzPHv0W1a+vra5TXmg9r3HjvI0jAqVKwWOPwerV2swj0fTvD5s3w8iR/lhA3KS8TcB+oFau7bWAdfk8527gO+fcUOfcAufcF0Av4HIR+VfsQv0nS5ySUZ06ekZnDSJMgtu5UztA+a0xRLj0dB1xSpauW0X2wQe6VlN6uraYLl/e64h8acgQWLQInn8eDjjA62h8oHFjXRDX54vhhpxyil4XGDgQtm71OprIffUV/Pe/2qfluOO8jsYYcM7tAeYAnXM91BntrpeXSmiyFS70eVxzGUucklUgYCNOJuHNmaN9Tvy8SGNamq7luXSp15F4YOJEHTlo00Yne1Wq5HVEvrR0qXbQ69ZN120yQd266ZDyqlVeRxKRxx7T+WmJsv7R7t06ynn44XD//V5HY8zfDAOuEpFrRaSJiAwH6gIvAojIGBEZE7b/R8D5InKjiBwRbE/+NDDXObc6noFb4pSs2rWDX36BTZu8jsSYYvPjwre5pew8p6+/hi5doEkT+OwzqFLF64h8yTltCFGhgk7MN2FC5XrvvuttHBFq3lwHV59+Wsv2/O7RR2HJEnjhBZ0naoxfOOfGArcBA4D5wInAWc650FWU+sFbaP9X0RbmNwM/AuOApcD58YtaiUvZ+hJVuXJll5WV5XUY0Td9OnTsCJ9+Cmee6XU0xhTLRRfB99/Dzz97HUn+nIPateH002HMmML3TwrffQenngp162otkM9bSnvp1Vfh6qvhxRfh+uu9jsaH2rTRtZ1mzfI6koisXq1Vhj166M/WrxYv1tK8rl3hjbgvEWpSmYjscM4lbapuI07JqnVrndFq85xMggotfOvnMj3Qc74TT0yhEacFC3SyR40aujKoJU352rgR+vaFE06Anj29jsanuneH2bO1QiIB1K8PvXvrRZIFC7yOJm/Z2ZqkV64MTz7pdTTGJBdLnJLVAQdA06Y2z8kkrNWrYe1afzeGCElPh5Ur4ddfvY4kxpYsgc6ddS7T5Mlw6KFeR+RrffvqcnojR1p39nyFyvXeecfbOIrg7ruhalW46y6vI8nbyy/rhZzHH7frGsZEm/0pT2bt2mnilOLlmCYxZWbqvd9HnEAbRECSjzqtWKGtxUCTpsMP9zYen/vyS3jtNbjzTr2GZfJx2GH6vyqBEqdq1eA//9GpfVOneh3N361bp2sLn3SSlogaY6LLEqdkFgjo4g0rVngdiTFFlpEBFSvCscd6HUnhWrTQ3ghJmzj9/rvOadqxQzOCo47yOiJf27kTbrwRGjWCe+7xOpoE0L27Lti+bJnXkUSsd28t2+vfX0vj/OL22/XXdMQILSM2xkSXJU7JLBDQeyvXMwkoM1PXci5b1utICle6tM5jScqFcDdu1KRpwwb4/HNtLWYK9PDDsHy5nrxWqOB1NAmga1e9T5A1nUB/rg8/rNOz/DJY9tln8PbbmqzbtQ1jYsMSp2TWrJlesrcGESbB7NoFc+cmxvymkPR0XeA0qVYA2LoVTjtN19n55JOcizEmXz/8AEOHwlVXQadOXkeTIOrV0ysPfslAInTppXod4Z57YM8eb2PJytJRziZNtDzUGBMbljgls7JloVUrG3FKBc7pWVq3bvDnn15HU2Lz5sHevYkxvykkNM9pxgxv44ia7dvhrLNg4UJ4//2cBatMvrKzdcHRgw7SifmmCLp106xz8WKvI4lY6dK6KO4vv2i7eS/df79e3xg5EsqX9zYWY5KZJU7Jrl07vXS/d6/XkZhY+uEHmDZNF5Js1w5++snriEoktPBtIo04tW2rJyxJMc9p50447zxdW2fsWF2kyhTqxRe1xPTJJ6F6da+jSTBdu+qknAQbdTr9dDj5ZC3b8+qa1dy5+p7r2VOXRjDGxI4lTskuENC6px9/9DoSE0sTJuhJx9tva0OQQAA+/NDrqIotIwMaNNCFZRNF+fKasyZ84rR7t648PH06jB4NF1zgdUQJ4ffftT11585awmWKqG5dHdUcOzahOsGKwJAhWqI7ZEj8X3/fPh3lrFlTR7+MMbFliVOyC81JsHlOyW3CBOjQIWcxycaN4fzz4YEH/NXyKUKZmYlVpheSlqZXf//6y+tIimnVKj15/ewzHT6xDCBit9yiA/svvGDdzIqtWzct1Vu40OtIiqR1a7jkEhg2DNasie9rP/sszJkDw4fDwQfH97WNSUWWOCW7Bg2gRg2b55TMVq7USUGhkYH69XXY48or4cEHNYHats3TEIvit9/0lkhleiHp6bB/f06pYUL55BNo2VJPXN99Vy9jm4h88AG8957OM2nY0OtoEthFF+lKwQnUXS9k4EAd/bn//vi95urVMGCATkUMrSNsjIktS5ySnUjOQrgmOX3wgd6ff37OtooV4ZVX9HLk55/rBJxFi7yJr4gSaeHb3Nq31/O+hGpLvm8f3H03nHOOJt1z5+a0hzaF2r4dbr5Z1xvr29fraBJcrVrQsWPCleuBrgfdqxe8/HJ8/tQ6p6/nHDz3nI1yGhMvljilgkBA/5Jv3+51JCYWJkyAY46BI4/8+3YRuOkmmDJFR5zatdPL4j6XkaFrpLRo4XUkRVelijayTJh5TmvWwCmnwODBOrM8I+Of7yNToAEDdH7TyJGJseaY73Xvrgvhfv+915EU2YABcMABeh0i1saN00Hihx/WwhJjTHxY4pQKAgG9LDV7tteRmGjbvFmHN7p0yX+ftDQtgm/aVEthBgzQejKfyszUOQPlynkdSfGkpenXsHu315EUYsoULc2bPRvGjNEz/4oVvY4qoXz3HTzzjF75T8TSUl+68ELt852A5Xo1augaSh9+GNtlCf74Q+fUtWql98aY+LHEKRW0bav3Vq6XfD76SJs/FNb57F//0i5p11yjxfjnnquLm/rMnj2a4yXySWh6uiZNvr1OkZ2tl6k7d4Zq1fTvwuWXex1Vwtm7V6eB1akDgwZ5HU0SqVFDR0HfeSfhyvUAbrtNGwT26xe78O+6CzZs0GsdZcrE5jWMMXmzxCkVVK+uM5YtcUo+EyZAvXo6clCYChXgpZe07dekSZpQ+6xN/bx5mnQk4vymkNA6Kr6c57Rxo84kv+8+6NFD12lq1szrqBLSU09pNdmzz8KBB3odTZLp3l1XlZ0zx+tIiqxSJe3Jk5mp60ZH24wZMGIE3HqrjswbY+LLEqdUYQ0iks+OHTBxopbpRTozWARuuAGmToWsLB3aeffd2MZZBIncGCKkRg1o0sSH85y++UYT7KlTtdX466/rhAxTZBs3ave088+3Za5ioksXHUpJsMVwQ666Siuj7747umvP79kD11+vPVweeih6xzXGRM4Sp1QRCGiP53gvMmFiZ+JE2Lmz4PlN+TnhBL2a27y59rG9+25fzHvKyNABtLp1vY6kZNLTYeZMX3xLtV5o2DA46SRdpTcjQ8++rA1Xsb31lv7qPfKI15EkqWrV4LTTErZcr0wZ7beydKkO8kfLkCHa5+n55+2ahzFescQpVYQWwrVRp+Tx/vu64mF6evGeX7eujj5cf73+lz/rLNiyJboxFlGiLnybW1oa/PknLFjgcSB//KGT7fv21Xltc+bojHJTIqNH67fxmGO8jiSJdeumCzIn6OLt55yjfwcefDA6C2IvXaqJerducPbZJT+eMaZ4LHFKFccdp5fBLHFKDvv2aWOIc88t2ezg8uW1bGvkSJg2Ddq08exsf+1aPU9K5MYQIaFc1tNyvVCS9PHH8MQTMH48HHSQhwElhx9/1KWurrzS60iS3Pnna2vNBC3XE9ERovXr9devJJzTCusKFWD48OjEZ4wpHkucUkXFirowjiVOyeHrr7UrXnHK9PLSs6d23Qt1Znj77egc9Y2Z5wAAIABJREFUtwiSYX5TSL16cNhhHjWIcE6T4Q4ddILF9OnQp4+V5kXJ6NF6reKSS7yOJMkddBCcfromTtnZXkdTLMcfrytADB2qCVRxjR6txQFDhkDt2tGLzxhTdJY4pZJAQLtoJeg/IRNmwgS9/HjaadE75vHH6yhFy5Z6Vtivn45sxUlGhl5gjqRBYCJIT9f8Nq5TNP76Cy67DG68ETp10jaFHTrEMYDktm+f9tQ4+2yoWdPraFJA9+66unBGhteRFNugQbBrV/GbOWzcqJW2J5wA114b3diMMUVniVMqCQR04sWSJV5HYkrCOU2cTj8dKleO7rFr19aFUXv1gscfhzPOgE2bovsa+cjM1Mqy8uXj8nIxl5ama60sXRqnF1y4UFvMv/22rtP06afa4s9EzZdfwrp1VqYXN+edp38QEnAx3JDGjXW9r5EjYdmyoj+/Tx/Yvl2fX8rO2IzxnP0aphJrEJEc5s2D1aujV6aXW7ly8Nxz8N//6pBJmzb6mjG0d68uGJsMZXohaWl6H5d5Tq+9pr/fW7fq2f2AAXaWFQOjR+uyeDY5P06qVNGmNePG+aRFZfHcf7/mf//5T9Ge9+WXOsJ5113a3twY4z37z5pKjj5a/xFZ4pTYJkzQk+Jzzont61xzjZ7179un5V5vvBGzl/r+e23vnAyNIUKOOkrLuWKaOO3cqfPTrrgiJ8E9+eQYvmDq+uMP/dW75BK9tmDipHt37RwzY4bXkRRbrVpa+TxuXM5czsLs2KENIRo3LnrCZYyJHUucUkmpUlrKY4lTYpswQYcz4lGGFQjovKdAQOfO3H57TOY9JVNjiBAR/THFrEHEsmX6DXvpJV2Ha/JkqFMnRi9m3nlHe6dYmV6cnX22NjdK4HI90HlKtWpB//6RzXt86CH45RcYMUKnsxpj/MESp1QTCOjl/V27vI7EFMfy5fDDD3DBBfF7zVq1YNIkuOUWeOop6NxZZyxHUUaGLiv1r39F9bCeS0+HlSvh11+jfODx46F1ay3Z/PhjnYFekrb0plCjR2u5VOvWXkeSYg44QEfXx4+Pa7OaaDvgAC3Z+/pr/ZUtyIIFOsX0mmugY8e4hGeMiZAlTqkmENAJJfPnex2JKY4JE/T+/PPj+7ply+oCIqNH6/BQ69Y6EhUloYVvk61jdtTnOe3ZA7fdBl276ln8vHk24SYOli2Db77R0aZke48mhG7dtNPK9OleR1Ii116rpXd33ZV/Drh/v1bfVqumbcyNMf5iiVOqsQYRiW3CBF3MuEEDb17/iit0roGI9scdM6bEh9ywQUtSkml+U0iLFjqtMCqJ0+rVOoQ1fDjceqvWAB52WBQObAozZoxWOl92mdeRpKizztIOogm6GG5I2bI6OLxokV6DyssLL+i/56ee0uTJGOMvkSVOIknSINhw6KF6s8Qp8WzYADNnxq6bXqRat9YWeB066CX4W27RUcxiSsb5TSGlS2t+WeJ5Tp98ogtcLVoE776rZ1XWoSAusrM1cercWctJjQcqVdLW5OPHl+hvjR9ceKFeJLrvPm0AEe6333S64mmn2QLLxvhVpCNOaxB5GpHmMY3GxEcgYIlTIvrwQ51VHM/5TfmpWRMmTtRmEc88A6eeCuvXF+tQGRk6PadVqyjH6BPp6ZrvbN5cjCfv26cttc45B+rV0/LIrl2jHqPJ3/TpOthnTSE81q2b/hJNnep1JCUiAkOGwJo1OngcrndvLdV74QUrCTXGryJNnA4GbgLmIfIdIj0ROSCGcZlYCgS0aH/LFq8jMUUxYQIcfjgce6zXkagyZWDYMF1oZNYsHYkqRkKekaGDKRUrxiBGHwjNcypyN+W1azUhffRRnRyRkQGNGkU9PlOw0aPhwAO9H+hNeWecoXWvCd5dD/RvwnnnweDBOeuLv/++/ol/4AE44ghPwzPGFCDSxGkuIMFbG+BFYC0iLyGShAU2SS40z2nWLG/jMJHbvl0723Xp4r9LkZdeqiWEZcroGcHLL0f81H379G2YjGV6IW3b6uKXRSrXmzpVs8lZs/TMfdSo5M0sfeyvv3TtnW7d7NvvuQoVtCnOe+9pk5QE9+ij+v565BH480+4+WadE3n77V5HZowpSGSJk3NtgMbAfcBCNIGqDFwNzEDkR0RuQiSi1QZEpJeIrBCRXSIyR0TSCtj3VRFxedyycu13UvBYu0TkFxG5IaKvLRW1aaMn31aulzi++EIXkfHrZe+WLXXeU1oa/N//Qa9eEZ3c/PCD1vknY2OIkPLloV27CBtEZGfrmdSpp8LBB+vv6BVXxDxGk7f33oOsLPsR+Eb37roS8aRJXkdSYk2barvx55+Hq67SAeaRI7WBhDHGvyLvqufczzj3CM4dCzQHppMzCtUEeBr4CZGmBR1GRLoDw4FBQEvgG+AzEamfz1NuBerkuv0C/K+9jogcDnwaPFZL4FHgGRG5KOKvL5UceCA0aWKJUyJ5/31d8PaEE7yOJH81asDnn0O/flqkf/LJsG5dgU9J5sYQ4dLSYO5cvcKcr02btHvYvfdCjx462tSsWdxiNP80erSWTZ14oteRGEA7dFStmhTlegAPPqgD9e+/r/ObQsUgxhj/Klo7cpEDEekNvA2kA6H1r/cA2UB94LlCjtIHeNU5N8o5t9g51xtYC9yY187OuW3OuXWhG9AQOAIYFbbbDcAa51zv4DFHAaOBO4r09aWSUIOISJYwN97as0e7qp13nrZp87MyZXTm81tv6RpDrVvnZEd5yMjQ9XWTvat2erpO+s7IyGeHb77RUbupUzXpfP11XTHTeGb1av1xXHGF/6pjU1b58tocZ8IEHYFPcHXr6gBzy5Z6b4zxv0jbkbdF5L/AGuApoCk60rQBuB+oB7RAE6h8r5mISDmgNTAx10MTgQ4RxtwTWOic+yZsW/s8jvkF0EZEbOA7L4GAtrdetcrrSExhpk+Hbdv8W6aXlx49NEuoUEGzhlGj8twtWRe+za19e10H6B/znJzTBhsnnaQ1OhkZcMMNyf8NSQCvvaY/HivT85nu3XVS0BdfeB1JVPTpo80yq1TxOhJjTCQiHXH6FrgKqIQmTPOCn9fHuYdxbhPOLQJWAwXNc6oBlAZy9y1eD9QuLAgRqQp04++jTQSfm9cxywRfM/dxrhOR2SIye19+y3cnO1sIN3FMmKCLP556qteRFE3z5lpudvLJcN11cP31f7tKvGmTNndM5vlNIVWqaLv1v81z+uMPuOgi6NtX243PnZu8PdkTjHNappeero0sjY+ccoquDJvgi+GGs+skxiSOopTqOeADoCPOtca5MTiXeyW6/sA1UYvuny5DY36tJAdxzo10zrVxzrUpU6ZMdCJLNM2ba9mDJU7+lp2tidMZZyRmW69q1bTM8O67deZzx466gAnw7be6S7LPbwpJS9MRtt270SSpdWv46CN44gntQnDQQV6HaIIyMzWpt7WbfKhsWV1F9oMPYOdOr6MxxqSYSBOn4UAjnLsA5/JvquvcBJwbXcBxNgH7gVq5ttcCCp5FrnoC451zuRcgWpfPMfcFX9PkVrasXt22xMnfZs/WRCORyvRyK10aBg2Cd9/VNnqtW8PMmWRk6ENt2ngdYHykpWnSNHvABOjQQT+ZPl1rdeySs6+MHq3XKWytYZ/q1k07rXz2mdeRGGNSTKTtyG/HuRUlfTHn3B5gDtA510Od0Y54+RKRADqPKq/JEhn5HHO2++eomAkJBLS4OlXLFRPBhAmaXZx9tteRlFzXrnopv3Jl6NSJjHG/0aKFo1IlrwOLjxOP05Z6Xz+eqSNv8+ZpAmV8Zdcubdp24YXagNT4UKdO2sUzicr1jDGJIdLmEEMR+QWRO3JtvyO4fUgRXnMYcJWIXCsiTURkOFAXXVQXERkjImPyeN51wDLn3LQ8HnsROFREngoe81p0DtbjRYgr9QQCuojOwoVeR2LyM2GCnmQffLDXkUTHMcfArFnsP+U0vltSlfY7p+iZarJbtIiaZwdowiK+anQtfPop1KzpdVQmDx9+qNPPrEzPx8qU0fmBH32kC20ZY0ycRFqqdz5wGPBxru0fAg2Cj0fEOTcWuA0YAMwHTgTOcs6F2rvVD97+R0SqAD2Al/I55grgLLRF+nzgHuAW59z4SONKSe3a6b2V6/nTkiWweLG2300mBx/MwkEf8BdVOH7xKzoD/7ffvI4qdl57Ddq2hc2bST+nKjPXH8l+V7SVIEz8jB4N/8/efYdHWWUPHP9eehdRQAGpIoJKZ1jKRBZFRRQENVFR1HV1bfizsSpVUMGuqGtjXQ0qNpQBKYqiqGjiUkQQkN6lCII0KUnu74+TWUJMmSTzzp1yPs8zz2gy884Bkpn3vPfcc+rWlZ4mKoqlpMiFv+nTXUeilEogoX5618u+X5fr6xuy708pyotaa1+01ja01pa31razOfZNWWu7WWu75Xr8XmttFWttvitb1tqvrLVts4/ZyFr7clFiSkiNG8vmfU2colMgIPe9e7uNwwPpc2UeVacXB0hy2K5dHr26Y9wff0g3wQEDZCPXwoX4r6jLnj2waJHr4FRetm6VLtfXXBP9I9MSXlKSDIGLk2G4SqnYEGriFNwEc1aur5+V6/sqlhgj5XrB9mYqugQCcsJ9SpGuS8SEtDTZotD45vPk5696dWkz/MIL8TGUedUq2b80bhzcfz/MmgUnn0xSknz7mLbkKmq8/bYMKtYyvRhQurTsm5w2TRpFKJXoJk+GXbtcRxEyY8ytxpi1xpiDxpj5xhh/AY99wxhj87hFvFY31MRpefb9fzCmK8acgDFdOVo693P4Q1MR4fPJHif94IkuW7ZII4VY7qZXgGMG37ZoIauePXvCwIFw/fWx3Wb4ww+lY+X69TB1KowZI3sykBy4QYP4W1yLB8HZTT4fnH6662hUSFJSZI/kxx+7jkQpt9avlwsJDz3kOpKQGGNSkI7do4E2SIO4GcaY+vk85f+Ak3Pd1gAR7xATauL0LjL4tgXwFbA9+/4sZL7Tu55Ep7zn88msoAULXEeicpo8We7jbX8T8Ntv8PPPuQbfHnecrLA9+KCcvfr9sGFDfoeITocPw513yodX8+bSNS+Pboh+v6w4xcPCWjxZuFC65etqUwzp0gXq1NFyPaWezO6FdtddbuMI3d3AG9bacdbaZdbagcAW4Ja8Hmyt/d1auzV4A5oAjcm707anQk2cnkOyQZPrBvBt9vdVLPL55F73OUWXQACaNpUT8DiT7+DbUqVgxAhJGleulH1Ps2dHOrzi2bBB9lyMHQt33CGZUYMGeT40KQm2b5c/oooeqalQrhxccYXrSFTISpWCyy+XeU579riORik3tm2Df/9b9tPGQGm/MaYc0A6YmetbM4FQZ3TcCCyx1hY4ysgLxoZ62dOY8shS2cXIcNltSFe957D2kFcBeu2UU06xb775pusw3PrpJ6hUSZpFKPcyM+HHH2Xjc926rqMJu9dfb8hbbzVg6tQ5VKyYmfeDDh2SfUIHD8oHQa1akQ2yKPbsgbVrZeW2YcNCW8dv2FCJa6/1ce+9y+nVa0tkYlQFysgwXH55J1q2/J2RI3U8Q0zZv1+WsBs1kmZHSiWazZuls82ZZ0L58q6j4a9//ethYHGOL71qrX01+D/GmDrAZuDsnM3hjDHDgf7W2mYFHd8YcxyyOvWAtXZsWIMPQeiJU5yqXLmy3Z/ocyCuuEJ2669fX/hjlffefReuvBK+/TYuB6Sed56suCxcWMgD9+yRuqlAAK6+Gl55haialpuRAcOHyx6mli1h4kRZJSyEtZIT9+wpqxzKvSlToE8fub/4YtfRqCIJXrBo1Ur3OqnEs3s31K8PF14o5w5RwBhzwFpbuYDvlzRxug14Cqhjrf0tTGGHrGjDRIypjjEdMCbpTzcVu3w+KTXautV1JApg0iQ5sz5mE1B8yMqSUr0/lenlpVo1abTw0EPS7qxrV1i3zusQQ7NlC/ToIUnT3/8u3S5CSJpAGmL4/dogIpqkpso84gsucB2JKrJSpSA5WfrIx1BHMaXC4oUXYO9eeOAB15EUxQ4gE6ley6k2EMqJ6I3Ahy6SJgg1cTKmLMa8BvwKpANf5rp94VWAKgKCg3DnznUbh5IStenT5fJ3qfgbkrpsmSwkhZwTlioFQ4fKleQ1a6Q9+6xZnsZYqC+/hDZtJAN84w1pOV6xYpEOkZQkOeDGjZ5EqIpg50758erfH8qWdR2NKpbkZDhy5GhTHaUSwf798Oyz0oSoVSvX0YTMWnsYmA/0yPWtHkg/hXwZY3xAKxw0hQgK9czsXuB6oDR/bhCRs1GEikVt2shMDG0Q4d4XX0hr+DhuQw4hrjjl1KuXJPa1a0ut31NPRb4tXVYWPPIInHuu7GP673+L3YLNnz2tQuc5uffuu3LOrd30YliHDlKup931VCIZN06u/Awe7DqS4ngauM4Y83djTHNjzFigDvAygDFmvDFmfB7PuwlYaa2dHblQjxVq4nQF0nY8uCvBAh8BB4FVgFbqx7JKleCss3QQbjQIBKBqVeje3XUknkhLk/3bIVa1HatpU8m8+vaFe++Fq66SK26RsGOHJG9Dh8rsmLlzZSNuMbVqJf/Mmji5N368bFFr3dp1JKrYjJFVp88/lxNJpeLdoUPSgvzss2NyL7S19j3gTmAoklt0BS601gY329fPvv2PMaYqko/8G4dCTZyaZN9f9r+vWHsZcDnQCNAdmbHO55OTwaws15EkrsxMKTW58MKo6IzjhbQ0KdMzxV2jrloVPvgARo+Wq8udO0sJn5fS0mRV9osv4KWXZL9VlSolOmTp0jKCRhMnt37+uUQLhyqapKRIw5ZJk1xHopT33nxTuunF5moTANbaF621Da215a217XI2irDWdrPWdsv1+L3W2irW2scjHmwOoSZOwcrv9ciGLjCmIvA5Ur43MuyRqcjy+aQ7y6pVriNJXN9/L/MY4rRMb/duWLq0GGV6uRkjG2GnT5emJu3bw8zc4yDCwFqpH09Kks0v330HN99cgqzvWH4/LFmiF8hdSk2VJLZ/f9eRqBJr0waaNNFyPRX/MjLg0Udl1mGP3NuElNdCTZyCrWoqAsEuFsOAe7L/+9RwBqUcCDaI0H1O7gQCcoLes6frSDwR/NEKW7PACy6AefOgXj35O3vssfDte/r9d7jsMpnC3qsXzJ8vH1JhlJTdi3TOnLAeVoUoM1Mu2l5wgWydUzHOGFl1+uIL+PVX19Eo5Z2JE2H1alltCtOFPBW6UBOnYC1MXWAB0gziPuAhZL/T2vCHpiKqeXOoXFkTJ1eslRKT7t3huONcR+OJ9HR5j/f5wnjQJk2klO6yy+D+++XEad++kh3zhx+gbVsZ6vPUU/LvUshQ2+Lo0EEqMrUtuRtffCGVLlqmF0dSUqTc/MMPXUeilDeslVL15s3jtjol2oWaOH0GrABOB54Esji2m96o8IemIqp0aSl50gYRbixdKmWSffu6jsQzaWlwxhkynimsKleW1miPPy4nTJ06Fa/k1FoZstupk2y8nT0b7r7bsyt65cvLQq/uc3IjNRWqV9eBt3HlrLOgWTN4/33XkSjljWnTYPFiKVePw5ElsSC0v3VrR2Btc6ydjLWzgCRgLDK5NwnpjqFinc8HCxfKSaOKrEBA7nv3dhuHR4o0+LY4jIFBg+CTT+CXX2Q5Z8aM0J+/bx9cc43sYTr7bFl16tLFo2CP8vthwYKSL5KpotmzBz76CK64AipUcB2NCptgud5XX+lAdxV/rJWRGA0bypuXKhJjMMZwUkmPU3jiZEx5jPkPxryGMdJdz9rvsPYurB2Etd+WNAgVJTp2hMOHYdEi15EknkBANv+cfLLrSDyxYgXs2hXG/U356dFD9j01aCB7k0aPLnzf09KlctFgwgQYNUqaTtSs6XGgIilJ9tqkpUXk5VS2iRPhjz+0TC8uJSfLlZqJE11HolR4zZ4tNe///KdO6y4Ga7HAFyU9TuGJk7WHkL7p1wG/lPQFVRQLbj7RfU6RtXGjnOzHcb1ysQffFkejRtIB74orYMgQ2f+0d2/ej33rLVmd2rkTPvsMhg2TstUI6dRJqi10n1NkpabCaacd7Ymj4sgZZ8hNy/VUvBk9WjrZXH+960hi2SpjqFqSA4RaIBkcfBuZy7DKjXr14KSTdJ9TpE2eLPdxvr+penXZfhARlSrJvKWnnpLVvI4dZdkr6OBB+Mc/pDyvfXspzTvnnAgFd1TVqtJFWfc5Rc7atZKoXnutNqSKW8nJ0q5y82bXkSgVHv/9rwx4vucerS8umd+Bj42hZXEPEGri9E/gEPAyxmjj1ngVbHmmK06RFQhIh5zTTnMdiWfS0iR3ieheVmOkucNnn0l74g4dYOpUaePaqRO8+ircdx/MmgV16kQwsGMlJcm1Ct1aGBnjx8uPxjXXuI5EeSYlRUp0tVxPxYsxY+Tq4803u44k1q0B9gGfGsN2Y5hmDA8W5QChnsaMRwbfng/8gjFbMGZNjtvqIoWtopfPB8uXy7RS5b1du6RuOY7L9PbuhZ9+ilCZXl66d5dSyFNPlRZqrVrB+vXw8ccyRLBMGUeBCb9fFsDmzXMaRkKwVhKn7t3hlFNcR6M806yZ/J7rMFwVD5YskQusd9whZQqq2KxlhLVcZC0nA22AVzjaITwkoSZODYFK2f9tgNrZX8t5U/EgWPSvZ3GRMW2adAeI48Tpv/+VE1bPG0MUpEEDKd35+9/lZ3zBArjoIocBHdW1q9xruZ735syBNWu0KURCSE6Wpe4NG1xHolTJPPqojN244w7XkcQ8Y7jNGILDMvcAn1nLiKIcI9TEaUOO2/o8bvrOFC/at5d7LdeLjEmTpEws+Pceh4KNIZxvxK9YEcaNk9K8hg0dB3NUzZpSqakNIryXmgpVqkC/fq4jUZ5LTpb7Dz5wG4dSJbFmDbzzjpTonXCC62jiwUBr+d0YKgPLgfXG0KMoBwh1jlNDrG1U4E3Fh+AOfm0Q4b0//pC5Q5dcEteD7NLSJDGoXt11JNErKQm+/VYWH5U3DhyQRmuXXSYXb1WcO/VUaNtWy/VUbHv8cen0evfdriOJF8GpiRcBHwGtwZs9TiqR+HySOBU2/0aVzOefy9lcHJfpWSsrTs72N8UIv1+GsuoINe8EArLfTsv0EkhKCsydK60UlYo1v/wCr78u7ccdNjCKMweNoQ6QAky0ll+AikU5QGiJkzEDCr2p+NGxI2zbBps2uY4kvgUCcNxxcPbZriPxzKpVMiLJ6f6mGOD3y73uc/JOaqpsdUtKch2JipjLL5d7nemkYtHTT0NGhgy8VUVmDHl1fhoGzAfqAV8bgwGKVIMQajupN4CClh8s0nlPxYOcg3C19ZQ3MjJgyhTo1QvKlXMdjWfS0uReV5wKVr++nNR/843u//XC5s2ywDt4cFxXxarcGjWSz7P335fRA0rFip074eWX4coroXFj19HEqkXGcJu1fBn8QvZ/nxz8f2NoBke/H4qifISYQm4qXrRsKSfzus/JO999Bzt2xPXQW5AyvWrVoEUL15FEP79fGkRohWz4vfUWZGXBAK2NSDwpKdJFc9Uq15EoFbrnn4f9++H++11HEssWA58bwzvGHE2WcrKW5dZSpOFYoSZO1+e63QiMAbYD+4Hbi/KiKsqVLw+tW2tnPS8FAvL3fP75riPxVFqaXPDVq/yFS0qC7dth5UrXkcQXa6VMr3NnaNrUdTQq4rRcT8WavXvhueegTx8480zX0cQsa0kBzgNaAT8bw93GULqkxw21q15qrttrWDsE8CPznbSrXrzx+WSWk7b5Cj9rJXE699y4Hma3f780O9AyvdAE9zlpW/LwmjcPli3TphAJ65RTJGvW7noqVrzyCuzaJbXFqkSsZRbQEhgNjAQWGkOJdrqW9DrwKuAA0L+Ex1HRpmNHOfNdtsx1JPFn0SLp8hTH3fRAmlllZWljiFA1ayYznbRBRHilpsribnCsj0pAycnyvvvzz64jUapgBw/CU0/JhdXgfnNVItaSYS2PAacDPwNfGsNbxlC7OMcrSVe9m4AA0o2iSnFeXEWx4C+s7nMKv0AAjIHevV1H4qmoGXwbI4w5us9JhcehQzI78pJLdI5YQrvsMvkF03I9Fe3eeAO2btXVpjAzhuOBBsAnSFe9q4A1xjDHGMYaQ8g7YI0NZSeyMVkU3FXvC6wt0uTdaFG5cmW7f/9+12FEn6wsmVKdnCzLxip82rSBKlXifmmhTx+5wLt8uetIYsfYsXDnnbBxI9Sr5zqa2PfRR3DppTB9OvTs6Toa5VRSknQqW7LEdSRK5e3IETjtNDjpJGkgZWKz75ox5oC11vmYcWPoCdwDnAnURBrZZQKrgWVI1dwpQDugkbWhdRoPR1e91cBtRTiOigWlSkGHDtogItzWrYOFC+O+TE8H3xaPznMKr9RUOQfpEZOX9VRYpaTA0qWaOKno9e67co4weHDMJk1R5nWgLvACcDmSQFWyltOtpa+1DLKWK6ylKVAj1IMWt6ve9cgyVxegOdauCP3PoWJGx46weDEcOOA6kvgRCMh9nCdOa9dKhzhNnIqmVSvpF6LleiX366+y0nT11VAm1ImFKn5deqlcENQmESoaZWXBmDFw1lky31GFwzfAAGt52Fo+spal1nIkrwday55QDxrax4m1qaEeUMURn0+66v3wA3Tp4jqa+BAIyBtjkyauI/FUcPCtNoYomtKl5VdNV5xKbsIEmTOt3fQUIEuPZ58t+5xGjtQr+iq6TJ4szbgmTND5HWFiLZd7cdxQm0M0wpgkjGme6+vNs7+u7cjjUYcOcq8NIsJjxw45I47z1SaQMr3KlXUERXH4/VJNtHOn60hiW2oqtG2rP4Mqh5QU2XS5aJHrSJQ6yloYPVouqF7uybm+CqNQ09p/AV8CuXsjts/++gvhDEpFiZNOgvr1dZ9TuEyhHxC3AAAgAElEQVSdKsvxCZA4BQffli7xqLnEk5Q9YWLOHLdxxLLFi2WhXFeb1DH69ZM3JS3XU9Hk889l4Nx992ldcQwINXFqm30/I9fXP0EaRLRFxSefTxOncJk0SRLRNm1cR+KpAwfgxx91f1Nxdeggc4d0n1PxpabK+ceVV7qOREWVmjWhe3cp1wulo7BSkTB6NNStCwNC7oitHAo1cTo++/5grq8fzr4PuRuFijEdO8pO/19/dR1JbNu/H2bOlNWmOK+tnz9f9pbo/qbiKV9efu10n1PxZGTAW2/J/uqaNV1Ho6JOcjKsXg0LFriORClpOz57Ntx7r7z5q6gXauK0K/s+d/Hlpbm+r+JNcBCurjqVzMyZMhE8Acr0goNvNXEqPr9fzuv27XMdSeyZORO2bdMyPZWPfv1kOVKH4apoMGaMzMy88UbXkagQhZo4pSMleS9izGsYcw/G/Bt4CRmMm+5VgMqxtm2lw4smTiUTCMDxxx8d1BPH0tJkj6te7S8+v18aWga7E6rQpabKeYh29FV5qlFDBntpuZ5y7ccfZe/znXdKNyUVE0JNnMYiCVIZ4DrgcWSWU9nsrz/jRXAqClSpAmecoYlTSRw5Ah9/DBdfHPcbP62Vk33d31QynTvL9Qot1yuaXbukq++VV0K5cq6jUVErOVkGjc6d6zoSlcjGjJHBfbfd5joSVQShJU7WfgncCRxBVp6Ct8PAXVj7lVcBqijQsaMkTnp1rni++UbO6Pr2dR2J5zZsgK1bNXEqqapVpYeINogomvffh0OHtExPFeKSS6BsWe2up9xZsULesG69VapRVMwIfcqWtc8DTYAbgaHZ902yv67imc8Hv/0Ga9a4jiQ2BQJQsSKcd57rSDyng2/DJylJRqgdOuQ6ktiRmgotWkC7dq4jUVGtenU4/3w5cc3Kch2NSkSPPy7NIO66y3UkqoiKNp7Y2s1Y+xrWjs6+3+xRXCqaBBtE6CDcorNWEqfzzoNKlVxH47n0dMkRW7Z0HUns8/uln8i8ea4jiQ0rVkjifu21cd+4UoVDSgps2nS0m41SkbJxI4wfDzfcALVru45GFVFoiZMxgzDmC4y5JdfXb8n++j1eBKeixBlnyNmw7nMqugUL5E0yAbrpgZy4dugQ91u5IqJrV7nXfU6hGT9e9oVdfbXrSFRM6N1brvhruZ6KtKeekouqgwa5jkQVQ6grTtcCZ/Pn7nnfAt2QRhEqXpUpI7UvDhOn1atjdItVICBncxdf7DoSzx08CD/8oPubwqVmTWjeXPc5hSIrC958U5ql1anjOhoVE6pVg5494YMPtFxPRc6vv8Krr8oVngYNXEejiiHUxKlh9v2yXF9fkX2v//rxrmNHWT05ciTiLz1rFpx6KvznPxF/6ZILBGSzygknuI7Ec8EfD93fFD5+P3z7rbQmV/mbPVsak2hTCFUkKSmwZQvMmeM6EpUoxo6Vq4z33ec6ElVMoSZOwYrxRrm+3riIx1GxyueTXeqLFkX0Za2FIUPkvx9+2EneVnyrVsFPPyVUmR7oilM4JSXBnj2weLHrSKJbaqosICTIr5oKl4sukjJ0HYarIuH33+GFF+DSS+H0011Ho4op1IRndfb9SxhTDyD7/l+5vq/iVbBBRITL9aZPl54Ul14qYzfGj4/oy5dMICD3ffq4jSNC0tOhUSPd6xpOwXnJWq6Xv3374MMPZTRPxYquo1ExpUoVmZQ8caIu6yrvvfiiJE8PPOA6ElUCoSZOAWTVyQ+sx5i9wHogCRmA+5E34amo0aCBbLqIYOJkLQwbBo0bw4QJ0L49PPJIDK06BQIyjKdhQ9eRRERampbphVv9+vKrpw0i8vfhh7B/v5bpqWJKToZt2+ArHUepPHTgADzzDFxwAbRt6zoaVQKhJk6PI/uZgoNvK+f47xXAk55Ep6KHMUcH4UbIpEnSbGD4cChXTu7XroW33opYCMW3bRt8913C1A5t2gSbN2uZnhf8fllxisnmKBGQmgpNmkCXLq4jUTGpVy8ZFaHlespLr70mjSEGD3YdiSqh0BIna/cBXYCXgc1ARvb9i9lfP9Gj+FQ08flg2TLZdOGxrCwYMQJOOw3695evXXSRXKh55BHIyPA8hJL5+GM5002QxEkH33onKQm2b4eVK11HEn3Wr4cvv4QBA3R2kyqmSpWk6+mHH8bAB4uKSYcPwxNPyIyJYP21ilmhN3WwdifW3oq1p2BtOaAl8BMwGVjlUXwqmvh8kgxEYCLn++9LX4WRI4/OBDJGVp1Wr5bSvagWCMiGn7POch1JRKSnQ4UK0KqV60jij+5zyl9w9XnAALdxqBiXkgI7dkgWrlS4vf22zHPU1aa4ULRueMaUxZh+GPMR8AvSHKILR7vuqXjWoYPce1yul5Ehq01nninl5zn17i0n5w8/HMUXB/fuhc8+g759E+YyeFqajPoqV851JPGnWTPZXqj7nI5lrZTpnX12wmwjVF7p2VMaRegwXBVumZnw6KOy3/mCC1xHE1WMMbcaY9YaYw4aY+YbYwpcjjPGlDPGjMp+ziFjzAZjzB2RijcotMTJGD/GvAJsBT4A+gDlOZowbfYkOhVdatSQgUoeJ05vvw0rVshqU6lcP6HBVaeVK+Hddz0No/g++USW5hOkTO/QIZnhpPubvGGMrDpp4nSs9HR5H9CmEKrEKlSQ7qcffRRD3YdUTPjoIzmhGTw4YS6khsIYkwKMBUYDbYDvgBnGmPoFPO1d4ALgJqAZcDkQ2Rk5gLH57Tg2phlwDXAVRwfc5v5Xt8D5wCzyPVB0q1y5st2/f7/rMGLH1VfLtMlNmzw5/JEjcoW9enWYPz/v95msLGjdWnKTJUugdGlPQim+/v1h5kzYujUKgwu/77+XvU0ffgj9+rmOJj49+yzcdZdUe9Sr5zqa6HDzzfDmm/JrVrWq62hUzPv4YylpmD5dVqCUKilrZWP2H39E6cmKN4wxB6y1lQt5zPfAImvtjTm+thKYaK39U792Y8x5yMJNE2vtjnDHXBQFrTgtAx4AGnK0g95u4E3g3v89ytrPYzVpUsXg80n7tM3eLDK+/rp0znvoofwvzpQqJatOy5dHYSOkw4dh2jT5AE6QN0ltDOG9pCS511UncfCgVFX166dJkwqT886D447Tcj0VPjNmwMKFcP/9CXM+EApjTDmgHTAz17dmAp3zedolwFzgbmPMJmPMSmPMc8aYKh6GmqcyITzGIstjqcAXWJuBMWcQJy3Ia9SowezZs12HETuaNIEnn4S5c8Pe5uvwYcOwYR1p3vwwlSotoKB/lho1oGHDDjzwANSqNTd63pP27JHhU6eeSoF/gDgyZUoLateuxooV6axY4Tqa+JSZCZUqdeXdd7dx8snaXu/LL2uye/cZtG79I7Nn73IdjooXzz4Lu3dLkwgtq1IltXEjPPecDONLkPOBbGWMMTm7iL1qrX01x/+fCJQGtuV63jbg3HyO2RjoChwCLgWqA88DdYDLwhF0qAoq1ctCkiaAP4BPkGWyaUjp3mLAYm2RT1mNMbcCg4CTgSXAndbafK+lZmenQ5HSwTrIX+6T1trnsr9/HfB6Hk+taK09WFAsWqpXRAcPQrVqcM89MGZMWA/9wgswcKD0VTg3v1+dHN57D664QvY6paSENZTiu/VWGD9e5jVUrOg6moho0EBWm/RCrbd69pTP4Z9+ch2Je716waJFsG6dXshVYTR9uvxwTZkiLcqVKq6vv5bONc8/D7ff7jqaiCqsVM8YUwfpjXC2tfbrHF8fDvS31jbL4zkzAT9wkrX29+yvnQd8mv213EmYZwoq1UvnaIleJaAvMAHYDrxS3Bf0cEPYASQR+9+tsKRJFUOw53SYG0QcOCDzmZKS4JxzQnvOZZdB8+ZS1peVFdZwiicrCyZPls45CZI0/fILbNigjSEiwe+XMvmdO11H4tbWrfDpp3DNNZo0qTA791w4/vgorAFXMWf0aKhVC264wXUk0WgHkAnUzvX12kgTurxsATYHk6Zsy7LvC8ofwi7/xMnazsCpwEhgJUeTqArA0dMkYyZgTFF2Ut4NvGGtHWetXWatHYj8hdyS14OzM8pzgAuttZ9Za9dZa7+31s7+U8TWbs15K0JMqih8PinVy8wM2yFfeklOiAra25Rb6dJSFbdkiTSucW7ePMkkEqSbHkhnM9D9TZEQ3Oc0Z47bOFx7+21569HZTSrsypWTjXOTJ0t1hVLFMX++XN25666EuYhaFNbaw8B8oEeub/VAFlPy8i1QJ9eeptOy79eHN8KCFdyO3No1WDsSWTbrhMxt2sGx3fVSgI9DeTGPN4RVNMasz37MVGNMm1BiUsXg88msouXLw3K4fftkzEGPHkdPDkOVnCxd+EaNioJVp0mTZFpvr16OA4mc9HQ512ijv22e69ABypdP7AYRwdlNPh+cfrrraFRcSk6Wz7cZM1xHomLVmDHSaOSWPNcDlHgauM4Y83djTHNjzFhkK87LAMaY8caY8TkePwHYCbxujDnDGNMFqV6baK3dHsnAQx+Aa+33yOpQHaA38D5wkKMrUaEoaEPYSfk8J7ghrBWyIex2pGzvjRyPWQ78DZkvdWV2XN8aY5rmdUBjzE3GmHnGmHkZUTtFNYr5fHIfpnK9556Toe0PPVT055YuDUOHwuLFEAiEJZziCwSgWzcp9UgQaWnSbbV8edeRxL/y5eVX7+uvC39svFq4UH7XdXaT8kz37nDCCVqup4pn2TIpgRk4UJInlSdr7XvAnUj/goXIef6F1trg6lF9cpTgWWv3IY0jjkMWU94HvkLO/SMq/+YQIT3bVEX2G/XH2kJ3pkRqQ5gxpjTyD/GltbbAqcLaHKIYsrIkOejfH158sUSH2r0bGjWCLl1g6tTiHSMjA1q0gEqV4IcfHDVD+vln2XD1wgtw220OAoi8w4ePXlR7+mnX0SSGoUNldXb3bqgS8Sas7t15p5T1btkinTWV8sQ//iE1odu3yweLUqG67jr44ANYvx5OPNF1NE6EMscploW+4pQXa/di7X9CSZqyRWRDmLU2E5gH5LnipEqoVCmpGwrDitMzz8hJ4KhRxT9GmTJyQvnjj9IMyYnJk+W+Tx9HAUTeokWyDUAbQ0SO3y/7e4KzsxLJkSMwYYI0O9OkSXkqJQX275cue0qFat06eOstuOmmhE2aEkHJEqciitSGMGOMAVoiSZfygs8nmcoffxT7EDt3SuLUr5+Ue5XEVVfJiKmRI2UfRMRNmiTJZL16Dl7cDR18G3mdO8t1i0Tc5zRjhnT51zI95bmkJOmIpjMWVFE88YS8Qd9zj+tIlIcimjhlC/uGMGPMCGPM+caYxsaY1sBrSOL0cgT/XInF55MauYULi32IJ5+UxhAjR5Y8nDJlYMgQKdUrbslfsf3yC3z/fUJ10wNpDFG3LpxyiutIEkfVqtKIIxH3OaWmQs2a0u1fKU+VKSPzLqZNkw8ppQqzdSu89ppc2UmgC6iJKOKJk0cbwqoDryIlfDOBukCStTa8w4bUUSVsELFtmzSFuOIKOPPM8IR09dWyX2rUqAivOgXrAxMscUpL09UmF5KSJE8/dMh1JJGzcyd8/LFsqyxb1nU0KiGkpEhFRcSvxKmY9MwzUk/8z3+6jkR5zMWKE9baF621Da215a217XI2irDWdrPWdsv1+OXW2vOstZWstXWttbdZa/fm+P5d1toG2cerZa0931qbgLsAIqhOHbmqUszE6bHHZH/MiBHhC6lsWVl1mjcvwp1kAwFo2lSaQySIbdtg7Vrd3+SC3y+/O/PmuY4kct59V85JtExPRUyXLnDyyVqupwq3a5c0ykpOlnMBFdecJE4qTvh8cum7iDZvls5Y11wjM5jCacAAaNAggnudfv8dvvgC+vZ11M7PDR18607XrnKfSPucUlOhZUto3dp1JCphlC4Nl18uV+H27HEdjYpmL7wgJZ0PPOA6EhUBmjip4vP5YPVqqaMpgtGjZXvU8OHhD6lsWRg8WBbCPv00/Mf/k+nT5VJ4gpXppafL33W7dq4jSTw1a8riZqIkTsuWwdy5utqkHEhOlppYZ+1aVdTbtw+efVbafbZs6ToaFQGaOKniC+5zmjs35KesXw/jxsHf/gaNG3sT1nXXQf36EVp1CgSgdm3o2NHjF4ouaWnSpKBCBdeRJCa/H+bMkdbk8S41VS7+9+/vOhKVcDp1kpJ0HYar8jNuHPz2m1yxVQlBEydVfO3aSXlaEfY5PfywPGXoUO/CKldOVszT0+Hzz717HQ4dkhWnPn2kBWmCyMiQXFnL9NxJSpLqocWLXUfircxMePNN6aRXO/f0P6W8VqqUlOt98okMHFQqp0OHpD3wX/+qH4gJJHHO9lT4VasGLVqEnDitWgWvvy5D2b1uYX399XKh0NNVp1mzZJk+wcr0Fi+GAwe0MYRLfr/cx3tb8lmzpNu/lukpZ1JSpBw7OORcqaDx4+UNSlebEoomTqpkgg0iQshORo2SfTGR2D9Zvjzcfz98+630bvBEICCDdbp39+gFopMOvnWvfn1pghLv+5xSU6F6ddk+oJQTPp/8sml3PZVTRgY8+qgMvj/nHNfRqAjSxEmVjM8HO3bAunUFPmzZMnj7bbj9dunwGgk33CBd00eN8uDgmZlyBfLCCyVLSyDp6XDSSXIuodzx+2XFKaIzyyJozx6YNElmveleOuWMMdIk4rPPitwIScWx99+HNWtktSmBOuoqTZxUSYU4CPfBB6FixcjOhqtQQVadvv4aZs8O88G//x62b0+4Mj04OvhWPyvc8vvlR3DlSteReOODD2T+qJbpKedSUmSFIRBwHYmKBllZMGaMbFXo3dt1NCrCNHFSJXPWWZKhFJA4LVokF2f+7/+klXIk3XijrHCNHBnmA0+aJHWHPXuG+cDRbccO2aum+5vcS0qS+3gt10tNhdNOS7iGlSoatW0rbWC1XE8BTJ0KP/0k+w4SqDGUEvovrkqmbFn5UClgEO6IEXDccXDvvRGMK1uFCnDffbLiFLaN9NZK4nTOOfIHSyA6+DZ6NGsmFyLisUHEmjWSEF57ra5sqihgjKw6ffEF/Pqr62iUS9bCI49Ao0ZSR6wSjiZOquR8PliwQDoP5TJvnlQ33H03HH+8g9iAm26SVsZh2+u0dKkM/k3QMr0yZaB9e9eRKGOkXC8eV5zGj5c/3zXXuI5EqWzJybK39aOPXEeiXPryS6mwue8++TBUCUcTJ1VyPp9sRliy5E/fGj4catSAO+90EFe24N6qWbOky16JBevcE7C2OT0dWrWCSpVcR6JAEqe1a2HTJteRhE9WliRO3bt7P7ZAqZC1aiW1ozoMN7GNHi31/7r5MmFp4qRKLp8GEd99BzNmSNJSrZqDuHK4+WaoVStMe50mTZJatUi1B4wSmZnyT6xletEjHvc5zZkjyaCel6ioEizXmz0btm1zHY1y4fvv5QrsPfdoq88EpomTKrnGjeGEE/6UOA0bJsnK7bc7iiuHSpVg0CDpKBucQ1QsGzfC/PnQt2/YYosVS5bIvF9tDBE9WrWSUWLxtM8pNRWqVIF+/VxHolQuycmyJDpxoutIlAujR8ueg3/8w3UkyiFNnFTJGXN0EG622bNlH+3990Plyu5Cy+mWW+DEE0u46hScHp+g+5tAV5yiSenS0KVL/Kw4HTggbcgvuyx63jeU+p8zz5QW1Fqul3gWL4YpU6Q9cJUqrqNRDmnipMLD55Mlib17sVZWm+rUkRK5aFG5snT2+/TTApsAFiwQgObNpdY9waSnSxe3xo1dR6Jy8vvlVy8eZnNOmgR792qZnopiyclypeKXX1xHoiLp0UflJGLgQNeRKMc0cVLh4fNJm84FC5g5U/YpDBkijRmiyW23SVVhsTrs/fabLKUl4GoT6ODbaOX3y/2cOW7jCIfUVGjQ4OjeLaWiTnKyfNZpuV7iWL0a3n1XylZq1HAdjXJMEycVHtkNIuz3/2XYMKhfH264wXFMeahSRfZ1Tp8Oc+cW8cnTpkmHhATc3/Tbb7B8ue5vikYdOkD58rFfrrdpE3z+OQwYoDMlVRRr3lwGv+sw3MTx+OMys/Luu11HoqKAfjyp8DjxRGjcmKmBDObOlTbk5cu7Dipvt90m+zuLvOoUCEDdutCunSdxRbNgaaMmTtGnQgW5bhHrDSLeeksu5A8Y4DoSpQqRkiJtYzdudB2J8trmzfDGG/C3vyVcJ12VN02cVNhkdejIsHm9adIkuk9+qlWTC0dTp8rc3pD88Qd88gn06ZOQl8PT0uSPrYNvo1NSkvws79vnOpLisVbK9Lp0gVNPdR2NUoVITpb7Dz5wG4fy3tNPS6XJoEGuI1FRIvHOAJVnPqpwFT8eOYMR/7ebsmVdR1OwgQOhevUirDp99pm0/ErQ/U3p6dCypTYTilZ+v3y2l6jVvkNz58LPP2tTCBUjmjaFNm20XC/e7dwJL78MV10FjRq5jkZFCU2cVFhkZsKIr7pzOsu4ql701wwddxzcdZd0F1+4MIQnBALypG7dvA4t6mRlSametiGPXp07y4pgrO5zSk2VksPghXylol5KiswuXLfOdSTKK889JxdM77/fdSQqimjipMLi3Xdh6bpKjDQjKT2vuL2+I+uOOyQXKnTVKSND5jdcdBFRv5TmgWXLYM8e3d8UzapWlQvgsbjP6dAheOcdWcw97jjX0SgVossvl3ud6RSf9uyRxKlvX5ndpVQ2TZxUiWVkyFDZli3hslYr5SpcDKheXWbZTZoEixYV8MDvvpMl+wQt09PBt7HB75eVwUOHXEdSNFOnwq5dWqanYkzjxtLSUhOn+PTyy7B7NzzwgOtIVJTRxEmV2JtvwsqVsnJTqmMH2bCQleU6rJDceac0i3jooQIeNGmStAi84IKIxRVN0tNl9lXTpq4jUQVJSoKDB2H+fNeRFE1qqjSr6tHDdSRKFVFysvzCrVrlOhIVTn/8IU0hevSQ5FipHDRxUiVy+LCsNrVvD717I32Rf/9dMqkYcPzxUrI3cSL89FMeD7BW9jf16JGwnRF08G1s6NpV7mOpXG/7dpgxA66+GkqXdh2NUkWk3fXi0+uvw7ZtMHiw60hUFNLESZXIf/4D69fLapMxQMeO8o0YKdcDWXWqUiWfVadFi2Tzb4KW6e3eDUuXapleLKhZU2ZzxlKDiHfekVLfaB5foFS+6teXzZ/aXS9+HDkiA287dYKzz3YdjYpCmjipYjt4EB5+WDp6/a+K7fTTJQv5PjYaRICUoQ0cKBcNly7N9c1AQDLCiy92EptrwfxXG0PEBr8f5syRLpexIDUV2raFM890HYlSxZScDD/+CMuXu45EhcM778jV4CFDtMxC5UkTJ1Vsr7wiQ7UfeijH+0vp0lK3F0MrTiADcStVkkTwGJMmyVTOWrWcxOVaWpr822qZd2xISpJmUIsXu46kcIsXww8/aFMIFeO0u178yMqCMWOk09WFF7qORkUpTZxUsRw4IO8v3bpB9+65vunzyXCkGGrvdeKJcPvt0lb955+zv7h2rVxJ7NvXaWwupafLakC1aq4jUaHw++U+FvY5paZCmTJw5ZWuI1GqBOrWlQ2GWq4X+wIBOQEYPFhXm1S+NHFSxfKvf8neyTz3BXXsKHXCP/4Y8bhK4p57oGLFHKtOkyfLfZ8+zmJyKStLEifd3xQ76teXW7Tvc8rIgLfegl69ZG+WUjEtJQWWLJGbik3WwujRcOqpcNllrqNRUUwTJ1Vke/fCY4/B+ecf7eR1DJ9P7mNonxPICdytt0qJ84oVyNWns86CJk1ch+bEihXSHEL3N8WWpCRJnKx1HUn+Zs6UCy9apqfiwqWXygqFluvFrpkzpbX8/fdri09VIE2cVJGNHSvzYEeNyucBdevKYJYY2+cEcO+9MrLpkWEH5ewzQbvpgQ6+jVV+vyQl0TwRIDVVmrL06uU6EqXC4OSTpQPb++9H9xULlb/Ro6FePbjmGteRqCiniZMqkl274MknZWZTcGHpT4yRb8Zg4lS7NtxyC7w9sRyrshol9P6mtDSoXh2aNXMdiSqKpCS5j9ZyvV27pAr2yiuhXDnX0SgVJikpsj8mFjqzqGPNmSMbQ++9V9+UVKE0cVJF8vTTMt8239WmIJ9Par127YpIXOE0aBCU5QiPVB4NrVu7DseZ4P6mUvouEVOaNZOy02htEPH++9I3Rsv0VFzp10/eLLVJROwZM0Y6RP39764jUTFAT4lUyHbsgGefle6rrVoV8uDgINx58zyPK9xOqrqff5hxvHngUtasTczOOnv2wE8/aZleLDJGyvWidcUpNRVatIB27VxHolQY1aolLWa1XC+2LFwI06fDXXdB5cquo1ExQBMnFbInnoD9++HBB0N4cPv2ch9jDSIA+PRT/pk5mjJlpOw5Ec2dK5/92hgiNvn90k1/0ybXkRxrxQopAb32Wu32q+JQcjKsWiUDylRsGDNG5m3ceqvrSFSM0MRJhWTrVnj+ebjqKrlaXKjjjoPTT4/JfU4EAtSpcYibbjKkpsK6da4DirxgY4h897GpqBat+5zGj5dqpquvdh2JUh7o1086sml3vdiwYgV88AHcdpts6FUqBJo4qZA8+igcPgwjRhThScEGEbFUtnDkCEydChdfzH0PlKJUqcRcdUpPlwRZP0tiU6tWULVqdCVOWVnw5pvQowfUqeM6GqU8cMIJcO65ss8plj73EtVjj0kb3TvvdB2JiiGaOKlCbdoEL78s5TVNmxbhiR07Sl/kjRs9iy3svvlGGlpccgl168pe0TfegPXrXQcWOdbq4NtYV7o0dO4cXQ0iZs+GDRu0KYSKcykpUqYQg/t7E8qGDbIEfuONsj9NqRBp4qQK9cgjcrV42LAiPjEWB+FOmgQVK8J55wEyCw9kxS1RrFolc7p0f1NsS0qCJUvk3zIapKbKVoIEHo2mEsEll0DZsiZYTDMAACAASURBVNpdL9o9+aTc33uv2zgSmDHmVmPMWmPMQWPMfGOMv4DHdjPG2Dxup0cyZtDESRVi3Tp47TVZeWnYsIhPbtlSZiLEyj4nayEQgPPPh0qVADjlFLjhBvk7iKWFs5II7m/SxCm2+bM/gubMcRsHwL598OGHsne+YkXX0SjloeOPlwtv778vVxxV9Nm+HcaNk2G39eu7jiYhGWNSgLHAaKAN8B0wwxhT2D/IGcDJOW4RH/WuiZMq0EMPyWbuIUOK8eRy5aBNm9hJnBYskLrEXJfEH3hA7hNl1SktTVYGmjd3HYkqiQ4dpHw/GvY5ffihdOTUMj2VEFJS5EpbLFVbJJJnn5Vhcvfd5zqSRHY38Ia1dpy1dpm1diCwBbilkOdtt9ZuzXHL9D7UY5WJ9AtGmxo1ajB79mzXYUSlTZsq8sYbPvr23czKlatYWZy8/rbbZABULPwd//KLLN/Xq/eneM877zTGjTuJbt3SqVnzsJv4IuTzz9tx2mlH+PrrRa5DUSXUrFlrpk0rxUUXLXAax9ixrahTpwJHjnwfE28FSpVInTrw1FNS93zokOtoVE6ZmbKn6d//hi1b5KbCrYwxJucmv1etta8G/8cYUw5oBzyZ63kzgc6FHHueMaY8sBR42Fr7ZTgCLgpjE7zzS+XKle3+/ftdhxGVrr5atvysXg0nnVTMg7z9thxo0SI466ywxhd2Z54JNWvCl3/+PVy3Thpj3HILPPdc5EOLlH37pJP8kCEwapTraFRJDR0qK6W7d0OVKm5iWL9eynxHjoThw93EoFTEXXKJDMTbuFHKNlR0GD1aPuB++AFat3YdTVwyxhyw1uY7TdgYUwfYDJxtrf06x9eHA/2ttc3yeE4z4K/AXKAccA1wc/YxIlpXob/NKk9Ll8KECXD77SVImiB2GkSsXCk76fv2zfPbDRtKmdGrr8rCVLyaN0/K8nV/U3zw++UCa3Dfmgtvvin3Awa4i0GpiEtJkQ+Lb791HYkKOnAAnnkGLrxQk6YYY61dbq192Vo731qbZq29FfgEGBTpWDRxUnkaMQIqV4ZBJf2RPPVUGQYU7fucJk+W+z598n3I4MGQkQGPPx6hmBwInmB37Og2DhUenTrJxW5X+5yslY6/Z59djOYySsWyiy6CChV0GG40+fe/ZevA4MGuI0l0O4BMoHaur9cGthbhON8DRRmSExaaOKk/WbgQJk6Eu+6CE08s4cGMOToIN5oFAtLIokGDfB/SuLE04Xnllfgti05Ph2bNoEYN15GocKhWTX6sXSVOaWmymKtNIVTCqVoVevWSD9PMiO9fV7kdPgxPPCFzGrp0cR1NQrPWHgbmAz1yfasH0l0vVK2RhhIRpYmT+pMRI2SR6O67w3TAjh3hp5+krVY02rYNvvsupAEzQ4bAkSNHR0DEE2vlRFfL9OKL3y8JsYs96qmp0tn/sssi/9pKOZecDFu3Rtck6kT15pvSNVdXm6LF08B1xpi/G2OaG2PGAnWAlwGMMeONMeODDzbG3GmMucQY09QYc4YxZgxwCfBCpAPXxEkd47//hSlT4J57JHkKC59PrrgtcNvZK19TpkjWkM/+ppxOPRX694eXXpJ8K56sWQO//gp/+YvrSFQ4JSXBwYMwf35kX/ePP2QGaL9+cvFdqYTTq5dcOdByPbcyM6VLTtu2/xtur9yy1r4H3AkMBRYCXYELrbXrsx9SP/sWVA54AlgEfJP9+F7W2o8iFnQ2TZzUMYYPhxNOgP/7vzAetEMHuY/Wcr1AQOrwzjwzpIcPGSJX7+Nt1Sk9Xe51xSm+dO0q95G+6D1lCvz+u5bpqQRWubLsdfrwQ9kgq9yYOFFaww8eLNsHVFSw1r5orW1orS1vrW2Xs8OetbabtbZbjv9/3Frb1Fpb0Vpbw1rrt9ZOdxG3Jk7qf+bMgU8/lZlwYb1CXLu27B2KxsRp7174/HMp0wvxDfW00+DKK+HFF2UAebxIS5OW1Wec4ToSFU41a8ow40jvc0pNlZFof/1rZF9XqaiSkiJL+TrAzA1rpQX56aeHVFWiVGE0cVL/M2yY5Di33ebBwaO1QcSMGbJpNIT9TTkNHSqlSE8/7VFcDqSnyz9T6dKuI1Hh5vfLhZFI7VHfskUuwlxzjf48qQTXs6dckXrvPdeRJKbp02WO5AMP6DwtFRb6U6QA+OILuSA2eLCUZIddx44yRTbalmgCAbkk37mwYdXHOv10uOIKeOEF6W4a6w4cgB9/1P1N8crvhz17YPHiyLze22/LPDAt01MJr2JF6N0bPvpIOgupyLEWHnlEKl6uvNJ1NCpOaOKksFZWm+rVg5tu8uhFgoNwo2nV6fBhmDZNPtSKcVl86FBJOOJh1Wn+fCnB1/1N8SkpSe4jUa5nrZTpdewore2VSngpKfDbbzBrlutIEsvXX0sN+j//CWXLuo5GxQlNnBSffCLduIcMkXl9nmjbVpbJoylxmj1bLsMXsUwvqEULuPxyeP552LkzvKFFmg6+jW/168stEg0ifvhBpg/oapNS2c4/X4aqableZI0eLfsPrr/edSQqjmjilOCslU56DRvC3/7m4QtVrixd66IpcZo0SeI699xiH2LYMNi3D559NoxxOZCeLq3Wa9Z0HYnySlKSrDhZ6+3rpKZCuXJykV0pBZQvLxfoJk2SSgflvXnzYOZMGUhZsaLraFQc0cQpwU2ZIu8vw4fLyY6nOnaUxMnrM7dQZGXB5MmycbcEy2xnninDPZ97DnbtCmN8EaSDbxOD3y+zx1au9O41Dh+GCROk+rVGDe9eR6mYk5ws/flnznQdSWIYPVqGUd58s+tIVJzRxCmBZWXJiknTptL9ynM+n2QXq1ZF4MUKMXeutP4qZpleTsOGScVfrK46rV8vw+21MUR8i8Q+pxkzpFmKlukplUuPHnD88ToMNxKWLpXVvYEDpURSqTDSxCmBTZwoXbYefBDKlInAC0ZTg4hAQP7QF15Y4kO1bCnjIcaOhd27wxBbhOng28TQrJmUYnq5zyk1FWrVki0dSqkcypWTD4pAAA4edB1NfHv0UWkPfMcdriNRcchJ4mSMudUYs9YYc9AYM98Y4y/k8eWMMaOyn3PIGLPBGHNHrsdcaoxZmv39pcYYnXRWgMxMGDFCGhxEbC9CixbyZhYNidOkSdCtm1wBDIPhw6UK47nnwnK4iEpLk3+Ws85yHYnykjFSrufVitPOnTB1KvTvrw2slMpTcrIMXf/0U9eRxK+1a6Ve+B//gBNPdB2NikMRT5yMMSnAWGA00Ab4DphhjKlfwNPeBS4AbgKaAZcDi3IcsxPwHvA20Dr7/gNjjPYIy8eECfDzzzByZAQHVJYpA+3auU+cfv4Zli8PS5leUOvW0KcPPPOMJFCxJD0dOnSI0Kqjcsrvl/OKTZvCf+x33pExNVqmp1Q+uneHE07Q7npeeuIJOam55x7Xkag45WLF6W7gDWvtOGvtMmvtQGALcEteDzbGnAecA1xorf3MWrvOWvu9tXZ2jofdCXxprX0k+5iPALOzv65yOXJEEqbWraFfvwi/eMeO0q/YZWehQEDu+/QJ62GHD5dSveefD+thPXXwoPxz6P6mxODPXtv3YtUpNRVatZKbUioPZcvKh+6UKTIEUIXXli3wn//AdddB3bquo1FxKqKJkzGmHNAOyN1WZibQOZ+nXQLMBe42xmwyxqw0xjxnjKmS4zGd8jjmpwUcM6GlpsLq1TBqlIxWiiifDw4dgkWLCn+sVwIBWWKpVy+sh23bFi66SAbi7tkT1kN7ZsECSaR1f1NiaNUKqlYNf+K0dKl05xwwILzHVSrupKTA/v3SSUWF1zPPyAfaP//pOhIVxyJ92nwiUBrYluvr24CT8nlOY6Ar0Aq4FLgdKdt7I8djTirKMY0xNxlj5hlj5mVkZBQl/ph36BA89JDkLxdd5CAA1w0iNm+G778Pa5leTiNGSOPAf/3Lk8OHXXDwra44JYYyZaBz5/A3iEhNleqY/v3De1yl4s7ZZ0uXFi3XC6/ffoOXXoIrroAmTVxHo+JYLHTVKwVY4KrsEr1PkeTpUmNM7eIc0Fr7qrW2vbW2fZkE29jx2muwYYMkT8Y4CKB+fWm75SpxmjJF7j1KnNq3l0Z9Tz0lg3GjXVoaNGokw9VVYkhKgiVLpJlDOGRmwltvwQUX6M+RUoUqU0aG/02dKitPKjyef14+dO+/33UkKs5FOnHaAWQCuT9eawNb83nOFmCztTbnlvtl2ffBhhJbi3jMhPTHH/Dww9C1q4yUcMKYo4NwXQgE4LTToHlzz15ixAg5KY2FVaf0dC3TSzTBfU5z5oTneJ9/Dr/8ok0hlApZSop8IE+d6jqS+LB3r8wD6d1b28Mqz0U0cbLWHgbmA7lP23sg3fXy8i1QJ9eeptOy79dn36cV8ZgJ6eWXZe/kww87Wm0K8vmks12k28/t3g1ffCGrTR7+Bfh8cvX9ySeje9Vp40apXNQyvcTSoQOULx++fU6pqVC9Olx8cXiOp1Tc69oVTjpJh+GGy6uvSo38Aw+4jkQlABelek8D1xlj/m6MaW6MGQvUAV4GMMaMN8aMz/H4CcBO4HVjzBnGmC5IO/OJ1trt2Y8ZC3Q3xtxvjDndGPMA8Ffg2Uj9oaLdvn0yE+6cc6TE2imfD6yV3eSRNH06ZGR4VqaX0/DhsGOHJKvRSgffJqYKFeRXMBz7nH7/XUaiXXGFHFcpFYLSpeHyy+Uzae9e19HEtoMHpTa+e3e9CqgiIuKJk7X2PaRN+FBgIdL44UJrbXD1qD5HS/Cw1u4DzgWOQ7rrvQ98Bfwtx2O+A64ArkPmOw0AUqy133v8x4kZL7wA27fL3ibn2reX+0iX6wUCcpWvo/fjvTp1knLIJ56I3q6zaWlystuypetIVKT5/dJRsaQroh98IOctWqanVBElJ8svT3DfrSqe1FQppRkyxHUkKkEYa63rGJyqXLmy3R/nGzT37JEGAH/5C0yb5jqabKedBmecIZerI+HgQelk1L9/xJaBvv1WKjKeegruvjsiL1kknTvLhU8vZvqo6Pbpp1JO+tlncO65xT9OUhJs2yaVt07Lf5WKNVlZ0iypXTuYPNl1NLEpI0POJWrVkiuB+iYUFYwxB6y1lV3H4ZVY6KqnSujZZ6VT56hRriPJoWNHaQseqcT9iy/k8noEyvSCunSR0sjHH4++VadDh2D+fK1sSFSdOskMt5KU661ZI0n3tdfq+YpSRVaqlJTrffKJ7L9VRffee7B2LQwerG9CKmI0cYpzv/0mKx6XXCIXtqKGzyfL65s3R+b1AgGZ/PnXv0bm9bINHy5X5MeNi+jLFuqHH+DwYd3flKiqVYM2bUq22jh+vJyrXHNN+OJSKqGkpMgbsZbrFV1WFowZA2ee6WgopUpUmjjFuaeekr2nUbXaBJEdhJuZKaUQF14o7cQiKCkJunWDxx6TasFoEWwMoStOicvvl5+DQ4eK/tysLEmcuneHU04Jf2xKJYSOHaVcb9w4KTXbti1yVRix7uOPZSDdAw/I6p1SEaI/bXHs119ltEFychSONmjVCsqWjUzilJ4unTH69vX+tfIwYoQsrkXTqlNamnxe16njOhLlSlKSJPPz5xf9uXPmSIWMNoVQqgSMgQED5Beqc2dpXlSlinxg9+kDd90lg12nTYNly2T2k5Lk8pFHoHFjOcFRKoLKuA5Aeeexx+R99sEHXUeShwoVoHXryCROgYAkaT17ev9aeejWTU5SH30UbrwxOto26+Bb1bWr3H/9tZyzFUVqqpzf9esX/riUSigjR8JVV8mmweBt7Vq5nzULcjevOvlkSRjyup10UmKsvsyaBXPnwiuvQBk9jVWRpV314rSr3pYtRy/GpKa6jiYft98uwe3eLe3dvGAtNG0qtxkzvHmNEMyaJd3L/vUvuPVWZ2EA8MsvULcuPPMM3Hmn21iUW82by/tEUbptHjgg52eXXgqvv+5dbEolPGuldCRnUpXztmnTsaV9FSpIC91Gjf6cVDVqJFc74kH37tLKc+3aiJffq8LFe1c9TdXj1JgxcOSINCeIWj6fZBI//yytyb2wZAmsXg2DBnlz/BB17y5d9saMgRtucPter4NvVVBSkjSmyswM/drFpEmyb1LL9JTymDHSartWrbw3pB46BBs25J1UzZkjs0hyqlXr2EQqZ2JVt653FzDDKS0NvvxSNnBr0qQc0MQpDm3YICvY118PTZq4jqYAORtEeJU4BQLy4dOnjzfHD5ExstfpvPPkKv3NN7uLJS0NypWTSkmV2Px+ePVVWLw49J+H1FRo0ECSLqWUQ+XLH62oyM1aaaubs/QveEtLO3rFJKhsWWjYMO8SwEaN4LjjIvbHKtCYMXDCCXDTTa4jUQlKS/XisFTvH/+AN96AlSulAUDUysqCGjXgyivhpZe8eY327SVL+O47b45fBNbKqtPmzfJvU66cmzj8fvm8jIK/EuXYhg2SBD33HAwcWPjjN22S95ShQ6OwU6dSKnRHjsDGjXnvrVqzRpKunGrUyH9v1SmnRGav0aJF0lhq1CgYNsz711PFoqV6KqasWQP/+Y8kT1GdNIFsYu3QQQbhemHjRmkZ9thj3hy/iIyR0smePSWxdXHB7PBhmDfP/T4rFR3q15fb11+Hlji99ZZcABgwwPvYlFIeKlv2aOKTl927/7xStWYNLFgAH30EGRlHH1u6tFyByWtvVePGcPzx4RlQ++ijsk/r9ttLfiylikkTpzgzapRc+Bk82HUkIfL5jrb/q1gxvMcOBOT+kkvCe9wSOP98+SOPHi2llGXLRvb1f/xRWlDr/CYVlJQEn30mCVFB5zbWSplely5w6qmRi08p5UD16jIlu02bP38vM1NKJ/LaWxUISEOLnI477s+lf8H/btAgtPKLVaukvPDeeyURU8oRTZziyPLl8Oab0iktZubz+HzyJvzDD0XviVyYQABatIDTTgvvcUsguNepVy8ZIHrDDZF9fW0MoXLz+2UlaeXKgn9V5s6VPi6vvhq52JRSUah06aPL1d26/fn7e/ceXa3KuWq1ZAlMnXrs1O1SpaBevfz3VtWsKR+cjz8uVxrvuitif0yl8qKJUxwZOVIWbe67z3UkRZCzQUQ4E6fffoOvvorKv4yePWXr1SOPSMlTJFed0tKkeVK9epF7TRXdgk0evvmm4MQpNVW6Heu8SaVUgapWhZYt5ZZbVpbMS8lrb9WMGfK9nCpXliTq559lEOJJJ0Xmz6BUPjRxihM//QTvvit5Qq1arqMpgpNPlo2l4R6EO22arGRFUZleUHCvU+/ecqX/+usj99o6+Fbl1qyZXNT95pv8V0APHYJ33pFfp2hprqWUikGlSsnVu7p1Zbk7twMHYN26P5cAVq8O998f8XCVyk0TpzgxYoRc5HE8rqh4fL7wN4iYNEnemNu1C+9xw+Sii6BtW1l1uuaayDQk2rZNLuzddpv3r6VihzHQtas0iMjP1Kmwa5fOblJKeaxSJSmxb9HCdSRK5amU6wBUyQWb3Nx1l3QMjTk+n1xR2rEjPMc7cAA++UQuj5eKzh/x4KrT6tUwYUJkXlP3N6n8JCVJUr1pU97fT02VxeEePSIbl1JKKRVNovOsUhXJ8OHSZCZm90wG9znNnRue433+uXTpi8IyvZx695aRFA8/fGxnV6+kpcl+qrZtvX8tFVuCFTPffPPn723fLlsPrr5a9oQrpZRSiUoTpxiXni7beQYNiuG9B+3ayRJMuPY5BQLyl3H22eE5nkeCq04rV8r+NK+lpUln2QoVvH8tFVtatZJS37wSpwkTJLHXMj2llFKJzlhrXcfgVOXKle3+/ftdh1Fs550HCxdKpVuVKq6jKYGzzpImEdOnl+w4GRnSdeeCC6TzQpTLyoLWrWUw7ZIl3l3Rz8iAatVk6O6zz3rzGiq2XXCBlOr99NOxX2/TRn4u581zE5dSSqnYYYw5YK2t7DoOr+iKUwz7+msZXHnffTGeNIGU6/33vzJlsyS+/RZ27oS+fcMTl8dKlZJVp+XL4f33vXudRYukelEH36r8JCVJ8r5z59GvLVokF2Z0tUkppZTSxClmWQvDhsmG7VtucR1NGPh8csa2dm3JjhMIQPnycP754YkrAvr1gzPOgIcekg7qXtDGEKowwX1O33579GupqbIv7sor3cSklFJKRRNNnGLUrFmy4jR4sHTvjHk5B+EWl7WSOPXoEVNLcKVKSRK8bBlMnOjNa6SlSQVj/freHF/Fvg4doFy5o23JMzLg7behVy848US3sSmllFLRQBOnGGQtDB0qW4JuvNF1NGFy5plQsWLJ5jn9+KMMzovybnp5uewyaN5cVp2yssJ//ODgW2PCf2wVHypUgI4djzaI+PRTmf2lZXpKKaWU0MQpBk2fLvnFsGFSlRYXgn2yS7LiFAjI8k3v3uGLK0JKl5Z/zyVLZCZXOP36K6xapfubVOH8fpg/H/btkzK9E06ACy90HZVSSikVHTRxijHBvU2NG8N117mOJsx8Ppnme+RI8Z4fCECXLlCzZnjjipDkZDj9dBg1KryrTsFFPN3fpAqTlCT77GbMgMmT4aqrpHxPKaWUCidjzK3GmLXGmIPGmPnGGH+Iz+tqjMkwxvxU+KPDTxOnGDNpEvzwA4wYIYs0ccXng4MH/9wPORRr10qpXgyW6QWVLi0lmIsXSw4YLmlpUKaMjMtSqiCdOsmi7aBB0iJfy/SUUkqFmzEmBRgLjAbaAN8BM4wxBe7ENsYcD4wHZnkeZD40cYohWVmSMDVrBv37u47GAyVpEBHMNGI4cQJISYGmTWXVKVwj1tLSZMBpXDQRUZ6qVk3mNq1fL50e27Z1HZFSSqk4dDfwhrV2nLV2mbV2ILAFKKxP9GtAKpDmdYD50cQphrz/vizGPPigd4NSnWrUSNp3FadBRCAALVtKDWMMK1NGVp1+/BGmTCn58TIzJQ/VMj0VqmBb8muv1WYiSimlwssYUw5oB8zM9a2ZQOcCnncrUBt42LvoClfG5YtHgxo1ajB79mzXYRQqM9Pwz392oFGjLGrVmkcMhFw8Y8ZIjVBR/oAZGdIQ4uSTi/a8KFW3rqFu3Q4MGpRJtWrzS3TyumpVZfbv70C1akuZPXt7+IJUcatx42rUq3c6TZosZPbsw67DUUopFVvKGGPm5fj/V621r+b4/xOB0sC2XM/bBpyb1wGNMWcBI4C/WGszjcOregmfOP32229069bNdRiFSk2FjRul41r37t0cR+Ohr76CkSPh99+hatXQnvP663DvvdJYok0bb+OLkIcfhuuvh337unHxxcU/zvLlcn/DDS1o3LhFeIJTca1bNxg4EAq48KeUUkrlJ8Na2z5cBzPGlAfeA+611q4N13GLS0v1YsCRI5JLtG0b81t4Cufzyeae+fNDf86kSdCgAbRu7V1cEda/v1QulnSvU1qaNBls1Ch8sSmllFJKFdMOIBMpu8upNrA1j8efDDQHXs/uppcBDAfOyP7/8zyNNhdNnGLA669L07iHHkqAPQfBBhGh7nPatw9mzpSMMo7+csqWhSFDYN48aQ1dXDr4VimllFLRwlp7GJgP9Mj1rR5Id73cNgNnAa1z3F4GVmX/d17P8YwmTlHu0CEp2/rLX6BnT9fR/H979x4kV1kmYPx5CcSEBBGIhESEcBFEvFFQgyII7hpFVEAQRbRiUEGCkBJFJV6QYhHZgkXAKmXBWgIqtezikiowShQFJCQQ2EUKpDCuGJZIiCEhQGKuvPvH11O0Y890ZtK3mX5+VV2nzznfOfN215fufvPdWmCXXWCffbZ8Zr1588qbNAKb4qZNgylTSmvjUFqdVq4sXfWcGEKSJHWQy4HpEfGZiDggIq4EJlMSIiLihoi4ASAzN2bmI9UPYDmwvrL/YisDN3HqcNdeW8Y2dUVrU6+eni1PnObMgZ13hsMPb25MbbDddvDVr5a34vbbB3/9woVl+7a3NTYuSZKkocrMm4DPA18HHgIOB47JzCWVIntUHh0nslGLxQxT48aNyzVr1rQ7jJrWri2NL/vvD7/+dRclTldcAeecA0uXwuTJ/ZfbuBF23RWOOw5mz25ZeK20YUNZ12nyZLj33sHVgfPPh299C55/HsaNa16MkiRJABGxNjNH7K8OW5w62Pe/D8uWdVlrE7w8zmnRooHL3X03PPccfOhDzY+pTUaPhlmzSuvRL385uGsXLChLW5k0SZIkbT0Tpw714otwySUwderLC1J2jYMOKivB1psgYs4cGDu2vEkj2Kmnwu67D26s0+bN5e1zfJMkSVJjmDh1qKuughUrSmtT1xk7tjSVDDTOKbMkTu99L2y/fetia4NXvKK0Os2fD7/61ZZd89hj8MILjm+SJElqFBOnDrR6NVx2GXzgA3Dooe2Opk16ekpXvZdeqn3+wQfhqadG5Gx6tXzqU2Wc04UXbln53okhbHGSJElqDBOnDvSd78CqVVv+I3lE6ukpsxr8/ve1z8+ZA6NGleyyC4wZA+edV4Z13Xln/fILFpSZ3ffdt+mhSZIkdQUTpw7z7LNw+eVw4ollqE/X6m1q62+c05w58M53luygS5x2GkyaVMY61bNwYemm11WTikiSJDWRiVOHueyyMjHElvw4HtH23x922KH2OKfFi+HRR7umm16vMWPgK18pLU53391/ueeeg9/9zm56kiRJjWTi1EGWLy+TQpx8Mhx4YLujabNRo+CQQ2onTnPmlO1xx7U2pg5w+ukwceLAiXVvI50TQ0iSJDWOiVMHueQSWLcOLrig3ZF0iJ4e+O1vy5tSbc6c0o9xzz3bE1cbjR0LX/5ymV3vnntql1m4sHTR610OS5IkSVvPxKlD/PnPZcHbadNgv/3aHU2H6OmBjRtL8tRr2bIy88EIXvS2njPOgF137X/ykAUL4I1vLD0dJUmS1BgmTh3i4oth0yY4kQAouwAADG9JREFU//x2R9JBak0QceutZQ2nLhvfVG377eFLX4Jf/KIkSdVeesmFbyVJkprBxKkDLFkC11wDn/407LVXu6PpIK95TVm8qHqc0y23wN57lyaVLjZjBkyY8PdjnR5/vEwO4fgmSZKkxjJx6gAXXVTGpHzta+2OpAP19LycOD3/PNxxR2lt6vJ5tseNg3PPhdtv/9sGORe+lSRJag4Tpzb7wx/guuvgs5+F17623dF0oJ6eMv34ypXw85/Dhg1dPb6p2uc+V5axqh7rtGAB7LST4+QkSZIazcSpzW68EUaPhlmz2h1Jh+od57RoUZlN79WvtjmlYvx4+OIXYe7c8vZASZwOPRS28V+2JElSQ/nzqs2+8Q146CGYNKndkXSogw8u3fLuuQd++lM49tiyxpMAOOss2Hnn0ur0/PNlXWDzSkmSpMYzcWqzCLtVDWjHHeH1r4fvfa9kBl08m14tO+wA55wDt90GV19dJhx0YghJkqTGi8xsdwxtNW7cuFyzZk27w9BApk+H668vMyKsWAFjxrQ7oo6yejVMmQJr1pQp7VetKvmmJElSK0XE2swc1+44msUWJ3W+np6yfd/7TJpq2HHH0uq0cSMccIBJkyRJUjOYOKnzHXFE2Z50Unvj6GAzZ5bZ9I46qt2RSJIkjUx21bOr3vCweDHsu2/Xr980kKVLS2vT+PHtjkSSJHWjkd5Vz8TJxEmSJEnaaiM9cbKrniRJkiTVYeIkSZIkSXWYOEmSJElSHW1JnCLizIh4IiLWRcSDEXHEAGWPiois8Xh9VZnp/ZRx7mpJkiRJW23bVv/BiPgocCVwJnBPZfuziHhDZj45wKUHAiur9v/S5/xaYJ/qA5m5busjliRJktTtWp44AV8AZmfmtZX9syPiaGAGMGuA65Zn5ooBzmdmLmtUkJIkSZLUq6Vd9SJiNHAwMK/PqXnAYXUufyAino6IOyLiXTXOj42IJRHxVETcFhEHNSJmSZIkSWr1GKcJwCjgmT7HnwF26+eapymtUScCJwCPA3f0GRf1OPAp4DjgY8A6YH5EvK7WDSPi9Ih4ICIe2LRp01BfiyRJkqQu0dIFcCNiMrAUODIz7646fj7w8czcfwvvMxfYlJnH9nN+FPAQ8OvMnDnQvVwAV5IkSdp6LoDbWCuAzcDEPscnAoMZn3QfULM1CSAzNwMPDFRGkiRJkrZUSxOnzNwAPAhM7XNqKnDvIG71VkoXvpoiIoA3D1RGkiRJkrZUO2bVuxz4YUTcD8wHzgAmA1cDRMQNAJk5rbL/eeBPwKPAaOATwPGUMU9UynwTWAgsBl4JzKQkTjNa8YIkSZIkjWwtT5wy86aI2AX4OjAJeAQ4JjOXVIrs0eeS0cClwO7AXykJ1Pszc25VmVcB11AmmFgN/A/wzsy8v2kvRJIkSVLXaOnkEJ3IySEkSZKkrefkEJIkSZLU5bq+xSkiXqJ0AWynbQEXlFIjWJfUCNYjNYp1SY1gPRo+xmbmiG2Y6frEqRNExAOZeUi749DwZ11SI1iP1CjWJTWC9UidYsRmhJIkSZLUKCZOkiRJklSHiVNnuKbdAWjEsC6pEaxHahTrkhrBeqSO4BgnSZIkSarDFidJkiRJqsPESZIkSZLqMHGSJEmSpDpMnFogIs6MiCciYl1EPBgRR9Qpf2Sl3LqI+GNEnNGqWNXZBlOXIuKEiJgXEX+JiBci4r6IOLaV8aozDfYzqeq6wyNiU0Q80uwYNTwM4fttdERcWLlmfUQ8GREzWxWvOtMQ6tEpEfFQRKyNiGUR8aOI2K1V8ap7mTg1WUR8FLgSuBg4CLgX+FlE7NFP+b2AuZVyBwHfBr4bESe2JmJ1qsHWJeBI4FfA+yvl5wK3bOmPZI1MQ6hHvdftBNwA3NH0IDUsDLEu/TtwNHA6sD9wEvBwk0NVBxvC76R3AD8ErgcOBI4H3gD8uCUBq6s5q16TRcR9wMOZeVrVscXAzZk5q0b5fwZOyMzXVR37AXBgZr69FTGrMw22LvVzj/uB32TmF5sUpjrcUOtRRPwX8FsggA9n5hubHqw62hC+394D/CewT2auaF2k6mRDqEfnAmdn5p5Vx04FvpuZ41sRs7qXLU5NFBGjgYOBeX1OzQMO6+eyt9cofztwSERs19gINVwMsS7VsgOwqlFxaXgZaj2KiDOBicBFzYtOw8kQ69LxwCLgCxHxVEQsjoirIsIfu11qiPVoPjApIj4YxQTgZEqvCqmpTJyaawIwCnimz/FngP764u7WT/ltK/dTdxpKXfobEfE5YHdKFwd1p0HXo4h4E/BN4BOZubm54WkYGcpn0t7A4cBbgBOBsyjd9mY3J0QNA4OuR5m5gJIo/RjYAPyF0hL+yeaFKRUmTlIXqIyRuxQ4JTOXtDseDQ8R8QrgJuDczHyi3fFo2NsGSMrn0H2ZeTsleToxIia2NzQNFxHxBuC7wD9RWquOpiRZ/9rOuNQdtm13ACPcCmAzpYtLtYnAsn6uWdZP+U2V+6k7DaUuARARH6YM6p+Wmbc2JzwNE4OtR5OAA4DrIuK6yrFtgIiITcAxmdm3i426w1A+k54Glmbm6qpjj1W2e/D3rQ4a+YZSj2YB92fmpZX9hyNiDfCbiPhqZj7VnFAlW5yaKjM3AA8CU/ucmkqZNaaWBf2UfyAzNzY2Qg0XQ6xLRMRHKF3zpmfmzc2LUMPBEOrRUuBNwFurHlcDf6g877fuaWQb4mfSfGBynzFN+1W2toR3oSHWo+0pyVa13n1/16qpbHFqvsuBH1ZmM5sPnAFMpvz4ICJuAMjMaZXyVwNnRcQVlGbndwDTgY+1Nmx1oEHVpYg4mZI0nQvcXbXGxYbMXNni2NU5trgeVf6z5m/WbIqI5cD6zHQtJw32++1G4BuUFswLgFdRpqG+OTOXtzZ0dZDB1qNbgWsjYgZl8qxJwBXAf2fmky2OXV3GxKnJMvOmiNgF+DrlH/cjlO4tvf+7tkef8k9ExDHAd4AZwJ+BmZn5kxaGrQ402LpE+fLZlvKFckXV8buAo5obrTrVEOqRVNMQvt9ejIh3U8anLKLM8DkHOK91UavTDKEezY6IHSjj4/4FWE1Zs/ArrYta3cp1nCRJkiSpDvuCSpIkSVIdJk6SJEmSVIeJkyRJkiTVYeIkSZIkSXWYOEmSJElSHSZOkiRJklSHiZMkdbGIuCAisvK4oN3x1FKJ8YKImL4FZadUvZ56j9nNj16SNFK4AK4kqdN9s7K9C5jdxjgkSV3MFidJ0oiRmX/KzOh9AO+qOn1X9bnMnN6mMCVJw5CJkyTp70TE7KoubYdFxI8iYlVEPBsRP4mI3arKVnePmx0Rp0bEYxGxPiIe79vFLiLu7C0/0PGImN6nzJFVf+fOBr7WiyPivoh4JiI2RMSaiHgoIr4cEdtVlTu66u+fV++4JGlksaueJKmenwKvqto/AdgReHeNsu8HPlm1vx9wXURsk5n/1rwQt8opwJ5V+9sBb6k89gJmtCMoSVJnscVJklTPE8A+lCRoeeXYP0bEpBplJ1ASp1cCH686/u3q1pstkZmzK93telV3tTtqMPeq41zgAEoyOJryOh+tnPtMRIxv4N+SJA1TJk6SpHrOz8w/ZuZi4DdVx/esUfbezLwhM1/IzBuBeyvHdwXe3OxAh2gNcBXwv8Bfgd8DB1bObUtJGiVJXc6uepKkeh6ver6m6vmYGmWfrLF/WOX5hDp/p+XfSRHxD5SuiDFAsbF1buN3qSR1AVucJEn1bKx6nv2WKvYYYH9FZbu+90BEjKlsgzKeqNU+wstJ04XA+Er3wLk1yq6vel6dNO7dpNgkSR3ExEmS1EiHRcTHI2J8RJzCy61Ny4GHK8+XVJX/YGV7NjC5n3s+W9nuGRE7NTRa2FT1/AVgc0QcD0ytUbY67vdExOiImAzMbHBMkqQOZOIkSWqkp4EfUZKQH1cdn5WZvS1X1cf/IyJeAK6kjC+qZWFlOwVYWZn2+4IGxXtL1fNLKzHcDPxf34KZ+ceqWN4OrKqUqzVJhiRphDFxkiQ10jzKbHqPARuAxcCp1VORZ+ZdwGcokzCsp0zKcBJwfz/3PJvSdW5Vo4PNzDuA6VWxPEKZbn1RP5d8BLgNWA2sBX4AnNbouCRJnScy63VXlySpfxExhTJlOcD1mTm9bcFIktQktjhJkiRJUh0mTpIkSZJUh131JEmSJKkOW5wkSZIkqQ4TJ0mSJEmqw8RJkiRJkuowcZIkSZKkOkycJEmSJKmO/welwvr5B2QpBAAAAABJRU5ErkJggg==\n"
+ },
+ "metadata": {
+ "needs_background": "light"
+ }
+ }
+ ],
+ "source": [
+ "fig, ax1 = plt.subplots(figsize=(13,7))\n",
+ "ax1.plot(all_tau, accuracies, color='r')\n",
+ "ax1.set_title('Accuracy and $\\gamma_{sr}$ vs Tau', fontsize=16, fontweight='bold')\n",
+ "ax1.set_xlabel('Input Tau', fontsize=16, fontweight='bold')\n",
+ "ax1.set_ylabel('Accuracy', color='r', fontsize=16, fontweight='bold')\n",
+ "ax1.xaxis.set_tick_params(labelsize=14)\n",
+ "ax1.yaxis.set_tick_params(labelsize=14)\n",
+ "\n",
+ "ax2 = ax1.twinx()\n",
+ "ax2.plot(all_tau, statistical_rates, color='b')\n",
+ "ax2.set_ylabel('$\\gamma_{sr}$', color='b', fontsize=16, fontweight='bold')\n",
+ "ax2.yaxis.set_tick_params(labelsize=14)\n",
+ "ax2.grid(True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HRHHvcuQC1uS"
+ },
+ "source": [
+ "References:\n",
+ "\n",
+ " Celis, L. E., Huang, L., Keswani, V., & Vishnoi, N. K. (2018).\n",
+ " \"Classification with Fairness Constraints: A Meta-Algorithm with Provable Guarantees.\"\"\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3.6.9 64-bit",
+ "language": "python",
+ "name": "python_defaultSpec_1596663900877"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 2
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython2",
+ "version": "2.7.15"
+ },
+ "colab": {
+ "provenance": []
}
- ],
- "source": [
- "fig, ax1 = plt.subplots(figsize=(13,7))\n",
- "ax1.plot(all_tau, accuracies, color='r')\n",
- "ax1.set_title('Accuracy and $\\gamma_{sr}$ vs Tau', fontsize=16, fontweight='bold')\n",
- "ax1.set_xlabel('Input Tau', fontsize=16, fontweight='bold')\n",
- "ax1.set_ylabel('Accuracy', color='r', fontsize=16, fontweight='bold')\n",
- "ax1.xaxis.set_tick_params(labelsize=14)\n",
- "ax1.yaxis.set_tick_params(labelsize=14)\n",
- "\n",
- "ax2 = ax1.twinx()\n",
- "ax2.plot(all_tau, statistical_rates, color='b')\n",
- "ax2.set_ylabel('$\\gamma_{sr}$', color='b', fontsize=16, fontweight='bold')\n",
- "ax2.yaxis.set_tick_params(labelsize=14)\n",
- "ax2.grid(True)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "References:\n",
- "\n",
- " Celis, L. E., Huang, L., Keswani, V., & Vishnoi, N. K. (2018). \n",
- " \"Classification with Fairness Constraints: A Meta-Algorithm with Provable Guarantees.\"\"\n"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3.6.9 64-bit",
- "language": "python",
- "name": "python_defaultSpec_1596663900877"
},
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 2
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython2",
- "version": "2.7.15"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
+ "nbformat": 4,
+ "nbformat_minor": 0
}
\ No newline at end of file
diff --git a/examples/demo_ot_metric.ipynb b/examples/demo_ot_metric.ipynb
index 9c8bc904..dd8217d8 100644
--- a/examples/demo_ot_metric.ipynb
+++ b/examples/demo_ot_metric.ipynb
@@ -1,1379 +1,1485 @@
{
- "cells": [
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Measuring bias with Optimal Transport by calculating the Wasserstein distance"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Table of contents\n",
- "\n",
- "- Introduction\n",
- "- General Optimal Transport examples\n",
- "- Usage\n",
- "- Application to Compas Dataset\n",
- "- Application to Adult Dataset\n",
- "- More details\n",
- " - OT for mapping estimation\n",
- " - Kantorovich optimal transport problem\n",
- " - Solving optimal transport\n",
- " - Necessity and priority of usage"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## References\n",
- "\n",
- "\"FlipTest: fairness testing via optimal transport\" https://dl.acm.org/doi/abs/10.1145/3351095.3372845\n",
- "\n",
- "\"Obtaining Fairness using Optimal Transport Theory\" http://proceedings.mlr.press/v97/gordaliza19a.html\n",
- "\n",
- "\"Computational Optimal Transport\" https://arxiv.org/abs/1803.00567\n",
- "\n",
- "\"POT: Python Optimal Transport\" https://jmlr.org/papers/v22/20-451.html"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Introduction\n",
- "\n",
- "Optimal Transport (OT) is a field of mathematics which studies the geometry of probability spaces. Among its many contributions, OT provides a principled way to compare and align probability distributions by taking into account the underlying geometry of the\n",
- "considered metric space.\n",
- "\n",
- "Optimal Transport (OT) is a mathematical problem that was first introduced by Gaspard Monge in 1781. It addresses the task of determining the most efficient method for transporting mass from one distribution to another. In this problem, the cost associated with moving a unit of mass from one position to another is referred to as the ground cost. The primary objective of OT is to minimize the total cost incurred when moving one mass distribution onto another. The optimization problem can be expressed for two distributions $\\mu_s$ and $\\mu_t$ as\n",
- "\n",
- "$$\n",
- "\\min_{m, m_{\\#} \\mu_s=\\mu_t} \\int c(x, m(x)) d \\mu_s(x)\n",
- "$$\n",
- "in the continuous case, and\n",
- "$$\n",
- "\\min_{\\sigma \\in \\text{Perm}(n)} \\frac{1}{n} \\sum_{i=1}^n \\textbf{C}_{i,\\sigma(i)}\n",
- "$$\n",
- "in the discrete case, where $\\textbf{C}_{\\cdot, \\cdot}$ is the ground cost and the constraint $m_{\\#} \\mu_s=\\mu_t$ ensures that $\\mu_s$ is completely transported to $\\mu_t$. Where $T_{\\#} \\mu_s = \\mu_s(T^{-1}(B)) = u_{t}(B)$ with $T$ as a trasportation matrix between $\\mu_s$ and $\\mu_t$ at point $B$. "
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "OT can be used to detect **model-induced bias** by calculating the above cost (also known as **Earth Mover's distance** or **Wasserstein distance**) between the distribution of ground truth labels and model predictions for each of the **protected groups**. If its value is close to 1, the model is **biased** towards this group."
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## General Optimal Transport examples"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let us start with some simple examples of calculating the Earth Mover's distance between two distributions - the basis of Optimal Transport for bias detection. We do this using the `earth_movers_distance` function.\n",
- "\n",
- "For concrete examples of bias detection on real datasets, skip to the next chapter."
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 1. General Optimal Transport\n",
- "\n",
- "Suppose we have two distributions $a$ and $b$ (as shown in the picture below), and we need to calculate the Wasserstein distance between these two distributions."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy as np\n",
- "\n",
- "# Initial distribution\n",
- "a = np.array([0., 0.01547988, 0.03095975, 0.04643963, 0.05727554, 0.05417957, 0.04643963, 0.07739938, \n",
- " 0.10835913, 0.12383901, 0.11764706, 0.10526316, 0.09287926, 0.07739938, 0.04643962, 0. ])\n",
- "# Required distribution\n",
- "b = np.array([0., 0.01829787, 0.02702128, 0.04106383, 0.07, 0.10829787, 0.14212766, 0.14468085, \n",
- " 0.13, 0.10808511, 0.08255319, 0.05170213, 0.03361702, 0.02702128, 0.01553191, 0. ])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "source": [
+ "[](https://colab.research.google.com/github/Trusted-AI/AIF360/blob/main/examples/demo_ot_metric.ipynb)"
+ ],
+ "metadata": {
+ "id": "vGqYImdfDCyy"
+ }
+ },
{
- "data": {
- "image/png": "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jkbhwoUNm82W+H4knO+jjz5KlulOq/Xt2LHDaN26tZE/f36jQIECRteuXY0zZ84kOT4qKsp47bXXDE9PTyNPnjxG/fr1jd27dydbWSe17/21a9eM119/3fDy8jIcHR2N4sWLGy+88ILx77//Jmnn5eVltGzZMtnruvU9FRERkTtL6borwbVr14zSpUsb5cuXN2JjYw3DMIw9e/YYnTp1Mh544AHD0dHR8PDwMJ544glj6tSpSY795ZdfjFq1ahnOzs6Gh4eH8eqrrxpffPFFspXg7vWa4nar9aV0fWgYhhETE2OMHz8+8Zouf/78RqVKlYx+/foZhw4dSvL8r7zyivHAAw8YLi4uRq1atYzg4OAUr2lScuLECaNDhw6J100dOnQwtm7detfV+oKDg4127doZXl5ehrOzs+Hq6mrUr1/fWL58eZLzr1+/3qhevbrh7OycZAXBhPOdO3cuWabbrdbXsmVLY9GiRcbDDz9sODk5Gd7e3saECROSHX/w4EEjICDAKFiwoFGsWDHjpZdeMlauXJns/b/dNahhpHw9/ueffxqtW7c2ChUqZDg5ORm+vr5J3iPD+O/f+dtvv02yP6VrV5GcwGYYhpF5pTAREREREZHcbfbs2fTu3ZujR4+mufePiEhOojmnRERERERERETEMipOiYiIiIiIiIiIZTSsT0RERERERERELKOeUyIiIiIiIiIiYhkVp0RERERERERExDIqTomIiIiIiIiIiGUcrA6QXuLj4zl16hQFChTAZrNZHUdERETSmWEYXLlyhRIlSmBnp8/Xshtdq4mIiOR8ab1eyzHFqVOnTuHp6Wl1DBEREclgoaGhlCpVyuoYkkq6VhMREck9Unu9lmOKUwUKFADMN6BgwYIWpxEREZH0dvnyZTw9PRP/z5fsRddqIiIiOV9ar9dyTHEqoXt4wYIFdcEjIiKSg2lIWPakazUREZHcI7XXa5qwQURERERERERELKPilIiIiIiIiIiIWEbFKRERERERERERsYyKUyIiIiIiIiIiYhkVp0RERERERERExDIqTomIiIiIiIiIiGVUnBIREREREREREcuoOCUiIiIiIiIiIpZxsDqAiEiOFx8H57bAtdOQpzgU8wc7e6tTiYiIiIiIZAkqTomIZKTQJbBjEESe+G9f3lLgNxE821uXS0REREREJIvQsD4RkYwSugS2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jk1paWlER0fb1LEJ/i50fvvtN77//nsqVqxodaQCERwczC+//JLt+OTj48OoUaNYvXq11fHyjaurK82aNbOb45MUDtVrtkX1mu0dD+2xVgPVa7ZI9Vrej0+6rC8fjRw5kuDgYAICAggMDGTOnDnExsYSEhJidbR88/zzz7N48WK++uorSpcundXdL1u2LCVKlLA4Xf4pXbp0jnEZPDw8qFixos2N1zBixAiaN2/OlClT6N69O1u3bmXOnDnMmTPH6mj57tFHH2Xy5Mn4+vpyxx13sGPHDmbMmEG/fv2sjvavJCUl8fvvv2d9f+jQIXbu3EmFChXw9fVl+PDhTJkyhdq1a1O7dm2mTJlCyZIl6d27t4Wp8+56++nj40O3bt3Yvn07X3/9NRkZGVnHpwoVKuDq6mpV7Ftyo5/pPws5FxcXvL29qVu3bmFH/VdutJ+jRo2iR48etGrVijZt2vDdd9+xcuVKoqKirAstxZ7qNdVrxZG91Gu2WquB6jXVa6rXbuhfz/cn2bz//vtG9erVDVdXV+Ouu+6yuSl7gVxvH374odXRCpytTk1sGIaxcuVKo2HDhoabm5tRr149Y86cOVZHKhDnz583hg0bZvj6+hru7u5GzZo1jfHjxxupqalWR/tX1q1bl+v/y6eeesowDHN64okTJxre3t6Gm5ub0apVK+PXX3+1NvQtuN5+Hjp06JrHp3Xr1lkdPc9u9DP9p+I6NfHN7Gd4eLhx++23G+7u7kbjxo2N5cuXWxdYbIbqNduleq14s9VazTBUr6leU712Iw6GYRh5a2eJiIiIiIiIiIjkD405JSIiIiIiIiIillFzSkRERERERERELKPmlIiIiIiIiIiIWEbNKRERERERERERsYyaUyIiIiIiIiIiYhk1p0RERERERERExDJqTomIiIiIiIiIiGXUnBIREREREREREcuoOSUiIiIiIiIiIpZRc0pERERERERERCyj5pSIiIiIiIiIiFjm/wEQlDvJOgiVrgAAAABJRU5ErkJggg==",
- "text/plain": [
- "
"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Fp89L1E4DCXe"
+ },
+ "source": [
+ "# Measuring bias with Optimal Transport by calculating the Wasserstein distance"
]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "from scipy.interpolate import make_interp_spline\n",
- "\n",
- "# Drawing both of them\n",
- "figure, axis = plt.subplots(1, 2)\n",
- "figure.set_figheight(4)\n",
- "figure.set_figwidth(12)\n",
- "figure.tight_layout(w_pad = 5)\n",
- "\n",
- "def draw(y, id):\n",
- " x = np.array(range(0, np.size(y)))\n",
- " XYSpline = make_interp_spline(x, y) \n",
- " X = np.linspace(x.min(), x.max(), 500)\n",
- " Y = XYSpline(X)\n",
- " axis[id].bar(x, y, color=\"lightgreen\", ec='black')\n",
- " axis[id].scatter(x, y, color=\"orange\")\n",
- " axis[id].plot(X, Y, color='blue')\n",
- "\n",
- "axis[0].title.set_text(\"Initial distribution\")\n",
- "axis[1].title.set_text(\"Required distribution\")\n",
- "draw(a, 0)\n",
- "draw(b, 1)\n",
- "\n",
- "plt.show()"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In order to better understand how Optimal Transport works, below is presented the code considering the case when the matrix cost distance is presented and defined as the absolute difference between positions of each part of the distribution. That is $\\text{distance}[i][j] = abs(i - j)$."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pandas as pd\n",
- "\n",
- "_a = pd.Series(a)\n",
- "_b = pd.Series(b)\n",
- "distance = np.zeros((np.size(a), np.size(b)))\n",
- "for i in range(np.size(a)):\n",
- " for j in range(np.size(b)):\n",
- " distance[i][j] = abs(i - j)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Wasserstein distance is equal to 1.3773703499999999.\n"
- ]
- }
- ],
- "source": [
- "from aif360.sklearn.metrics import ot_distance\n",
- "c0 = ot_distance(y_true=_a, y_pred=_b, cost_matrix=distance, mode='continuous')\n",
- "\n",
- "print(\"Wasserstein distance is equal to \", c0, \".\", sep=\"\")"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 2. Randomly distributed samples\n",
- "\n",
- "Suppose we have two distributions $a$ and $b$ with length $N$, that are generated randomly, and we need to calculate earth_movers_distance for them."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy as np\n",
- "\n",
- "N = 1000\n",
- "np.random.seed(seed=1)\n",
- "\n",
- "# Initial distribution\n",
- "a = np.random.rand(N)\n",
- "a /= np.sum(a)\n",
- "\n",
- "# Required distribution\n",
- "b = np.random.rand(N)\n",
- "b /= np.sum(b)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "1Fk66iQtDCXg"
+ },
+ "source": [
+ "## Table of contents\n",
+ "\n",
+ "- Introduction\n",
+ "- General Optimal Transport examples\n",
+ "- Usage\n",
+ "- Application to Compas Dataset\n",
+ "- Application to Adult Dataset\n",
+ "- More details\n",
+ " - OT for mapping estimation\n",
+ " - Kantorovich optimal transport problem\n",
+ " - Solving optimal transport\n",
+ " - Necessity and priority of usage"
+ ]
+ },
{
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAABeAAAAGUCAYAAABdvoLAAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAABPVUlEQVR4nO3dfZhVZb0//vfIwwCeEQFzBhKU+kKWmA9oFJZgPJj5GJUZktqxwoN2JDWSTB08CUVFnDQ1rYQyss5Ry04nE8Owwn4BPpSAWicSKkfCLwLKyOP6/cGXfRgBZWA2MzCv13XtS/da917rs242a6/15t73riiKoggAAAAAANCk9mvuAgAAAAAAYF8kgAcAAAAAgDIQwAMAAAAAQBkI4AEAAAAAoAwE8AAAAAAAUAYCeAAAAAAAKAMBPAAAAAAAlIEAHgAAAAAAykAADwAAAAAAZSCAB/Y606ZNS0VFRebNm7dLr6+oqEhtbW3p+cKFC1NbW5u//OUv27S94IILcthhh+3SfnbntUkyePDgDB48uPT8L3/5SyoqKjJt2rRGbWfGjBmZOnVqo16zvX3V1tamoqIiy5cvb9S2Xk25+h4AgJZry/X8lkfbtm3TvXv3nHPOOfnjH//Y3OWV/PKXv0xFRUV++ctf7pH9HXbYYbngggt26bWvdv3eGGvWrEltbW2jj3l7+zrssMNy2mmnNWo7r+XV7m1eeZ8H0FII4IFW5+GHH87HPvax0vOFCxdmwoQJ2w2Br7766txzzz17sLod6969ex5++OGceuqpjXrdrgTwu7qvxtpb+h4AgKZ3++235+GHH84DDzyQSy65JPfee2/e+c53ZsWKFc1dWpLk2GOPzcMPP5xjjz22uUvZJR/72Mfy8MMPN+o1a9asyYQJExodwO/KvnbFq93bvPI+D6ClaNvcBQDsaW9/+9t3uu0b3/jGMlbSOJWVlY2qfVds3LgxGzZs2CP7ei0tqe8BAGh6/fr1y3HHHZdk87c/N27cmGuvvTY/+tGP8tGPfrSZq0sOOOCAnbomXrNmTTp16rQHKmqcQw45JIccckhZ97Hl2PfEvl5Lc9+/AOyIEfDAPuGCCy7IP/3TP+VPf/pT3vve9+af/umf0rNnz1x++eVZu3Ztg7ZbfzVx2rRp+eAHP5gkOemkk0pfg93y1c3tTYPy9a9/PSeeeGIOPvjg7L///jnyyCMzefLkrF+/fpdqL4oikydPzqGHHpoOHTrk2GOPzc9+9rNt2m3va6X/+Mc/8olPfCI9e/ZMZWVlXve61+WEE07IAw88kGTzjcxPf/rTPPPMMw2+5rv19iZPnpzPf/7z6d27dyorK/Pggw++6nQ3S5cuzYgRI3LAAQekc+fOGTVqVP7xj380aLOjr39u/bXaXen7l19+OePHj0/v3r3Tvn37vP71r8/FF1+cF154YZv9nHbaabnvvvty7LHHpmPHjjn88MPz7W9/ewd/CgAANLctYfxzzz3XYPm8efNyxhlnpGvXrunQoUOOOeaY/PCHP9zm9b/97W9zwgknpEOHDunRo0fGjx+f2267LRUVFQ2+cbkz16rJ9qeg2XLf8Yc//CHDhw9PVVVVhgwZkiRZt25dPv/5z+fwww8vXZt/9KMf3eZaef369Rk3blxqamrSqVOnvPOd78zvfve7ne6nv//97zn77LNTVVWVzp0750Mf+lDq6uq2abe9aWFmzZqVwYMHp1u3bunYsWN69eqV97///VmzZk3+8pe/5HWve12SZMKECaXr8y19smV7jzzySD7wgQ+kS5cupUEzrzbdzT333JO3vvWt6dChQ97whjfka1/7WoP1W6YkeuW3Yl/Z/692b5Ns/8/1iSeeyJlnnpkuXbqkQ4cOOfroozN9+vTt7uf73/9+rrrqqvTo0SMHHHBAhg4dmqeeemq7xwTQGEbAA/uM9evX54wzzsiFF16Yyy+/PA899FD+7d/+LZ07d84111yz3deceuqpmThxYj772c/m61//eunrpa82+vp//ud/MnLkyFII/Pjjj+f666/Pk08+uUsB74QJEzJhwoRceOGF+cAHPpClS5fm4x//eDZu3Jg3velNr/raj3zkI3nkkUdy/fXXp2/fvnnhhRfyyCOP5Pnnn0+S3HTTTfnEJz6R//mf/9nhdC5f+9rX0rdv33z5y1/OAQcckD59+rzqPt/3vvfl7LPPzkUXXZQFCxbk6quvzsKFC/P//X//X9q1a7fTx93Yvi+KImeddVZ+8YtfZPz48XnXu96V3//+97n22mvz8MMP5+GHH05lZWWp/eOPP57LL788V155Zaqrq/PNb34zF154Yf7P//k/OfHEE3e6TgAA9ozFixcnSfr27Vta9uCDD+Y973lPBgwYkFtuuSWdO3fOnXfemQ996ENZs2ZNKRxeuHBhhgwZksMOOyzTpk1Lp06dctNNN2XGjBlNXue6detyxhlnZPTo0bnyyiuzYcOGbNq0KWeeeWZ+9atfZdy4cRk4cGCeeeaZXHvttRk8eHDmzZuXjh07Jkk+/vGP5zvf+U6uuOKKDBs2LE888URGjBiR1atXv+a+6+vrM3To0Pz973/PpEmT0rdv3/z0pz/Nhz70odd87V/+8peceuqpede73pVvf/vbOfDAA/O3v/0t9913X9atW5fu3bvnvvvuy3ve855ceOGFpelctoTyW4wYMSLnnHNOLrroorz00kuvus/HHnssY8eOTW1tbWpqavK9730vl156adatW5crrrjiNWve2s7c22ztqaeeysCBA3PwwQfna1/7Wrp165Y77rgjF1xwQZ577rmMGzeuQfvPfvazOeGEE/LNb34zq1atymc+85mcfvrpWbRoUdq0adOoWgG2JoAH9hnr1q3LhAkTSqOqhwwZknnz5mXGjBk7DOBf97rXlQLnt7zlLTv1tcUpU6aU/n/Tpk1517velW7duuWjH/1ovvKVr6RLly47XfMLL7yQL37xi3nf+96Xb37zm6XlRxxxRE444YTXDOB/85vf5GMf+1g+/vGPl5adeeaZpf9/y1vekgMPPPBVp5Tp0KFDfv7znzcIz7c3J/sWI0aMyOTJk5Mkw4cPT3V1dc4999z88Ic/zLnnnvuq9W6tsX1///335+c//3kmT56cT3/600mSYcOGpWfPnvnQhz6U73znOw36Yfny5fnNb36TXr16JUlOPPHE/OIXv8iMGTME8AAALcCW6Q9ffvnl/OY3v8nnP//5nHjiiTnjjDNKbcaMGZMjjjgis2bNStu2myOMk08+OcuXL89nP/vZnHfeedlvv/1y3XXXpSiKzJo1K9XV1Uk2D/jo169fk9e9fv36XHPNNQ2mybnzzjtz33335a677sqIESNKy4866qgcf/zxmTZtWv7lX/4lTz75ZKZPn55PfepTpWvqYcOGla6pX8v06dOzaNGi/PjHPy710/Dhw1NfX5/bbrvtVV87f/78vPzyy/nSl76Uo446qrR85MiRpf/v379/ks3T1+zo+vz888/PhAkTXrPWZPNo/UcffbS0v1NOOSXLli3Lv/3bv2XMmDGNmrpnZ+5ttlZbW5t169blwQcfTM+ePZMk733ve/PCCy9kwoQJGT16dDp37txg+3fccUfpeZs2bXL22Wdn7ty5prcBdospaIB9RkVFRU4//fQGy9761rfmmWeeadL9PProoznjjDPSrVu3tGnTJu3atct5552XjRs35umnn27Uth5++OG8/PLL21xsDxw4MIceeuhrvv5tb3tbpk2bls9//vP57W9/u0vT4JxxxhmNGrn+ylrPPvvstG3bNg8++GCj990Ys2bNSpIGXwtOkg9+8IPZf//984tf/KLB8qOPProUvieb/6Ghb9++Tf5+AABg17z97W9Pu3btUlVVlfe85z3p0qVLfvzjH5eC9j/96U958sknS9efGzZsKD3e+9735tlnny1NEfLggw9myJAhpfA92Ryg7szI8F3x/ve/v8Hz//qv/8qBBx6Y008/vUGdRx99dGpqakrTqGy5Zt7RNfVrefDBB1NVVdXgHymShiH6jhx99NFp3759PvGJT2T69On585///Jqv2Z5XHvurOeKIIxqE/cnmWletWpVHHnlkl/a/s2bNmpUhQ4aUwvctLrjggqxZs2abH419ZZ++9a1vTRL3D8BuE8AD+4xOnTqlQ4cODZZVVlbm5ZdfbrJ9LFmyJO9617vyt7/9Lf/+7/+eX/3qV5k7d26+/vWvJ9n8ldDG2DJVTE1NzTbrtrfslX7wgx/k/PPPzze/+c284x3vSNeuXXPeeedtdw7IHenevfvOF7ydutq2bZtu3bqVjqVcnn/++bRt23abr8BWVFSkpqZmm/1369Ztm21UVlY2+s8IAIDy+M53vpO5c+dm1qxZGT16dBYtWpQPf/jDpfVb5oK/4oor0q5duwaPMWPGJNn8rcdk87Xirl5TN1anTp1ywAEHNFj23HPP5YUXXkj79u23qbWurq5Bndura8s19Wt5/vnnG/wjwxY7c5xvfOMb88ADD+Tggw/OxRdfnDe+8Y154xvfmH//939/zddurTH3D6/2Z7In7h+2V2uPHj22u/9X9v+W6S3dPwC7yxQ0AI3wox/9KC+99FLuvvvuBiPUH3vssV3a3paLvO0F5nV1ddv8COkrHXTQQZk6dWqmTp2aJUuW5N57782VV16ZZcuW5b777tupGnb0Y0k7UldXl9e//vWl5xs2bMjzzz/f4IK1srJymx+/TXbvIrtbt27ZsGFD/vGPfzQI4YuiSF1dXY4//vhd3jYAAHvem9/85tIPr5500knZuHFjvvnNb+Y///M/84EPfCAHHXRQkmT8+PENpnXZ2pYpG7t167bDa+pX2t1r1e1dPx900EHp1q3bDq/Bq6qqSnVuqWt719SvpVu3btv9wdadHYDzrne9K+9617uycePGzJs3LzfccEPGjh2b6urqnHPOOTu1jcbcP7zan8mWvtgyiOqVfyZb/tFiV3Xr1i3PPvvsNsv//ve/J0np/QVQbkbAA61eY0Y2bLnY3PrHPouieM35Fnfk7W9/ezp06JDvfe97DZbPmTOn0V917NWrVy655JIMGzaswdc5m3rU9ytr/eEPf5gNGzZk8ODBpWWHHXZYfv/73zdoN2vWrLz44osNljWm74cMGZIkDeZlTJK77rorL730Umk9AAB7p8mTJ6dLly655pprsmnTprzpTW9Knz598vjjj+e4447b7mNLsH3SSSflF7/4RWnUfLJ5jvkf/OAH2+xnZ69VG+O0007L888/n40bN263zi3/ULDlmnlH19Sv5aSTTsrq1atz7733Nlje2B+bbdOmTQYMGFD6Ju+W+4emHvW9YMGCPP744w2WzZgxI1VVVTn22GOTpDTo6JV/Jq88xi317WxtQ4YMyaxZs0qB+xbf+c530qlTJ/O6A3uMEfBAq7flh5luvfXWVFVVpUOHDundu/d2vwI6bNiwtG/fPh/+8Iczbty4vPzyy7n55puzYsWKXdp3ly5dcsUVV+Tzn/98Pvaxj+WDH/xgli5dmtra2tf8GunKlStz0kknZeTIkTn88MNTVVWVuXPn5r777mswQujII4/M3XffnZtvvjn9+/fPfvvtVxpptCvuvvvutG3bNsOGDcuCBQty9dVX56ijjsrZZ59davORj3wkV199da655poMGjQoCxcuzI033tjgR46Sxvf9ySefnM985jNZtWpVTjjhhPz+97/Ptddem2OOOSYf+chHdvmYAABofl26dMn48eMzbty4zJgxI6NGjco3vvGNnHLKKTn55JNzwQUX5PWvf33+7//9v1m0aFEeeeSR/Md//EeS5HOf+1zuvffevPvd784111yTTp065etf/3peeumlbfazs9eqjXHOOefke9/7Xt773vfm0ksvzdve9ra0a9cuf/3rX/Pggw/mzDPPzPve9768+c1vzqhRozJ16tS0a9cuQ4cOzRNPPJEvf/nL20xrsz3nnXdevvrVr+a8887L9ddfnz59+uS///u/8/Of//w1X3vLLbdk1qxZOfXUU9OrV6+8/PLL+fa3v50kGTp0aJLNI/UPPfTQ/PjHP86QIUPStWvXHHTQQa/5zdwd6dGjR84444zU1tame/fuueOOOzJz5sx88YtfLP0A6/HHH583velNueKKK7Jhw4Z06dIl99xzT379619vs73G3Ntce+21+a//+q+cdNJJueaaa9K1a9d873vfy09/+tNMnjx5t/68ARrDCHig1evdu3emTp2axx9/PIMHD87xxx+fn/zkJ9tte/jhh+euu+7KihUrMmLEiHzyk5/M0Ucfna997Wu7vP/rrrsukyZNyv33358zzjgjN9xwQ2655ZbSKJkd6dChQwYMGJDvfve7Offcc3PKKafkm9/8Zj7zmc80GJF/6aWX5gMf+EA++9nP5u1vf/tuT9Vy991358knn8yIESNyzTXX5PTTT8/999+f9u3bl9p8+tOfzqc//elMmzYtp59+eu6666788Ic/zIEHHthgW43p+4qKivzoRz/KZZddlttvvz3vfe978+Uvfzkf+chHMmvWrAbfSgAAYO/0yU9+Mr169cp1112XjRs35qSTTsrvfve7HHjggRk7dmyGDh2af/mXf8kDDzxQCo2TzQM7HnjggRxwwAE5//zz84lPfCJvfetbc/XVV2+zj529Vm2MNm3a5N57781nP/vZ3H333Xnf+96Xs846K1/4whfSoUOHHHnkkaW23/rWt3LZZZdl2rRpOeOMM/LDH/4wd911V7p06fKa++nUqVNmzZqVoUOH5sorr8wHPvCB/PWvf82dd975mq89+uijs2HDhlx77bU55ZRT8pGPfCT/+Mc/cu+992b48OEN6uvUqVPOOOOMHH/88amtrd2lPtmyzylTpuQrX/lKzjzzzPzmN7/JlClTMm7cuFKbNm3a5Cc/+UkOP/zwXHTRRTnvvPNSWVmZG2+8cZvtNebe5k1velPmzJmTN73pTbn44otz1lln5Yknnsjtt9+eT3/607t8TACNVVEURdHcRQAAAAA0tWnTpuWjH/1oFi9evMujuAFgdxgBDwAAAAAAZSCABwAAAACAMjAFDQAAAAAAlIER8AAAAAAAUAYCeAAAAAAAKAMBPAAAAAAAlEHb5i5gV2zatCl///vfU1VVlYqKiuYuBwCAvURRFFm9enV69OiR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0CH9+/fPr371q1dtP3v27PTv3z8dOnTIG97whtxyyy3btLnrrrvylre8JZWVlXnLW96Se+65p9H7LYoitbW16dGjRzp27JjBgwdnwYIFDdqsXbs2n/zkJ3PQQQdl//33zxlnnJG//vWvu9ALO6cl9tX69evzmc98JkceeWT233//9OjRI+edd17+/ve/N9jG4MGDU1FR0eBxzjnn7GJPvLaW2FdJcsEFF2zTD29/+9sbtPG+2uyV/bTl8aUvfanUpjW8rx566KGcfvrp6dGjRyoqKvKjH/1om204X232Wn3lfPW/duZ95Xy12c70lfPVZpMmTcrxxx+fqqqqHHzwwTnrrLPy1FNPNWjTEs9XNJ+Weg3Skt+nLbHPWurn687Wvz2t+d5gZ2t/pdZ8j9CY+l+pNd8vbNES+8x5bd+6h9haS+23lnxua/X3FcU+4M477yzatWtX3HbbbcXChQuLSy+9tNh///2LZ555Zrvt//znPxedOnUqLr300mLhwoXFbbfdVrRr1674z//8z1KbOXPmFG3atCkmTpxYLFq0qJg4cWLRtm3b4re//W2j9vuFL3yhqKqqKu66667iD3/4Q/GhD32o6N69e7Fq1apSm4suuqh4/etfX8ycObN45JFHipNOOqk46qijig0bNrSavnrhhReKoUOHFj/4wQ+KJ598snj44YeLAQMGFP37929Qz6BBg4qPf/zjxbPPPlt6vPDCC03eTztT8yvtyffV+eefX7znPe9p0A/PP/98g3q8rzbbuo+effbZ4tvf/nZRUVFR/M///E+pTWt4X/33f/93cdVVVxV33XVXkaS45557ttmX89Vmr9VXzleNe185X222M33lfLXZySefXNx+++3FE088UTz22GPFqaeeWvTq1at48cUXS21a2vmK5tOSr0Fa6vu0pfZZS/x83VpL7beiaHmftY2pfWut+R5hay35WsV5be+/b9haS+23omi557WiaNn91lLPbe4rimKfCODf9ra3FRdddFGDZYcffnhx5ZVXbrf9uHHjisMPP7zBstGjRxdvf/vbS8/PPvvs4j3veU+DNieffHJxzjnn7PR+N23aVNTU1BRf+MIXSutffvnlonPnzsUtt9xSFMXmE3K7du2KO++8s9Tmb3/7W7HffvsV991332see2O11L7ant/97ndFkgZ/IQcNGlRceumlO3xNU2rJfXX++ecXZ5555g5r977a8fvqzDPPLN797nc3WNYa3ldb296HtfPVzvfV9rTW89XWXu3i2fmqoZ19XzlfbbZs2bIiSTF79uyiKFrm+Yrm01KvQVry+7Sl9tn2NPfn69Zacr+1tM/aLVpyn71Sc3/mbq0lfP7uLfcLW7TUPtse57WG9pZ7iK215H57pZZybmsJfVYUzXtfsddPQbNu3brMnz8/w4cPb7B8+PDhmTNnznZf8/DDD2/T/uSTT868efOyfv36V22zZZs7s9/Fixenrq6uQZvKysoMGjSo1Gb+/PlZv359gzY9evRIv379dlj/rmrJfbU9K1euTEVFRQ488MAGy7/3ve/loIMOyhFHHJErrrgiq1ev3vFB76K9oa9++ctf5uCDD07fvn3z8Y9/PMuWLSut877a/n6fe+65/PSnP82FF164zbp9+X21M5yvdq/+1ni+aozWfr7aFc5X/2vlypVJkq5duyZpeecrmk9LvgZpqe/Tltxn29Ocn69b2xv6raV81m6xN/TZFs39mbu1lvT5+0rOa3v/fcPW9oZ+a2nntWTv6LctWsq5rSX1WXPeV7Td6ZYt1PLly7Nx48ZUV1c3WF5dXZ26urrtvqaurm677Tds2JDly5ene/fuO2yzZZs7s98t/91em2eeeabUpn379unSpctO17+rWnJfvdLLL7+cK6+8MiNHjswBBxxQWn7uueemd+/eqampyRNPPJHx48fn8ccfz8yZM3euE3ZSS++rU045JR/84Adz6KGHZvHixbn66qvz7ne/O/Pnz09lZaX31Q72O3369FRVVWXEiBENlu/r76ud4Xy16/W31vPVznK+2jXOV5sVRZHLLrss73znO9OvX7/Sfra87pXbaY7zFc2nJV+DtNT3aUvus1dq7s/XrbX0fmtJn7VbtPQ+21pzf+ZuraV8/u5oP1te98rttMbz2q5wXtt5LfG8lrT8fttaSzm3tZQ+a+77ir0+gN+ioqKiwfOiKLZZ9lrtX7l8Z7bZVG1eaWfa7KqW3FfJ5h8qOeecc7Jp06bcdNNNDdZ9/OMfL/1/v3790qdPnxx33HF55JFHcuyxx+7wGHZVS+2rD33oQ6X/79evX4477rgceuih+elPf7rNybUx9e+OltpXW/v2t7+dc889Nx06dGiwvLW8r8pR2+7sqxz17Mm+Spyvdobz1a5xvtrskksuye9///v8+te/3u3adrYNe5+WfA3SUt+nLbnPkpb1+bq1ltpvLfGzdouW2mdbaymfuVtr7s/fpqxtd/bVGC25zxLntX3hHmJrLbXfttbSzm3N3WfNfV+x109Bc9BBB6VNmzbb/KvDsmXLtvkXjC1qamq2275t27bp1q3bq7bZss2d2W9NTU2SvGabdevWZcWKFTtd/65qyX21xfr163P22Wdn8eLFmTlzZoN/Fd6eY489Nu3atcsf//jHV23XWHtDX22te/fuOfTQQ0v94H217X5/9atf5amnnsrHPvax1zymfe19tTOcrxpff2s/X+2q1ni+aiznq80++clP5t57782DDz6YQw45pMF+kpZzvqL5tORrkJb6Pm3JfbZFS/l83dre0G9ba87P2i32lj5rCZ+5W2sJn7874ry29983bG1v6LettYTzWrL39FtLOre1hD5rCfcVe30A3759+/Tv33+br0rMnDkzAwcO3O5r3vGOd2zT/v77789xxx2Xdu3avWqbLdvcmf1u+UrH1m3WrVuX2bNnl9r0798/7dq1a9Dm2WefzRNPPLHD+ndVS+6r5H8/lP74xz/mgQceKP2lejULFizI+vXr071799ds2xgtva9e6fnnn8/SpUtL/eB9te1+v/Wtb6V///456qijXvOY9rX31c5wvmpc/c5Xu641nq8aq7Wfr4qiyCWXXJK77747s2bNSu/evRu0b2nnK5pPS74Gaanv05bcZ0nL+nzdWkvvt1dqzs/aLfaWPmsJn7lba8nXKs5re/99w9Zaer+9Uks4ryV7T7+1pHOb+4r/LWavd+eddxbt2rUrvvWtbxULFy4sxo4dW+y///7FX/7yl6IoiuLKK68sPvKRj5Ta//nPfy46depUfOpTnyoWLlxYfOtb3yratWtX/Od//mepzW9+85uiTZs2xRe+8IVi0aJFxRe+8IWibdu2xW9/+9ud3m9RFMUXvvCFonPnzsXdd99d/OEPfyg+/OEPF927dy9WrVpVanPRRRcVhxxySPHAAw8UjzzySPHud7+7OOqoo4oNGza0mr5av359ccYZZxSHHHJI8dhjjxXPPvts6bF27dqiKIriT3/6UzFhwoRi7ty5xeLFi4uf/vSnxeGHH14cc8wxraqvVq9eXVx++eXFnDlzisWLFxcPPvhg8Y53vKN4/etf7321nb+DRVEUK1euLDp16lTcfPPN29TeWt5Xq1evLh599NHi0UcfLZIUU6ZMKR599NHimWeeKbVxvtq5vnK+2vm+cr5q3N/BonC+Koqi+Jd/+Zeic+fOxS9/+csGf7/WrFlTatPSzlc0n5Z8DdJS36cttc9a4ufr3tBvLfGztqX32RYt6TN3ay35WsV5be+/b9gb+q0ln9dacr9t0RLPbe4rimKfCOCLoii+/vWvF4ceemjRvn374thjjy1mz55dWnf++ecXgwYNatD+l7/8ZXHMMccU7du3Lw477LDtvjH/4z/+o3jTm95UtGvXrjj88MOLu+66q1H7LYqi2LRpU3HttdcWNTU1RWVlZXHiiScWf/jDHxq0qa+vLy655JKia9euRceOHYvTTjutWLJkyW70xqtriX21ePHiIsl2Hw8++GBRFEWxZMmS4sQTTyy6du1atG/fvnjjG99Y/Ou//mvx/PPPN03HbEdL7Ks1a9YUw4cPL173utcV7dq1K3r16lWcf/7527xnvK/+1ze+8Y2iY8eOxQsvvLDNutbyvnrwwQe3+/fr/PPPL7VxvtrstfrK+ep/vVZfOV/9r535O1gUzldFUezw79ftt99eatMSz1c0n5Z6DdKS36ctsc9a6ufrztbv3mD7WmKfbdHSPnO31lKvVZzX9o37hq21xH5r6ee1omiZ/bZFSz23tfb7ior/VxAAAAAAANCE9vo54AEAAAAAoCUSwAMAAAAAQBkI4AEAAAAAoAwE8AAAAAAAUAYCeAAAAAAAKAMBPAAAAAAAlIEAHgAAAAAAykAADwAAAAAAZSCABwAAAACAMhDAAwAAAABAGQjgAQAAAACgDP5/Z7Rf8h6lGGIAAAAASUVORK5CYII=",
- "text/plain": [
- "
"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "l6BtF35fDCXg"
+ },
+ "source": [
+ "## References\n",
+ "\n",
+ "\"FlipTest: fairness testing via optimal transport\" https://dl.acm.org/doi/abs/10.1145/3351095.3372845\n",
+ "\n",
+ "\"Obtaining Fairness using Optimal Transport Theory\" http://proceedings.mlr.press/v97/gordaliza19a.html\n",
+ "\n",
+ "\"Computational Optimal Transport\" https://arxiv.org/abs/1803.00567\n",
+ "\n",
+ "\"POT: Python Optimal Transport\" https://jmlr.org/papers/v22/20-451.html"
]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "\n",
- "# Drawing both of them\n",
- "figure, axis = plt.subplots(1, 2)\n",
- "figure.set_figheight(4)\n",
- "figure.set_figwidth(15)\n",
- "figure.tight_layout(w_pad = 5)\n",
- "\n",
- "def draw(y, id):\n",
- " axis[id].hist(y, color='lightgreen', ec='black', bins=10)\n",
- "\n",
- "axis[0].title.set_text(\"Initial distribution\")\n",
- "axis[1].title.set_text(\"Required distribution\")\n",
- "draw(a, 0)\n",
- "draw(b, 1)\n",
- "\n",
- "plt.show()"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In this case the Wasserstein distance tends to zero as the size of the samples increase."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "Wasserstein distance is: 2.003382269162742e-05.\n"
- ]
- }
- ],
- "source": [
- "import pandas as pd\n",
- "from aif360.sklearn.metrics import ot_distance\n",
- "\n",
- "_a = pd.Series(a)\n",
- "_b = pd.Series(b)\n",
- "c = ot_distance(y_true=_a, y_pred=_b, mode='continuous')\n",
- "\n",
- "print(\"Wasserstein distance is: \", c, \".\", sep=\"\")"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 3. Permutations\n",
- "\n",
- "Another example that shows clearly what the permutations in the first formula refer to is the one presented below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy as np\n",
- "\n",
- "# Initial distribution\n",
- "a = np.array([0., 0.1, 0.1, 0.1, 0.08, 0., 0.1, 0.1, 0.08, 0.08, 0., 0.1, 0.08, 0.08, 0.08, 0.])\n",
- "# Required distribution\n",
- "b = np.array([0., 0.08, 0.08, 0.08, 0.1, 0., 0.08, 0.08, 0.1, 0.1, 0., 0.08, 0.1, 0.1, 0.1, 0.])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "iy6Q5v0gDCXg"
+ },
+ "source": [
+ "## Introduction\n",
+ "\n",
+ "Optimal Transport (OT) is a field of mathematics which studies the geometry of probability spaces. Among its many contributions, OT provides a principled way to compare and align probability distributions by taking into account the underlying geometry of the\n",
+ "considered metric space.\n",
+ "\n",
+ "Optimal Transport (OT) is a mathematical problem that was first introduced by Gaspard Monge in 1781. It addresses the task of determining the most efficient method for transporting mass from one distribution to another. In this problem, the cost associated with moving a unit of mass from one position to another is referred to as the ground cost. The primary objective of OT is to minimize the total cost incurred when moving one mass distribution onto another. The optimization problem can be expressed for two distributions $\\mu_s$ and $\\mu_t$ as\n",
+ "\n",
+ "$$\n",
+ "\\min_{m, m_{\\#} \\mu_s=\\mu_t} \\int c(x, m(x)) d \\mu_s(x)\n",
+ "$$\n",
+ "in the continuous case, and\n",
+ "$$\n",
+ "\\min_{\\sigma \\in \\text{Perm}(n)} \\frac{1}{n} \\sum_{i=1}^n \\textbf{C}_{i,\\sigma(i)}\n",
+ "$$\n",
+ "in the discrete case, where $\\textbf{C}_{\\cdot, \\cdot}$ is the ground cost and the constraint $m_{\\#} \\mu_s=\\mu_t$ ensures that $\\mu_s$ is completely transported to $\\mu_t$. Where $T_{\\#} \\mu_s = \\mu_s(T^{-1}(B)) = u_{t}(B)$ with $T$ as a trasportation matrix between $\\mu_s$ and $\\mu_t$ at point $B$."
+ ]
+ },
{
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAABKcAAAGUCAYAAADgVkHZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAABEFElEQVR4nO3deXxTVf7/8XftkoBCgVa6MG0pDEsVWSwqW0FGKQKCCwjICC6gdsAvtBUFBCw4AiPDMB1GoF82GR9uOIKKIyJFlsGhLqxuKKMCRaRCcSlr1/v7g2/zMySFpqScNnk9H488tCfn5p6Te0g+j3ducgMsy7IEAAAAAAAAGHCZ6QEAAAAAAADAfxFOAQAAAAAAwBjCKQAAAAAAABhDOAUAAAAAAABjCKcAAAAAAABgDOEUAAAAAAAAjCGcAgAAAAAAgDGEUwAAAAAAADCGcAoAAAAAAADGEE4BNdjy5csVEBCgbdu2VWn7gIAATZs2zfH3F198oWnTpmn//v0ufe+77z41bdq0Svu5mG0l6cYbb9SNN97o+Hv//v0KCAjQ8uXLPXqcl156SZmZmR5t425f06ZNU0BAgPLz8z16rPOpruceAAB4R3ndVX4LCgpSVFSUhg4dqv/+97+mh+ewadMmBQQEaNOmTZdkf02bNtV9991XpW3PV2d54tSpU5o2bZrHc3a3r6ZNm+rWW2/16HEu5Hw16Ln1OAD3CKcAH5aTk6NRo0Y5/v7iiy80ffp0twHJ1KlT9frrr1/C0VUsKipKOTk56tevn0fbVSWcquq+PFVbnnsAAPzdc889p5ycHK1fv16PPPKIVq9erW7duumnn34yPTRJ0rXXXqucnBxde+21podSJaNGjVJOTo5H25w6dUrTp0/3OJyqyr6q4nw16Ln1OAD3gkwPAED16dSpU6X7Nm/evBpH4hmbzebR2KuitLRUJSUll2RfF1KTnnsAAPxdmzZt1LFjR0lnz+4uLS1VRkaG3njjDd1///2GRyfVr1+/UrXLqVOnVLdu3UswIs/85je/0W9+85tq3Uf53C/Fvi7EdJ0J1BacOQXUMvfdd5+uuOIKff311+rbt6+uuOIKxcTE6NFHH1VhYaFT31+fRrx8+XLdddddkqSePXs6TlkvP83a3VfL5s+fr+7du6tx48a6/PLLdc0112j27NkqLi6u0tgty9Ls2bMVFxcnu92ua6+9Vu+8845LP3engB89elQPPfSQYmJiZLPZdOWVV6pr165av369pLPF49tvv60DBw44nZL/68ebPXu2nn76acXHx8tms2njxo3n/QrhwYMHdeedd6p+/foKDQ3VPffco6NHjzr1qehU7V+fAl+V5/7MmTOaNGmS4uPjFRISoiZNmmjMmDH6+eefXfZz6623au3atbr22mtVp04dtW7dWsuWLavgKAAAAE+UB1U//PCDU/u2bds0YMAANWrUSHa7XR06dNCrr77qsv0HH3ygrl27ym63Kzo6WpMmTdLixYsVEBDgdEZ1ZWoKyf3X+srrw08//VTJycmqV6+ebrrpJklSUVGRnn76abVu3dpRQ91///0uNU1xcbEef/xxRUZGqm7duurWrZs++uijSj9P33//vQYPHqx69eopNDRUQ4YMUV5enks/d1+127Bhg2688UaFhYWpTp06io2N1cCBA3Xq1Cnt379fV155pSRp+vTpjjqq/Dkpf7wdO3Zo0KBBatiwoeODv/N9hfD1119X27ZtZbfb1axZM82bN8/p/vKveZ571vu5z//5alDJ/XH97LPPdNttt6lhw4ay2+1q3769/vGPf7jdz8svv6zJkycrOjpa9evX180336yvvvrK7ZyA2owzp4BaqLi4WAMGDNDIkSP16KOP6t///rf++Mc/KjQ0VE8++aTbbfr166eZM2fqiSee0Pz58x2ngp/vrJ1vvvlGw4YNcwQku3fv1owZM/Tll19WKfyYPn26pk+frpEjR2rQoEE6ePCgHnzwQZWWlqpVq1bn3Xb48OHasWOHZsyYoZYtW+rnn3/Wjh07dOzYMUnSggUL9NBDD+mbb76p8Cty8+bNU8uWLTVnzhzVr19fLVq0OO8+77jjDg0ePFgpKSn6/PPPNXXqVH3xxRf68MMPFRwcXOl5e/rcW5al22+/Xe+9954mTZqkpKQkffLJJ8rIyFBOTo5ycnJks9kc/Xfv3q1HH31UEydOVEREhJYsWaKRI0fqt7/9rbp3717pcQIAAFf79u2TJLVs2dLRtnHjRt1yyy264YYblJWVpdDQUL3yyisaMmSITp065QhOvvjiC910001q2rSpli9frrp162rBggV66aWXvD7OoqIiDRgwQA8//LAmTpyokpISlZWV6bbbbtOWLVv0+OOPq0uXLjpw4IAyMjJ04403atu2bapTp44k6cEHH9Tzzz+v8ePHq1evXvrss89055136vjx4xfc9+nTp3XzzTfr+++/16xZs9SyZUu9/fbbGjJkyAW33b9/v/r166ekpCQtW7ZMDRo00KFDh7R27VoVFRUpKipKa9eu1S233KKRI0c6viJXHliVu/POOzV06FClpKTo5MmT593nrl27lJqaqmnTpikyMlIvvviixo0bp6KiIo0fP/6CY/61ytSgv/bVV1+pS5cuaty4sebNm6ewsDC98MILuu+++/TDDz/o8ccfd+r/xBNPqGvXrlqyZIkKCgo0YcIE9e/fX3v27FFgYKBHYwVqNAtAjfXcc89ZkqyPP/7Y0XbvvfdakqxXX33VqW/fvn2tVq1aObVJsjIyMhx///Of/7QkWRs3bnTZ17333mvFxcVVOJbS0lKruLjYev75563AwEDrxx9/rPS2lmVZP/30k2W326077rjDqf0///mPJcnq0aOHo23fvn2WJOu5555ztF1xxRVWamrqeffRr18/t+Mof7zmzZtbRUVFbu/79b4yMjIsSVZaWppT3xdffNGSZL3wwguOtnOf43JxcXHWvffe6/jbk+d+7dq1liRr9uzZTv1WrFhhSbIWLVrktB+73W4dOHDA0Xb69GmrUaNG1sMPP+yyLwAA4F553fXBBx9YxcXF1vHjx621a9dakZGRVvfu3a3i4mJH39atW1sdOnRwarMsy7r11lutqKgoq7S01LIsyxoyZIhVp04dKy8vz9GnpKTEat26tSXJ2rdvn6O9sjXFxo0bXWqK8vpw2bJlTtu+/PLLliRr5cqVTu0ff/yxJclasGCBZVmWtWfPnvPWPr/evzsLFy60JFlvvvmmU/uDDz5YYZ1V7rXXXrMkWbt27arw8Y8ePVrh81P+eE8++WSF9/1aXFycFRAQ4LK/Xr16WfXr17dOnjxpWdb/Xw+/PkaW5f75r6gGtSzX4zp06FDLZrNZubm5Tv369Olj1a1b1/r555+d9tO3b1+nfq+++qolycrJyXG7P6C24mt9QC0UEBCg/v37O7W1bdtWBw4c8Op+du7cqQEDBigsLEyBgYEKDg7WiBEjVFpaqr1793r0WDk5OTpz5ox+//vfO7V36dJFcXFxF9z++uuv1/Lly/X000/rgw8+qNJXCwcMGODRGU/njnXw4MEKCgrSxo0bPd63JzZs2CBJLlfGueuuu3T55Zfrvffec2pv3769YmNjHX/b7Xa1bNnS6+sBAAB/0KlTJwUHB6tevXq65ZZb1LBhQ7355psKCjr7pZOvv/5aX375paNOKCkpcdz69u2rw4cPO752tXHjRt10002KiIhwPH5gYGClziiqioEDBzr9/a9//UsNGjRQ//79ncbZvn17RUZGOr6aVl7bVFT7XMjGjRtVr149DRgwwKl92LBhF9y2ffv2CgkJ0UMPPaR//OMf+vbbby+4jTvnzv18rr76arVr186pbdiwYSooKNCOHTuqtP/K2rBhg2666SbFxMQ4td933306deqUyw+4n/uctm3bVpKo8+BzCKeAWqhu3bqy2+1ObTabTWfOnPHaPnJzc5WUlKRDhw7pb3/7m7Zs2aKPP/5Y8+fPl3T29G1PlH/9LjIy0uU+d23nWrFihe69914tWbJEnTt3VqNGjTRixAi3v2VQkaioqMoP2M24goKCFBYW5phLdTl27JiCgoJcTlcPCAhQZGSky/7DwsJcHsNms3l8jAAAgPT888/r448/1oYNG/Twww9rz549uvvuux33l//21Pjx4xUcHOx0Gz16tCQpPz9f0tn39KrWPp6qW7eu6tev79T2ww8/6Oeff1ZISIjLWPPy8pzG6W5c5bXPhRw7dswpgCtXmXk2b95c69evV+PGjTVmzBg1b95czZs319/+9rcLbvtrntR55zsml6LOczfW6Ohot/s/9/kv/2kH6jz4Gn5zCoBbb7zxhk6ePKlVq1Y5ndm0a9euKj1e+RuruzApLy/P5QfBzxUeHq7MzExlZmYqNzdXq1ev1sSJE3XkyBGtXbu2UmOo6AcxK5KXl6cmTZo4/i4pKdGxY8ecigSbzebyQ/TSxRU2YWFhKikp0dGjR50CKsuylJeXp+uuu67Kjw0AAM4vISHB8SPoPXv2VGlpqZYsWaLXXntNgwYNUnh4uCRp0qRJuvPOO90+RvlvaYaFhVVY+5zrYmsKd3VOeHi4wsLCKqyV6tWr5xhn+bjc1T4XEhYW5vbH0yv7IWJSUpKSkpJUWlqqbdu26e9//7tSU1MVERGhoUOHVuoxPKnzzndMyp+L8g+Czz0m5YFeVYWFhenw4cMu7d9//70kOdYX4G84cwrwI5580lL+Bv/rH962LEuLFy+u0r47deoku92uF1980al969atHp+WHBsbq0ceeUS9evVyOvXa22cLnTvWV199VSUlJbrxxhsdbU2bNtUnn3zi1G/Dhg06ceKEU5snz3351XVeeOEFp/aVK1fq5MmTjvsBAED1mz17tho2bKgnn3xSZWVlatWqlVq0aKHdu3erY8eObm/loU/Pnj313nvvOV3pr7S0VCtWrHDZT2VrCk/ceuutOnbsmEpLS92OszxEK69tKqp9LqRnz546fvy4Vq9e7dTu6Q+/BwYG6oYbbnCcqV9e53n7bKHPP/9cu3fvdmp76aWXVK9ePceFa8o/OD33mJw7x/LxVXZsN910kzZs2OAIo8o9//zzqlu3rjp16lTZaQA+hTOnAD/Spk0bSdKiRYtUr1492e12xcfHuz1du1evXgoJCdHdd9+txx9/XGfOnNHChQv1008/VWnfDRs21Pjx4/X0009r1KhRuuuuu3Tw4EHHVVLO55dfflHPnj01bNgwtW7dWvXq1dPHH3+stWvXOn1iec0112jVqlVauHChEhMTddlllzk++ayKVatWKSgoSL169XJcra9du3YaPHiwo8/w4cM1depUPfnkk+rRo4e++OILPfvsswoNDXV6LE+f+969e2vChAkqKChQ165dHVfr69Chg4YPH17lOQEAAM80bNhQkyZN0uOPP66XXnpJ99xzj/73f/9Xffr0Ue/evXXfffepSZMm+vHHH7Vnzx7t2LFD//znPyVJU6ZM0erVq/W73/1OTz75pOrWrav58+e7vZpcZWsKTwwdOlQvvvii+vbtq3Hjxun6669XcHCwvvvuO23cuFG33Xab7rjjDiUkJOiee+5RZmamgoODdfPNN+uzzz5zXOH4QkaMGKG//vWvGjFihGbMmKEWLVpozZo1evfddy+4bVZWljZs2KB+/fopNjZWZ86ccVwV+uabb5Z09gyvuLg4vfnmm7rpppvUqFEjhYeHX/DM+4pER0drwIABmjZtmqKiovTCCy8oOztbzzzzjOrWrStJuu6669SqVSuNHz9eJSUlatiwoV5//XW9//77Lo/nSQ2akZGhf/3rX+rZs6eefPJJNWrUSC+++KLefvttzZ49+6KON1Crmf5FdgAVq+hqfZdffrlLX3dXI5Gbq5pkZmZa8fHxVmBgoNPVU9xdce+tt96y2rVrZ9ntdqtJkybWY489Zr3zzjturxBzoav1WZZllZWVWbNmzbJiYmKs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H35YAQEByszMvGTj85bKzHPPnj0aMGCAQkNDVa9ePXXq1Em5ubmXfrDwKdRrvol6rfbz1VpNol6TqNeo186PcMqLVqxYodTUVE2ePFk7d+5UUlKS+vTp41NF9ObNmzVmzBh98MEHys7OVklJiZKTk3Xy5EnTQ6s2H3/8sRYtWqS2bduaHkq1+Omnn9S1a1cFBwfrnXfe0RdffKG//OUvatCggemhed0zzzyjrKwsPfvss9qzZ49mz56tP//5z/r73/9uemgX5eTJk2rXrp2effZZt/fPnj1bc+fO1bPPPquPP/5YkZGR6tWrl44fP36JR3pxzjfPU6dOaceOHZo6dap27NihVatWae/evRowYICBkV68Cx3Tcm+88YY+/PBDRUdHX6KRedeF5vnNN9+oW7duat26tTZt2qTdu3dr6tSpstvtl3ik8CXUa76Jes03+GqtJlGvSdRr1GsXYMFrrr/+eislJcWprXXr1tbEiRMNjaj6HTlyxJJkbd682fRQqsXx48etFi1aWNnZ2VaPHj2scePGmR6S102YMMHq1q2b6WFcEv369bMeeOABp7Y777zTuueeewyNyPskWa+//rrj77KyMisyMtL605/+5Gg7c+aMFRoaamVlZRkYoXecO093PvroI0uSdeDAgUszqGpS0Vy/++47q0mTJtZnn31mxcXFWX/9618v+di8yd08hwwZ4lP/PlEzUK/5Huo13+EPtZplUa/9GvVa7VKd9RpnTnlJUVGRtm/fruTkZKf25ORkbd261dCoqt8vv/wiSWrUqJHhkVSPMWPGqF+/frr55ptND6XarF69Wh07dtRdd92lxo0bq0OHDlq8eLHpYVWLbt266b333tPevXslSbt379b777+vvn37Gh5Z9dm3b5/y8vKcXptsNpt69Ojh069N0tnXp4CAAJ/7VFmSysrKNHz4cD322GO6+uqrTQ+nWpSVlentt99Wy5Yt1bt3bzVu3Fg33HDDeU+ZBy6Eeo16rbbyl3rNH2s1iXqNeq328ma9RjjlJfn5+SotLVVERIRTe0REhPLy8gyNqnpZlqX09HR169ZNbdq0MT0cr3vllVe0fft2zZo1y/RQqtW3336rhQsXqkWLFnr33XeVkpKisWPH6vnnnzc9NK+bMGGC7r77brVu3VrBwcHq0KGDUlNTdffdd5seWrUpf/3xp9cmSTpz5owmTpyoYcOGqX79+qaH43XPPPOMgoKCNHbsWNNDqTZHjhzRiRMn9Kc//Um33HKL1q1bpzvuuEN33nmnNm/ebHp4qKWo16jXait/qdf8sVaTqNeo12ovb9ZrQdU0Rr8VEBDg9LdlWS5tvuKRRx7RJ598ovfff9/0ULzu4MGDGjdunNatW+fzv21SVlamjh07aubMmZKkDh066PPPP9fChQs1YsQIw6PzrhUrVuiFF17QSy+9pKuvvlq7du1SamqqoqOjde+995oeXrXyp9em4uJiDR06VGVlZVqwYIHp4Xjd9u3b9be//U07duzw2WMoyfHjt7fddpvS0tIkSe3bt9fWrVuVlZWlHj16mBweajl/ek2kXvMN/lKv+XOtJvnXaxP1mm/wZr3GmVNeEh4ersDAQJdk+8iRIy4JuC/4n//5H61evVobN27Ub37zG9PD8brt27fryJEjSkxMVFBQkIKCgrR582bNmzdPQUFBKi0tNT1Er4mKitJVV13l1JaQkOBTPwxb7rHHHtPEiRM1dOhQXXPNNRo+fLjS0tJ8+tPWyMhISfKb16bi4mINHjxY+/btU3Z2tk9+CrdlyxYdOXJEsbGxjtenAwcO6NFHH1XTpk1ND89rwsPDFRQU5DevT7g0qNd8C/Wa770e+mOtJlGvUa/VXt6s1winvCQkJESJiYnKzs52as/OzlaXLl0Mjcr7LMvSI488olWrVmnDhg2Kj483PaRqcdNNN+nTTz/Vrl27HLeOHTvq97//vXbt2qXAwEDTQ/Sarl27ulxeeu/evYqLizM0oupz6tQpXXaZ88teYGCgT1yeuCLx8fGKjIx0em0qKirS5s2bfeq1Sfr/hc5///tfrV+/XmFhYaaHVC2GDx+uTz75xOn1KTo6Wo899pjeffdd08PzmpCQEF133XV+8/qES4N6zbdQr/ne66E/1moS9Zovol7z/PWJr/V5UXp6uoYPH66OHTuqc+fOWrRokXJzc5WSkmJ6aF4zZswYvfTSS3rzzTdVr149R7ofGhqqOnXqGB6d99SrV8/ldxkuv/xyhYWF+dzvNaSlpalLly6aOXOmBg8erI8++kiLFi3SokWLTA/N6/r3768ZM2YoNjZWV199tXbu3Km5c+fqgQceMD20i3LixAl9/fXXjr/37dunXbt2qVGjRoqNjVVqaqpmzpypFi1aqEWLFpo5c6bq1q2rYcOGGRy15843z+joaA0aNEg7duzQv/71L5WWljpenxo1aqSQkBBTw66SCx3Tcwu54OBgRUZGqlWrVpd6qBflQvN87LHHNGTIEHXv3l09e/bU2rVr9dZbb2nTpk3mBo1aj3qNeq028pd6zVdrNYl6jXqNeu2CLvp6f3Ayf/58Ky4uzgoJCbGuvfZan7tkryS3t+eee8700Kqdr16a2LIs66233rLatGlj2Ww2q3Xr1taiRYtMD6laFBQUWOPGjbNiY2Mtu91uNWvWzJo8ebJVWFhoemgXZePGjW7/Xd57772WZZ29PHFGRoYVGRlp2Ww2q3v37tann35qdtBVcL557tu3r8LXp40bN5oeuscudEzPVVsvTVyZeS5dutT67W9/a9ntdqtdu3bWG2+8YW7A8BnUa76Leq1289VazbKo16jXqNcuJMCyLMuzOAsAAAAAAADwDn5zCgAAAAAAAMYQTgEAAAAAAMAYwikAAAAAAAAYQzgFAAAAAAAAYwinAAAAAAAAYAzhFAAAAAAAAIwhnAIAAAAAAIAxhFMAAAAAAAAwhnAKAAAAAAAAxhBOAQAAAAAAwBjCKQAAAAAAABjz/wAcfxIVwbXtywAAAABJRU5ErkJggg==",
- "text/plain": [
- "
"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "euPQoGR9DCXh"
+ },
+ "source": [
+ "OT can be used to detect **model-induced bias** by calculating the above cost (also known as **Earth Mover's distance** or **Wasserstein distance**) between the distribution of ground truth labels and model predictions for each of the **protected groups**. If its value is close to 1, the model is **biased** towards this group."
]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "\n",
- "# Drawing both of them\n",
- "figure, axis = plt.subplots(1, 2)\n",
- "figure.set_figheight(4)\n",
- "figure.set_figwidth(12)\n",
- "figure.tight_layout(w_pad = 5)\n",
- "\n",
- "def draw(y, id):\n",
- " x = np.array(range(0, np.size(y)))\n",
- " axis[id].bar(x, y, color=\"lightgreen\", ec='black')\n",
- " axis[id].scatter(x, y, color=\"orange\")\n",
- "\n",
- "axis[0].title.set_text(\"Initial distribution\")\n",
- "axis[1].title.set_text(\"Required distribution\")\n",
- "draw(a, 0)\n",
- "draw(b, 1)\n",
- "\n",
- "plt.show()"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "There, since we can go from the initial distribution to the desired one just using permutations, the Wasserstein distance is zero."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "0.0\n"
- ]
- }
- ],
- "source": [
- "import pandas as pd\n",
- "from aif360.sklearn.metrics import ot_distance\n",
- "\n",
- "_a = pd.Series(a)\n",
- "_b = pd.Series(b)\n",
- "c = ot_distance(_a, _b, mode='continuous')\n",
- "\n",
- "print(c)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 4. Extreme case\n",
- "\n",
- "One more example that is closer to our case is \"normalization\". It's an explanation of why the maximum Wasserstein distance we can get in our case is approaching 1 (with increasing the size of the sample), that is, it is normalized. We get this in the case that all our population has a value 0 of the 2-year recidivism (which is presented in the paragraph \"Compas Dataset\") and the classifier fails massively in all the cases labeling all with a 1. That would be the worst-case scenario."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy as np\n",
- "\n",
- "# Initial distribution\n",
- "a = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.001])\n",
- "# Required distribution\n",
- "b = np.array([0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "e1VAdG5wDCXh"
+ },
+ "source": [
+ "## General Optimal Transport examples"
+ ]
+ },
{
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAABLgAAAGUCAYAAAA285u8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAABVNklEQVR4nO3de1yUdd7/8TdxmNFSSlxBChDdCsxSg1ahEL1NTC076Eq1YaVW3NQqkKVopnlXpLkuax74WZrrr1La0LKNNbGUtZXa5GAnN6slMYM1rEQzOV6/P/wxd+MMyCA6Xszr+Xhcj73nO5/r+ny/w0if+8N18DIMwxAAAAAAAABgUue5ewIAAAAAAADA6aDBBQAAAAAAAFOjwQUAAAAAAABTo8EFAAAAAAAAU6PBBQAAAAAAAFOjwQUAAAAAAABTo8EFAAAAAAAAU6PBBQAAAAAAAFOjwQUAAAAAAABTo8EFeIA1a9bIy8tLu3btatP+Xl5emjdvnu31Z599pnnz5unrr792iL3nnnvUq1evNuU5nX0laejQoRo6dKjt9ddffy0vLy+tWbPGpeO88sorysrKcmkfZ7nmzZsnLy8vVVVVuXSslpypzx4AALSPprqrafPx8VHPnj11++2364svvnD39Gy2b98uLy8vbd++/azk69Wrl+6555427dtSneWKY8eOad68eS6v2VmuXr166cYbb3TpOKfSUg16cj0OwBENLgCnVFhYqClTpthef/bZZ3riiSecNlnmzJmjjRs3nsXZNa9nz54qLCzUmDFjXNqvLQ2utuZylVk+ewAAPN2LL76owsJCbd26VQ899JA2bdqk6667Tj/88IO7pyZJuvrqq1VYWKirr77a3VNpkylTpqiwsNClfY4dO6YnnnjC5QZXW3K1RUs16Mn1OABHPu6eAIBz3+DBg1sd26dPnzM4E9dYLBaX5t4WDQ0Nqq+vPyu5TuVc+uwBAPB0/fr1U3R0tKQTZ5k3NDRo7ty5ev3113Xvvfe6eXZS165dW1W7HDt2TJ07dz4LM3LNJZdcoksuueSM5mha+9nIdSrurjMBM+AMLsBD3XPPPbrgggv05ZdfavTo0brgggsUEhKihx9+WDU1NXaxvzwles2aNfrtb38rSRo2bJjt9PumU8adXSa3bNkyDRkyRD169ND555+vK6+8UgsXLlRdXV2b5m4YhhYuXKiwsDBZrVZdffXV+tvf/uYQ5+x09u+++07333+/QkJCZLFY9Ktf/UrXXnuttm7dKulEAfrWW29p3759dpcX/PJ4Cxcu1JNPPqnw8HBZLBZt27atxcsh9+/fr9tuu01du3aVv7+/7rrrLn333Xd2Mc2ddv7L0/nb8tkfP35cGRkZCg8Pl5+fny6++GI9+OCD+vHHHx3y3Hjjjdq8ebOuvvpqderUSREREVq9enUzPwUAAOCKpmbXf/7zH7vxXbt2aezYserWrZusVqsGDhyoV1991WH/999/X9dee62sVquCg4OVkZGh559/Xl5eXnZndremppCcX6LYVB9+/PHHSkhIUJcuXTR8+HBJUm1trZ588klFRETYaqh7773Xoaapq6vTo48+qqCgIHXu3FnXXXed/vnPf7b6c/r22281YcIEdenSRf7+/kpMTFRlZaVDnLPLBt99910NHTpUAQEB6tSpk0JDQzVu3DgdO3ZMX3/9tX71q19Jkp544glbHdX0mTQdr7i4WOPHj9dFF11k++NhS5dDbty4UVdddZWsVqt69+6tJUuW2L3fdMnqyWffn/z5t1SDSs5/rp988oluvvlmXXTRRbJarRowYID+/Oc/O82zbt06zZ49W8HBweratauuv/56ff75507XBJgVZ3ABHqyurk5jx47V5MmT9fDDD+vvf/+7/ud//kf+/v56/PHHne4zZswYPf3005o1a5aWLVtmO629pbOHvvrqK9155522Jsvu3bv11FNP6V//+lebGihPPPGEnnjiCU2ePFnjx4/X/v37dd9996mhoUGXX355i/smJSWpuLhYTz31lC677DL9+OOPKi4u1qFDhyRJy5cv1/3336+vvvqq2cv9lixZossuu0yLFi1S165ddemll7aY89Zbb9WECROUnJysTz/9VHPmzNFnn32mDz74QL6+vq1et6ufvWEYuuWWW/TOO+8oIyNDcXFx+uijjzR37lwVFhaqsLBQFovFFr979249/PDDmjlzpgIDA/XCCy9o8uTJ+vWvf60hQ4a0ep4AAMBRWVmZJOmyyy6zjW3btk033HCDBg0apOzsbPn7+2v9+vVKTEzUsWPHbM2Xzz77TMOHD1evXr20Zs0ade7cWcuXL9crr7zS7vOsra3V2LFj9cADD2jmzJmqr69XY2Ojbr75Zu3YsUOPPvqoYmNjtW/fPs2dO1dDhw7Vrl271KlTJ0nSfffdp7Vr12r69OkaMWKEPvnkE9122206cuTIKXP//PPPuv766/Xtt98qMzNTl112md566y0lJiaect+vv/5aY8aMUVxcnFavXq0LL7xQBw4c0ObNm1VbW6uePXtq8+bNuuGGGzR58mTb5X5NTa8mt912m26//XYlJyfrp59+ajFnaWmpUlNTNW/ePAUFBenll1/WtGnTVFtbq+nTp59yzr/Umhr0lz7//HPFxsaqR48eWrJkiQICAvTSSy/pnnvu0X/+8x89+uijdvGzZs3StddeqxdeeEHV1dWaMWOGbrrpJu3Zs0fe3t4uzRU4ZxkAOrwXX3zRkGR8+OGHtrG7777bkGS8+uqrdrGjR482Lr/8crsxScbcuXNtr//yl78Ykoxt27Y55Lr77ruNsLCwZufS0NBg1NXVGWvXrjW8vb2N77//vtX7GoZh/PDDD4bVajVuvfVWu/F//OMfhiQjPj7eNlZWVmZIMl588UXb2AUXXGCkpqa2mGPMmDFO59F0vD59+hi1tbVO3/tlrrlz5xqSjLS0NLvYl19+2ZBkvPTSS7axkz/jJmFhYcbdd99te+3KZ79582ZDkrFw4UK7uJycHEOSsXLlSrs8VqvV2Ldvn23s559/Nrp162Y88MADDrkAAIBzTXXX+++/b9TV1RlHjhwxNm/ebAQFBRlDhgwx6urqbLERERHGwIED7cYMwzBuvPFGo2fPnkZDQ4NhGIaRmJhodOrUyaisrLTF1NfXGxEREYYko6yszDbe2ppi27ZtDjVFU324evVqu33XrVtnSDJyc3Ptxj/88ENDkrF8+XLDMAxjz549LdY+v8zvzIoVKwxJxhtvvGE3ft999zVbZzV57bXXDElGaWlps8f/7rvvmv18mo73+OOPN/veL4WFhRleXl4O+UaMGGF07drV+OmnnwzD+N/vwy9/Robh/PNvrgY1DMef6+23325YLBajvLzcLm7UqFFG586djR9//NEuz+jRo+3iXn31VUOSUVhY6DQfYEZcogh4MC8vL9100012Y1dddZX27dvXrnlKSko0duxYBQQEyNvbW76+vpo4caIaGhq0d+9el45VWFio48eP63e/+53deGxsrMLCwk65/29+8xutWbNGTz75pN5///02XSY5duxYl868OnmuEyZMkI+Pj7Zt2+Zyble8++67kuTwxKLf/va3Ov/88/XOO+/YjQ8YMEChoaG211arVZdddlm7fx8AAPAEgwcPlq+vr7p06aIbbrhBF110kd544w35+Jy4iObLL7/Uv/71L1udUF9fb9tGjx6tiooK2yVk27Zt0/DhwxUYGGg7vre3d6vObGqLcePG2b3+61//qgsvvFA33XST3TwHDBigoKAg22V2TbVNc7XPqWzbtk1dunTR2LFj7cbvvPPOU+47YMAA+fn56f7779ef//xn/fvf/z7lPs6cvPaWXHHFFerfv7/d2J133qnq6moVFxe3KX9rvfvuuxo+fLhCQkLsxu+55x4dO3bM4ab4J3+mV111lSRR56FDocEFeLDOnTvLarXajVksFh0/frzdcpSXlysuLk4HDhzQn/70J+3YsUMffvihli1bJunEqeiuaLqUMCgoyOE9Z2Mny8nJ0d13360XXnhBMTEx6tatmyZOnOj03g7N6dmzZ+sn7GRePj4+CggIsK3lTDl06JB8fHwcTr338vJSUFCQQ/6AgACHY1gsFpd/RgAAQFq7dq0+/PBDvfvuu3rggQe0Z88e3XHHHbb3m+7FNX36dPn6+tptKSkpkqSqqipJJ/6b3tbax1WdO3dW165d7cb+85//6Mcff5Sfn5/DXCsrK+3m6WxeTbXPqRw6dMiuidekNevs06ePtm7dqh49eujBBx9Unz591KdPH/3pT3865b6/5Eqd19LP5GzUec7mGhwc7DT/yZ9/020qqPPQkXAPLgBn1Ouvv66ffvpJGzZssDvDqrS0tE3Ha/qPs7OGVGVlpcNN1k/WvXt3ZWVlKSsrS+Xl5dq0aZNmzpypgwcPavPmza2aQ3M3GW1OZWWlLr74Ytvr+vp6HTp0yK7QsFgsDjf3l06vOAoICFB9fb2+++47uyaXYRiqrKzUNddc0+ZjAwCAlkVGRtpuLD9s2DA1NDTohRde0Guvvabx48ere/fukqSMjAzddtttTo/RdG/RgICAZmufk51uTeGszunevbsCAgKarZW6dOlim2fTvJzVPqcSEBDg9Ib0rf1DZFxcnOLi4tTQ0KBdu3bpueeeU2pqqgIDA3X77be36hiu1Hkt/UyaPoumPyaf/DNpagq2VUBAgCoqKhzGv/32W0myfb8AT8IZXABc5spffJqKhF/ezNwwDD3//PNtyj148GBZrVa9/PLLduM7d+50+RTr0NBQPfTQQxoxYoTdaeTtfdbSyXN99dVXVV9fr6FDh9rGevXqpY8++sgu7t1339XRo0ftxlz57JueevTSSy/Zjefm5uqnn36yvQ8AAM68hQsX6qKLLtLjjz+uxsZGXX755br00ku1e/duRUdHO92aGkfDhg3TO++8Y/cExoaGBuXk5DjkaW1N4Yobb7xRhw4dUkNDg9N5NjXimmqb5mqfUxk2bJiOHDmiTZs22Y27ejN9b29vDRo0yHbFQFOd195nLX366afavXu33dgrr7yiLl262B4G1PTH15N/JievsWl+rZ3b8OHD9e6779oaWk3Wrl2rzp07a/Dgwa1dBtBhcAYXAJf169dPkrRy5Up16dJFVqtV4eHhTk89HzFihPz8/HTHHXfo0Ucf1fHjx7VixQr98MMPbcp90UUXafr06XryySc1ZcoU/fa3v9X+/fttT69pyeHDhzVs2DDdeeedioiIUJcuXfThhx9q8+bNdn85vfLKK7VhwwatWLFCUVFROu+882x/gW2LDRs2yMfHRyNGjLA9RbF///6aMGGCLSYpKUlz5szR448/rvj4eH322WdaunSp/P397Y7l6mc/cuRIzZgxQ9XV1br22mttT1EcOHCgkpKS2rwmAADgmosuukgZGRl69NFH9corr+iuu+7S//k//0ejRo3SyJEjdc899+jiiy/W999/rz179qi4uFh/+ctfJEmPPfaYNm3apP/6r//S448/rs6dO2vZsmVOn/LX2prCFbfffrtefvlljR49WtOmTdNvfvMb+fr66ptvvtG2bdt0880369Zbb1VkZKTuuusuZWVlydfXV9dff70++eQT25OnT2XixIn64x//qIkTJ+qpp57SpZdeqry8PL399tun3Dc7O1vvvvuuxowZo9DQUB0/ftz2tO7rr79e0okzzcLCwvTGG29o+PDh6tatm7p3737KKwCaExwcrLFjx2revHnq2bOnXnrpJeXn52vBggXq3LmzJOmaa67R5ZdfrunTp6u+vl4XXXSRNm7cqPfee8/heK7UoHPnztVf//pXDRs2TI8//ri6deuml19+WW+99ZYWLlx4Wj9vwLTcfZd7AGdec09RPP/88x1inT0lRk6eNpOVlWWEh4cb3t7edk+1cfYkxDfffNPo37+/YbVajYsvvth45JFHjL/97W9On9xzqqcoGoZh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AAAAAACAqdHgAgAAgFPLly9XeHi4rFaroqKitGPHjhbjCwoKFBUVJavVqt69eys7O9sh5scff9SDDz6onj17ymq1KjIyUnl5eWdqCQAAwEPQ4AIAAICDnJwcpaamavbs2SopKVFcXJxGjRpl9zChXyorK9Po0aMVFxenkpISzZo1S1OnTlVubq4tpra2ViNGjNDXX3+t1157TZ9//rmef/55XXzxxWdrWQAAoINq003mAQAA0LEtXrxYkydP1pQpUyRJWVlZevvtt7VixQplZmY6xGdnZys0NFRZWVmSpMjISO3atUuLFi2yPT179erV+v7777Vz5075+vpKku1J3AAAAKeDM7gAAABgp7a2VkVFRUpISLAbT0hI0M6dO53uU1hY6BA/cuRI7dq1S3V1dZKkTZs2KSYmRg8++KACAwPVr18/Pf3002poaGh2LjU1NaqurrbbAAAATkaDCwAAAHaqqqrU0NCgwMBAu/HAwEBVVlY63aeystJpfH19vaqqqiRJ//73v/Xaa6+poaFBeXl5euyxx/SHP/xBTz31VLNzyczMlL+/v20LCQk5zdUBAICOiAYXAAAAnPLy8rJ7bRiGw9ip4n853tjYqB49emjlypWKiorS7bffrtmzZ2vFihXNHjMjI0OHDx+2bfv372/rcgAAQAfGPbgAAABgp3v37vL29nY4W+vgwYMOZ2k1CQoKchrv4+OjgIAASVLPnj3l6+srb29vW0xkZKQqKytVW1srPz8/h+NaLBZZLJbTXRIAAOjgOIMLAAAAdvz8/BQVFaX8/Hy78fz8fMXGxjrdJyYmxiF+y5Ytio6Ott1Q/tprr9WXX36pxsZGW8zevXvVs2dPp80tAACA1qLBBQAAAAfp6el64YUXtHr1au3Zs0dpaWkqLy9XcnKypBOXDk6cONEWn5ycrH379ik9PV179uzR6tWrtWrVKk2fPt0W89///d86dOiQpk2bpr179+qtt97S008/rQcffPCsrw8AAHQsXKIIAAAAB4mJiTp06JDmz5+viooK9evXT3l5eQoLC5MkVVRUqLy83BYfHh6uvLw8paWladmyZQoODtaSJUs0btw4W0xISIi2bNmitLQ0XXXVVbr44os1bdo0zZgx46yvDwAAdCw0uAAAAOBUSkqKUlJSnL63Zs0ah7H4+HgVFxe3eMyYmBi9//777TE9AAAA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",
- "text/plain": [
- "
"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Jl5mlU65DCXh"
+ },
+ "source": [
+ "Let us start with some simple examples of calculating the Earth Mover's distance between two distributions - the basis of Optimal Transport for bias detection. We do this using the `earth_movers_distance` function.\n",
+ "\n",
+ "For concrete examples of bias detection on real datasets, skip to the next chapter."
]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "\n",
- "# Drawing both of them\n",
- "figure, axis = plt.subplots(1, 2)\n",
- "figure.set_figheight(4)\n",
- "figure.set_figwidth(12)\n",
- "figure.tight_layout(w_pad = 5)\n",
- "\n",
- "def draw(y, id):\n",
- " x = np.array(range(0, np.size(y)))\n",
- " axis[id].bar(x, y, color=\"lightgreen\", ec='black')\n",
- " axis[id].scatter(x, y, color=\"orange\")\n",
- "\n",
- "axis[0].title.set_text(\"Initial distribution\")\n",
- "axis[1].title.set_text(\"Required distribution\")\n",
- "draw(a, 0)\n",
- "draw(b, 1)\n",
- "\n",
- "plt.show()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
+ },
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "0.9375\n"
- ]
- }
- ],
- "source": [
- "import pandas as pd\n",
- "from aif360.sklearn.metrics import ot_distance\n",
- "\n",
- "_a = pd.Series(a)\n",
- "_b = pd.Series(b)\n",
- "c = ot_distance(_a, _b)\n",
- "\n",
- "print(c)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Usage\n",
- "\n",
- "The type of outcomes must be provided using the `mode` keyword argument. The definition for the four types of outcomes supported are provided below:\n",
- "- Binary: Yes/no outcomes. Outcomes must 0 or 1.\n",
- "- Continuous: Continuous outcomes. Outcomes could be any real number.\n",
- "- Nominal: Multiclass outcomes with no rank or order between them. Outcomes must be a finite set of integers.\n",
- "- Ordinal: Multiclass outcomes that are ranked in a specific order. Outcomes must be positive integers."
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Compas Dataset"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We'll demonstrate finding the scanning for bias with earth_movers_distance using the Compas dataset. We scan for bias in the predictions of an `sklearn` logistic regression model with respect to different groups."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 23,
- "metadata": {},
- "outputs": [],
- "source": [
- "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_compas\n",
- "\n",
- "import numpy as np\n",
- "import pandas as pd\n",
- "\n",
- "np.random.seed(0)\n",
- "dataset_orig = load_preproc_data_compas()"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We scan for bias at first with respect to `sex`, and then `age`.\n",
- "\n",
- "To scan for bias with respect for a feature that is one-hot encoded - in this case, age category - we need to convert it to nominal or ordinal format."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 24,
- "metadata": {},
- "outputs": [
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Zxqm3r4bDCXh"
+ },
+ "source": [
+ "### 1. General Optimal Transport\n",
+ "\n",
+ "Suppose we have two distributions $a$ and $b$ (as shown in the picture below), and we need to calculate the Wasserstein distance between these two distributions."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "mf1QLAOSDCXh"
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "# Initial distribution\n",
+ "a = np.array([0., 0.01547988, 0.03095975, 0.04643963, 0.05727554, 0.05417957, 0.04643963, 0.07739938,\n",
+ " 0.10835913, 0.12383901, 0.11764706, 0.10526316, 0.09287926, 0.07739938, 0.04643962, 0. ])\n",
+ "# Required distribution\n",
+ "b = np.array([0., 0.01829787, 0.02702128, 0.04106383, 0.07, 0.10829787, 0.14212766, 0.14468085,\n",
+ " 0.13, 0.10808511, 0.08255319, 0.05170213, 0.03361702, 0.02702128, 0.01553191, 0. ])"
+ ]
+ },
{
- "data": {
- "text/html": [
- "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
],
- "text/plain": [
- " sex race priors_count=0 priors_count=1 to 3 priors_count=More than 3 \\\n",
- "0 0.0 0.0 1.0 0.0 0.0 \n",
- "1 0.0 0.0 0.0 0.0 1.0 \n",
- "2 0.0 1.0 0.0 0.0 1.0 \n",
- "3 1.0 1.0 1.0 0.0 0.0 \n",
- "4 0.0 1.0 1.0 0.0 0.0 \n",
- "\n",
- " c_charge_degree=F c_charge_degree=M age_cat two_year_recid \n",
- "0 1.0 0.0 1 1.0 \n",
- "1 1.0 0.0 0 1.0 \n",
- "2 1.0 0.0 1 1.0 \n",
- "3 0.0 1.0 1 0.0 \n",
- "4 1.0 0.0 1 0.0 "
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "from scipy.interpolate import make_interp_spline\n",
+ "\n",
+ "# Drawing both of them\n",
+ "figure, axis = plt.subplots(1, 2)\n",
+ "figure.set_figheight(4)\n",
+ "figure.set_figwidth(12)\n",
+ "figure.tight_layout(w_pad = 5)\n",
+ "\n",
+ "def draw(y, id):\n",
+ " x = np.array(range(0, np.size(y)))\n",
+ " XYSpline = make_interp_spline(x, y)\n",
+ " X = np.linspace(x.min(), x.max(), 500)\n",
+ " Y = XYSpline(X)\n",
+ " axis[id].bar(x, y, color=\"lightgreen\", ec='black')\n",
+ " axis[id].scatter(x, y, color=\"orange\")\n",
+ " axis[id].plot(X, Y, color='blue')\n",
+ "\n",
+ "axis[0].title.set_text(\"Initial distribution\")\n",
+ "axis[1].title.set_text(\"Required distribution\")\n",
+ "draw(a, 0)\n",
+ "draw(b, 1)\n",
+ "\n",
+ "plt.show()"
]
- },
- "execution_count": 24,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "dataset_orig_df = pd.DataFrame(dataset_orig.features, columns=dataset_orig.feature_names)\n",
- "# Binning the features corresponding to age ('reshaping' them into one ordinal column)\n",
- "age_cat_cols = ['age_cat=Less than 25', 'age_cat=25 to 45', 'age_cat=Greater than 45']\n",
- "age_cat = np.argmax(dataset_orig_df[age_cat_cols].values, axis=1).reshape(-1, 1)\n",
- "df = dataset_orig_df.drop(age_cat_cols, axis=1)\n",
- "df['age_cat'] = age_cat\n",
- "df['two_year_recid'] = dataset_orig.labels\n",
- "df.head()"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Measuring bias with respect to `sex`\n",
- "\n",
- "\n",
- "We train a linear regression model on the dataset, and scan its results for bias with respect to `sex` using `earth_movers_distance`.\n",
- "\n",
- "The arguments are as follows:\n",
- "- `ground_truth`: ground truth labels;\n",
- "- `classifier`: predicted labels;\n",
- "- `prot_attr`: the values of the sensitive attributes (with respect to which the classifier may be introducing bias);\n",
- "- `num_iters`: maximum number of iterations performed when calculating the Earth Mover's Distance;\n",
- "- `mode`: mode of the labels, one of binary, nominal, ordinal and continious; in our case the labels are binary."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 25,
- "metadata": {},
- "outputs": [],
- "source": [
- "from aif360.sklearn.metrics import ot_distance\n",
- "from sklearn.linear_model import LogisticRegression\n",
- "from sklearn.model_selection import train_test_split\n",
- "\n",
- "X = df.drop('two_year_recid', axis=1)\n",
- "y = df['two_year_recid']\n",
- "clf = LogisticRegression(solver='lbfgs', max_iter=10000, C=1.0, penalty='l2')\n",
- "clf.fit(X, y)\n",
- "preds = pd.Series(clf.predict_proba(X)[:,0])\n",
- "\n",
- "ot_val1 = ot_distance(y_true=y, y_pred=preds, prot_attr=df['sex'])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 26,
- "metadata": {},
- "outputs": [
+ },
{
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
sex
\n",
- "
ot_val
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
0
\n",
- "
0.0
\n",
- "
0.000209
\n",
- "
\n",
- "
\n",
- "
1
\n",
- "
1.0
\n",
- "
0.001647
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_w9qXdzUDCXj"
+ },
+ "source": [
+ "In order to better understand how Optimal Transport works, below is presented the code considering the case when the matrix cost distance is presented and defined as the absolute difference between positions of each part of the distribution. That is $\\text{distance}[i][j] = abs(i - j)$."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "3Y0nUhvcDCXj"
+ },
+ "outputs": [],
+ "source": [
+ "import pandas as pd\n",
+ "\n",
+ "_a = pd.Series(a)\n",
+ "_b = pd.Series(b)\n",
+ "distance = np.zeros((np.size(a), np.size(b)))\n",
+ "for i in range(np.size(a)):\n",
+ " for j in range(np.size(b)):\n",
+ " distance[i][j] = abs(i - j)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "P2i9hj8VDCXj",
+ "outputId": "d2affef6-0656-40f3-f5ad-1be39352a547"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Wasserstein distance is equal to 1.3773703499999999.\n"
+ ]
+ }
],
- "text/plain": [
- " sex ot_val\n",
- "0 0.0 0.000209\n",
- "1 1.0 0.001647"
+ "source": [
+ "from aif360.sklearn.metrics import ot_distance\n",
+ "c0 = ot_distance(y_true=_a, y_pred=_b, cost_matrix=distance, mode='continuous')\n",
+ "\n",
+ "print(\"Wasserstein distance is equal to \", c0, \".\", sep=\"\")"
]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "bs1 = pd.DataFrame({\"sex\": ot_val1.keys(), \"ot_val\": ot_val1.values()})\n",
- "display(bs1)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "We get the bias value for each each of the protected groups - in this case, Male (`0`) and Female (`1`). \n",
- "\n",
- "These values range from 0 to 1 and can be interpreted as the difference in percent between the ground truth distribution and the distribution of the protected group: for example, a value of 0.3 would mean a 30% difference."
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Measuring bias with respect to `age_cat`\n",
- "\n",
- "Now we measure the bias of the same classifier with respect to the age category."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 27,
- "metadata": {},
- "outputs": [],
- "source": [
- "ot_val2 = ot_distance(y_true=y, y_pred=preds, prot_attr=df['age_cat'])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 28,
- "metadata": {},
- "outputs": [
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "iXt221UKDCXk"
+ },
+ "source": [
+ "### 2. Randomly distributed samples\n",
+ "\n",
+ "Suppose we have two distributions $a$ and $b$ with length $N$, that are generated randomly, and we need to calculate earth_movers_distance for them."
+ ]
+ },
{
- "data": {
- "text/html": [
- "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
],
- "text/plain": [
- " sex ot_val\n",
- "0 0.0 0.000503\n",
- "1 1.0 0.000067"
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "# Drawing both of them\n",
+ "figure, axis = plt.subplots(1, 2)\n",
+ "figure.set_figheight(4)\n",
+ "figure.set_figwidth(12)\n",
+ "figure.tight_layout(w_pad = 5)\n",
+ "\n",
+ "def draw(y, id):\n",
+ " x = np.array(range(0, np.size(y)))\n",
+ " axis[id].bar(x, y, color=\"lightgreen\", ec='black')\n",
+ " axis[id].scatter(x, y, color=\"orange\")\n",
+ "\n",
+ "axis[0].title.set_text(\"Initial distribution\")\n",
+ "axis[1].title.set_text(\"Required distribution\")\n",
+ "draw(a, 0)\n",
+ "draw(b, 1)\n",
+ "\n",
+ "plt.show()"
]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "bs1 = pd.DataFrame({\"sex\": ot_val1.keys(), \"ot_val\": ot_val1.values()})\n",
- "display(bs1)"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Measuring bias with respect to `race`"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {},
- "outputs": [],
- "source": [
- "ot_val2 = ot_distance(y_true=y, y_pred=preds, prot_attr=data['race'])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "2b8iTs6CDCXl"
+ },
+ "source": [
+ "There, since we can go from the initial distribution to the desired one just using permutations, the Wasserstein distance is zero."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "PeRPqgPyDCXl",
+ "outputId": "bd589c43-78e5-4232-a8bb-674d8368066c"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0.0\n"
+ ]
+ }
+ ],
+ "source": [
+ "import pandas as pd\n",
+ "from aif360.sklearn.metrics import ot_distance\n",
+ "\n",
+ "_a = pd.Series(a)\n",
+ "_b = pd.Series(b)\n",
+ "c = ot_distance(_a, _b, mode='continuous')\n",
+ "\n",
+ "print(c)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "V5JjvRvJDCXl"
+ },
+ "source": [
+ "### 4. Extreme case\n",
+ "\n",
+ "One more example that is closer to our case is \"normalization\". It's an explanation of why the maximum Wasserstein distance we can get in our case is approaching 1 (with increasing the size of the sample), that is, it is normalized. We get this in the case that all our population has a value 0 of the 2-year recidivism (which is presented in the paragraph \"Compas Dataset\") and the classifier fails massively in all the cases labeling all with a 1. That would be the worst-case scenario."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "C-XRy_ZNDCXl"
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "# Initial distribution\n",
+ "a = np.array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.001])\n",
+ "# Required distribution\n",
+ "b = np.array([0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625, 0.0625])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "Wr7ugRoUDCXl",
+ "outputId": "73b33e31-cbe6-4ab1-994c-2922c8b51be2"
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAABLgAAAGUCAYAAAA285u8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAABVNklEQVR4nO3de1yUdd7/8TdxmNFSSlxBChDdCsxSg1ahEL1NTC076Eq1YaVW3NQqkKVopnlXpLkuax74WZrrr1La0LKNNbGUtZXa5GAnN6slMYM1rEQzOV6/P/wxd+MMyCA6Xszr+Xhcj73nO5/r+ny/w0if+8N18DIMwxAAAAAAAABgUue5ewIAAAAAAADA6aDBBQAAAAAAAFOjwQUAAAAAAABTo8EFAAAAAAAAU6PBBQAAAAAAAFOjwQUAAAAAAABTo8EFAAAAAAAAU6PBBQAAAAAAAFOjwQUAAAAAAABTo8EFeIA1a9bIy8tLu3btatP+Xl5emjdvnu31Z599pnnz5unrr792iL3nnnvUq1evNuU5nX0laejQoRo6dKjt9ddffy0vLy+tWbPGpeO88sorysrKcmkfZ7nmzZsnLy8vVVVVuXSslpypzx4AALSPprqrafPx8VHPnj11++2364svvnD39Gy2b98uLy8vbd++/azk69Wrl+6555427dtSneWKY8eOad68eS6v2VmuXr166cYbb3TpOKfSUg16cj0OwBENLgCnVFhYqClTpthef/bZZ3riiSecNlnmzJmjjRs3nsXZNa9nz54qLCzUmDFjXNqvLQ2utuZylVk+ewAAPN2LL76owsJCbd26VQ899JA2bdqk6667Tj/88IO7pyZJuvrqq1VYWKirr77a3VNpkylTpqiwsNClfY4dO6YnnnjC5QZXW3K1RUs16Mn1OABHPu6eAIBz3+DBg1sd26dPnzM4E9dYLBaX5t4WDQ0Nqq+vPyu5TuVc+uwBAPB0/fr1U3R0tKQTZ5k3NDRo7ty5ev3113Xvvfe6eXZS165dW1W7HDt2TJ07dz4LM3LNJZdcoksuueSM5mha+9nIdSrurjMBM+AMLsBD3XPPPbrgggv05ZdfavTo0brgggsUEhKihx9+WDU1NXaxvzwles2aNfrtb38rSRo2bJjt9PumU8adXSa3bNkyDRkyRD169ND555+vK6+8UgsXLlRdXV2b5m4YhhYuXKiwsDBZrVZdffXV+tvf/uYQ5+x09u+++07333+/QkJCZLFY9Ktf/UrXXnuttm7dKulEAfrWW29p3759dpcX/PJ4Cxcu1JNPPqnw8HBZLBZt27atxcsh9+/fr9tuu01du3aVv7+/7rrrLn333Xd2Mc2ddv7L0/nb8tkfP35cGRkZCg8Pl5+fny6++GI9+OCD+vHHHx3y3Hjjjdq8ebOuvvpqderUSREREVq9enUzPwUAAOCKpmbXf/7zH7vxXbt2aezYserWrZusVqsGDhyoV1991WH/999/X9dee62sVquCg4OVkZGh559/Xl5eXnZndremppCcX6LYVB9+/PHHSkhIUJcuXTR8+HBJUm1trZ588klFRETYaqh7773Xoaapq6vTo48+qqCgIHXu3FnXXXed/vnPf7b6c/r22281YcIEdenSRf7+/kpMTFRlZaVDnLPLBt99910NHTpUAQEB6tSpk0JDQzVu3DgdO3ZMX3/9tX71q19Jkp544glbHdX0mTQdr7i4WOPHj9dFF11k++NhS5dDbty4UVdddZWsVqt69+6tJUuW2L3fdMnqyWffn/z5t1SDSs5/rp988oluvvlmXXTRRbJarRowYID+/Oc/O82zbt06zZ49W8HBweratauuv/56ff75507XBJgVZ3ABHqyurk5jx47V5MmT9fDDD+vvf/+7/ud//kf+/v56/PHHne4zZswYPf3005o1a5aWLVtmO629pbOHvvrqK9155522Jsvu3bv11FNP6V//+lebGihPPPGEnnjiCU2ePFnjx4/X/v37dd9996mhoUGXX355i/smJSWpuLhYTz31lC677DL9+OOPKi4u1qFDhyRJy5cv1/3336+vvvqq2cv9lixZossuu0yLFi1S165ddemll7aY89Zbb9WECROUnJysTz/9VHPmzNFnn32mDz74QL6+vq1et6ufvWEYuuWWW/TOO+8oIyNDcXFx+uijjzR37lwVFhaqsLBQFovFFr979249/PDDmjlzpgIDA/XCCy9o8uTJ+vWvf60hQ4a0ep4AAMBRWVmZJOmyyy6zjW3btk033HCDBg0apOzsbPn7+2v9+vVKTEzUsWPHbM2Xzz77TMOHD1evXr20Zs0ade7cWcuXL9crr7zS7vOsra3V2LFj9cADD2jmzJmqr69XY2Ojbr75Zu3YsUOPPvqoYmNjtW/fPs2dO1dDhw7Vrl271KlTJ0nSfffdp7Vr12r69OkaMWKEPvnkE9122206cuTIKXP//PPPuv766/Xtt98qMzNTl112md566y0lJiaect+vv/5aY8aMUVxcnFavXq0LL7xQBw4c0ObNm1VbW6uePXtq8+bNuuGGGzR58mTb5X5NTa8mt912m26//XYlJyfrp59+ajFnaWmpUlNTNW/ePAUFBenll1/WtGnTVFtbq+nTp59yzr/Umhr0lz7//HPFxsaqR48eWrJkiQICAvTSSy/pnnvu0X/+8x89+uijdvGzZs3StddeqxdeeEHV1dWaMWOGbrrpJu3Zs0fe3t4uzRU4ZxkAOrwXX3zRkGR8+OGHtrG7777bkGS8+uqrdrGjR482Lr/8crsxScbcuXNtr//yl78Ykoxt27Y55Lr77ruNsLCwZufS0NBg1NXVGWvXrjW8vb2N77//vtX7GoZh/PDDD4bVajVuvfVWu/F//OMfhiQjPj7eNlZWVmZIMl588UXb2AUXXGCkpqa2mGPMmDFO59F0vD59+hi1tbVO3/tlrrlz5xqSjLS0NLvYl19+2ZBkvPTSS7axkz/jJmFhYcbdd99te+3KZ79582ZDkrFw4UK7uJycHEOSsXLlSrs8VqvV2Ldvn23s559/Nrp162Y88MADDrkAAIBzTXXX+++/b9TV1RlHjhwxNm/ebAQFBRlDhgwx6urqbLERERHGwIED7cYMwzBuvPFGo2fPnkZDQ4NhGIaRmJhodOrUyaisrLTF1NfXGxEREYYko6yszDbe2ppi27ZtDjVFU324evVqu33XrVtnSDJyc3Ptxj/88ENDkrF8+XLDMAxjz549LdY+v8zvzIoVKwxJxhtvvGE3ft999zVbZzV57bXXDElGaWlps8f/7rvvmv18mo73+OOPN/veL4WFhRleXl4O+UaMGGF07drV+OmnnwzD+N/vwy9/Robh/PNvrgY1DMef6+23325YLBajvLzcLm7UqFFG586djR9//NEuz+jRo+3iXn31VUOSUVhY6DQfYEZcogh4MC8vL9100012Y1dddZX27dvXrnlKSko0duxYBQQEyNvbW76+vpo4caIaGhq0d+9el45VWFio48eP63e/+53deGxsrMLCwk65/29+8xutWbNGTz75pN5///02XSY5duxYl868OnmuEyZMkI+Pj7Zt2+Zyble8++67kuTwxKLf/va3Ov/88/XOO+/YjQ8YMEChoaG211arVZdddlm7fx8AAPAEgwcPlq+vr7p06aIbbrhBF110kd544w35+Jy4iObLL7/Uv/71L1udUF9fb9tGjx6tiooK2yVk27Zt0/DhwxUYGGg7vre3d6vObGqLcePG2b3+61//qgsvvFA33XST3TwHDBigoKAg22V2TbVNc7XPqWzbtk1dunTR2LFj7cbvvPPOU+47YMAA+fn56f7779ef//xn/fvf/z7lPs6cvPaWXHHFFerfv7/d2J133qnq6moVFxe3KX9rvfvuuxo+fLhCQkLsxu+55x4dO3bM4ab4J3+mV111lSRR56FDocEFeLDOnTvLarXajVksFh0/frzdcpSXlysuLk4HDhzQn/70J+3YsUMffvihli1bJunEqeiuaLqUMCgoyOE9Z2Mny8nJ0d13360XXnhBMTEx6tatmyZOnOj03g7N6dmzZ+sn7GRePj4+CggIsK3lTDl06JB8fHwcTr338vJSUFCQQ/6AgACHY1gsFpd/RgAAQFq7dq0+/PBDvfvuu3rggQe0Z88e3XHHHbb3m+7FNX36dPn6+tptKSkpkqSqqipJJ/6b3tbax1WdO3dW165d7cb+85//6Mcff5Sfn5/DXCsrK+3m6WxeTbXPqRw6dMiuidekNevs06ePtm7dqh49eujBBx9Unz591KdPH/3pT3865b6/5Eqd19LP5GzUec7mGhwc7DT/yZ9/020qqPPQkXAPLgBn1Ouvv66ffvpJGzZssDvDqrS0tE3Ha/qPs7OGVGVlpcNN1k/WvXt3ZWVlKSsrS+Xl5dq0aZNmzpypgwcPavPmza2aQ3M3GW1OZWWlLr74Ytvr+vp6HTp0yK7QsFgsDjf3l06vOAoICFB9fb2+++47uyaXYRiqrKzUNddc0+ZjAwCAlkVGRtpuLD9s2DA1NDTohRde0Guvvabx48ere/fukqSMjAzddtttTo/RdG/RgICAZmufk51uTeGszunevbsCAgKarZW6dOlim2fTvJzVPqcSEBDg9Ib0rf1DZFxcnOLi4tTQ0KBdu3bpueeeU2pqqgIDA3X77be36hiu1Hkt/UyaPoumPyaf/DNpagq2VUBAgCoqKhzGv/32W0myfb8AT8IZXABc5spffJqKhF/ezNwwDD3//PNtyj148GBZrVa9/PLLduM7d+50+RTr0NBQPfTQQxoxYoTdaeTtfdbSyXN99dVXVV9fr6FDh9rGevXqpY8++sgu7t1339XRo0ftxlz57JueevTSSy/Zjefm5uqnn36yvQ8AAM68hQsX6qKLLtLjjz+uxsZGXX755br00ku1e/duRUdHO92aGkfDhg3TO++8Y/cExoaGBuXk5DjkaW1N4Yobb7xRhw4dUkNDg9N5NjXimmqb5mqfUxk2bJiOHDmiTZs22Y27ejN9b29vDRo0yHbFQFOd195nLX366afavXu33dgrr7yiLl262B4G1PTH15N/JievsWl+rZ3b8OHD9e6779oaWk3Wrl2rzp07a/Dgwa1dBtBhcAYXAJf169dPkrRy5Up16dJFVqtV4eHhTk89HzFihPz8/HTHHXfo0Ucf1fHjx7VixQr98MMPbcp90UUXafr06XryySc1ZcoU/fa3v9X+/fttT69pyeHDhzVs2DDdeeedioiIUJcuXfThhx9q8+bNdn85vfLKK7VhwwatWLFCUVFROu+882x/gW2LDRs2yMfHRyNGjLA9RbF///6aMGGCLSYpKUlz5szR448/rvj4eH322WdaunSp/P397Y7l6mc/cuRIzZgxQ9XV1br22mttT1EcOHCgkpKS2rwmAADgmosuukgZGRl69NFH9corr+iuu+7S//k//0ejRo3SyJEjdc899+jiiy/W999/rz179qi4uFh/+ctfJEmPPfaYNm3apP/6r//S448/rs6dO2vZsmVOn/LX2prCFbfffrtefvlljR49WtOmTdNvfvMb+fr66ptvvtG2bdt0880369Zbb1VkZKTuuusuZWVlydfXV9dff70++eQT25OnT2XixIn64x//qIkTJ+qpp57SpZdeqry8PL399tun3Dc7O1vvvvuuxowZo9DQUB0/ftz2tO7rr79e0okzzcLCwvTGG29o+PDh6tatm7p3737KKwCaExwcrLFjx2revHnq2bOnXnrpJeXn52vBggXq3LmzJOmaa67R5ZdfrunTp6u+vl4XXXSRNm7cqPfee8/heK7UoHPnztVf//pXDRs2TI8//ri6deuml19+WW+99ZYWLlx4Wj9vwLTcfZd7AGdec09RPP/88x1inT0lRk6eNpOVlWWEh4cb3t7edk+1cfYkxDfffNPo37+/YbVajYsvvth45JFHjL/97W9On9xzqqcoGoZh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AAAAAACAqdHgAgAAgFPLly9XeHi4rFaroqKitGPHjhbjCwoKFBUVJavVqt69eys7O9sh5scff9SDDz6onj17ymq1KjIyUnl5eWdqCQAAwEPQ4AIAAICDnJwcpaamavbs2SopKVFcXJxGjRpl9zChXyorK9Po0aMVFxenkpISzZo1S1OnTlVubq4tpra2ViNGjNDXX3+t1157TZ9//rmef/55XXzxxWdrWQAAoINq003mAQAA0LEtXrxYkydP1pQpUyRJWVlZevvtt7VixQplZmY6xGdnZys0NFRZWVmSpMjISO3atUuLFi2yPT179erV+v7777Vz5075+vpKku1J3AAAAKeDM7gAAABgp7a2VkVFRUpISLAbT0hI0M6dO53uU1hY6BA/cuRI7dq1S3V1dZKkTZs2KSYmRg8++KACAwPVr18/Pf3002poaGh2LjU1NaqurrbbAAAATkaDCwAAAHaqqqrU0NCgwMBAu/HAwEBVVlY63aeystJpfH19vaqqqiRJ//73v/Xaa6+poaFBeXl5euyxx/SHP/xBTz31VLNzyczMlL+/v20LCQk5zdUBAICOiAYXAAAAnPLy8rJ7bRiGw9ip4n853tjYqB49emjlypWKiorS7bffrtmzZ2vFihXNHjMjI0OHDx+2bfv372/rcgAAQAfGPbgAAABgp3v37vL29nY4W+vgwYMOZ2k1CQoKchrv4+OjgIAASVLPnj3l6+srb29vW0xkZKQqKytVW1srPz8/h+NaLBZZLJbTXRIAAOjgOIMLAAAAdvz8/BQVFaX8/Hy78fz8fMXGxjrdJyYmxiF+y5Ytio6Ott1Q/tprr9WXX36pxsZGW8zevXvVs2dPp80tAACA1qLBBQAAAAfp6el64YUXtHr1au3Zs0dpaWkqLy9XcnKypBOXDk6cONEWn5ycrH379ik9PV179uzR6tWrtWrVKk2fPt0W89///d86dOiQpk2bpr179+qtt97S008/rQcffPCsrw8AAHQsXKIIAAAAB4mJiTp06JDmz5+viooK9evXT3l5eQoLC5MkVVRUqLy83BYfHh6uvLw8paWladmyZQoODtaSJUs0btw4W0xISIi2bNmitLQ0XXXVVbr44os1bdo0zZgx46yvDwAAdCw0uAAAAOBUSkqKUlJSnL63Zs0ah7H4+HgVFxe3eMyYmBi9//777TE9AAAA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",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "# Drawing both of them\n",
+ "figure, axis = plt.subplots(1, 2)\n",
+ "figure.set_figheight(4)\n",
+ "figure.set_figwidth(12)\n",
+ "figure.tight_layout(w_pad = 5)\n",
+ "\n",
+ "def draw(y, id):\n",
+ " x = np.array(range(0, np.size(y)))\n",
+ " axis[id].bar(x, y, color=\"lightgreen\", ec='black')\n",
+ " axis[id].scatter(x, y, color=\"orange\")\n",
+ "\n",
+ "axis[0].title.set_text(\"Initial distribution\")\n",
+ "axis[1].title.set_text(\"Required distribution\")\n",
+ "draw(a, 0)\n",
+ "draw(b, 1)\n",
+ "\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "RuuNFQUcDCXl",
+ "outputId": "b0dc6795-09a7-4160-b8b5-fbef7b182f11"
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0.9375\n"
+ ]
+ }
+ ],
+ "source": [
+ "import pandas as pd\n",
+ "from aif360.sklearn.metrics import ot_distance\n",
+ "\n",
+ "_a = pd.Series(a)\n",
+ "_b = pd.Series(b)\n",
+ "c = ot_distance(_a, _b)\n",
+ "\n",
+ "print(c)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "982f8APNDCXm"
+ },
+ "source": [
+ "## Usage\n",
+ "\n",
+ "The type of outcomes must be provided using the `mode` keyword argument. The definition for the four types of outcomes supported are provided below:\n",
+ "- Binary: Yes/no outcomes. Outcomes must 0 or 1.\n",
+ "- Continuous: Continuous outcomes. Outcomes could be any real number.\n",
+ "- Nominal: Multiclass outcomes with no rank or order between them. Outcomes must be a finite set of integers.\n",
+ "- Ordinal: Multiclass outcomes that are ranked in a specific order. Outcomes must be positive integers."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "AXZLAng1DCXm"
+ },
+ "source": [
+ "## Compas Dataset"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "JbzgBkZ6DCXm"
+ },
+ "source": [
+ "We'll demonstrate finding the scanning for bias with earth_movers_distance using the Compas dataset. We scan for bias in the predictions of an `sklearn` logistic regression model with respect to different groups."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "S_a5CpBmDCXm"
+ },
+ "outputs": [],
+ "source": [
+ "from aif360.algorithms.preprocessing.optim_preproc_helpers.data_preproc_functions import load_preproc_data_compas\n",
+ "\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "\n",
+ "np.random.seed(0)\n",
+ "dataset_orig = load_preproc_data_compas()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "MetVVGgEDCXm"
+ },
+ "source": [
+ "We scan for bias at first with respect to `sex`, and then `age`.\n",
+ "\n",
+ "To scan for bias with respect for a feature that is one-hot encoded - in this case, age category - we need to convert it to nominal or ordinal format."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "nKjqZpvrDCXm",
+ "outputId": "eb2d18a6-3d40-4a89-f9de-89b7b82441cd"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
sex
\n",
+ "
race
\n",
+ "
priors_count=0
\n",
+ "
priors_count=1 to 3
\n",
+ "
priors_count=More than 3
\n",
+ "
c_charge_degree=F
\n",
+ "
c_charge_degree=M
\n",
+ "
age_cat
\n",
+ "
two_year_recid
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1
\n",
+ "
1.0
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1
\n",
+ "
0.0
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
0.0
\n",
+ "
1.0
\n",
+ "
0.0
\n",
+ "
1
\n",
+ "
0.0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sex race priors_count=0 priors_count=1 to 3 priors_count=More than 3 \\\n",
+ "0 0.0 0.0 1.0 0.0 0.0 \n",
+ "1 0.0 0.0 0.0 0.0 1.0 \n",
+ "2 0.0 1.0 0.0 0.0 1.0 \n",
+ "3 1.0 1.0 1.0 0.0 0.0 \n",
+ "4 0.0 1.0 1.0 0.0 0.0 \n",
+ "\n",
+ " c_charge_degree=F c_charge_degree=M age_cat two_year_recid \n",
+ "0 1.0 0.0 1 1.0 \n",
+ "1 1.0 0.0 0 1.0 \n",
+ "2 1.0 0.0 1 1.0 \n",
+ "3 0.0 1.0 1 0.0 \n",
+ "4 1.0 0.0 1 0.0 "
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "dataset_orig_df = pd.DataFrame(dataset_orig.features, columns=dataset_orig.feature_names)\n",
+ "# Binning the features corresponding to age ('reshaping' them into one ordinal column)\n",
+ "age_cat_cols = ['age_cat=Less than 25', 'age_cat=25 to 45', 'age_cat=Greater than 45']\n",
+ "age_cat = np.argmax(dataset_orig_df[age_cat_cols].values, axis=1).reshape(-1, 1)\n",
+ "df = dataset_orig_df.drop(age_cat_cols, axis=1)\n",
+ "df['age_cat'] = age_cat\n",
+ "df['two_year_recid'] = dataset_orig.labels\n",
+ "df.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "gdQM2Pf0DCXm"
+ },
+ "source": [
+ "### Measuring bias with respect to `sex`\n",
+ "\n",
+ "\n",
+ "We train a linear regression model on the dataset, and scan its results for bias with respect to `sex` using `earth_movers_distance`.\n",
+ "\n",
+ "The arguments are as follows:\n",
+ "- `ground_truth`: ground truth labels;\n",
+ "- `classifier`: predicted labels;\n",
+ "- `prot_attr`: the values of the sensitive attributes (with respect to which the classifier may be introducing bias);\n",
+ "- `num_iters`: maximum number of iterations performed when calculating the Earth Mover's Distance;\n",
+ "- `mode`: mode of the labels, one of binary, nominal, ordinal and continious; in our case the labels are binary."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "1UMII6BdDCXn"
+ },
+ "outputs": [],
+ "source": [
+ "from aif360.sklearn.metrics import ot_distance\n",
+ "from sklearn.linear_model import LogisticRegression\n",
+ "from sklearn.model_selection import train_test_split\n",
+ "\n",
+ "X = df.drop('two_year_recid', axis=1)\n",
+ "y = df['two_year_recid']\n",
+ "clf = LogisticRegression(solver='lbfgs', max_iter=10000, C=1.0, penalty='l2')\n",
+ "clf.fit(X, y)\n",
+ "preds = pd.Series(clf.predict_proba(X)[:,0])\n",
+ "\n",
+ "ot_val1 = ot_distance(y_true=y, y_pred=preds, prot_attr=df['sex'])"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "dJ9XucBpDCXn",
+ "outputId": "14f8db55-0698-4582-f0ab-a0d5b51d32c0"
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
sex
\n",
+ "
ot_val
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
0.0
\n",
+ "
0.000209
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
1.0
\n",
+ "
0.001647
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " sex ot_val\n",
+ "0 0.0 0.000209\n",
+ "1 1.0 0.001647"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "bs1 = pd.DataFrame({\"sex\": ot_val1.keys(), \"ot_val\": ot_val1.values()})\n",
+ "display(bs1)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "qMuMdXc9DCXn"
+ },
+ "source": [
+ "We get the bias value for each each of the protected groups - in this case, Male (`0`) and Female (`1`).\n",
+ "\n",
+ "These values range from 0 to 1 and can be interpreted as the difference in percent between the ground truth distribution and the distribution of the protected group: for example, a value of 0.3 would mean a 30% difference."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "AZHqN9JTDCXn"
+ },
+ "source": [
+ "### Measuring bias with respect to `age_cat`\n",
+ "\n",
+ "Now we measure the bias of the same classifier with respect to the age category."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "usxcylLxDCXn"
+ },
+ "outputs": [],
+ "source": [
+ "ot_val2 = ot_distance(y_true=y, y_pred=preds, prot_attr=df['age_cat'])"
+ ]
+ },
{
- "data": {
- "text/html": [
- "