<p align="center">
<img src="assets/ExFairness.svg" alt="ExFairness" width="150"/>
</p>
# ExFairness
**Fairness and Bias Detection Library for Elixir AI/ML Systems**
[](https://elixir-lang.org)
[](https://www.erlang.org)
[](https://github.com/North-Shore-AI/ExFairness/blob/main/LICENSE)
[](https://hexdocs.pm/ex_fairness)
---
ExFairness is a comprehensive library for detecting, measuring, and mitigating bias in AI/ML systems built with Elixir. It provides rigorous fairness metrics, bias detection algorithms, and mitigation techniques to ensure your models make equitable predictions across different demographic groups.
## Features
### Fairness Metrics
- **Demographic Parity**: Ensures equal positive prediction rates across groups
- **Equalized Odds**: Ensures equal true positive and false positive rates across groups
- **Equal Opportunity**: Ensures equal true positive rates across groups (recall parity)
- **Predictive Parity**: Ensures equal positive predictive values (precision parity)
- **Calibration**: Ensures predicted probabilities match actual outcomes across groups
- **Individual Fairness**: Ensures similar individuals receive similar predictions
- **Counterfactual Fairness**: Predictions remain unchanged under counterfactual changes to sensitive attributes
### Bias Detection
- **Statistical Parity Testing**: Detects disparate impact in model outcomes
- **Disparate Impact Analysis**: Measures the ratio of outcomes between groups
- **Simpson's Paradox Detection**: Identifies misleading aggregated statistics
- **Subgroup Analysis**: Analyzes fairness across intersectional groups
- **Temporal Bias Drift**: Monitors fairness metrics over time
- **Label Bias Detection**: Identifies bias in training labels
- **Representation Bias**: Measures data imbalance across groups
### Mitigation Techniques
- **Pre-processing**: Reweighting, resampling, and fair representation learning
- **In-processing**: Adversarial debiasing, fairness constraints during training
- **Post-processing**: Threshold optimization, calibration, and reject option classification
- **Fair Feature Selection**: Identifies and removes biased features
- **Fairness-Aware Ensemble Methods**: Combines models to improve fairness
## Design Principles
1. **Mathematical Rigor**: All metrics based on established fairness research
2. **Transparency**: Clear explanations of fairness definitions and trade-offs
3. **Actionability**: Concrete mitigation strategies with implementation guidance
4. **Flexibility**: Support for multiple fairness definitions and use cases
5. **Integration**: Works seamlessly with Nx, Axon, and other Elixir ML tools
## Installation
Add `ex_fairness` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:ex_fairness, "~> 0.1.0"}
]
end
```
Or install from GitHub:
```elixir
def deps do
[
{:ex_fairness, github: "North-Shore-AI/ExFairness"}
]
end
```
## Quick Start
### Measure Demographic Parity
```elixir
# Binary classification predictions and sensitive attributes
predictions = Nx.tensor([1, 0, 1, 1, 0, 1, 0, 1])
sensitive_attr = Nx.tensor([0, 0, 0, 0, 1, 1, 1, 1]) # 0 = group A, 1 = group B
result = ExFairness.demographic_parity(predictions, sensitive_attr)
# => %{
# group_a_rate: 0.50,
# group_b_rate: 0.75,
# disparity: 0.25,
# passes: false,
# threshold: 0.10
# }
```
### Measure Equalized Odds
```elixir
# Include ground truth labels
predictions = Nx.tensor([1, 0, 1, 1, 0, 1, 0, 1])
labels = Nx.tensor([1, 0, 1, 0, 0, 1, 1, 1])
sensitive_attr = Nx.tensor([0, 0, 0, 0, 1, 1, 1, 1])
result = ExFairness.equalized_odds(predictions, labels, sensitive_attr)
# => %{
# group_a_tpr: 0.67,
# group_b_tpr: 0.67,
# group_a_fpr: 0.50,
# group_b_fpr: 0.00,
# tpr_disparity: 0.00,
# fpr_disparity: 0.50,
# passes: false
# }
```
### Equal Opportunity Analysis
```elixir
result = ExFairness.equal_opportunity(predictions, labels, sensitive_attr)
# => %{
# group_a_tpr: 0.67,
# group_b_tpr: 0.67,
# disparity: 0.00,
# passes: true,
# interpretation: "Model provides equal opportunity across groups"
# }
```
### Comprehensive Fairness Report
```elixir
report = ExFairness.fairness_report(predictions, labels, sensitive_attr,
metrics: [:demographic_parity, :equalized_odds, :equal_opportunity, :predictive_parity]
)
# => %{
# demographic_parity: %{passes: false, disparity: 0.25},
# equalized_odds: %{passes: false, tpr_disparity: 0.00, fpr_disparity: 0.50},
# equal_opportunity: %{passes: true, disparity: 0.00},
# predictive_parity: %{passes: true, disparity: 0.05},
# overall_assessment: "2 of 4 fairness metrics passed",
# recommendations: ["Consider threshold optimization for demographic parity", ...]
# }
```
## Bias Detection
### Disparate Impact Analysis
```elixir
# Measure the 80% rule (4/5ths rule)
impact = ExFairness.disparate_impact(predictions, sensitive_attr)
# => %{
# ratio: 0.67,
# passes_80_percent_rule: false,
# interpretation: "Group B receives 67% the rate of positive outcomes as Group A"
# }
```
### Intersectional Fairness
```elixir
# Analyze fairness across multiple sensitive attributes
gender = Nx.tensor([0, 0, 1, 1, 0, 0, 1, 1])
race = Nx.tensor([0, 1, 0, 1, 0, 1, 0, 1])
result = ExFairness.intersectional_fairness(predictions, labels,
sensitive_attrs: [gender, race],
attr_names: ["gender", "race"]
)
# Analyzes all combinations: (male, white), (male, black), (female, white), (female, black)
```
### Temporal Bias Monitoring
```elixir
# Monitor fairness drift over time
metrics_history = [
{~D[2025-01-01], %{demographic_parity: 0.10}},
{~D[2025-02-01], %{demographic_parity: 0.15}},
{~D[2025-03-01], %{demographic_parity: 0.25}}
]
drift = ExFairness.temporal_drift(metrics_history, threshold: 0.05)
# => %{
# drift_detected: true,
# drift_magnitude: 0.15,
# alert_level: :warning
# }
```
## Mitigation Techniques
### Reweighting (Pre-processing)
```elixir
# Reweight training samples to achieve demographic parity
{reweighted_data, weights} = ExFairness.Mitigation.reweight(
training_data,
sensitive_attr,
target: :demographic_parity
)
```
### Threshold Optimization (Post-processing)
```elixir
# Find optimal decision thresholds per group
probabilities = Nx.tensor([0.3, 0.7, 0.8, 0.6, 0.4, 0.9, 0.5, 0.7])
thresholds = ExFairness.Mitigation.optimize_thresholds(
probabilities,
labels,
sensitive_attr,
target_metric: :equalized_odds
)
# => %{group_a_threshold: 0.55, group_b_threshold: 0.45}
# Apply group-specific thresholds
fair_predictions = ExFairness.Mitigation.apply_thresholds(
probabilities,
sensitive_attr,
thresholds
)
```
### Adversarial Debiasing (In-processing)
```elixir
# Train with fairness constraints (integrates with Axon)
model = ExFairness.Mitigation.adversarial_debiasing(
base_model,
sensitive_attr,
adversary_strength: 0.5
)
```
### Fair Representation Learning
```elixir
# Learn fair representations that remove sensitive information
{fair_features, encoder} = ExFairness.Mitigation.fair_representation(
features,
sensitive_attr,
method: :variational_fair_autoencoder
)
```
## Module Structure
```
lib/ex_fairness/
├── ex_fairness.ex # Main API
├── metrics/
│ ├── demographic_parity.ex # Demographic parity metrics
│ ├── equalized_odds.ex # Equalized odds metrics
│ ├── equal_opportunity.ex # Equal opportunity metrics
│ ├── predictive_parity.ex # Predictive parity metrics
│ ├── calibration.ex # Calibration metrics
│ ├── individual_fairness.ex # Individual fairness metrics
│ └── counterfactual.ex # Counterfactual fairness
├── detection/
│ ├── disparate_impact.ex # Disparate impact analysis
│ ├── statistical_parity.ex # Statistical parity testing
│ ├── intersectional.ex # Intersectional analysis
│ ├── temporal_drift.ex # Temporal bias monitoring
│ ├── label_bias.ex # Label bias detection
│ └── representation.ex # Representation bias
├── mitigation/
│ ├── reweighting.ex # Reweighting techniques
│ ├── resampling.ex # Resampling techniques
│ ├── threshold_optimization.ex # Threshold optimization
│ ├── adversarial_debiasing.ex # Adversarial debiasing
│ ├── fair_representation.ex # Fair representation learning
│ └── calibration.ex # Calibration techniques
├── report.ex # Fairness reporting
└── utils.ex # Utility functions
```
## Use Cases
### Loan Approval Models
```elixir
# Ensure fair lending practices
loan_predictions = model_predict(applicant_features)
fairness = ExFairness.fairness_report(
loan_predictions,
actual_defaults,
applicant_race,
metrics: [:demographic_parity, :equalized_odds]
)
# Apply mitigation if needed
if not fairness.demographic_parity.passes do
thresholds = ExFairness.Mitigation.optimize_thresholds(
loan_scores,
actual_defaults,
applicant_race
)
end
```
### Hiring and Recruitment
```elixir
# Analyze resume screening model
screening_results = screen_resumes(resumes)
ExFairness.intersectional_fairness(
screening_results,
interview_outcomes,
sensitive_attrs: [gender, ethnicity],
attr_names: ["gender", "ethnicity"]
)
```
### Healthcare Risk Prediction
```elixir
# Ensure equitable healthcare predictions
risk_scores = predict_health_risk(patient_data)
ExFairness.calibration(
risk_scores,
actual_outcomes,
patient_race,
bins: 10
)
```
### Content Moderation
```elixir
# Monitor bias in content moderation
moderation_decisions = moderate_content(posts)
ExFairness.temporal_drift(
collect_metrics_over_time(moderation_decisions, user_demographics),
threshold: 0.05
)
```
## Fairness Metrics Reference
### Group Fairness Metrics
| Metric | Definition | When to Use |
|--------|------------|-------------|
| Demographic Parity | P(Y=1\|A=0) = P(Y=1\|A=1) | When equal positive rates across groups is required |
| Equalized Odds | TPR and FPR equal across groups | When both error types matter equally |
| Equal Opportunity | TPR equal across groups | When true positive rate parity is critical |
| Predictive Parity | PPV equal across groups | When precision parity is important |
### Individual Fairness Metrics
| Metric | Definition | When to Use |
|--------|------------|-------------|
| Individual Fairness | Similar individuals get similar predictions | When individual treatment matters |
| Counterfactual Fairness | Predictions unchanged under counterfactuals | When causal fairness is required |
## Fairness-Accuracy Trade-offs
ExFairness helps you navigate the inherent trade-offs between fairness and accuracy:
```elixir
# Analyze the Pareto frontier
tradeoffs = ExFairness.fairness_accuracy_tradeoff(
model,
test_data,
sensitive_attr,
fairness_metrics: [:demographic_parity, :equalized_odds]
)
# Visualize trade-offs
ExFairness.plot_pareto_frontier(tradeoffs)
```
## Best Practices
### 1. Define Your Fairness Requirements
Different applications require different fairness definitions:
```elixir
# For lending: Equalized odds (equal TPR and FPR)
# For hiring: Equal opportunity (equal TPR for qualified candidates)
# For content recommendation: Demographic parity (equal exposure)
```
### 2. Analyze Multiple Metrics
No single metric captures all aspects of fairness:
```elixir
comprehensive_report = ExFairness.fairness_report(
predictions,
labels,
sensitive_attr,
metrics: :all
)
```
### 3. Consider Intersectionality
Analyze fairness across multiple sensitive attributes:
```elixir
ExFairness.intersectional_fairness(
predictions,
labels,
sensitive_attrs: [race, gender, age_group]
)
```
### 4. Monitor Over Time
Fairness can degrade as data distributions shift:
```elixir
ExFairness.temporal_drift(metrics_over_time)
```
### 5. Document Trade-offs
Be transparent about fairness-accuracy trade-offs:
```elixir
report = ExFairness.generate_fairness_documentation(
model,
fairness_metrics,
accuracy_metrics
)
File.write!("fairness_report.md", report)
```
## Limitations
- **Impossibility Theorems**: Some fairness definitions are mutually exclusive (e.g., demographic parity and equalized odds with different base rates)
- **Sensitive Attributes**: Requires access to sensitive attributes for measurement
- **Causal Assumptions**: Some metrics require causal assumptions that may not hold
- **Sample Size**: Statistical tests require adequate sample sizes per group
## Research Foundations
### Key Papers
- Hardt, M., et al. (2016). "Equality of Opportunity in Supervised Learning." *NeurIPS*.
- Chouldechova, A. (2017). "Fair prediction with disparate impact." *Big Data*.
- Corbett-Davies, S., & Goel, S. (2018). "The Measure and Mismeasure of Fairness." *Journal of Machine Learning Research*.
- Dwork, C., et al. (2012). "Fairness through awareness." *ITCS*.
- Kusner, M., et al. (2017). "Counterfactual fairness." *NeurIPS*.
### Fairness Frameworks
- **Google's What-If Tool**: Interactive fairness analysis
- **IBM AI Fairness 360**: Comprehensive fairness toolkit (Python)
- **Microsoft Fairlearn**: Fairness assessment and mitigation (Python)
- **Aequitas**: Bias and fairness audit toolkit
## Contributing
This is part of the North Shore AI Research Infrastructure. Contributions are welcome!
## License
MIT License - see [LICENSE](https://github.com/North-Shore-AI/ExFairness/blob/main/LICENSE) file for details