Metadata-Version: 2.4
Name: fairness_training
Version: 0.1.0
Summary: Guaranteed fairness constraints for PyTorch neural networks via differentiable optimization layers.
Author-email: David Troxell <davidtroxell@g.ucla.edu>
License-Expression: MIT
Project-URL: Homepage, https://github.com/dtroxell19/fairness_training
Project-URL: Issues, https://github.com/dtroxell19/fairness_training/issues
Project-URL: Changelog, https://github.com/dtroxell19/fairness_training/blob/main/CHANGELOG.md
Keywords: fairness,machine learning,deep learning,pytorch,cvxpy,constrained optimization
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: full
Requires-Dist: torch==2.6.0; extra == "full"
Requires-Dist: torchvision==0.21.0; extra == "full"
Requires-Dist: torch-geometric==2.6.1; extra == "full"
Requires-Dist: numpy==2.2.4; extra == "full"
Requires-Dist: scipy==1.15.2; extra == "full"
Requires-Dist: cvxpy==1.6.5; extra == "full"
Requires-Dist: cvxpylayers>=0.1.6; extra == "full"
Requires-Dist: ecos==2.0.14; extra == "full"
Requires-Dist: diffcp>=1.1.5; extra == "full"
Requires-Dist: datasets==3.5.0; extra == "full"
Requires-Dist: transformers==4.50.3; extra == "full"
Requires-Dist: openml>=0.15.1; extra == "full"
Requires-Dist: openai>=1.77.0; extra == "full"
Requires-Dist: opencv-python==4.11.0.86; extra == "full"
Requires-Dist: scikit-image>=0.25.2; extra == "full"
Requires-Dist: seaborn>=0.13.2; extra == "full"
Requires-Dist: tensorboard>=2.19.0; extra == "full"
Requires-Dist: pycocotools>=2.0.8; extra == "full"
Requires-Dist: cog>=0.14.4; extra == "full"
Provides-Extra: train
Requires-Dist: torch==2.6.0; extra == "train"
Requires-Dist: torchvision==0.21.0; extra == "train"
Requires-Dist: numpy==2.2.4; extra == "train"
Requires-Dist: scipy==1.15.2; extra == "train"
Requires-Dist: transformers==4.50.3; extra == "train"
Provides-Extra: verify
Requires-Dist: cvxpy==1.6.5; extra == "verify"
Requires-Dist: cvxpylayers>=0.1.6; extra == "verify"
Requires-Dist: ecos==2.0.14; extra == "verify"
Requires-Dist: diffcp>=1.1.5; extra == "verify"
Provides-Extra: cpu
Requires-Dist: torch==2.6.0; platform_system != "Windows" and extra == "cpu"
Requires-Dist: numpy==2.2.4; extra == "cpu"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: black>=25.9.0; extra == "dev"
Provides-Extra: viz
Requires-Dist: matplotlib>=3.7; extra == "viz"
Dynamic: license-file

# fairness_training

