Fairness in Machine Learning
Equality of Opportunity
Read aloud is unavailable in this browser.
Beyond Equal Outcomes: Equal Opportunities
Consider a loan approval system at two banks:
Bank A uses demographic parity—approving equal percentages of applicants from each demographic group. But this means some unqualified applicants from underrepresented groups get approved while qualified applicants from overrepresented groups are rejected.
Bank B uses a different criterion—among applicants who would actually repay their loans, approval rates are equal across groups. Qualified applicants have equal chances regardless of group membership.
Bank B implements Equality of Opportunity—a fairness criterion introduced by Hardt, Price, and Srebro (2016) that has become foundational in modern ML fairness. It captures an intuitive notion: people with the same true qualifications should have the same chance of receiving favorable outcomes.
By the end of this page, you will master the formal definition of equality of opportunity and its relationship to equalized odds, understand when and why to use this criterion, implement practical methods for achieving it, and analyze its advantages and limitations.
Formal Definition and Intuition
Equality of Opportunity requires that the True Positive Rate (TPR) is equal across protected groups.
Mathematical Definition:
$$P(\hat{Y}=1 | Y=1, A=0) = P(\hat{Y}=1 | Y=1, A=1)$$
Where:
- Ŷ is the predicted outcome (1 = positive/favorable)
- Y is the true outcome (ground truth)
- A is the protected attribute
In Plain Language:
Among individuals who truly deserve a positive outcome (Y=1), the probability of receiving that positive prediction (Ŷ=1) should be identical regardless of group membership.
Key Insight:
Equality of opportunity is a relaxation of equalized odds. While equalized odds requires both equal TPR and equal FPR across groups, equality of opportunity focuses only on TPR—the "opportunity" for qualified individuals.
| Criterion | Requirement | Focus | Use Case |
|---|---|---|---|
| Demographic Parity | P(Ŷ=1|A=0) = P(Ŷ=1|A=1) | Equal selection rates overall | Anti-classification contexts |
| Equality of Opportunity | P(Ŷ=1|Y=1,A=0) = P(Ŷ=1|Y=1,A=1) | Equal TPR (opportunities for qualified) | Merit-based selection |
| Equalized Odds | TPR and FPR equal across groups | Equal TPR AND equal FPR | Error balance for all |
| Predictive Equality | P(Ŷ=1|Y=0,A=0) = P(Ŷ=1|Y=0,A=1) | Equal FPR only | Protecting negatives from false alarms |
In many contexts, the positive outcome represents a beneficial opportunity (job, loan, admission). Equality of opportunity ensures qualified individuals aren't denied these opportunities based on group membership. It's about 'deserving' individuals getting what they 'deserve.'
Implementing Equality of Opportunity
There are several approaches to achieving equality of opportunity, from post-hoc threshold adjustment to in-processing constraints.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
import numpy as npfrom scipy.optimize import minimize_scalarfrom typing import Dict, Tuple class EqualityOfOpportunityClassifier: """ Wrapper that adjusts thresholds to achieve equality of opportunity. """ def __init__(self, base_model, protected_attr_idx=None): """ Args: base_model: Fitted model with predict_proba method protected_attr_idx: Index of protected attribute in features """ self.base_model = base_model self.protected_attr_idx = protected_attr_idx self.thresholds = {} def fit_thresholds(self, X, y, protected_attr): """ Find optimal thresholds per group to equalize TPR. Args: X: Features y: True labels protected_attr: Protected attribute values """ probas = self.base_model.predict_proba(X)[:, 1] protected_attr = np.array(protected_attr) y = np.array(y) groups = np.unique(protected_attr) # Find TPRs at various thresholds for each group def compute_tpr(threshold, probas, y): preds = (probas >= threshold).astype(int) positives = y == 1 if positives.sum() == 0: return 0.0 return preds[positives].mean() # Target: average TPR across groups at default threshold default_threshold = 0.5 target_tpr = np.mean([ compute_tpr(default_threshold, probas[protected_attr == g], y[protected_attr == g]) for g in groups ]) # Find threshold for each group that achieves target TPR for g in groups: mask = protected_attr == g g_probas = probas[mask] g_y = y[mask] def objective(threshold): tpr = compute_tpr(threshold, g_probas, g_y) return (tpr - target_tpr) ** 2 result = minimize_scalar(objective, bounds=(0.01, 0.99), method='bounded') self.thresholds[g] = result.x return self def predict(self, X, protected_attr): """Predict using group-specific thresholds.""" probas = self.base_model.predict_proba(X)[:, 1] protected_attr = np.array(protected_attr) predictions = np.zeros(len(X), dtype=int) for g, threshold in self.thresholds.items(): mask = protected_attr == g predictions[mask] = (probas[mask] >= threshold).astype(int) return predictions def evaluate(self, X, y, protected_attr) -> Dict: """Evaluate equality of opportunity satisfaction.""" predictions = self.predict(X, protected_attr) protected_attr = np.array(protected_attr) y = np.array(y) results = {'groups': {}, 'thresholds': self.thresholds} for g in np.unique(protected_attr): mask = protected_attr == g g_preds = predictions[mask] g_y = y[mask] positives = g_y == 1 tpr = g_preds[positives].mean() if positives.sum() > 0 else 0 negatives = g_y == 0 fpr = g_preds[negatives].mean() if negatives.sum() > 0 else 0 accuracy = (g_preds == g_y).mean() results['groups'][g] = { 'tpr': tpr, 'fpr': fpr, 'accuracy': accuracy, 'n_positive': positives.sum(), 'n_negative': negatives.sum() } # Calculate TPR disparity tprs = [r['tpr'] for r in results['groups'].values()] results['tpr_disparity'] = max(tprs) - min(tprs) results['equality_of_opportunity_satisfied'] = results['tpr_disparity'] < 0.05 return results def equality_of_opportunity_loss(y_true, y_pred_proba, protected_attr, lambda_fairness=1.0): """ Combined loss function for training with EO constraint. Loss = CrossEntropy + lambda * EO_violation Args: y_true: Ground truth labels y_pred_proba: Predicted probabilities protected_attr: Protected attribute values lambda_fairness: Fairness penalty weight Returns: Combined loss value """ import numpy as np y_true = np.array(y_true) y_pred_proba = np.clip(np.array(y_pred_proba), 1e-7, 1-1e-7) protected_attr = np.array(protected_attr) # Cross-entropy loss ce_loss = -np.mean( y_true * np.log(y_pred_proba) + (1 - y_true) * np.log(1 - y_pred_proba) ) # Equality of opportunity violation groups = np.unique(protected_attr) group_tprs = [] for g in groups: mask = (protected_attr == g) & (y_true == 1) if mask.sum() > 0: group_tprs.append(y_pred_proba[mask].mean()) if len(group_tprs) >= 2: eo_violation = np.var(group_tprs) else: eo_violation = 0.0 return ce_loss + lambda_fairness * eo_violation # Example usageif __name__ == "__main__": from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split np.random.seed(42) n = 2000 # Generate data with group-based differences protected = np.random.binomial(1, 0.4, n) x1 = np.random.randn(n) + 0.5 * protected x2 = np.random.randn(n) X = np.column_stack([x1, x2]) # True outcome (qualification) y = (x1 + x2 + np.random.randn(n) * 0.5 > 0).astype(int) X_train, X_test, y_train, y_test, prot_train, prot_test = train_test_split(X, y, protected, test_size=0.3, random_state=42) # Train base model base_model = LogisticRegression() base_model.fit(X_train, y_train) # Apply equality of opportunity adjustment eo_classifier = EqualityOfOpportunityClassifier(base_model) eo_classifier.fit_thresholds(X_train, y_train, prot_train) # Evaluate results = eo_classifier.evaluate(X_test, y_test, prot_test) print("Thresholds:", results['thresholds']) print("TPR Disparity:", f"{results['tpr_disparity']:.4f}") print("EO Satisfied:", results['equality_of_opportunity_satisfied']) for g, metrics in results['groups'].items(): print(f"Group {g}: TPR={metrics['tpr']:.3f}, FPR={metrics['fpr']:.3f}")When to Use Equality of Opportunity
Equality of opportunity is particularly appropriate in certain contexts but inappropriate in others. Understanding when to apply it is crucial for responsible ML practice.
- •Clear ground truth exists: Loan repayment, disease presence, job performance are measurable
- •Merit-based selection: Systems designed to identify qualified individuals
- •Positive outcome is beneficial: Jobs, loans, admissions, favorable diagnoses
- •False negatives harm individuals: Missing qualified candidates is the key harm
- •Base rates legitimately differ: Groups may have different qualification rates due to non-discriminatory factors
- •Labels are biased: If Y itself reflects discrimination, EO perpetuates it
- •False positives cause harm: Criminal prediction, invasive treatments
- •Positive outcome is harmful: Predicting recidivism, credit risk
- •Representation goals exist: Affirmative action requires demographic parity
- •No meaningful ground truth: Subjective outcomes without validation
EO assumes ground truth labels Y are unbiased. But if historical data reflects discrimination (biased hiring decisions, racially-biased arrests), equalizing TPR with respect to biased labels perpetuates that bias. Always audit your labels before applying EO.
Tradeoffs and Limitations
No fairness criterion is perfect. Understanding the tradeoffs of equality of opportunity helps practitioners make informed decisions.
- •FPR may vary: EO only constrains TPR; groups may experience different false positive rates. This can mean unqualified members of some groups are more likely to receive positive predictions.
- •Accuracy-fairness conflict: Achieving equal TPR often requires sacrificing overall accuracy, particularly when group distributions differ significantly.
- •Threshold manipulation: Using different thresholds per group may violate calibration—a 70% score means different things for different groups.
- •Doesn't address representation: EO can be satisfied with wildly different selection rates; it doesn't ensure proportional representation.
- •Assumes static ground truth: Real qualifications may change over time; EO based on historical data may not reflect current capabilities.
The FPR Problem:
When equalizing TPR leads to different FPRs, consider who bears the cost:
- In lending, higher FPR for a group means more unqualified applicants approved → potential defaults
- In hiring, higher FPR means more unqualified candidates hired → performance issues
- These costs may fall on the group itself or on the organization
This is why equalized odds (constraining both TPR and FPR) is sometimes preferred, despite being harder to achieve.
Practical Applications
Equality of opportunity has been applied across various domains. Here are notable examples and considerations:
| Domain | Positive Outcome | Ground Truth | Considerations |
|---|---|---|---|
| Hiring | Job offer | Job performance (future) | Must validate that performance metrics aren't biased |
| Lending | Loan approval | Loan repayment | Repayment affected by loan terms; circularity risk |
| College Admissions | Admission | Academic success / graduation | Success affected by campus resources; confounded |
| Medical Diagnosis | Positive diagnosis | Disease presence | Different disease prevalence may justify different TPRs |
| Recidivism Prediction | Predicted low-risk | Actual non-recidivism | Recidivism affected by parole conditions; label bias |
Before applying EO: (1) Validate that labels aren't biased, (2) Confirm that equalizing TPR is the right goal, (3) Analyze FPR impact on different groups, (4) Consider whether post-hoc thresholds or in-processing is more appropriate, (5) Document tradeoffs for stakeholders.
Summary: Opportunity for All
- •EO equalizes TPR across groups — Qualified individuals have equal chance of positive prediction regardless of group membership.
- •It's a relaxation of equalized odds — Focuses only on opportunity (TPR) not false positives (FPR).
- •Implementation via thresholds — Different classification thresholds per group can achieve EO post-hoc.
- •Requires unbiased labels — If ground truth Y is biased, EO perpetuates discrimination.
- •Appropriate for merit-based systems — Where positive outcomes represent genuine opportunities.
You now understand equality of opportunity as a fairness criterion. The next page explores the broader landscape of Fairness Metrics—quantitative measures for evaluating and comparing fairness across different dimensions.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.