# error-parity <!-- omit in toc -->
![Tests status](https://github.com/socialfoundations/error-parity/actions/workflows/python-tests.yml/badge.svg)
![PyPI status](https://github.com/socialfoundations/error-parity/actions/workflows/python-publish.yml/badge.svg)
![Documentation status](https://github.com/socialfoundations/error-parity/actions/workflows/python-docs.yml/badge.svg)
![PyPI version](https://badgen.net/pypi/v/error-parity)
![OSI license](https://badgen.net/pypi/license/error-parity)
![Python compatibility](https://badgen.net/pypi/python/error-parity)
<!-- ![PyPI version](https://img.shields.io/pypi/v/error-parity) -->
<!-- ![OSI license](https://img.shields.io/pypi/l/error-parity) -->
<!-- ![Compatible python versions](https://img.shields.io/pypi/pyversions/error-parity) -->
> Work presented as an _oral at ICLR 2024_, titled ["Unprocessing Seven Years of Algorithmic Fairness"](https://openreview.net/forum?id=jr03SfWsBS).
Fast postprocessing of any score-based predictor to meet fairness criteria.
The `error-parity` package can achieve strict or relaxed fairness constraint fulfillment,
which can be useful to compare ML models at equal fairness levels.
Package documentation available [here](https://socialfoundations.github.io/error-parity/).
Contents:
- [Installing](#installing)
- [Getting started](#getting-started)
- [How it works](#how-it-works)
- [Fairness constraints](#fairness-constraints)
- [Equalized odds relaxations](#equalized-odds-relaxations)
- [Citing](#citing)
## Installing
Install package from [PyPI](https://pypi.org/project/error-parity/):
```
pip install error-parity
```
Or, for development, you can clone the repo and install from local sources:
```
git clone https://github.com/socialfoundations/error-parity.git
pip install ./error-parity
```
## Getting started
> See detailed example notebooks under the [**examples folder**](./examples/)
> and on the [**package documentation**](https://socialfoundations.github.io/error-parity/notebooks.html).
```py
from error_parity import RelaxedThresholdOptimizer
# Given any trained model that outputs real-valued scores
fair_clf = RelaxedThresholdOptimizer(
predictor=lambda X: model.predict_proba(X)[:, -1], # for sklearn API
# predictor=model, # use this for a callable model
constraint="equalized_odds", # other constraints are available
tolerance=0.05, # fairness constraint tolerance
)
# Fit the fairness adjustment on some data
# This will find the optimal _fair classifier_
fair_clf.fit(X=X, y=y, group=group)
# Now you can use `fair_clf` as any other classifier
# You have to provide group information to compute fair predictions
y_pred_test = fair_clf(X=X_test, group=group_test)
```
## How it works
Given a callable score-based predictor (i.e., `y_pred = predictor(X)`), and some `(X, Y, S)` data to fit, `RelaxedThresholdOptimizer` will:
1. Compute group-specific ROC curves and their convex hulls;
2. Compute the $r$-relaxed optimal solution for the chosen fairness criterion (using [cvxpy](https://www.cvxpy.org));
3. Find the set of group-specific binary classifiers that match the optimal solution found.
- each group-specific classifier is made up of (possibly randomized) group-specific thresholds over the given predictor;
- if a group's ROC point is in the interior of its ROC curve, partial randomization of its predictions may be necessary.
## Fairness constraints
You can choose specific fairness constraints via the `constraint` key-word argument to
the `RelaxedThresholdOptimizer` constructor.
The equation under each constraint details how it is evaluated, where $r$ is the
relaxation (or tolerance) and $\mathcal{S}$ is the set of sensitive groups.
Currently implemented fairness constraints:
- [x] equalized odds (Hardt et al., 2016) **[default]**;
- i.e., equal group-specific TPR and FPR;
- use `constraint="equalized_odds"`;
- $\max_{a, b \in \mathcal{S}} \max_{y \in \{0, 1\}} \left( \mathbb{P}[\hat{Y}=1 | S=a, Y=y] - \mathbb{P}[\hat{Y}=1 | S=b, Y=y] \right) \leq r$
- [other relaxations available](#equalized-odds-relaxations) by changing the `l_p_norm` parameter;
- [x] equal opportunity;
- i.e., equal group-specific TPR;
- use `constraint="true_positive_rate_parity"`;
- $\max_{a, b \in \mathcal{S}} \left( \mathbb{P}[\hat{Y}=1 | S=a, Y=1] - \mathbb{P}[\hat{Y}=1 | S=b, Y=1] \right) \leq r$
- [x] predictive equality;
- i.e., equal group-specific FPR;
- use `constraint="false_positive_rate_parity"`;
- $\max_{a, b \in \mathcal{S}} \left( \mathbb{P}[\hat{Y}=1 | S=a, Y=0] - \mathbb{P}[\hat{Y}=1 | S=b, Y=0] \right) \leq r$
- [x] demographic parity;
- i.e., equal group-specific predicted prevalence;
- use `constraint="demographic_parity"`;
- $\max_{a, b \in \mathcal{S}} \left( \mathbb{P}[\hat{Y}=1 | S=a] - \mathbb{P}[\hat{Y}=1 | S=b] \right) \leq r$
> We welcome community contributions for [cvxpy](https://www.cvxpy.org) implementations of other fairness constraints.
### Equalized odds relaxations
When using `constraint="equalized_odds"`, different relaxations can be chosen by
altering the `l_p_norm` parameter (which dictates how to compute the distance
between group-specific ROC points).
A few useful values:
- `l_p_norm=np.inf` **[default]** evaluates equalized-odds as the maximum
between group-wise TPR and FPR differences (as shown above);
- `l_p_norm=1` evaluates equalized-odds as the sum of absolute difference in group-wise TPR and FPR;
- corresponds to *twice* the "average absolute odds" metric;
- accordingly, use twice the `tolerance` target to constrain the `average_abs_odds_difference`;
The actual equalized odds constraint implemented is:
$\max_{a, b \in \mathcal{S}} \left\lVert ROC_a - ROC_b \right\rVert_p \leq r,$ where $ROC_a$ is the ROC point of group $S=a$ and $ROC_b$ is the ROC point of group $S=b$.
## Citing
```
@inproceedings{
cruz2024unprocessing,
title={Unprocessing Seven Years of Algorithmic Fairness},
author={Andr{\'e} Cruz and Moritz Hardt},
booktitle={The Twelfth International Conference on Learning Representations},
year={2024},
url={https://openreview.net/forum?id=jr03SfWsBS}
}
```
Raw data
{
"_id": null,
"home_page": "https://github.com/socialfoundations/error-parity",
"name": "error-parity",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "ml, optimization, fairness, error-parity, equal-odds",
"author": "AndreFCruz",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/d9/09/cc40f372a4f53872241e7db0216d6bac6daf715c7baa2a2e86b03b6b1bae/error_parity-0.3.11.tar.gz",
"platform": null,
"description": "# error-parity <!-- omit in toc -->\n\n![Tests status](https://github.com/socialfoundations/error-parity/actions/workflows/python-tests.yml/badge.svg)\n![PyPI status](https://github.com/socialfoundations/error-parity/actions/workflows/python-publish.yml/badge.svg)\n![Documentation status](https://github.com/socialfoundations/error-parity/actions/workflows/python-docs.yml/badge.svg)\n![PyPI version](https://badgen.net/pypi/v/error-parity)\n![OSI license](https://badgen.net/pypi/license/error-parity)\n![Python compatibility](https://badgen.net/pypi/python/error-parity)\n<!-- ![PyPI version](https://img.shields.io/pypi/v/error-parity) -->\n<!-- ![OSI license](https://img.shields.io/pypi/l/error-parity) -->\n<!-- ![Compatible python versions](https://img.shields.io/pypi/pyversions/error-parity) -->\n\n> Work presented as an _oral at ICLR 2024_, titled [\"Unprocessing Seven Years of Algorithmic Fairness\"](https://openreview.net/forum?id=jr03SfWsBS).\n\n\nFast postprocessing of any score-based predictor to meet fairness criteria.\n\nThe `error-parity` package can achieve strict or relaxed fairness constraint fulfillment, \nwhich can be useful to compare ML models at equal fairness levels.\n\nPackage documentation available [here](https://socialfoundations.github.io/error-parity/).\n\nContents:\n- [Installing](#installing)\n- [Getting started](#getting-started)\n- [How it works](#how-it-works)\n- [Fairness constraints](#fairness-constraints)\n - [Equalized odds relaxations](#equalized-odds-relaxations)\n- [Citing](#citing)\n\n\n## Installing\n\nInstall package from [PyPI](https://pypi.org/project/error-parity/):\n```\npip install error-parity\n```\n\nOr, for development, you can clone the repo and install from local sources:\n```\ngit clone https://github.com/socialfoundations/error-parity.git\npip install ./error-parity\n```\n\n\n## Getting started\n\n> See detailed example notebooks under the [**examples folder**](./examples/) \n> and on the [**package documentation**](https://socialfoundations.github.io/error-parity/notebooks.html).\n\n```py\nfrom error_parity import RelaxedThresholdOptimizer\n\n# Given any trained model that outputs real-valued scores\nfair_clf = RelaxedThresholdOptimizer(\n predictor=lambda X: model.predict_proba(X)[:, -1], # for sklearn API\n # predictor=model, # use this for a callable model\n constraint=\"equalized_odds\", # other constraints are available\n tolerance=0.05, # fairness constraint tolerance\n)\n\n# Fit the fairness adjustment on some data\n# This will find the optimal _fair classifier_\nfair_clf.fit(X=X, y=y, group=group)\n\n# Now you can use `fair_clf` as any other classifier\n# You have to provide group information to compute fair predictions\ny_pred_test = fair_clf(X=X_test, group=group_test)\n```\n\n\n## How it works\n\nGiven a callable score-based predictor (i.e., `y_pred = predictor(X)`), and some `(X, Y, S)` data to fit, `RelaxedThresholdOptimizer` will:\n1. Compute group-specific ROC curves and their convex hulls;\n2. Compute the $r$-relaxed optimal solution for the chosen fairness criterion (using [cvxpy](https://www.cvxpy.org));\n3. Find the set of group-specific binary classifiers that match the optimal solution found.\n - each group-specific classifier is made up of (possibly randomized) group-specific thresholds over the given predictor;\n - if a group's ROC point is in the interior of its ROC curve, partial randomization of its predictions may be necessary.\n\n\n## Fairness constraints\n\nYou can choose specific fairness constraints via the `constraint` key-word argument to\nthe `RelaxedThresholdOptimizer` constructor.\nThe equation under each constraint details how it is evaluated, where $r$ is the\nrelaxation (or tolerance) and $\\mathcal{S}$ is the set of sensitive groups.\n\nCurrently implemented fairness constraints:\n- [x] equalized odds (Hardt et al., 2016) **[default]**;\n - i.e., equal group-specific TPR and FPR;\n - use `constraint=\"equalized_odds\"`;\n - $\\max_{a, b \\in \\mathcal{S}} \\max_{y \\in \\{0, 1\\}} \\left( \\mathbb{P}[\\hat{Y}=1 | S=a, Y=y] - \\mathbb{P}[\\hat{Y}=1 | S=b, Y=y] \\right) \\leq r$\n - [other relaxations available](#equalized-odds-relaxations) by changing the `l_p_norm` parameter;\n- [x] equal opportunity;\n - i.e., equal group-specific TPR;\n - use `constraint=\"true_positive_rate_parity\"`;\n - $\\max_{a, b \\in \\mathcal{S}} \\left( \\mathbb{P}[\\hat{Y}=1 | S=a, Y=1] - \\mathbb{P}[\\hat{Y}=1 | S=b, Y=1] \\right) \\leq r$\n- [x] predictive equality;\n - i.e., equal group-specific FPR;\n - use `constraint=\"false_positive_rate_parity\"`;\n - $\\max_{a, b \\in \\mathcal{S}} \\left( \\mathbb{P}[\\hat{Y}=1 | S=a, Y=0] - \\mathbb{P}[\\hat{Y}=1 | S=b, Y=0] \\right) \\leq r$\n- [x] demographic parity;\n - i.e., equal group-specific predicted prevalence;\n - use `constraint=\"demographic_parity\"`;\n - $\\max_{a, b \\in \\mathcal{S}} \\left( \\mathbb{P}[\\hat{Y}=1 | S=a] - \\mathbb{P}[\\hat{Y}=1 | S=b] \\right) \\leq r$\n\n> We welcome community contributions for [cvxpy](https://www.cvxpy.org) implementations of other fairness constraints.\n\n### Equalized odds relaxations\n\nWhen using `constraint=\"equalized_odds\"`, different relaxations can be chosen by\naltering the `l_p_norm` parameter (which dictates how to compute the distance \nbetween group-specific ROC points).\n\nA few useful values:\n- `l_p_norm=np.inf` **[default]** evaluates equalized-odds as the maximum\nbetween group-wise TPR and FPR differences (as shown above);\n- `l_p_norm=1` evaluates equalized-odds as the sum of absolute difference in group-wise TPR and FPR;\n - corresponds to *twice* the \"average absolute odds\" metric;\n - accordingly, use twice the `tolerance` target to constrain the `average_abs_odds_difference`;\n\nThe actual equalized odds constraint implemented is:\n\n$\\max_{a, b \\in \\mathcal{S}} \\left\\lVert ROC_a - ROC_b \\right\\rVert_p \\leq r,$ where $ROC_a$ is the ROC point of group $S=a$ and $ROC_b$ is the ROC point of group $S=b$.\n\n\n\n## Citing\n\n```\n@inproceedings{\n cruz2024unprocessing,\n title={Unprocessing Seven Years of Algorithmic Fairness},\n author={Andr{\\'e} Cruz and Moritz Hardt},\n booktitle={The Twelfth International Conference on Learning Representations},\n year={2024},\n url={https://openreview.net/forum?id=jr03SfWsBS}\n}\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Achieve error-rate parity between protected groups for any predictor",
"version": "0.3.11",
"project_urls": {
"Homepage": "https://github.com/socialfoundations/error-parity"
},
"split_keywords": [
"ml",
" optimization",
" fairness",
" error-parity",
" equal-odds"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "bee565b9cec0ca880ef9565e07d0f423b65551838891b0b86b11072b94a36e3c",
"md5": "8aaa1996bdc530de994f3f5eaa23ca78",
"sha256": "efe0d9366bd8afa30584e9c30f78aef21be79b1669080f68ebcce0fdf8851cf6"
},
"downloads": -1,
"filename": "error_parity-0.3.11-py3-none-any.whl",
"has_sig": false,
"md5_digest": "8aaa1996bdc530de994f3f5eaa23ca78",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 44212,
"upload_time": "2024-04-26T09:42:54",
"upload_time_iso_8601": "2024-04-26T09:42:54.475574Z",
"url": "https://files.pythonhosted.org/packages/be/e5/65b9cec0ca880ef9565e07d0f423b65551838891b0b86b11072b94a36e3c/error_parity-0.3.11-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "d909cc40f372a4f53872241e7db0216d6bac6daf715c7baa2a2e86b03b6b1bae",
"md5": "bc435ba28069ae1df51197ea48fb4e1a",
"sha256": "e105d4196c8ef4c028fea0d77cdccf8bd066d43c69ef11808a2164ff7c525704"
},
"downloads": -1,
"filename": "error_parity-0.3.11.tar.gz",
"has_sig": false,
"md5_digest": "bc435ba28069ae1df51197ea48fb4e1a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 40912,
"upload_time": "2024-04-26T09:42:56",
"upload_time_iso_8601": "2024-04-26T09:42:56.177439Z",
"url": "https://files.pythonhosted.org/packages/d9/09/cc40f372a4f53872241e7db0216d6bac6daf715c7baa2a2e86b03b6b1bae/error_parity-0.3.11.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-04-26 09:42:56",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "socialfoundations",
"github_project": "error-parity",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "error-parity"
}