[![CI](https://github.com/dtroxell19/fairness_training/actions/workflows/ci.yml/badge.svg)](https://github.com/dtroxell19/fairness_training/actions/workflows/ci.yml)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![PyTorch 2.0+](https://img.shields.io/badge/PyTorch-2.0+-ee4c2c.svg)](https://pytorch.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/dtroxell19/fairness_training/blob/main/notebooks/quickstart.ipynb)

**Guaranteed fairness constraints for PyTorch neural networks**

## Why fairness_training?

Most fairness methods either *encourage* fairness (soft penalties) or correct predictions *after* training (post-hoc). Both leave the door open for violations.

`fairness_training` uses a **differentiable convex optimization layer** appended to any neural network. This layer projects predictions onto the set satisfying your fairness constraints — making violations mathematically impossible on every batch.

| Approach | Guarantee | Differentiable | Works with any architecture |
|----------|-----------|----------------|-----------------------------|
| Penalty / regularization | No | Yes | Yes |
| Post-hoc calibration | No | No | Yes |
| **fairness_training** | **Yes** | **Yes** | **Yes** |

---

## Features

- **Hard constraints** — fairness gaps are bounded by your chosen ε on every training batch
- **Drop-in wrapper** — `FairModel.wrap(your_model, ...)` works with any `nn.Module`
- **Online inference** — primal-dual algorithm provides aggregate guarantees for streaming / small-batch deployment
- **Three built-in metrics** — demographic parity, mean residual fairness, equalized odds
- **Extensible** — subclass `FairnessMetric` to define custom affine fairness constraints

---

## Installation

```bash
pip install fairness_training[train,verify]       # training + fairness layer (recommended)
pip install fairness_training[train,verify,viz]   # + matplotlib visualization utilities
pip install fairness_training[full]               # everything
```

---

## Quick Example

```python
import torch.nn as nn
import torch.optim as optim
from fairness_training import FairModel, FairTrainer, create_stratified_dataloaders

# Any standard PyTorch backbone
backbone = nn.Sequential(
    nn.Linear(20, 64), nn.ReLU(),
    nn.Linear(64, 32), nn.ReLU(),
    nn.Linear(32, 1), nn.Sigmoid(),
)

# Wrap with fairness constraints — bounds inferred automatically
model = FairModel.wrap(
    backbone,
    protected_attr_idx=0,       # column index of protected attribute in X
    fairness_tolerance=0.05,    # ε: max allowed group mean prediction gap
    fairness_metric='mean_pred',
)

# Stratified batching keeps group ratios constant → per-batch constraints = aggregate fairness
train_loader, val_loader, test_loader = create_stratified_dataloaders(
    X_train, y_train, X_val, y_val, X_test, y_test,
    protected_attr_idx=0, batch_size_train=256,
)

trainer = FairTrainer(model, nn.BCELoss(), optim.Adam(model.parameters()))
history = trainer.fit(train_loader, val_loader, epochs=50)

metrics = trainer.evaluate(test_loader)
print(f"Test loss:    {metrics['test_loss']:.4f}")
print(f"Fairness gap: {metrics['weighted_avg_fairness_gap']:.4f}  (target ≤ 0.05)")
```

---

## Supported Fairness Metrics

| Metric | Constraint | String alias |
|--------|-----------|--------------|
| Mean Prediction Parity | `|E[ŷ|A=0] − E[ŷ|A=1]| ≤ ε` | `'mean_pred'` |
| Mean Residual Fairness | `|E[y−ŷ|A=a]| ≤ ε ∀a` | `'mean_residual'` |
| Equalized Odds | `|E[ŷ|A=0,y=c] − E[ŷ|A=1,y=c]| ≤ ε ∀c` | `'equalized_odds'` |
| Custom | Subclass `FairnessMetric` | — |

---

## Documentation

Full documentation — concepts, API reference, and end-to-end examples — is available at the project's GitHub Pages site.

---

## Citation

If you use `fairness_training` in your research, please cite:

```bibtex
@inproceedings{troxell2026fairness,
  title     = {Differentiable Optimization Layers for Guaranteed Fairness in Deep Learning},
  author    = {Troxell, David and Roemer, Noah and Mont{\'u}far, Guido},
  booktitle = {Proceedings of the 43rd International Conference on Machine Learning},
  year      = {2026},
  note      = {To appear}
}
```

This library builds on the excellent [cvxpylayers](https://locuslab.github.io/2019-10-28-cvxpylayers/) package:

```bibtex
@inproceedings{agrawal2019differentiable,
  title={Differentiable Convex Optimization Layers},
  author={Agrawal, Akshay and Amos, Brandon and Barratt, Shane and Boyd, Stephen and Diamond, Steven and Kolter, Zico},
  booktitle={Advances in Neural Information Processing Systems},
  volume={32},
  year={2019}
}
```

---

## License

MIT
