From a6591e48a553aa559773549e586556d6009d9522 Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Thu, 10 Sep 2026 15:22:28 +0530 Subject: [PATCH 1/2] Add max-min (Rawlsian) fairness explainer (closes #543) New explainers/maxmin-fairness.md. Covers the fairness objective that minimizes the maximum group-level loss (Rawls' difference principle) rather than equalizing a rate across groups, and the real conflict between it and parity metrics: a model can move toward max-min fairness while still failing demographic parity. - Objective-function contrast: demographic parity's constrained average loss vs max-min's min-of-max over per-group losses. - Concrete example on the Audit 03 German Credit data (age, under 30): a plain logistic-regression baseline (older 81.0% acc, younger 70.9%, worst-group error 29.1%) vs an iterative group-reweighting max-min loop (worst-group error 27.9%, older unchanged, overall accuracy flat) that leaves the ~12-point selection-rate gap almost untouched. Cross-refs the repo's own frozen accuracy_equality_diff (-0.153, p=0.031) as the gap max-min targets and S1-S4 do not. - Fully runnable detection/implementation code (fit_maxmin + maxmin_report), deterministic output verified against the pasted block. - Limitations: tiny/noisy subgroups without a min-group-size floor; worst-off is only defined once a loss is chosen; silent on between-group gaps; levelling-down permitted at the margin without a Pareto constraint. - Citations verified: Rawls (1971); Hashimoto et al. ICML 2018; Martinez, Bertran & Sapiro ICML 2020; Diana et al. AIES 2021. Regenerated the HTML page, OG images, explainers-data.{js,json}, package mirror, sitemap.xml and llms-full.txt; check_explainer_count.py --fix bumped the 60 -> 61 mentions. The companion DRO explainer (#544) will add the reciprocal cross-links. --- CONTRIBUTORS.md | 2 +- METRICS.md | 2 +- README.md | 6 +- ROADMAP.md | 2 +- assets/explainers-data.js | 9 + assets/explainers-data.json | 7 + assets/og-light/maxmin-fairness.png | Bin 0 -> 62622 bytes assets/og/maxmin-fairness.png | Bin 0 -> 62852 bytes explainers/maxmin-fairness.html | 385 ++++++++++++++++++++++++ explainers/maxmin-fairness.md | 247 +++++++++++++++ faircode/_explainers/data.json | 7 + faircode/_explainers/maxmin-fairness.md | 247 +++++++++++++++ llms-full.txt | 254 ++++++++++++++++ sitemap.xml | 4 + 14 files changed, 1166 insertions(+), 6 deletions(-) create mode 100644 assets/og-light/maxmin-fairness.png create mode 100644 assets/og/maxmin-fairness.png create mode 100644 explainers/maxmin-fairness.html create mode 100644 explainers/maxmin-fairness.md create mode 100644 faircode/_explainers/maxmin-fairness.md diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 052583e..fa0d496 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -55,7 +55,7 @@ the [Git identity map](#git-identity-map) so `git shortlog -sne` can be reconcil | | Who | Role | |:--|-----|------| -| yakew7 | **Yash Kewlani** - [@yakew7](https://github.com/yakew7) | Creator and maintainer. Author of the seven audits, the `faircode` library and benchmark harness, the Open Dataset Profiler, the website, and the bulk of the 60 explainers. Code owner for `faircode/`, `paper/`, every `audit.yaml`, and project policy (`CLAUDE.md`, `CONTRIBUTING.md`); co-owner of `explainers/`. | +| yakew7 | **Yash Kewlani** - [@yakew7](https://github.com/yakew7) | Creator and maintainer. Author of the seven audits, the `faircode` library and benchmark harness, the Open Dataset Profiler, the website, and the bulk of the 61 explainers. Code owner for `faircode/`, `paper/`, every `audit.yaml`, and project policy (`CLAUDE.md`, `CONTRIBUTING.md`); co-owner of `explainers/`. | Contact: [yashkewlani2020@gmail.com](mailto:yashkewlani2020@gmail.com) · [@thefaircodeproject](https://instagram.com/thefaircodeproject) diff --git a/METRICS.md b/METRICS.md index e135893..1b3a1c2 100644 --- a/METRICS.md +++ b/METRICS.md @@ -6,7 +6,7 @@ ![Contributors](https://img.shields.io/badge/Contributors-29-blue?style=flat-square) ![Forks](https://img.shields.io/badge/Forks-33-orange?style=flat-square) ![Watching](https://img.shields.io/badge/Watching-8-yellow?style=flat-square) -![Explainers](https://img.shields.io/badge/Explainers-60-blueviolet?style=flat-square) +![Explainers](https://img.shields.io/badge/Explainers-61-blueviolet?style=flat-square) ![Countries](https://img.shields.io/badge/Countries-20-informational?style=flat-square) ![Updated](https://img.shields.io/badge/Updated-Weekly-lightgrey?style=flat-square) diff --git a/README.md b/README.md index b0fe3b6..2e04b4a 100644 --- a/README.md +++ b/README.md @@ -781,10 +781,10 @@ features = [ ## Explainers -60 short, plain-language write-ups of individual fairness concepts, each with runnable detection code. The healthcare-focused ones are called out above in [Healthcare AI Bias Focus](#healthcare-ai-bias-focus). +61 short, plain-language write-ups of individual fairness concepts, each with runnable detection code. The healthcare-focused ones are called out above in [Healthcare AI Bias Focus](#healthcare-ai-bias-focus).
-Show all 60 explainers → +Show all 61 explainers → | Explainer | What it covers | |-----------|----------------| @@ -1205,7 +1205,7 @@ The full public roadmap - with phases, completion status, and content schedule - | Combined Social Reach (Instagram + LinkedIn) | 30K+ | | Countries Reached (Website Visitors) | 20 | | Code Audits Published | 7 | -| Explainers Published | 60 | +| Explainers Published | 61 | Tracked weekly in [METRICS.md](METRICS.md). diff --git a/ROADMAP.md b/ROADMAP.md index 1359c12..c63f8a8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -44,7 +44,7 @@ Fair Code is an open-source responsible AI platform explaining algorithmic bias, ## Phase 1 - Bias Glossary and Beginner Explainers ✅ -**Status: Foundational library complete - 60 explainers published, expanding toward a 60+ library** +**Status: Foundational library complete - 61 explainers published, expanding toward a 60+ library** Build the foundational vocabulary and explain core fairness concepts clearly enough for a non-technical reader. diff --git a/assets/explainers-data.js b/assets/explainers-data.js index 987f97d..d864abd 100644 --- a/assets/explainers-data.js +++ b/assets/explainers-data.js @@ -608,5 +608,14 @@ window.FAIR_CODE_EXPLAINERS = [ "data", "metrics" ] + }, + { + "slug": "maxmin-fairness", + "title": "What Is Max-Min (Rawlsian) Fairness?", + "subtitle": "Make the worst-off group's outcome as good as possible, even if the groups end up unequal.", + "summary": "Learn how max-min (Rawlsian) fairness minimizes the maximum group-level loss instead of equalizing a rate across groups, and why a model can move toward it while still failing demographic parity. Worked on the Audit 03 German Credit data: an iterative group-reweighting loop cuts worst-group error from 29.1% to 27.9% and leaves the 12-point selection-rate gap almost untouched.", + "tags": [ + "metrics" + ] } ]; diff --git a/assets/explainers-data.json b/assets/explainers-data.json index fab4ce2..528c5dc 100644 --- a/assets/explainers-data.json +++ b/assets/explainers-data.json @@ -418,6 +418,13 @@ "subtitle": "The accuracy cost of a privacy guarantee falls hardest on underrepresented groups.", "summary": "Learn how DP-SGD's gradient clipping and noise addition disproportionately degrade accuracy for minority subgroups, so adding a privacy guarantee to a bias-mitigation pipeline is not free. Illustrative example from Bagdasaryan, Poursaeed and Shmatikov (NeurIPS 2019), plus a runnable DP-SGD noise-injection toy; this repo trains no DP model, so no frozen numbers are quoted.", "tags": ["data", "metrics"] + }, + { + "slug": "maxmin-fairness", + "title": "What Is Max-Min (Rawlsian) Fairness?", + "subtitle": "Make the worst-off group's outcome as good as possible, even if the groups end up unequal.", + "summary": "Learn how max-min (Rawlsian) fairness minimizes the maximum group-level loss instead of equalizing a rate across groups, and why a model can move toward it while still failing demographic parity. Worked on the Audit 03 German Credit data: an iterative group-reweighting loop cuts worst-group error from 29.1% to 27.9% and leaves the 12-point selection-rate gap almost untouched.", + "tags": ["metrics"] } ] diff --git a/assets/og-light/maxmin-fairness.png b/assets/og-light/maxmin-fairness.png new file mode 100644 index 0000000000000000000000000000000000000000..dd387692416b35b7b91280a00468a45e3d9f5390 GIT binary patch literal 62622 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

YVH(BkF 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 literal 0 HcmV?d00001 diff --git a/explainers/maxmin-fairness.html b/explainers/maxmin-fairness.html new file mode 100644 index 0000000..37230fb --- /dev/null +++ b/explainers/maxmin-fairness.html @@ -0,0 +1,385 @@ + + + + + +What Is Max-Min (Rawlsian) Fairness? · Fair Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
Explainer
+

What Is Max-Min (Rawlsian) Fairness?

+

Make the worst-off group's outcome as good as possible, even if the groups end up unequal.

+

Learn how max-min (Rawlsian) fairness minimizes the maximum group-level loss instead of equalizing a rate across groups, and why a model can move toward it while still failing demographic parity. Worked on the Audit 03 German Credit data: an iterative group-reweighting loop cuts worst-group error from 29.1% to 27.9% and leaves the 12-point selection-rate gap almost untouched.

+
+ +

What Is Max-Min (Rawlsian) Fairness?

+

Parity asks whether the groups are equal to each other. Max-min asks how good we can make the worst-off group's outcome - even if the groups end up unequal.

+
+

The One-Sentence Definition

+

Max-min fairness (also called Rawlsian fairness, after the "difference principle" in John Rawls' A Theory of Justice) is a fairness objective that minimizes the maximum group-level loss - it makes the worst-off group's outcome as good as possible - instead of equalizing a rate across groups the way parity metrics do.

+
+

Why It Matters

+

Every parity-based metric this repo already covers - demographic parity, equalized odds, predictive parity - measures a difference between groups and drives it toward zero. That framing has a well-known escape hatch: you can satisfy a parity constraint by making the better-off group worse rather than making the worse-off group better. "Parity in mediocrity" passes the test.

+

Max-min fairness rejects that move by construction. It does not look at the gap between groups at all. It looks at the single worst group-level outcome and tries to lift it. Concretely:

+
  • A model can satisfy demographic parity while both groups have poor accuracy. Max-min would prefer a model where the worst group's accuracy is higher, even if that widens the between-group gap.
  • Conversely, optimizing only for the worst-off group can widen a parity gap: if you pour modeling capacity into the group with the higher error rate, its predictions improve, but its selection rate can move away from the other group's.
+

So max-min and parity are genuinely different objectives, not two names for the same goal - and a model tuned for one can fail the other. This repo's Fairness Metric Conflicts explainer covers conflicts among parity metrics; the conflict between parity and max-min is a separate axis it does not touch.

+
+

Core Concept: A Different Objective Function

+

Write L_g(theta) for the expected loss (say, error rate) of model theta on group g.

+

Demographic parity constrains a rate to be equal across groups:

+
minimise  L(theta)          [average loss]
+subject to  P(Y_hat = 1 | A = a)  equal for all a
+

Max-min (Rawlsian) fairness changes the thing being minimized:

+
minimise over theta of   max over groups g of   L_g(theta)
+

There is no equality constraint. The objective is a min of a max: push down the largest per-group loss, then whatever the next-largest one is, and so on. If lifting the worst group also happens to help the others, fine; if it leaves a between-group gap, that gap is not penalized.

+

Two standard ways to approximate it without a bespoke solver:

+

1. Iterative group reweighting. Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. 2. Distributionally robust optimization (DRO). Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk without needing group labels, which is why max-min and DRO are usually discussed together.

+
+

Concrete Example: German Credit Lending - Audit 03

+

Audit 03 in this repo (German Credit Lending/) predicts bad credit risk on 1,000 real loan records. The protected attribute is age; here "young" means under 30 (the same cut unfair.py uses).

+

This repo's own frozen benchmark already records the gap max-min targets. In paper/results-frozen/results_fairness.csv (the earlier reference snapshot; see CLAUDE.md), the S0 baseline logistic-regression model on age has an accuracy_equality_diff of -0.153 (p = 0.031) - a statistically significant 15-point per-group accuracy gap. None of this repo's S1-S4 mitigation strategies (mitigation-strategies.md) target that number; they all target demographic parity. Max-min fairness is the objective that goes after accuracy_equality_diff directly.

+

The script below is an independent minimal implementation on the same dataset (not the S0-S4 harness): a plain logistic-regression baseline, then a max-min reweighting loop. Features drop age and personal_status (its proxy); inputs are standardized; the split is 80/20 stratified at random_state=42.

+
import numpy as np
+import pandas as pd
+from sklearn.linear_model import LogisticRegression
+from sklearn.model_selection import train_test_split
+
+
+def load_german_credit(path="German Credit Lending/credit_customers.csv"):
+    df = pd.read_csv(path)
+    df["y_bad"] = (df["class"] == "bad").astype(int)            # 1 = bad credit risk
+    df["age_group"] = np.where(df["age"] < 30, "young", "older")
+    drop = {"class", "y_bad", "age", "age_group", "personal_status"}  # drop age + its proxy
+    feats = [c for c in df.columns if c not in drop]
+    X = pd.get_dummies(df[feats], drop_first=True)
+    return X, df["y_bad"].to_numpy(), df["age_group"].to_numpy()
+
+
+def per_group(y_true, y_pred, groups):
+    out = {}
+    for g in ["older", "young"]:
+        m = groups == g
+        acc = (y_pred[m] == y_true[m]).mean()
+        out[g] = {"n": int(m.sum()), "accuracy": round(float(acc), 4),
+                  "error": round(float(1 - acc), 4),
+                  "selection_rate": round(float(y_pred[m].mean()), 4)}
+    worst = max(out["older"]["error"], out["young"]["error"])
+    dp_gap = out["young"]["selection_rate"] - out["older"]["selection_rate"]
+    return pd.DataFrame(out).T, round(float(worst), 4), round(float(dp_gap), 4)
+
+
+def fit_maxmin(Xtr, ytr, gtr, n_rounds=50, step=0.5):
+    """Rawlsian max-min: iteratively reweight groups toward the worst per-group
+    training error, so the optimizer minimizes the maximum group loss rather
+    than the average loss."""
+    gw = {"older": 1.0, "young": 1.0}
+    for _ in range(n_rounds):
+        w = np.array([gw[g] for g in gtr], dtype=float)
+        w *= len(w) / w.sum()
+        model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w)
+        err = {g: 1 - (model.predict(Xtr)[gtr == g] == ytr[gtr == g]).mean()
+               for g in gw}
+        for g in gw:                       # multiplicative-weights update
+            gw[g] *= np.exp(step * err[g])
+        total = sum(gw.values())
+        gw = {g: v / total * len(gw) for g, v in gw.items()}
+    return model, gw
+
+
+if __name__ == "__main__":
+    X, y, g = load_german_credit()
+    Xtr, Xte, ytr, yte, gtr, gte = train_test_split(
+        X, y, g, test_size=0.2, random_state=42, stratify=y)
+    mean, std = Xtr.mean(), Xtr.std().replace(0, 1)
+    Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy()
+
+    base = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr)
+    b_tbl, b_worst, b_dp = per_group(yte, base.predict(Xte_s), gte)
+    print("=== BASELINE (minimise average loss) ===")
+    print(b_tbl.to_string())
+    print(f"overall accuracy      : {(base.predict(Xte_s) == yte).mean():.4f}")
+    print(f"worst-group error     : {b_worst:.4f}")
+    print(f"demographic-parity gap: {b_dp:.4f}  (young selection rate - older)")
+
+    mm, gw = fit_maxmin(Xtr_s, ytr, gtr)
+    m_tbl, m_worst, m_dp = per_group(yte, mm.predict(Xte_s), gte)
+    print("\n=== MAX-MIN (minimise the worst group's loss) ===")
+    print(m_tbl.to_string())
+    print(f"final group weights   : older {gw['older']:.2f}x, young {gw['young']:.2f}x")
+    print(f"overall accuracy      : {(mm.predict(Xte_s) == yte).mean():.4f}")
+    print(f"worst-group error     : {m_worst:.4f}")
+    print(f"demographic-parity gap: {m_dp:.4f}  (young selection rate - older)")
+

Script Execution Output

+
=== BASELINE (minimise average loss) ===
+           n  accuracy   error  selection_rate
+older  121.0    0.8099  0.1901          0.2066
+young   79.0    0.7089  0.2911          0.3418
+overall accuracy      : 0.7700
+worst-group error     : 0.2911
+demographic-parity gap: 0.1352  (young selection rate - older)
+
+=== MAX-MIN (minimise the worst group's loss) ===
+           n  accuracy   error  selection_rate
+older  121.0    0.8099  0.1901          0.2066
+young   79.0    0.7215  0.2785          0.3291
+final group weights   : older 0.57x, young 1.43x
+overall accuracy      : 0.7750
+worst-group error     : 0.2785
+demographic-parity gap: 0.1225  (young selection rate - older)
+

(Deterministic with these library versions; a solver's last digits can shift across BLAS backends, so the reading below only uses the leading digits.)

+

What the two runs show:

+

The baseline hides a large per-group gap inside a decent average. Overall accuracy is 77%, but that splits into 81% for older applicants and 71% for younger ones - a 10-point accuracy gap, the same disparity the frozen accuracy_equality_diff flags. Average-loss training has no reason to close it: younger applicants are the smaller group (79 of 200 test rows) and carry a higher base rate of bad outcomes, so the optimizer spends its capacity where the rows are.

+

Max-min lifts the worst group, and barely moves the other. Reweighting drives the young group's weight up to 1.43x and the older group's down to 0.57x. Worst-group error falls from 29.1% to 27.9%, the young group's accuracy rises about a point, the older group's predictions are unchanged, and overall accuracy is flat (77.0% to 77.5%). A small effect from a deliberately simple loop - stronger max-min methods (Martinez et al. 2020) push further - but it moves the right number in the right direction.

+

It does not fix demographic parity, and was never trying to. The selection-rate gap goes from 13.5 points to 12.3 points and stays large and in the same direction. A model can move toward max-min fairness while still plainly failing demographic parity: the two objectives are optimizing different things. If you need the selection rates equalized, that is a parity constraint (S3/S4 in mitigation-strategies.md), not a max-min objective.

+
+

Detection and Implementation Code

+

The fit_maxmin function above is the implementation. The check below reports whether a max-min run actually reduced the worst-group loss and what it cost the other groups - the two numbers that decide whether the trade was worth it.

+
def maxmin_report(y_true, base_pred, maxmin_pred, groups):
+    """Compare a baseline model against a max-min run, per group.
+
+    Flags the change in worst-group error and whether any other group got
+    worse (the price of the max-min trade).
+    """
+    rows = {}
+    for g in sorted(set(groups)):
+        m = groups == g
+        base_err = float(1 - (base_pred[m] == y_true[m]).mean())
+        mm_err = float(1 - (maxmin_pred[m] == y_true[m]).mean())
+        rows[g] = {"n": int(m.sum()),
+                   "baseline_error": round(base_err, 4),
+                   "maxmin_error": round(mm_err, 4),
+                   "delta": round(mm_err - base_err, 4)}
+    base_worst = max(r["baseline_error"] for r in rows.values())
+    mm_worst = max(r["maxmin_error"] for r in rows.values())
+    regressed = [g for g, r in rows.items() if r["delta"] > 1e-4]
+    return {
+        "per_group": rows,
+        "worst_group_error_before": round(base_worst, 4),
+        "worst_group_error_after": round(mm_worst, 4),
+        "worst_group_improved": mm_worst < base_worst,
+        "groups_that_got_worse": regressed,
+    }
+
+

Limitations

+

1. A tiny or noisy subgroup can dominate

+

Max-min chases whichever group is currently worst. If one group is small, its measured loss is high-variance, and the loop can pour weight into fitting noise for a handful of rows - hurting everyone else for no real gain. In the example above the "young" test group is only 79 rows; a real deployment should pair max-min with a minimum-group-size floor, the same guard this repo's significance module applies with its small-sample warning. Without that floor, "worst-off group" is not a stable target.

+

2. "Worst-off" depends on a chosen loss function

+

The worst group under 0-1 error, under log-loss, under false-negative rate, and under calibration error can be three different groups. Max-min is only defined once you fix the loss, and that choice is a value judgment, not a technical default - the same point Fairness Metric Conflicts makes about parity metrics.

+

3. It is silent on between-group gaps

+

If your obligation is a legal disparate-impact standard (an 80%-rule selection-rate ratio), max-min does not help you meet it and can move you away from it, as the example shows. Max-min and parity are complementary tools for different requirements, not substitutes.

+

4. Levelling down is technically permitted at the margin

+

Minimizing the maximum loss is usually improved by raising the worst group, but a solver can also lower a better-off group if that reduces the maximum (for instance by shifting a shared threshold). Pareto-efficient formulations (Martinez et al. 2020) rule this out explicitly; a plain reweighting loop does not.

+
+ +
  • Why Fairness Metrics Conflict - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis.
  • Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here.
  • Mitigation Strategies - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss.
  • What Is Class Imbalance? - why the smaller, higher-base-rate group is the one average-loss training underserves.
  • What Is Demographic Parity? - the parity objective max-min is being contrasted against.
+
+ + +
+

Further Reading

+ +
+

Part of The Fair Code Project - exposing and fixing algorithmic bias with real data and open code.

+
+ + + + diff --git a/explainers/maxmin-fairness.md b/explainers/maxmin-fairness.md new file mode 100644 index 0000000..4dd9354 --- /dev/null +++ b/explainers/maxmin-fairness.md @@ -0,0 +1,247 @@ +# What Is Max-Min (Rawlsian) Fairness? + +> *Parity asks whether the groups are equal to each other. Max-min asks how good we can make the worst-off group's outcome - even if the groups end up unequal.* + +--- + +## The One-Sentence Definition + +**Max-min fairness** (also called Rawlsian fairness, after the "difference principle" in John Rawls' *A Theory of Justice*) is a fairness objective that minimizes the maximum group-level loss - it makes the worst-off group's outcome as good as possible - instead of equalizing a rate across groups the way parity metrics do. + +--- + +## Why It Matters + +Every parity-based metric this repo already covers - [demographic parity](demographic-parity.md), [equalized odds](equalized-odds.md), [predictive parity](predictive-parity.md) - measures a *difference between groups* and drives it toward zero. That framing has a well-known escape hatch: you can satisfy a parity constraint by making the better-off group worse rather than making the worse-off group better. "Parity in mediocrity" passes the test. + +Max-min fairness rejects that move by construction. It does not look at the gap between groups at all. It looks at the single worst group-level outcome and tries to lift it. Concretely: + +- A model can satisfy demographic parity while both groups have poor accuracy. Max-min would prefer a model where the worst group's accuracy is higher, even if that widens the between-group gap. +- Conversely, optimizing only for the worst-off group can *widen* a parity gap: if you pour modeling capacity into the group with the higher error rate, its predictions improve, but its selection rate can move away from the other group's. + +So max-min and parity are genuinely different objectives, not two names for the same goal - and a model tuned for one can fail the other. This repo's [Fairness Metric Conflicts](fairness-metric-conflicts.md) explainer covers conflicts *among* parity metrics; the conflict between parity and max-min is a separate axis it does not touch. + +--- + +## Core Concept: A Different Objective Function + +Write `L_g(theta)` for the expected loss (say, error rate) of model `theta` on group `g`. + +**Demographic parity** constrains a rate to be equal across groups: + +``` +minimise L(theta) [average loss] +subject to P(Y_hat = 1 | A = a) equal for all a +``` + +**Max-min (Rawlsian) fairness** changes the thing being minimized: + +``` +minimise over theta of max over groups g of L_g(theta) +``` + +There is no equality constraint. The objective is a `min` of a `max`: push down the largest per-group loss, then whatever the next-largest one is, and so on. If lifting the worst group also happens to help the others, fine; if it leaves a between-group gap, that gap is not penalized. + +Two standard ways to approximate it without a bespoke solver: + +1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. +2. **Distributionally robust optimization (DRO).** Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk *without needing group labels*, which is why max-min and DRO are usually discussed together. + +--- + +## Concrete Example: German Credit Lending - Audit 03 + +Audit 03 in this repo ([`German Credit Lending/`](../German%20Credit%20Lending/)) predicts bad credit risk on 1,000 real loan records. The protected attribute is `age`; here "young" means under 30 (the same cut `unfair.py` uses). + +This repo's own frozen benchmark already records the gap max-min targets. In `paper/results-frozen/results_fairness.csv` (the earlier reference snapshot; see [CLAUDE.md](../CLAUDE.md)), the S0 baseline logistic-regression model on `age` has an **`accuracy_equality_diff` of -0.153** (p = 0.031) - a statistically significant 15-point per-group accuracy gap. None of this repo's S1-S4 mitigation strategies ([mitigation-strategies.md](mitigation-strategies.md)) target that number; they all target demographic parity. Max-min fairness is the objective that goes after `accuracy_equality_diff` directly. + +The script below is an independent minimal implementation on the same dataset (not the S0-S4 harness): a plain logistic-regression baseline, then a max-min reweighting loop. Features drop `age` and `personal_status` (its proxy); inputs are standardized; the split is 80/20 stratified at `random_state=42`. + +```python +import numpy as np +import pandas as pd +from sklearn.linear_model import LogisticRegression +from sklearn.model_selection import train_test_split + + +def load_german_credit(path="German Credit Lending/credit_customers.csv"): + df = pd.read_csv(path) + df["y_bad"] = (df["class"] == "bad").astype(int) # 1 = bad credit risk + df["age_group"] = np.where(df["age"] < 30, "young", "older") + drop = {"class", "y_bad", "age", "age_group", "personal_status"} # drop age + its proxy + feats = [c for c in df.columns if c not in drop] + X = pd.get_dummies(df[feats], drop_first=True) + return X, df["y_bad"].to_numpy(), df["age_group"].to_numpy() + + +def per_group(y_true, y_pred, groups): + out = {} + for g in ["older", "young"]: + m = groups == g + acc = (y_pred[m] == y_true[m]).mean() + out[g] = {"n": int(m.sum()), "accuracy": round(float(acc), 4), + "error": round(float(1 - acc), 4), + "selection_rate": round(float(y_pred[m].mean()), 4)} + worst = max(out["older"]["error"], out["young"]["error"]) + dp_gap = out["young"]["selection_rate"] - out["older"]["selection_rate"] + return pd.DataFrame(out).T, round(float(worst), 4), round(float(dp_gap), 4) + + +def fit_maxmin(Xtr, ytr, gtr, n_rounds=50, step=0.5): + """Rawlsian max-min: iteratively reweight groups toward the worst per-group + training error, so the optimizer minimizes the maximum group loss rather + than the average loss.""" + gw = {"older": 1.0, "young": 1.0} + for _ in range(n_rounds): + w = np.array([gw[g] for g in gtr], dtype=float) + w *= len(w) / w.sum() + model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w) + err = {g: 1 - (model.predict(Xtr)[gtr == g] == ytr[gtr == g]).mean() + for g in gw} + for g in gw: # multiplicative-weights update + gw[g] *= np.exp(step * err[g]) + total = sum(gw.values()) + gw = {g: v / total * len(gw) for g, v in gw.items()} + return model, gw + + +if __name__ == "__main__": + X, y, g = load_german_credit() + Xtr, Xte, ytr, yte, gtr, gte = train_test_split( + X, y, g, test_size=0.2, random_state=42, stratify=y) + mean, std = Xtr.mean(), Xtr.std().replace(0, 1) + Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy() + + base = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr) + b_tbl, b_worst, b_dp = per_group(yte, base.predict(Xte_s), gte) + print("=== BASELINE (minimise average loss) ===") + print(b_tbl.to_string()) + print(f"overall accuracy : {(base.predict(Xte_s) == yte).mean():.4f}") + print(f"worst-group error : {b_worst:.4f}") + print(f"demographic-parity gap: {b_dp:.4f} (young selection rate - older)") + + mm, gw = fit_maxmin(Xtr_s, ytr, gtr) + m_tbl, m_worst, m_dp = per_group(yte, mm.predict(Xte_s), gte) + print("\n=== MAX-MIN (minimise the worst group's loss) ===") + print(m_tbl.to_string()) + print(f"final group weights : older {gw['older']:.2f}x, young {gw['young']:.2f}x") + print(f"overall accuracy : {(mm.predict(Xte_s) == yte).mean():.4f}") + print(f"worst-group error : {m_worst:.4f}") + print(f"demographic-parity gap: {m_dp:.4f} (young selection rate - older)") +``` + +### Script Execution Output + +``` +=== BASELINE (minimise average loss) === + n accuracy error selection_rate +older 121.0 0.8099 0.1901 0.2066 +young 79.0 0.7089 0.2911 0.3418 +overall accuracy : 0.7700 +worst-group error : 0.2911 +demographic-parity gap: 0.1352 (young selection rate - older) + +=== MAX-MIN (minimise the worst group's loss) === + n accuracy error selection_rate +older 121.0 0.8099 0.1901 0.2066 +young 79.0 0.7215 0.2785 0.3291 +final group weights : older 0.57x, young 1.43x +overall accuracy : 0.7750 +worst-group error : 0.2785 +demographic-parity gap: 0.1225 (young selection rate - older) +``` + +(Deterministic with these library versions; a solver's last digits can shift across BLAS backends, so the reading below only uses the leading digits.) + +What the two runs show: + +**The baseline hides a large per-group gap inside a decent average.** Overall accuracy is 77%, but that splits into **81% for older applicants and 71% for younger ones** - a 10-point accuracy gap, the same disparity the frozen `accuracy_equality_diff` flags. Average-loss training has no reason to close it: younger applicants are the smaller group (79 of 200 test rows) and carry a higher base rate of bad outcomes, so the optimizer spends its capacity where the rows are. + +**Max-min lifts the worst group, and barely moves the other.** Reweighting drives the young group's weight up to 1.43x and the older group's down to 0.57x. Worst-group error falls from **29.1% to 27.9%**, the young group's accuracy rises about a point, the older group's predictions are unchanged, and overall accuracy is flat (77.0% to 77.5%). A small effect from a deliberately simple loop - stronger max-min methods (Martinez et al. 2020) push further - but it moves the right number in the right direction. + +**It does not fix demographic parity, and was never trying to.** The selection-rate gap goes from 13.5 points to 12.3 points and stays large and in the same direction. A model can move toward max-min fairness while still plainly failing demographic parity: the two objectives are optimizing different things. If you need the selection rates equalized, that is a parity constraint (S3/S4 in [mitigation-strategies.md](mitigation-strategies.md)), not a max-min objective. + +--- + +## Detection and Implementation Code + +The `fit_maxmin` function above is the implementation. The check below reports whether a max-min run actually reduced the worst-group loss and what it cost the other groups - the two numbers that decide whether the trade was worth it. + +```python +def maxmin_report(y_true, base_pred, maxmin_pred, groups): + """Compare a baseline model against a max-min run, per group. + + Flags the change in worst-group error and whether any other group got + worse (the price of the max-min trade). + """ + rows = {} + for g in sorted(set(groups)): + m = groups == g + base_err = float(1 - (base_pred[m] == y_true[m]).mean()) + mm_err = float(1 - (maxmin_pred[m] == y_true[m]).mean()) + rows[g] = {"n": int(m.sum()), + "baseline_error": round(base_err, 4), + "maxmin_error": round(mm_err, 4), + "delta": round(mm_err - base_err, 4)} + base_worst = max(r["baseline_error"] for r in rows.values()) + mm_worst = max(r["maxmin_error"] for r in rows.values()) + regressed = [g for g, r in rows.items() if r["delta"] > 1e-4] + return { + "per_group": rows, + "worst_group_error_before": round(base_worst, 4), + "worst_group_error_after": round(mm_worst, 4), + "worst_group_improved": mm_worst < base_worst, + "groups_that_got_worse": regressed, + } +``` + +--- + +## Limitations + +### 1. A tiny or noisy subgroup can dominate + +Max-min chases whichever group is currently worst. If one group is small, its measured loss is high-variance, and the loop can pour weight into fitting noise for a handful of rows - hurting everyone else for no real gain. In the example above the "young" test group is only 79 rows; a real deployment should pair max-min with a minimum-group-size floor, the same guard this repo's [significance module](../faircode/significance.py) applies with its small-sample warning. Without that floor, "worst-off group" is not a stable target. + +### 2. "Worst-off" depends on a chosen loss function + +The worst group under 0-1 error, under log-loss, under false-negative rate, and under calibration error can be three different groups. Max-min is only defined once you fix the loss, and that choice is a value judgment, not a technical default - the same point [Fairness Metric Conflicts](fairness-metric-conflicts.md) makes about parity metrics. + +### 3. It is silent on between-group gaps + +If your obligation is a legal disparate-impact standard (an 80%-rule selection-rate ratio), max-min does not help you meet it and can move you away from it, as the example shows. Max-min and parity are complementary tools for different requirements, not substitutes. + +### 4. Levelling down is technically permitted at the margin + +Minimizing the maximum loss is usually improved by *raising* the worst group, but a solver can also lower a better-off group if that reduces the maximum (for instance by shifting a shared threshold). Pareto-efficient formulations (Martinez et al. 2020) rule this out explicitly; a plain reweighting loop does not. + +--- + +## Related Concepts + +- [Why Fairness Metrics Conflict](fairness-metric-conflicts.md) - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis. +- Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here. +- [Mitigation Strategies](mitigation-strategies.md) - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss. +- [What Is Class Imbalance?](class-imbalance.md) - why the smaller, higher-base-rate group is the one average-loss training underserves. +- [What Is Demographic Parity?](demographic-parity.md) - the parity objective max-min is being contrasted against. + +--- + +## Related Projects in This Repo + +- [`German Credit Lending/`](../German%20Credit%20Lending/) - Audit 03, the dataset and `age` attribute used above. +- [`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/) - the frozen `accuracy_equality_diff` figure this explainer cites as the repo's own record of the per-group accuracy gap. +- [`faircode/significance.py`](../faircode/significance.py) - the small-sample warning referenced in Limitation 1. + +--- + +## Further Reading + +- [Rawls, J. (1971, rev. 1999): *A Theory of Justice*, Harvard University Press](https://www.hup.harvard.edu/books/9780674000780) - the "difference principle": social and economic inequalities are just only if they benefit the least-advantaged members of society. +- [Hashimoto, Srivastava, Namkoong & Liang (2018): Fairness Without Demographics in Repeated Loss Minimization, ICML 2018, PMLR 80:1929-1938](https://proceedings.mlr.press/v80/hashimoto18a.html) - a DRO objective that bounds the minority group's risk without using group labels. +- [Martinez, Bertran & Sapiro (2020): Minimax Pareto Fairness: A Multi Objective Perspective, ICML 2020, PMLR 119:6755-6764](https://proceedings.mlr.press/v119/martinez20a.html) - treats each group's risk as a separate objective and finds a classifier that is minimax and Pareto-efficient, with no test-time access to the protected attribute. +- [Diana, Gill, Kearns, Kenthapadi & Roth (2021): Minimax Group Fairness: Algorithms and Experiments, AIES 2021](https://doi.org/10.1145/3461702.3462523) - practical algorithms for the min-max group-error objective and how it compares to statistical-parity mitigation. + +--- + +*Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* diff --git a/faircode/_explainers/data.json b/faircode/_explainers/data.json index fab4ce2..528c5dc 100644 --- a/faircode/_explainers/data.json +++ b/faircode/_explainers/data.json @@ -418,6 +418,13 @@ "subtitle": "The accuracy cost of a privacy guarantee falls hardest on underrepresented groups.", "summary": "Learn how DP-SGD's gradient clipping and noise addition disproportionately degrade accuracy for minority subgroups, so adding a privacy guarantee to a bias-mitigation pipeline is not free. Illustrative example from Bagdasaryan, Poursaeed and Shmatikov (NeurIPS 2019), plus a runnable DP-SGD noise-injection toy; this repo trains no DP model, so no frozen numbers are quoted.", "tags": ["data", "metrics"] + }, + { + "slug": "maxmin-fairness", + "title": "What Is Max-Min (Rawlsian) Fairness?", + "subtitle": "Make the worst-off group's outcome as good as possible, even if the groups end up unequal.", + "summary": "Learn how max-min (Rawlsian) fairness minimizes the maximum group-level loss instead of equalizing a rate across groups, and why a model can move toward it while still failing demographic parity. Worked on the Audit 03 German Credit data: an iterative group-reweighting loop cuts worst-group error from 29.1% to 27.9% and leaves the 12-point selection-rate gap almost untouched.", + "tags": ["metrics"] } ] diff --git a/faircode/_explainers/maxmin-fairness.md b/faircode/_explainers/maxmin-fairness.md new file mode 100644 index 0000000..4dd9354 --- /dev/null +++ b/faircode/_explainers/maxmin-fairness.md @@ -0,0 +1,247 @@ +# What Is Max-Min (Rawlsian) Fairness? + +> *Parity asks whether the groups are equal to each other. Max-min asks how good we can make the worst-off group's outcome - even if the groups end up unequal.* + +--- + +## The One-Sentence Definition + +**Max-min fairness** (also called Rawlsian fairness, after the "difference principle" in John Rawls' *A Theory of Justice*) is a fairness objective that minimizes the maximum group-level loss - it makes the worst-off group's outcome as good as possible - instead of equalizing a rate across groups the way parity metrics do. + +--- + +## Why It Matters + +Every parity-based metric this repo already covers - [demographic parity](demographic-parity.md), [equalized odds](equalized-odds.md), [predictive parity](predictive-parity.md) - measures a *difference between groups* and drives it toward zero. That framing has a well-known escape hatch: you can satisfy a parity constraint by making the better-off group worse rather than making the worse-off group better. "Parity in mediocrity" passes the test. + +Max-min fairness rejects that move by construction. It does not look at the gap between groups at all. It looks at the single worst group-level outcome and tries to lift it. Concretely: + +- A model can satisfy demographic parity while both groups have poor accuracy. Max-min would prefer a model where the worst group's accuracy is higher, even if that widens the between-group gap. +- Conversely, optimizing only for the worst-off group can *widen* a parity gap: if you pour modeling capacity into the group with the higher error rate, its predictions improve, but its selection rate can move away from the other group's. + +So max-min and parity are genuinely different objectives, not two names for the same goal - and a model tuned for one can fail the other. This repo's [Fairness Metric Conflicts](fairness-metric-conflicts.md) explainer covers conflicts *among* parity metrics; the conflict between parity and max-min is a separate axis it does not touch. + +--- + +## Core Concept: A Different Objective Function + +Write `L_g(theta)` for the expected loss (say, error rate) of model `theta` on group `g`. + +**Demographic parity** constrains a rate to be equal across groups: + +``` +minimise L(theta) [average loss] +subject to P(Y_hat = 1 | A = a) equal for all a +``` + +**Max-min (Rawlsian) fairness** changes the thing being minimized: + +``` +minimise over theta of max over groups g of L_g(theta) +``` + +There is no equality constraint. The objective is a `min` of a `max`: push down the largest per-group loss, then whatever the next-largest one is, and so on. If lifting the worst group also happens to help the others, fine; if it leaves a between-group gap, that gap is not penalized. + +Two standard ways to approximate it without a bespoke solver: + +1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. +2. **Distributionally robust optimization (DRO).** Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk *without needing group labels*, which is why max-min and DRO are usually discussed together. + +--- + +## Concrete Example: German Credit Lending - Audit 03 + +Audit 03 in this repo ([`German Credit Lending/`](../German%20Credit%20Lending/)) predicts bad credit risk on 1,000 real loan records. The protected attribute is `age`; here "young" means under 30 (the same cut `unfair.py` uses). + +This repo's own frozen benchmark already records the gap max-min targets. In `paper/results-frozen/results_fairness.csv` (the earlier reference snapshot; see [CLAUDE.md](../CLAUDE.md)), the S0 baseline logistic-regression model on `age` has an **`accuracy_equality_diff` of -0.153** (p = 0.031) - a statistically significant 15-point per-group accuracy gap. None of this repo's S1-S4 mitigation strategies ([mitigation-strategies.md](mitigation-strategies.md)) target that number; they all target demographic parity. Max-min fairness is the objective that goes after `accuracy_equality_diff` directly. + +The script below is an independent minimal implementation on the same dataset (not the S0-S4 harness): a plain logistic-regression baseline, then a max-min reweighting loop. Features drop `age` and `personal_status` (its proxy); inputs are standardized; the split is 80/20 stratified at `random_state=42`. + +```python +import numpy as np +import pandas as pd +from sklearn.linear_model import LogisticRegression +from sklearn.model_selection import train_test_split + + +def load_german_credit(path="German Credit Lending/credit_customers.csv"): + df = pd.read_csv(path) + df["y_bad"] = (df["class"] == "bad").astype(int) # 1 = bad credit risk + df["age_group"] = np.where(df["age"] < 30, "young", "older") + drop = {"class", "y_bad", "age", "age_group", "personal_status"} # drop age + its proxy + feats = [c for c in df.columns if c not in drop] + X = pd.get_dummies(df[feats], drop_first=True) + return X, df["y_bad"].to_numpy(), df["age_group"].to_numpy() + + +def per_group(y_true, y_pred, groups): + out = {} + for g in ["older", "young"]: + m = groups == g + acc = (y_pred[m] == y_true[m]).mean() + out[g] = {"n": int(m.sum()), "accuracy": round(float(acc), 4), + "error": round(float(1 - acc), 4), + "selection_rate": round(float(y_pred[m].mean()), 4)} + worst = max(out["older"]["error"], out["young"]["error"]) + dp_gap = out["young"]["selection_rate"] - out["older"]["selection_rate"] + return pd.DataFrame(out).T, round(float(worst), 4), round(float(dp_gap), 4) + + +def fit_maxmin(Xtr, ytr, gtr, n_rounds=50, step=0.5): + """Rawlsian max-min: iteratively reweight groups toward the worst per-group + training error, so the optimizer minimizes the maximum group loss rather + than the average loss.""" + gw = {"older": 1.0, "young": 1.0} + for _ in range(n_rounds): + w = np.array([gw[g] for g in gtr], dtype=float) + w *= len(w) / w.sum() + model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w) + err = {g: 1 - (model.predict(Xtr)[gtr == g] == ytr[gtr == g]).mean() + for g in gw} + for g in gw: # multiplicative-weights update + gw[g] *= np.exp(step * err[g]) + total = sum(gw.values()) + gw = {g: v / total * len(gw) for g, v in gw.items()} + return model, gw + + +if __name__ == "__main__": + X, y, g = load_german_credit() + Xtr, Xte, ytr, yte, gtr, gte = train_test_split( + X, y, g, test_size=0.2, random_state=42, stratify=y) + mean, std = Xtr.mean(), Xtr.std().replace(0, 1) + Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy() + + base = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr) + b_tbl, b_worst, b_dp = per_group(yte, base.predict(Xte_s), gte) + print("=== BASELINE (minimise average loss) ===") + print(b_tbl.to_string()) + print(f"overall accuracy : {(base.predict(Xte_s) == yte).mean():.4f}") + print(f"worst-group error : {b_worst:.4f}") + print(f"demographic-parity gap: {b_dp:.4f} (young selection rate - older)") + + mm, gw = fit_maxmin(Xtr_s, ytr, gtr) + m_tbl, m_worst, m_dp = per_group(yte, mm.predict(Xte_s), gte) + print("\n=== MAX-MIN (minimise the worst group's loss) ===") + print(m_tbl.to_string()) + print(f"final group weights : older {gw['older']:.2f}x, young {gw['young']:.2f}x") + print(f"overall accuracy : {(mm.predict(Xte_s) == yte).mean():.4f}") + print(f"worst-group error : {m_worst:.4f}") + print(f"demographic-parity gap: {m_dp:.4f} (young selection rate - older)") +``` + +### Script Execution Output + +``` +=== BASELINE (minimise average loss) === + n accuracy error selection_rate +older 121.0 0.8099 0.1901 0.2066 +young 79.0 0.7089 0.2911 0.3418 +overall accuracy : 0.7700 +worst-group error : 0.2911 +demographic-parity gap: 0.1352 (young selection rate - older) + +=== MAX-MIN (minimise the worst group's loss) === + n accuracy error selection_rate +older 121.0 0.8099 0.1901 0.2066 +young 79.0 0.7215 0.2785 0.3291 +final group weights : older 0.57x, young 1.43x +overall accuracy : 0.7750 +worst-group error : 0.2785 +demographic-parity gap: 0.1225 (young selection rate - older) +``` + +(Deterministic with these library versions; a solver's last digits can shift across BLAS backends, so the reading below only uses the leading digits.) + +What the two runs show: + +**The baseline hides a large per-group gap inside a decent average.** Overall accuracy is 77%, but that splits into **81% for older applicants and 71% for younger ones** - a 10-point accuracy gap, the same disparity the frozen `accuracy_equality_diff` flags. Average-loss training has no reason to close it: younger applicants are the smaller group (79 of 200 test rows) and carry a higher base rate of bad outcomes, so the optimizer spends its capacity where the rows are. + +**Max-min lifts the worst group, and barely moves the other.** Reweighting drives the young group's weight up to 1.43x and the older group's down to 0.57x. Worst-group error falls from **29.1% to 27.9%**, the young group's accuracy rises about a point, the older group's predictions are unchanged, and overall accuracy is flat (77.0% to 77.5%). A small effect from a deliberately simple loop - stronger max-min methods (Martinez et al. 2020) push further - but it moves the right number in the right direction. + +**It does not fix demographic parity, and was never trying to.** The selection-rate gap goes from 13.5 points to 12.3 points and stays large and in the same direction. A model can move toward max-min fairness while still plainly failing demographic parity: the two objectives are optimizing different things. If you need the selection rates equalized, that is a parity constraint (S3/S4 in [mitigation-strategies.md](mitigation-strategies.md)), not a max-min objective. + +--- + +## Detection and Implementation Code + +The `fit_maxmin` function above is the implementation. The check below reports whether a max-min run actually reduced the worst-group loss and what it cost the other groups - the two numbers that decide whether the trade was worth it. + +```python +def maxmin_report(y_true, base_pred, maxmin_pred, groups): + """Compare a baseline model against a max-min run, per group. + + Flags the change in worst-group error and whether any other group got + worse (the price of the max-min trade). + """ + rows = {} + for g in sorted(set(groups)): + m = groups == g + base_err = float(1 - (base_pred[m] == y_true[m]).mean()) + mm_err = float(1 - (maxmin_pred[m] == y_true[m]).mean()) + rows[g] = {"n": int(m.sum()), + "baseline_error": round(base_err, 4), + "maxmin_error": round(mm_err, 4), + "delta": round(mm_err - base_err, 4)} + base_worst = max(r["baseline_error"] for r in rows.values()) + mm_worst = max(r["maxmin_error"] for r in rows.values()) + regressed = [g for g, r in rows.items() if r["delta"] > 1e-4] + return { + "per_group": rows, + "worst_group_error_before": round(base_worst, 4), + "worst_group_error_after": round(mm_worst, 4), + "worst_group_improved": mm_worst < base_worst, + "groups_that_got_worse": regressed, + } +``` + +--- + +## Limitations + +### 1. A tiny or noisy subgroup can dominate + +Max-min chases whichever group is currently worst. If one group is small, its measured loss is high-variance, and the loop can pour weight into fitting noise for a handful of rows - hurting everyone else for no real gain. In the example above the "young" test group is only 79 rows; a real deployment should pair max-min with a minimum-group-size floor, the same guard this repo's [significance module](../faircode/significance.py) applies with its small-sample warning. Without that floor, "worst-off group" is not a stable target. + +### 2. "Worst-off" depends on a chosen loss function + +The worst group under 0-1 error, under log-loss, under false-negative rate, and under calibration error can be three different groups. Max-min is only defined once you fix the loss, and that choice is a value judgment, not a technical default - the same point [Fairness Metric Conflicts](fairness-metric-conflicts.md) makes about parity metrics. + +### 3. It is silent on between-group gaps + +If your obligation is a legal disparate-impact standard (an 80%-rule selection-rate ratio), max-min does not help you meet it and can move you away from it, as the example shows. Max-min and parity are complementary tools for different requirements, not substitutes. + +### 4. Levelling down is technically permitted at the margin + +Minimizing the maximum loss is usually improved by *raising* the worst group, but a solver can also lower a better-off group if that reduces the maximum (for instance by shifting a shared threshold). Pareto-efficient formulations (Martinez et al. 2020) rule this out explicitly; a plain reweighting loop does not. + +--- + +## Related Concepts + +- [Why Fairness Metrics Conflict](fairness-metric-conflicts.md) - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis. +- Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here. +- [Mitigation Strategies](mitigation-strategies.md) - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss. +- [What Is Class Imbalance?](class-imbalance.md) - why the smaller, higher-base-rate group is the one average-loss training underserves. +- [What Is Demographic Parity?](demographic-parity.md) - the parity objective max-min is being contrasted against. + +--- + +## Related Projects in This Repo + +- [`German Credit Lending/`](../German%20Credit%20Lending/) - Audit 03, the dataset and `age` attribute used above. +- [`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/) - the frozen `accuracy_equality_diff` figure this explainer cites as the repo's own record of the per-group accuracy gap. +- [`faircode/significance.py`](../faircode/significance.py) - the small-sample warning referenced in Limitation 1. + +--- + +## Further Reading + +- [Rawls, J. (1971, rev. 1999): *A Theory of Justice*, Harvard University Press](https://www.hup.harvard.edu/books/9780674000780) - the "difference principle": social and economic inequalities are just only if they benefit the least-advantaged members of society. +- [Hashimoto, Srivastava, Namkoong & Liang (2018): Fairness Without Demographics in Repeated Loss Minimization, ICML 2018, PMLR 80:1929-1938](https://proceedings.mlr.press/v80/hashimoto18a.html) - a DRO objective that bounds the minority group's risk without using group labels. +- [Martinez, Bertran & Sapiro (2020): Minimax Pareto Fairness: A Multi Objective Perspective, ICML 2020, PMLR 119:6755-6764](https://proceedings.mlr.press/v119/martinez20a.html) - treats each group's risk as a separate objective and finds a classifier that is minimax and Pareto-efficient, with no test-time access to the protected attribute. +- [Diana, Gill, Kearns, Kenthapadi & Roth (2021): Minimax Group Fairness: Algorithms and Experiments, AIES 2021](https://doi.org/10.1145/3461702.3462523) - practical algorithms for the min-max group-error objective and how it compares to statistical-parity mitigation. + +--- + +*Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* diff --git a/llms-full.txt b/llms-full.txt index dd42aae..1e14bdb 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -12242,3 +12242,257 @@ The Detection Code has no privacy accountant, so its `sigma` values do not map t *Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* +--- + +# What Is Max-Min (Rawlsian) Fairness? +URL: https://www.thefaircode.xyz/explainers/maxmin-fairness.html +Summary: Learn how max-min (Rawlsian) fairness minimizes the maximum group-level loss instead of equalizing a rate across groups, and why a model can move toward it while still failing demographic parity. Worked on the Audit 03 German Credit data: an iterative group-reweighting loop cuts worst-group error from 29.1% to 27.9% and leaves the 12-point selection-rate gap almost untouched. + +# What Is Max-Min (Rawlsian) Fairness? + +> *Parity asks whether the groups are equal to each other. Max-min asks how good we can make the worst-off group's outcome - even if the groups end up unequal.* + +--- + +## The One-Sentence Definition + +**Max-min fairness** (also called Rawlsian fairness, after the "difference principle" in John Rawls' *A Theory of Justice*) is a fairness objective that minimizes the maximum group-level loss - it makes the worst-off group's outcome as good as possible - instead of equalizing a rate across groups the way parity metrics do. + +--- + +## Why It Matters + +Every parity-based metric this repo already covers - [demographic parity](demographic-parity.md), [equalized odds](equalized-odds.md), [predictive parity](predictive-parity.md) - measures a *difference between groups* and drives it toward zero. That framing has a well-known escape hatch: you can satisfy a parity constraint by making the better-off group worse rather than making the worse-off group better. "Parity in mediocrity" passes the test. + +Max-min fairness rejects that move by construction. It does not look at the gap between groups at all. It looks at the single worst group-level outcome and tries to lift it. Concretely: + +- A model can satisfy demographic parity while both groups have poor accuracy. Max-min would prefer a model where the worst group's accuracy is higher, even if that widens the between-group gap. +- Conversely, optimizing only for the worst-off group can *widen* a parity gap: if you pour modeling capacity into the group with the higher error rate, its predictions improve, but its selection rate can move away from the other group's. + +So max-min and parity are genuinely different objectives, not two names for the same goal - and a model tuned for one can fail the other. This repo's [Fairness Metric Conflicts](fairness-metric-conflicts.md) explainer covers conflicts *among* parity metrics; the conflict between parity and max-min is a separate axis it does not touch. + +--- + +## Core Concept: A Different Objective Function + +Write `L_g(theta)` for the expected loss (say, error rate) of model `theta` on group `g`. + +**Demographic parity** constrains a rate to be equal across groups: + +``` +minimise L(theta) [average loss] +subject to P(Y_hat = 1 | A = a) equal for all a +``` + +**Max-min (Rawlsian) fairness** changes the thing being minimized: + +``` +minimise over theta of max over groups g of L_g(theta) +``` + +There is no equality constraint. The objective is a `min` of a `max`: push down the largest per-group loss, then whatever the next-largest one is, and so on. If lifting the worst group also happens to help the others, fine; if it leaves a between-group gap, that gap is not penalized. + +Two standard ways to approximate it without a bespoke solver: + +1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. +2. **Distributionally robust optimization (DRO).** Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk *without needing group labels*, which is why max-min and DRO are usually discussed together. + +--- + +## Concrete Example: German Credit Lending - Audit 03 + +Audit 03 in this repo ([`German Credit Lending/`](../German%20Credit%20Lending/)) predicts bad credit risk on 1,000 real loan records. The protected attribute is `age`; here "young" means under 30 (the same cut `unfair.py` uses). + +This repo's own frozen benchmark already records the gap max-min targets. In `paper/results-frozen/results_fairness.csv` (the earlier reference snapshot; see [CLAUDE.md](../CLAUDE.md)), the S0 baseline logistic-regression model on `age` has an **`accuracy_equality_diff` of -0.153** (p = 0.031) - a statistically significant 15-point per-group accuracy gap. None of this repo's S1-S4 mitigation strategies ([mitigation-strategies.md](mitigation-strategies.md)) target that number; they all target demographic parity. Max-min fairness is the objective that goes after `accuracy_equality_diff` directly. + +The script below is an independent minimal implementation on the same dataset (not the S0-S4 harness): a plain logistic-regression baseline, then a max-min reweighting loop. Features drop `age` and `personal_status` (its proxy); inputs are standardized; the split is 80/20 stratified at `random_state=42`. + +```python +import numpy as np +import pandas as pd +from sklearn.linear_model import LogisticRegression +from sklearn.model_selection import train_test_split + + +def load_german_credit(path="German Credit Lending/credit_customers.csv"): + df = pd.read_csv(path) + df["y_bad"] = (df["class"] == "bad").astype(int) # 1 = bad credit risk + df["age_group"] = np.where(df["age"] < 30, "young", "older") + drop = {"class", "y_bad", "age", "age_group", "personal_status"} # drop age + its proxy + feats = [c for c in df.columns if c not in drop] + X = pd.get_dummies(df[feats], drop_first=True) + return X, df["y_bad"].to_numpy(), df["age_group"].to_numpy() + + +def per_group(y_true, y_pred, groups): + out = {} + for g in ["older", "young"]: + m = groups == g + acc = (y_pred[m] == y_true[m]).mean() + out[g] = {"n": int(m.sum()), "accuracy": round(float(acc), 4), + "error": round(float(1 - acc), 4), + "selection_rate": round(float(y_pred[m].mean()), 4)} + worst = max(out["older"]["error"], out["young"]["error"]) + dp_gap = out["young"]["selection_rate"] - out["older"]["selection_rate"] + return pd.DataFrame(out).T, round(float(worst), 4), round(float(dp_gap), 4) + + +def fit_maxmin(Xtr, ytr, gtr, n_rounds=50, step=0.5): + """Rawlsian max-min: iteratively reweight groups toward the worst per-group + training error, so the optimizer minimizes the maximum group loss rather + than the average loss.""" + gw = {"older": 1.0, "young": 1.0} + for _ in range(n_rounds): + w = np.array([gw[g] for g in gtr], dtype=float) + w *= len(w) / w.sum() + model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w) + err = {g: 1 - (model.predict(Xtr)[gtr == g] == ytr[gtr == g]).mean() + for g in gw} + for g in gw: # multiplicative-weights update + gw[g] *= np.exp(step * err[g]) + total = sum(gw.values()) + gw = {g: v / total * len(gw) for g, v in gw.items()} + return model, gw + + +if __name__ == "__main__": + X, y, g = load_german_credit() + Xtr, Xte, ytr, yte, gtr, gte = train_test_split( + X, y, g, test_size=0.2, random_state=42, stratify=y) + mean, std = Xtr.mean(), Xtr.std().replace(0, 1) + Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy() + + base = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr) + b_tbl, b_worst, b_dp = per_group(yte, base.predict(Xte_s), gte) + print("=== BASELINE (minimise average loss) ===") + print(b_tbl.to_string()) + print(f"overall accuracy : {(base.predict(Xte_s) == yte).mean():.4f}") + print(f"worst-group error : {b_worst:.4f}") + print(f"demographic-parity gap: {b_dp:.4f} (young selection rate - older)") + + mm, gw = fit_maxmin(Xtr_s, ytr, gtr) + m_tbl, m_worst, m_dp = per_group(yte, mm.predict(Xte_s), gte) + print("\n=== MAX-MIN (minimise the worst group's loss) ===") + print(m_tbl.to_string()) + print(f"final group weights : older {gw['older']:.2f}x, young {gw['young']:.2f}x") + print(f"overall accuracy : {(mm.predict(Xte_s) == yte).mean():.4f}") + print(f"worst-group error : {m_worst:.4f}") + print(f"demographic-parity gap: {m_dp:.4f} (young selection rate - older)") +``` + +### Script Execution Output + +``` +=== BASELINE (minimise average loss) === + n accuracy error selection_rate +older 121.0 0.8099 0.1901 0.2066 +young 79.0 0.7089 0.2911 0.3418 +overall accuracy : 0.7700 +worst-group error : 0.2911 +demographic-parity gap: 0.1352 (young selection rate - older) + +=== MAX-MIN (minimise the worst group's loss) === + n accuracy error selection_rate +older 121.0 0.8099 0.1901 0.2066 +young 79.0 0.7215 0.2785 0.3291 +final group weights : older 0.57x, young 1.43x +overall accuracy : 0.7750 +worst-group error : 0.2785 +demographic-parity gap: 0.1225 (young selection rate - older) +``` + +(Deterministic with these library versions; a solver's last digits can shift across BLAS backends, so the reading below only uses the leading digits.) + +What the two runs show: + +**The baseline hides a large per-group gap inside a decent average.** Overall accuracy is 77%, but that splits into **81% for older applicants and 71% for younger ones** - a 10-point accuracy gap, the same disparity the frozen `accuracy_equality_diff` flags. Average-loss training has no reason to close it: younger applicants are the smaller group (79 of 200 test rows) and carry a higher base rate of bad outcomes, so the optimizer spends its capacity where the rows are. + +**Max-min lifts the worst group, and barely moves the other.** Reweighting drives the young group's weight up to 1.43x and the older group's down to 0.57x. Worst-group error falls from **29.1% to 27.9%**, the young group's accuracy rises about a point, the older group's predictions are unchanged, and overall accuracy is flat (77.0% to 77.5%). A small effect from a deliberately simple loop - stronger max-min methods (Martinez et al. 2020) push further - but it moves the right number in the right direction. + +**It does not fix demographic parity, and was never trying to.** The selection-rate gap goes from 13.5 points to 12.3 points and stays large and in the same direction. A model can move toward max-min fairness while still plainly failing demographic parity: the two objectives are optimizing different things. If you need the selection rates equalized, that is a parity constraint (S3/S4 in [mitigation-strategies.md](mitigation-strategies.md)), not a max-min objective. + +--- + +## Detection and Implementation Code + +The `fit_maxmin` function above is the implementation. The check below reports whether a max-min run actually reduced the worst-group loss and what it cost the other groups - the two numbers that decide whether the trade was worth it. + +```python +def maxmin_report(y_true, base_pred, maxmin_pred, groups): + """Compare a baseline model against a max-min run, per group. + + Flags the change in worst-group error and whether any other group got + worse (the price of the max-min trade). + """ + rows = {} + for g in sorted(set(groups)): + m = groups == g + base_err = float(1 - (base_pred[m] == y_true[m]).mean()) + mm_err = float(1 - (maxmin_pred[m] == y_true[m]).mean()) + rows[g] = {"n": int(m.sum()), + "baseline_error": round(base_err, 4), + "maxmin_error": round(mm_err, 4), + "delta": round(mm_err - base_err, 4)} + base_worst = max(r["baseline_error"] for r in rows.values()) + mm_worst = max(r["maxmin_error"] for r in rows.values()) + regressed = [g for g, r in rows.items() if r["delta"] > 1e-4] + return { + "per_group": rows, + "worst_group_error_before": round(base_worst, 4), + "worst_group_error_after": round(mm_worst, 4), + "worst_group_improved": mm_worst < base_worst, + "groups_that_got_worse": regressed, + } +``` + +--- + +## Limitations + +### 1. A tiny or noisy subgroup can dominate + +Max-min chases whichever group is currently worst. If one group is small, its measured loss is high-variance, and the loop can pour weight into fitting noise for a handful of rows - hurting everyone else for no real gain. In the example above the "young" test group is only 79 rows; a real deployment should pair max-min with a minimum-group-size floor, the same guard this repo's [significance module](../faircode/significance.py) applies with its small-sample warning. Without that floor, "worst-off group" is not a stable target. + +### 2. "Worst-off" depends on a chosen loss function + +The worst group under 0-1 error, under log-loss, under false-negative rate, and under calibration error can be three different groups. Max-min is only defined once you fix the loss, and that choice is a value judgment, not a technical default - the same point [Fairness Metric Conflicts](fairness-metric-conflicts.md) makes about parity metrics. + +### 3. It is silent on between-group gaps + +If your obligation is a legal disparate-impact standard (an 80%-rule selection-rate ratio), max-min does not help you meet it and can move you away from it, as the example shows. Max-min and parity are complementary tools for different requirements, not substitutes. + +### 4. Levelling down is technically permitted at the margin + +Minimizing the maximum loss is usually improved by *raising* the worst group, but a solver can also lower a better-off group if that reduces the maximum (for instance by shifting a shared threshold). Pareto-efficient formulations (Martinez et al. 2020) rule this out explicitly; a plain reweighting loop does not. + +--- + +## Related Concepts + +- [Why Fairness Metrics Conflict](fairness-metric-conflicts.md) - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis. +- Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here. +- [Mitigation Strategies](mitigation-strategies.md) - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss. +- [What Is Class Imbalance?](class-imbalance.md) - why the smaller, higher-base-rate group is the one average-loss training underserves. +- [What Is Demographic Parity?](demographic-parity.md) - the parity objective max-min is being contrasted against. + +--- + +## Related Projects in This Repo + +- [`German Credit Lending/`](../German%20Credit%20Lending/) - Audit 03, the dataset and `age` attribute used above. +- [`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/) - the frozen `accuracy_equality_diff` figure this explainer cites as the repo's own record of the per-group accuracy gap. +- [`faircode/significance.py`](../faircode/significance.py) - the small-sample warning referenced in Limitation 1. + +--- + +## Further Reading + +- [Rawls, J. (1971, rev. 1999): *A Theory of Justice*, Harvard University Press](https://www.hup.harvard.edu/books/9780674000780) - the "difference principle": social and economic inequalities are just only if they benefit the least-advantaged members of society. +- [Hashimoto, Srivastava, Namkoong & Liang (2018): Fairness Without Demographics in Repeated Loss Minimization, ICML 2018, PMLR 80:1929-1938](https://proceedings.mlr.press/v80/hashimoto18a.html) - a DRO objective that bounds the minority group's risk without using group labels. +- [Martinez, Bertran & Sapiro (2020): Minimax Pareto Fairness: A Multi Objective Perspective, ICML 2020, PMLR 119:6755-6764](https://proceedings.mlr.press/v119/martinez20a.html) - treats each group's risk as a separate objective and finds a classifier that is minimax and Pareto-efficient, with no test-time access to the protected attribute. +- [Diana, Gill, Kearns, Kenthapadi & Roth (2021): Minimax Group Fairness: Algorithms and Experiments, AIES 2021](https://doi.org/10.1145/3461702.3462523) - practical algorithms for the min-max group-error objective and how it compares to statistical-parity mitigation. + +--- + +*Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* + diff --git a/sitemap.xml b/sitemap.xml index 15b23d8..6187c07 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -248,4 +248,8 @@ https://www.thefaircode.xyz/explainers/differential-privacy.html 2026-09-09 + + https://www.thefaircode.xyz/explainers/maxmin-fairness.html + 2026-09-10 + From b6ef0b9c5c91a20730da484f2ea17276b356862a Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Thu, 10 Sep 2026 15:28:11 +0530 Subject: [PATCH 2/2] Add distributionally robust optimization (DRO) for fairness explainer (closes #544) New explainers/distributionally-robust-optimization.md. Covers DRO as an in-processing fairness technique - minimise worst-case loss over an uncertainty set of subgroup distributions - and contrasts it with this repo's S3 constraint-based ExponentiatedGradient (needs a chosen parity metric and a named group up front; DRO needs neither, and no group labels at prediction time). - Min-max objective spelled out against ExponentiatedGradient's constrained objective; the online group-DRO algorithm (Sagawa et al. 2020) and the uncertainty-set radius as the 'robustness knob'. - Concrete example on Audit 03 German Credit (age): an ERM baseline (young held-out log_loss 0.588) vs online group DRO sweeping the robustness knob. DRO reliably drives q(young) from 0.50 to ~0.63, but worst-group test log_loss does not improve (drifts to ~0.599) and overall accuracy stays flat - a direct, honest illustration of Sagawa et al.'s 'importance of regularization for worst-case generalization'. Cross-refs the frozen S0 -> S3 demographic_parity_diff (-0.129 -> +0.023) to show DRO targets a different quantity than the constraint does. - Runnable fit_group_dro + dro_report; pasted output verified against the extracted block, deterministic with pinned libs. - Limitations: uncertainty-set size is a free parameter with no data-driven default; still needs training-time group labels; worst-case train loss != worst-case test loss; grouping/loss choice bounds it. - Citations verified: Sagawa et al. ICLR 2020; Hashimoto et al. ICML 2018; Duchi & Namkoong AoS 2021; Agarwal et al. ICML 2018. Also adds the reciprocal cross-links in maxmin-fairness.md (#543). Regenerated HTML, OG images, explainers-data.{js,json}, package mirror, sitemap.xml, llms-full.txt; check_explainer_count.py --fix bumped 61 -> 62. Stacked on the #543 branch since the two explainers cross-reference each other; retarget to main once #543 merges. --- CONTRIBUTORS.md | 2 +- METRICS.md | 2 +- README.md | 6 +- ROADMAP.md | 2 +- assets/explainers-data.js | 10 + assets/explainers-data.json | 7 + .../distributionally-robust-optimization.png | Bin 0 -> 67799 bytes .../distributionally-robust-optimization.png | Bin 0 -> 68316 bytes .../distributionally-robust-optimization.html | 381 ++++++++++++++++++ .../distributionally-robust-optimization.md | 246 +++++++++++ explainers/maxmin-fairness.html | 8 +- explainers/maxmin-fairness.md | 4 +- faircode/_explainers/data.json | 7 + .../distributionally-robust-optimization.md | 246 +++++++++++ faircode/_explainers/maxmin-fairness.md | 4 +- llms-full.txt | 257 +++++++++++- sitemap.xml | 4 + 17 files changed, 1171 insertions(+), 15 deletions(-) create mode 100644 assets/og-light/distributionally-robust-optimization.png create mode 100644 assets/og/distributionally-robust-optimization.png create mode 100644 explainers/distributionally-robust-optimization.html create mode 100644 explainers/distributionally-robust-optimization.md create mode 100644 faircode/_explainers/distributionally-robust-optimization.md diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index fa0d496..222a815 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -55,7 +55,7 @@ the [Git identity map](#git-identity-map) so `git shortlog -sne` can be reconcil | | Who | Role | |:--|-----|------| -| yakew7 | **Yash Kewlani** - [@yakew7](https://github.com/yakew7) | Creator and maintainer. Author of the seven audits, the `faircode` library and benchmark harness, the Open Dataset Profiler, the website, and the bulk of the 61 explainers. Code owner for `faircode/`, `paper/`, every `audit.yaml`, and project policy (`CLAUDE.md`, `CONTRIBUTING.md`); co-owner of `explainers/`. | +| yakew7 | **Yash Kewlani** - [@yakew7](https://github.com/yakew7) | Creator and maintainer. Author of the seven audits, the `faircode` library and benchmark harness, the Open Dataset Profiler, the website, and the bulk of the 62 explainers. Code owner for `faircode/`, `paper/`, every `audit.yaml`, and project policy (`CLAUDE.md`, `CONTRIBUTING.md`); co-owner of `explainers/`. | Contact: [yashkewlani2020@gmail.com](mailto:yashkewlani2020@gmail.com) · [@thefaircodeproject](https://instagram.com/thefaircodeproject) diff --git a/METRICS.md b/METRICS.md index 1b3a1c2..6f73793 100644 --- a/METRICS.md +++ b/METRICS.md @@ -6,7 +6,7 @@ ![Contributors](https://img.shields.io/badge/Contributors-29-blue?style=flat-square) ![Forks](https://img.shields.io/badge/Forks-33-orange?style=flat-square) ![Watching](https://img.shields.io/badge/Watching-8-yellow?style=flat-square) -![Explainers](https://img.shields.io/badge/Explainers-61-blueviolet?style=flat-square) +![Explainers](https://img.shields.io/badge/Explainers-62-blueviolet?style=flat-square) ![Countries](https://img.shields.io/badge/Countries-20-informational?style=flat-square) ![Updated](https://img.shields.io/badge/Updated-Weekly-lightgrey?style=flat-square) diff --git a/README.md b/README.md index 2e04b4a..5e92293 100644 --- a/README.md +++ b/README.md @@ -781,10 +781,10 @@ features = [ ## Explainers -61 short, plain-language write-ups of individual fairness concepts, each with runnable detection code. The healthcare-focused ones are called out above in [Healthcare AI Bias Focus](#healthcare-ai-bias-focus). +62 short, plain-language write-ups of individual fairness concepts, each with runnable detection code. The healthcare-focused ones are called out above in [Healthcare AI Bias Focus](#healthcare-ai-bias-focus).
-Show all 61 explainers → +Show all 62 explainers → | Explainer | What it covers | |-----------|----------------| @@ -1205,7 +1205,7 @@ The full public roadmap - with phases, completion status, and content schedule - | Combined Social Reach (Instagram + LinkedIn) | 30K+ | | Countries Reached (Website Visitors) | 20 | | Code Audits Published | 7 | -| Explainers Published | 61 | +| Explainers Published | 62 | Tracked weekly in [METRICS.md](METRICS.md). diff --git a/ROADMAP.md b/ROADMAP.md index c63f8a8..4a56f99 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -44,7 +44,7 @@ Fair Code is an open-source responsible AI platform explaining algorithmic bias, ## Phase 1 - Bias Glossary and Beginner Explainers ✅ -**Status: Foundational library complete - 61 explainers published, expanding toward a 60+ library** +**Status: Foundational library complete - 62 explainers published, expanding toward a 60+ library** Build the foundational vocabulary and explain core fairness concepts clearly enough for a non-technical reader. diff --git a/assets/explainers-data.js b/assets/explainers-data.js index d864abd..cce2d0c 100644 --- a/assets/explainers-data.js +++ b/assets/explainers-data.js @@ -617,5 +617,15 @@ window.FAIR_CODE_EXPLAINERS = [ "tags": [ "metrics" ] + }, + { + "slug": "distributionally-robust-optimization", + "title": "What Is Distributionally Robust Optimization (DRO) for Fairness?", + "subtitle": "Train against the worst subgroup your data might be under-representing, without naming a parity metric.", + "summary": "Learn how DRO trains a model to minimize its worst-case loss over an uncertainty set of subgroup distributions - an in-processing alternative to constraint-based methods like ExponentiatedGradient that needs neither a chosen fairness metric nor group labels at prediction time. Worked on the Audit 03 German Credit data: online group DRO reliably shifts training weight onto the worst group, but on a capacity-limited linear model it buys no worst-group generalization - Sagawa et al.'s point about regularization.", + "tags": [ + "metrics", + "data" + ] } ]; diff --git a/assets/explainers-data.json b/assets/explainers-data.json index 528c5dc..261874d 100644 --- a/assets/explainers-data.json +++ b/assets/explainers-data.json @@ -425,6 +425,13 @@ "subtitle": "Make the worst-off group's outcome as good as possible, even if the groups end up unequal.", "summary": "Learn how max-min (Rawlsian) fairness minimizes the maximum group-level loss instead of equalizing a rate across groups, and why a model can move toward it while still failing demographic parity. Worked on the Audit 03 German Credit data: an iterative group-reweighting loop cuts worst-group error from 29.1% to 27.9% and leaves the 12-point selection-rate gap almost untouched.", "tags": ["metrics"] + }, + { + "slug": "distributionally-robust-optimization", + "title": "What Is Distributionally Robust Optimization (DRO) for Fairness?", + "subtitle": "Train against the worst subgroup your data might be under-representing, without naming a parity metric.", + "summary": "Learn how DRO trains a model to minimize its worst-case loss over an uncertainty set of subgroup distributions - an in-processing alternative to constraint-based methods like ExponentiatedGradient that needs neither a chosen fairness metric nor group labels at prediction time. Worked on the Audit 03 German Credit data: online group DRO reliably shifts training weight onto the worst group, but on a capacity-limited linear model it buys no worst-group generalization - Sagawa et al.'s point about regularization.", + "tags": ["metrics", "data"] } ] diff --git a/assets/og-light/distributionally-robust-optimization.png b/assets/og-light/distributionally-robust-optimization.png new file mode 100644 index 0000000000000000000000000000000000000000..4b42a307f9fef56d9fa7cd93f3cbde8cd98bae30 GIT binary patch literal 67799 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

Md|y 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∨c}CG6EtD{+ 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 z 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

v59z*bf>(BlC5=cr<;@xJyF`Aajs97> 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 + + + + +What Is Distributionally Robust Optimization (DRO) for Fairness? · Fair Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ + +
+
Explainer
+

What Is Distributionally Robust Optimization (DRO) for Fairness?

+

Train against the worst subgroup your data might be under-representing, without naming a parity metric.

+

Learn how DRO trains a model to minimize its worst-case loss over an uncertainty set of subgroup distributions - an in-processing alternative to constraint-based methods like ExponentiatedGradient that needs neither a chosen fairness metric nor group labels at prediction time. Worked on the Audit 03 German Credit data: online group DRO reliably shifts training weight onto the worst group, but on a capacity-limited linear model it buys no worst-group generalization - Sagawa et al.'s point about regularization.

+
+ +

What Is Distributionally Robust Optimization (DRO) for Fairness?

+

A constraint-based fix needs you to name the fairness metric and the group up front. DRO instead trains against the worst subgroup your data might be under-representing - whichever one that turns out to be.

+
+

The One-Sentence Definition

+

Distributionally Robust Optimization (DRO) is an in-processing fairness technique that trains a model to minimize its worst-case loss over a set of plausible reweightings of the training distribution, rather than minimizing average loss or constraining a specific parity metric.

+
+

Why It Matters

+

This repo's S3 in-processing strategy (mitigation-strategies.md) is Fairlearn's ExponentiatedGradient: optimize accuracy subject to a fairness constraint, such as demographic parity, on a named protected attribute. That works well when you already know which metric matters and which group is at risk. It has two requirements baked in:

+
  • You choose the fairness metric before training. The constraint is demographic parity, or equalized odds, or one specific thing. A gap on a metric you did not constrain is not touched.
  • You choose the group before training. The constraint is defined on race, or age, or a fixed intersection. A subgroup you did not think to check is invisible to it.
+

DRO relaxes both. Instead of a constraint on one metric for one group, it hedges against the worst subgroup the training data might be under-representing - without committing to a parity metric at all, and (in the Hashimoto et al. 2018 form) without needing group labels. If the group actually being harmed is not the one you would have constrained, DRO can still help; ExponentiatedGradient cannot.

+

The cost is a free parameter and no guarantee, both covered below.

+
+

Core Concept: The Min-Max Objective

+

ExponentiatedGradient solves a constrained problem:

+
minimise  average loss L(theta)
+subject to  |demographic_parity_gap(theta)|  <=  epsilon
+

Group DRO solves a min-max problem instead:

+
minimise over theta of   max over Q in U of   E_Q[ loss(theta) ]
+

where U is an uncertainty set: a ball of distributions around the empirical training distribution. E_Q[loss] is the expected loss if the data were reweighted to Q. The model is trained so that even the least favourable reweighting in U still has low loss.

+

When U is "all reweightings that shift mass between predefined groups", this is group DRO (Sagawa et al. 2020), and it has a simple online algorithm:

+

1. Keep a probability vector q over groups. 2. Each step, raise q for whichever group currently has the highest loss. 3. Fit the model on q-reweighted examples. 4. Repeat until q stabilizes.

+

The size of U - how far q is allowed to move from the group base rates - is the robustness knob. Bigger U means the model hedges against more extreme reweightings.

+

This is the same reweighting loop as iterative max-min (Rawlsian) fairness; the difference is framing. Max-min asks "make the worst group's outcome as good as possible." DRO asks "stay robust to a distribution shift that inflates the worst group" - and the uncertainty set U is the explicit statement of which shifts you are hedging against.

+
+

Concrete Example: German Credit Lending - Audit 03

+

Audit 03 (German Credit Lending/) predicts bad credit risk on 1,000 real loan records; the protected attribute is age, with "young" meaning under 30.

+

For reference, this repo's frozen benchmark (paper/results-frozen/results_fairness.csv, the earlier snapshot - see CLAUDE.md) records the S3 in_processing strategy driving the baseline logistic-regression demographic_parity_diff on age from -0.129 (p = 0.05) at S0 to +0.023 (p = 0.71) at S3 - the constraint closes the parity gap it was given. Group DRO does not target that metric at all; it targets worst-group loss.

+

The script below runs an ERM (average-loss) baseline, then online group DRO, sweeping the robustness knob. Features drop age and personal_status; inputs are standardized; the split is 80/20 stratified at random_state=42.

+
import numpy as np
+import pandas as pd
+from sklearn.linear_model import LogisticRegression
+from sklearn.metrics import log_loss
+from sklearn.model_selection import train_test_split
+
+
+def load_german_credit(path="German Credit Lending/credit_customers.csv"):
+    df = pd.read_csv(path)
+    df["y_bad"] = (df["class"] == "bad").astype(int)
+    df["age_group"] = np.where(df["age"] < 30, "young", "older")
+    drop = {"class", "y_bad", "age", "age_group", "personal_status"}
+    feats = [c for c in df.columns if c not in drop]
+    return pd.get_dummies(df[feats], drop_first=True), df["y_bad"].to_numpy(), df["age_group"].to_numpy()
+
+
+def per_group(model, X, y, groups):
+    pred, proba = model.predict(X), model.predict_proba(X)[:, 1]
+    rows = {}
+    for g in ["older", "young"]:
+        m = groups == g
+        rows[g] = {"n": int(m.sum()),
+                   "accuracy": round(float((pred[m] == y[m]).mean()), 4),
+                   "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)}
+    worst = max(rows["older"]["log_loss"], rows["young"]["log_loss"])
+    return pd.DataFrame(rows).T, round(float(worst), 4), round(float((pred == y).mean()), 4)
+
+
+def fit_group_dro(Xtr, ytr, gtr, robustness=1.0, n_rounds=100):
+    """Online group DRO (Sagawa et al. 2020): keep a distribution q over groups,
+    each round upweight whichever group has the highest loss, and fit the model
+    on q-reweighted examples. `robustness` is the uncertainty-set knob - larger
+    means q chases the worst group harder."""
+    q = {"older": 0.5, "young": 0.5}
+    frac = {g: (gtr == g).mean() for g in q}
+    for _ in range(n_rounds):
+        w = np.array([q[g] / frac[g] for g in gtr], dtype=float)
+        w *= len(w) / w.sum()
+        model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w)
+        proba = model.predict_proba(Xtr)[:, 1]
+        gl = {g: log_loss(ytr[gtr == g], proba[gtr == g], labels=[0, 1]) for g in q}
+        for g in q:
+            q[g] *= np.exp(robustness * gl[g])
+        total = sum(q.values())
+        q = {g: v / total for g, v in q.items()}
+    return model, q
+
+
+if __name__ == "__main__":
+    X, y, g = load_german_credit()
+    Xtr, Xte, ytr, yte, gtr, gte = train_test_split(
+        X, y, g, test_size=0.2, random_state=42, stratify=y)
+    mean, std = Xtr.mean(), Xtr.std().replace(0, 1)
+    Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy()
+
+    erm = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr)
+    tbl, worst, acc = per_group(erm, Xte_s, yte, gte)
+    print("=== ERM baseline (minimise average loss) ===")
+    print(tbl.to_string())
+    print(f"overall accuracy   : {acc:.4f}")
+    print(f"worst-group log_loss: {worst:.4f}\n")
+
+    print("=== Group DRO: sweep the robustness knob ===")
+    print(f"{'robustness':>10} | {'q(young)':>8} | {'worst-group log_loss':>20} | {'overall acc':>11}")
+    for r in [0.0, 0.5, 1.0, 2.0, 5.0]:
+        model, q = fit_group_dro(Xtr_s, ytr, gtr, robustness=r)
+        _, w, a = per_group(model, Xte_s, yte, gte)
+        print(f"{r:>10.1f} | {q['young']:>8.2f} | {w:>20.4f} | {a:>11.4f}")
+

Script Execution Output

+
=== ERM baseline (minimise average loss) ===
+           n  accuracy  log_loss
+older  121.0    0.8099    0.4285
+young   79.0    0.7089    0.5880
+overall accuracy   : 0.7700
+worst-group log_loss: 0.5880
+
+=== Group DRO: sweep the robustness knob ===
+robustness | q(young) | worst-group log_loss | overall acc
+       0.0 |     0.50 |               0.5910 |      0.7700
+       0.5 |     0.62 |               0.5988 |      0.7750
+       1.0 |     0.63 |               0.5991 |      0.7700
+       2.0 |     0.63 |               0.5991 |      0.7700
+       5.0 |     0.63 |               0.5991 |      0.7700
+

(Deterministic with these library versions; a solver's last digits can shift across BLAS backends.)

+

Three things this shows:

+

DRO reliably redirects training weight to the worst group. ERM weights groups by their size. As the robustness knob goes up, q(young) climbs from 0.50 to about 0.63 and then saturates - the min-max objective has found the worst group and is pushing on it. That part works exactly as designed.

+

On this problem, redirecting the weight buys no worst-group generalization. The young group's held-out log_loss is 0.588 under ERM and does not improve under any robustness setting - it drifts slightly up, to ~0.599. Overall accuracy stays flat at 0.77. A linear model on 800 training rows is capacity-limited, not distribution-limited: the young group is genuinely harder to predict here, and reweighting the objective toward it cannot manufacture signal that is not in the features.

+

This is the paper's own headline finding, not a bug in the loop. Sagawa et al. (2020) title their paper "On the Importance of Regularization for Worst-Case Generalization" precisely because plain group DRO increases worst-group training influence without improving worst-group test loss unless the model is strongly regularized (and, in their setting, overparameterized). The robustness knob is a real dial with a real cost - turn it up and q over-hedges toward an implausible worst case, shaving average-case accuracy - but it is not a guarantee of a fairer model.

+
+

Detection and Implementation Code

+

fit_group_dro above is the implementation. The helper below reports whether a DRO run actually moved worst-group loss and what it cost average accuracy - the trade you are actually making.

+
def dro_report(erm_model, dro_model, X, y, groups):
+    """Compare an ERM model against a group-DRO model on held-out data."""
+    def summarise(model):
+        pred, proba = model.predict(X), model.predict_proba(X)[:, 1]
+        per = {}
+        for g in sorted(set(groups)):
+            m = groups == g
+            from sklearn.metrics import log_loss
+            per[g] = {"acc": round(float((pred[m] == y[m]).mean()), 4),
+                      "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)}
+        return per, round(float((pred == y).mean()), 4)
+
+    erm_per, erm_acc = summarise(erm_model)
+    dro_per, dro_acc = summarise(dro_model)
+    erm_worst = max(v["log_loss"] for v in erm_per.values())
+    dro_worst = max(v["log_loss"] for v in dro_per.values())
+    return {
+        "erm_per_group": erm_per,
+        "dro_per_group": dro_per,
+        "worst_group_log_loss_before": erm_worst,
+        "worst_group_log_loss_after": dro_worst,
+        "worst_group_improved": dro_worst < erm_worst,
+        "overall_accuracy_delta": round(dro_acc - erm_acc, 4),
+    }
+
+

Limitations

+

1. The uncertainty-set size is a free parameter with no data-driven default

+

robustness (equivalently, the radius of U) has to be picked. Too small and DRO collapses to ERM. Too large and the model hedges against reweightings that will never occur, trading real average-case accuracy for robustness to a fantasy worst case. There is no held-out quantity that tells you the "right" size - it encodes how much distribution shift you believe is plausible, which is a judgment call.

+

2. It still needs group labels at training time

+

The group-DRO form used here requires knowing each training example's group to maintain q. It removes the need to pick a parity metric up front, and it does not need group labels at prediction time, but it is not label-free. (Hashimoto et al. 2018's version drops the training-time group labels too, at the cost of hedging against all low-probability subpopulations, not just the ones you care about.)

+

3. Worst-case training loss is not worst-case test loss

+

As the example shows, pushing q toward the worst training group does not by itself improve that group's generalization. Without strong regularization or capacity control, group DRO can overfit the worst group's training set - the central caveat in Sagawa et al. (2020).

+

4. "Worst group" is only defined once you fix the loss and the grouping

+

DRO over groups defined by age says nothing about groups defined by job or by an age x foreign_worker intersection. And the worst group under log-loss, under 0-1 error, and under false-negative rate can differ. The uncertainty set is only as good as the grouping and loss you put into it.

+
+ + +
+ + +
+

Further Reading

+ +
+

Part of The Fair Code Project - exposing and fixing algorithmic bias with real data and open code.

+
+ + + + diff --git a/explainers/distributionally-robust-optimization.md b/explainers/distributionally-robust-optimization.md new file mode 100644 index 0000000..babcc6b --- /dev/null +++ b/explainers/distributionally-robust-optimization.md @@ -0,0 +1,246 @@ +# What Is Distributionally Robust Optimization (DRO) for Fairness? + +> *A constraint-based fix needs you to name the fairness metric and the group up front. DRO instead trains against the worst subgroup your data might be under-representing - whichever one that turns out to be.* + +--- + +## The One-Sentence Definition + +**Distributionally Robust Optimization (DRO)** is an in-processing fairness technique that trains a model to minimize its *worst-case* loss over a set of plausible reweightings of the training distribution, rather than minimizing average loss or constraining a specific parity metric. + +--- + +## Why It Matters + +This repo's S3 in-processing strategy ([mitigation-strategies.md](mitigation-strategies.md)) is Fairlearn's `ExponentiatedGradient`: optimize accuracy subject to a fairness constraint, such as demographic parity, on a named protected attribute. That works well when you already know which metric matters and which group is at risk. It has two requirements baked in: + +- **You choose the fairness metric before training.** The constraint is demographic parity, or equalized odds, or one specific thing. A gap on a metric you did not constrain is not touched. +- **You choose the group before training.** The constraint is defined on `race`, or `age`, or a fixed intersection. A subgroup you did not think to check is invisible to it. + +DRO relaxes both. Instead of a constraint on one metric for one group, it hedges against the worst subgroup the training data might be under-representing - without committing to a parity metric at all, and (in the Hashimoto et al. 2018 form) without needing group labels. If the group actually being harmed is not the one you would have constrained, DRO can still help; `ExponentiatedGradient` cannot. + +The cost is a free parameter and no guarantee, both covered below. + +--- + +## Core Concept: The Min-Max Objective + +`ExponentiatedGradient` solves a **constrained** problem: + +``` +minimise average loss L(theta) +subject to |demographic_parity_gap(theta)| <= epsilon +``` + +Group DRO solves a **min-max** problem instead: + +``` +minimise over theta of max over Q in U of E_Q[ loss(theta) ] +``` + +where `U` is an *uncertainty set*: a ball of distributions around the empirical training distribution. `E_Q[loss]` is the expected loss if the data were reweighted to `Q`. The model is trained so that even the least favourable reweighting in `U` still has low loss. + +When `U` is "all reweightings that shift mass between predefined groups", this is **group DRO** (Sagawa et al. 2020), and it has a simple online algorithm: + +1. Keep a probability vector `q` over groups. +2. Each step, raise `q` for whichever group currently has the highest loss. +3. Fit the model on `q`-reweighted examples. +4. Repeat until `q` stabilizes. + +The size of `U` - how far `q` is allowed to move from the group base rates - is the **robustness knob**. Bigger `U` means the model hedges against more extreme reweightings. + +This is the same reweighting loop as iterative [max-min (Rawlsian) fairness](maxmin-fairness.md); the difference is framing. Max-min asks "make the worst group's *outcome* as good as possible." DRO asks "stay robust to a *distribution shift* that inflates the worst group" - and the uncertainty set `U` is the explicit statement of which shifts you are hedging against. + +--- + +## Concrete Example: German Credit Lending - Audit 03 + +Audit 03 ([`German Credit Lending/`](../German%20Credit%20Lending/)) predicts bad credit risk on 1,000 real loan records; the protected attribute is `age`, with "young" meaning under 30. + +For reference, this repo's frozen benchmark ([`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/), the earlier snapshot - see [CLAUDE.md](../CLAUDE.md)) records the S3 `in_processing` strategy driving the baseline logistic-regression `demographic_parity_diff` on `age` from **-0.129 (p = 0.05) at S0 to +0.023 (p = 0.71) at S3** - the constraint closes the parity gap it was given. Group DRO does not target that metric at all; it targets worst-group loss. + +The script below runs an ERM (average-loss) baseline, then online group DRO, sweeping the robustness knob. Features drop `age` and `personal_status`; inputs are standardized; the split is 80/20 stratified at `random_state=42`. + +```python +import numpy as np +import pandas as pd +from sklearn.linear_model import LogisticRegression +from sklearn.metrics import log_loss +from sklearn.model_selection import train_test_split + + +def load_german_credit(path="German Credit Lending/credit_customers.csv"): + df = pd.read_csv(path) + df["y_bad"] = (df["class"] == "bad").astype(int) + df["age_group"] = np.where(df["age"] < 30, "young", "older") + drop = {"class", "y_bad", "age", "age_group", "personal_status"} + feats = [c for c in df.columns if c not in drop] + return pd.get_dummies(df[feats], drop_first=True), df["y_bad"].to_numpy(), df["age_group"].to_numpy() + + +def per_group(model, X, y, groups): + pred, proba = model.predict(X), model.predict_proba(X)[:, 1] + rows = {} + for g in ["older", "young"]: + m = groups == g + rows[g] = {"n": int(m.sum()), + "accuracy": round(float((pred[m] == y[m]).mean()), 4), + "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)} + worst = max(rows["older"]["log_loss"], rows["young"]["log_loss"]) + return pd.DataFrame(rows).T, round(float(worst), 4), round(float((pred == y).mean()), 4) + + +def fit_group_dro(Xtr, ytr, gtr, robustness=1.0, n_rounds=100): + """Online group DRO (Sagawa et al. 2020): keep a distribution q over groups, + each round upweight whichever group has the highest loss, and fit the model + on q-reweighted examples. `robustness` is the uncertainty-set knob - larger + means q chases the worst group harder.""" + q = {"older": 0.5, "young": 0.5} + frac = {g: (gtr == g).mean() for g in q} + for _ in range(n_rounds): + w = np.array([q[g] / frac[g] for g in gtr], dtype=float) + w *= len(w) / w.sum() + model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w) + proba = model.predict_proba(Xtr)[:, 1] + gl = {g: log_loss(ytr[gtr == g], proba[gtr == g], labels=[0, 1]) for g in q} + for g in q: + q[g] *= np.exp(robustness * gl[g]) + total = sum(q.values()) + q = {g: v / total for g, v in q.items()} + return model, q + + +if __name__ == "__main__": + X, y, g = load_german_credit() + Xtr, Xte, ytr, yte, gtr, gte = train_test_split( + X, y, g, test_size=0.2, random_state=42, stratify=y) + mean, std = Xtr.mean(), Xtr.std().replace(0, 1) + Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy() + + erm = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr) + tbl, worst, acc = per_group(erm, Xte_s, yte, gte) + print("=== ERM baseline (minimise average loss) ===") + print(tbl.to_string()) + print(f"overall accuracy : {acc:.4f}") + print(f"worst-group log_loss: {worst:.4f}\n") + + print("=== Group DRO: sweep the robustness knob ===") + print(f"{'robustness':>10} | {'q(young)':>8} | {'worst-group log_loss':>20} | {'overall acc':>11}") + for r in [0.0, 0.5, 1.0, 2.0, 5.0]: + model, q = fit_group_dro(Xtr_s, ytr, gtr, robustness=r) + _, w, a = per_group(model, Xte_s, yte, gte) + print(f"{r:>10.1f} | {q['young']:>8.2f} | {w:>20.4f} | {a:>11.4f}") +``` + +### Script Execution Output + +``` +=== ERM baseline (minimise average loss) === + n accuracy log_loss +older 121.0 0.8099 0.4285 +young 79.0 0.7089 0.5880 +overall accuracy : 0.7700 +worst-group log_loss: 0.5880 + +=== Group DRO: sweep the robustness knob === +robustness | q(young) | worst-group log_loss | overall acc + 0.0 | 0.50 | 0.5910 | 0.7700 + 0.5 | 0.62 | 0.5988 | 0.7750 + 1.0 | 0.63 | 0.5991 | 0.7700 + 2.0 | 0.63 | 0.5991 | 0.7700 + 5.0 | 0.63 | 0.5991 | 0.7700 +``` + +(Deterministic with these library versions; a solver's last digits can shift across BLAS backends.) + +Three things this shows: + +**DRO reliably redirects training weight to the worst group.** ERM weights groups by their size. As the robustness knob goes up, `q(young)` climbs from 0.50 to about 0.63 and then saturates - the min-max objective has found the worst group and is pushing on it. That part works exactly as designed. + +**On this problem, redirecting the weight buys no worst-group generalization.** The young group's held-out `log_loss` is 0.588 under ERM and does not improve under any robustness setting - it drifts slightly *up*, to ~0.599. Overall accuracy stays flat at 0.77. A linear model on 800 training rows is capacity-limited, not distribution-limited: the young group is genuinely harder to predict here, and reweighting the objective toward it cannot manufacture signal that is not in the features. + +**This is the paper's own headline finding, not a bug in the loop.** Sagawa et al. (2020) title their paper *"On the Importance of Regularization for Worst-Case Generalization"* precisely because plain group DRO increases worst-group training influence without improving worst-group test loss unless the model is strongly regularized (and, in their setting, overparameterized). The robustness knob is a real dial with a real cost - turn it up and `q` over-hedges toward an implausible worst case, shaving average-case accuracy - but it is not a guarantee of a fairer model. + +--- + +## Detection and Implementation Code + +`fit_group_dro` above is the implementation. The helper below reports whether a DRO run actually moved worst-group loss and what it cost average accuracy - the trade you are actually making. + +```python +def dro_report(erm_model, dro_model, X, y, groups): + """Compare an ERM model against a group-DRO model on held-out data.""" + def summarise(model): + pred, proba = model.predict(X), model.predict_proba(X)[:, 1] + per = {} + for g in sorted(set(groups)): + m = groups == g + from sklearn.metrics import log_loss + per[g] = {"acc": round(float((pred[m] == y[m]).mean()), 4), + "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)} + return per, round(float((pred == y).mean()), 4) + + erm_per, erm_acc = summarise(erm_model) + dro_per, dro_acc = summarise(dro_model) + erm_worst = max(v["log_loss"] for v in erm_per.values()) + dro_worst = max(v["log_loss"] for v in dro_per.values()) + return { + "erm_per_group": erm_per, + "dro_per_group": dro_per, + "worst_group_log_loss_before": erm_worst, + "worst_group_log_loss_after": dro_worst, + "worst_group_improved": dro_worst < erm_worst, + "overall_accuracy_delta": round(dro_acc - erm_acc, 4), + } +``` + +--- + +## Limitations + +### 1. The uncertainty-set size is a free parameter with no data-driven default + +`robustness` (equivalently, the radius of `U`) has to be picked. Too small and DRO collapses to ERM. Too large and the model hedges against reweightings that will never occur, trading real average-case accuracy for robustness to a fantasy worst case. There is no held-out quantity that tells you the "right" size - it encodes how much distribution shift you believe is plausible, which is a judgment call. + +### 2. It still needs group labels at training time + +The group-DRO form used here requires knowing each training example's group to maintain `q`. It removes the need to pick a *parity metric* up front, and it does not need group labels at prediction time, but it is not label-free. (Hashimoto et al. 2018's version drops the training-time group labels too, at the cost of hedging against *all* low-probability subpopulations, not just the ones you care about.) + +### 3. Worst-case training loss is not worst-case test loss + +As the example shows, pushing `q` toward the worst training group does not by itself improve that group's generalization. Without strong regularization or capacity control, group DRO can overfit the worst group's training set - the central caveat in Sagawa et al. (2020). + +### 4. "Worst group" is only defined once you fix the loss and the grouping + +DRO over groups defined by `age` says nothing about groups defined by `job` or by an `age x foreign_worker` intersection. And the worst group under log-loss, under 0-1 error, and under false-negative rate can differ. The uncertainty set is only as good as the grouping and loss you put into it. + +--- + +## Related Concepts + +- [Mitigation Strategies](mitigation-strategies.md) - this repo's S3 `ExponentiatedGradient` in-processing strategy, the constraint-based method DRO is contrasted against. +- [What Is Max-Min (Rawlsian) Fairness?](maxmin-fairness.md) - the same reweighting loop, framed as an objective on group outcomes rather than robustness to distribution shift. +- [What Is Distribution Shift?](distribution-shift.md) - the deployment-time phenomenon whose worst case DRO's uncertainty set is meant to bound. +- [What Is the Fairness-Accuracy Trade-off?](fairness-accuracy-tradeoff.md) - the average-case accuracy DRO spends when the robustness knob is turned up. +- [What Is Class Imbalance?](class-imbalance.md) - why average-loss training underserves the smaller, harder group DRO reweights toward. + +--- + +## Related Projects in This Repo + +- [`German Credit Lending/`](../German%20Credit%20Lending/) - Audit 03, the dataset and `age` attribute used above. +- [`faircode/strategies.py`](../faircode/strategies.py) - the S0-S4 implementation, including the S3 `ExponentiatedGradient` strategy DRO is compared with. +- [`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/) - the frozen S0/S3 `demographic_parity_diff` figures cited above. + +--- + +## Further Reading + +- [Sagawa, Koh, Hashimoto & Liang (2020): Distributionally Robust Neural Networks for Group Shifts: On the Importance of Regularization for Worst-Case Generalization, ICLR 2020](https://arxiv.org/abs/1911.08731) - the group-DRO algorithm used here, and the finding that worst-case generalization needs strong regularization. +- [Hashimoto, Srivastava, Namkoong & Liang (2018): Fairness Without Demographics in Repeated Loss Minimization, ICML 2018, PMLR 80:1929-1938](https://proceedings.mlr.press/v80/hashimoto18a.html) - a DRO objective that bounds the minority group's risk without any group labels. +- [Duchi & Namkoong (2021): Learning Models with Uniform Performance via Distributionally Robust Optimization, Annals of Statistics 49(3), 1378-1406](https://doi.org/10.1214/20-AOS2004) - the statistical foundations of the uncertainty-set formulation. +- [Agarwal, Beygelzimer, Dudik, Langford & Wallach (2018): A Reductions Approach to Fair Classification, ICML 2018](https://proceedings.mlr.press/v80/agarwal18a.html) - the `ExponentiatedGradient` constraint-based method this repo uses at S3, for contrast. + +--- + +*Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* diff --git a/explainers/maxmin-fairness.html b/explainers/maxmin-fairness.html index 37230fb..f68fc4d 100644 --- a/explainers/maxmin-fairness.html +++ b/explainers/maxmin-fairness.html @@ -110,7 +110,9 @@ "@type": "DefinedTermSet", "name": "Fair Code Explainers", "url": "https://www.thefaircode.xyz/index.html#explainers" - } + }, + "datePublished": "2026-09-10", + "dateModified": "2026-09-10" }, { "@type": "FAQPage", @@ -208,7 +210,7 @@

Core Concept: A Different O
minimise over theta of   max over groups g of   L_g(theta)

There is no equality constraint. The objective is a min of a max: push down the largest per-group loss, then whatever the next-largest one is, and so on. If lifting the worst group also happens to help the others, fine; if it leaves a between-group gap, that gap is not penalized.

Two standard ways to approximate it without a bespoke solver:

-

1. Iterative group reweighting. Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. 2. Distributionally robust optimization (DRO). Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk without needing group labels, which is why max-min and DRO are usually discussed together.

+

1. Iterative group reweighting. Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization does. 2. Distributionally robust optimization (DRO). Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk without needing group labels, which is why max-min and DRO are usually discussed together.


Concrete Example: German Credit Lending - Audit 03

Audit 03 in this repo (German Credit Lending/) predicts bad credit risk on 1,000 real loan records. The protected attribute is age; here "young" means under 30 (the same cut unfair.py uses).

@@ -346,7 +348,7 @@

4. Levelling do

Minimizing the maximum loss is usually improved by raising the worst group, but a solver can also lower a better-off group if that reduces the maximum (for instance by shifting a shared threshold). Pareto-efficient formulations (Martinez et al. 2020) rule this out explicitly; a plain reweighting loop does not.


-
  • Why Fairness Metrics Conflict - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis.
  • Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here.
  • Mitigation Strategies - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss.
  • What Is Class Imbalance? - why the smaller, higher-base-rate group is the one average-loss training underserves.
  • What Is Demographic Parity? - the parity objective max-min is being contrasted against.
+
diff --git a/explainers/maxmin-fairness.md b/explainers/maxmin-fairness.md index 4dd9354..40498e8 100644 --- a/explainers/maxmin-fairness.md +++ b/explainers/maxmin-fairness.md @@ -44,7 +44,7 @@ There is no equality constraint. The objective is a `min` of a `max`: push down Two standard ways to approximate it without a bespoke solver: -1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. +1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what [group distributionally robust optimization](distributionally-robust-optimization.md) does. 2. **Distributionally robust optimization (DRO).** Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk *without needing group labels*, which is why max-min and DRO are usually discussed together. --- @@ -220,7 +220,7 @@ Minimizing the maximum loss is usually improved by *raising* the worst group, bu ## Related Concepts - [Why Fairness Metrics Conflict](fairness-metric-conflicts.md) - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis. -- Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here. +- [What Is Distributionally Robust Optimization (DRO) for Fairness?](distributionally-robust-optimization.md) - the worst-case-loss training method that is the online form of the reweighting loop here. - [Mitigation Strategies](mitigation-strategies.md) - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss. - [What Is Class Imbalance?](class-imbalance.md) - why the smaller, higher-base-rate group is the one average-loss training underserves. - [What Is Demographic Parity?](demographic-parity.md) - the parity objective max-min is being contrasted against. diff --git a/faircode/_explainers/data.json b/faircode/_explainers/data.json index 528c5dc..261874d 100644 --- a/faircode/_explainers/data.json +++ b/faircode/_explainers/data.json @@ -425,6 +425,13 @@ "subtitle": "Make the worst-off group's outcome as good as possible, even if the groups end up unequal.", "summary": "Learn how max-min (Rawlsian) fairness minimizes the maximum group-level loss instead of equalizing a rate across groups, and why a model can move toward it while still failing demographic parity. Worked on the Audit 03 German Credit data: an iterative group-reweighting loop cuts worst-group error from 29.1% to 27.9% and leaves the 12-point selection-rate gap almost untouched.", "tags": ["metrics"] + }, + { + "slug": "distributionally-robust-optimization", + "title": "What Is Distributionally Robust Optimization (DRO) for Fairness?", + "subtitle": "Train against the worst subgroup your data might be under-representing, without naming a parity metric.", + "summary": "Learn how DRO trains a model to minimize its worst-case loss over an uncertainty set of subgroup distributions - an in-processing alternative to constraint-based methods like ExponentiatedGradient that needs neither a chosen fairness metric nor group labels at prediction time. Worked on the Audit 03 German Credit data: online group DRO reliably shifts training weight onto the worst group, but on a capacity-limited linear model it buys no worst-group generalization - Sagawa et al.'s point about regularization.", + "tags": ["metrics", "data"] } ] diff --git a/faircode/_explainers/distributionally-robust-optimization.md b/faircode/_explainers/distributionally-robust-optimization.md new file mode 100644 index 0000000..babcc6b --- /dev/null +++ b/faircode/_explainers/distributionally-robust-optimization.md @@ -0,0 +1,246 @@ +# What Is Distributionally Robust Optimization (DRO) for Fairness? + +> *A constraint-based fix needs you to name the fairness metric and the group up front. DRO instead trains against the worst subgroup your data might be under-representing - whichever one that turns out to be.* + +--- + +## The One-Sentence Definition + +**Distributionally Robust Optimization (DRO)** is an in-processing fairness technique that trains a model to minimize its *worst-case* loss over a set of plausible reweightings of the training distribution, rather than minimizing average loss or constraining a specific parity metric. + +--- + +## Why It Matters + +This repo's S3 in-processing strategy ([mitigation-strategies.md](mitigation-strategies.md)) is Fairlearn's `ExponentiatedGradient`: optimize accuracy subject to a fairness constraint, such as demographic parity, on a named protected attribute. That works well when you already know which metric matters and which group is at risk. It has two requirements baked in: + +- **You choose the fairness metric before training.** The constraint is demographic parity, or equalized odds, or one specific thing. A gap on a metric you did not constrain is not touched. +- **You choose the group before training.** The constraint is defined on `race`, or `age`, or a fixed intersection. A subgroup you did not think to check is invisible to it. + +DRO relaxes both. Instead of a constraint on one metric for one group, it hedges against the worst subgroup the training data might be under-representing - without committing to a parity metric at all, and (in the Hashimoto et al. 2018 form) without needing group labels. If the group actually being harmed is not the one you would have constrained, DRO can still help; `ExponentiatedGradient` cannot. + +The cost is a free parameter and no guarantee, both covered below. + +--- + +## Core Concept: The Min-Max Objective + +`ExponentiatedGradient` solves a **constrained** problem: + +``` +minimise average loss L(theta) +subject to |demographic_parity_gap(theta)| <= epsilon +``` + +Group DRO solves a **min-max** problem instead: + +``` +minimise over theta of max over Q in U of E_Q[ loss(theta) ] +``` + +where `U` is an *uncertainty set*: a ball of distributions around the empirical training distribution. `E_Q[loss]` is the expected loss if the data were reweighted to `Q`. The model is trained so that even the least favourable reweighting in `U` still has low loss. + +When `U` is "all reweightings that shift mass between predefined groups", this is **group DRO** (Sagawa et al. 2020), and it has a simple online algorithm: + +1. Keep a probability vector `q` over groups. +2. Each step, raise `q` for whichever group currently has the highest loss. +3. Fit the model on `q`-reweighted examples. +4. Repeat until `q` stabilizes. + +The size of `U` - how far `q` is allowed to move from the group base rates - is the **robustness knob**. Bigger `U` means the model hedges against more extreme reweightings. + +This is the same reweighting loop as iterative [max-min (Rawlsian) fairness](maxmin-fairness.md); the difference is framing. Max-min asks "make the worst group's *outcome* as good as possible." DRO asks "stay robust to a *distribution shift* that inflates the worst group" - and the uncertainty set `U` is the explicit statement of which shifts you are hedging against. + +--- + +## Concrete Example: German Credit Lending - Audit 03 + +Audit 03 ([`German Credit Lending/`](../German%20Credit%20Lending/)) predicts bad credit risk on 1,000 real loan records; the protected attribute is `age`, with "young" meaning under 30. + +For reference, this repo's frozen benchmark ([`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/), the earlier snapshot - see [CLAUDE.md](../CLAUDE.md)) records the S3 `in_processing` strategy driving the baseline logistic-regression `demographic_parity_diff` on `age` from **-0.129 (p = 0.05) at S0 to +0.023 (p = 0.71) at S3** - the constraint closes the parity gap it was given. Group DRO does not target that metric at all; it targets worst-group loss. + +The script below runs an ERM (average-loss) baseline, then online group DRO, sweeping the robustness knob. Features drop `age` and `personal_status`; inputs are standardized; the split is 80/20 stratified at `random_state=42`. + +```python +import numpy as np +import pandas as pd +from sklearn.linear_model import LogisticRegression +from sklearn.metrics import log_loss +from sklearn.model_selection import train_test_split + + +def load_german_credit(path="German Credit Lending/credit_customers.csv"): + df = pd.read_csv(path) + df["y_bad"] = (df["class"] == "bad").astype(int) + df["age_group"] = np.where(df["age"] < 30, "young", "older") + drop = {"class", "y_bad", "age", "age_group", "personal_status"} + feats = [c for c in df.columns if c not in drop] + return pd.get_dummies(df[feats], drop_first=True), df["y_bad"].to_numpy(), df["age_group"].to_numpy() + + +def per_group(model, X, y, groups): + pred, proba = model.predict(X), model.predict_proba(X)[:, 1] + rows = {} + for g in ["older", "young"]: + m = groups == g + rows[g] = {"n": int(m.sum()), + "accuracy": round(float((pred[m] == y[m]).mean()), 4), + "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)} + worst = max(rows["older"]["log_loss"], rows["young"]["log_loss"]) + return pd.DataFrame(rows).T, round(float(worst), 4), round(float((pred == y).mean()), 4) + + +def fit_group_dro(Xtr, ytr, gtr, robustness=1.0, n_rounds=100): + """Online group DRO (Sagawa et al. 2020): keep a distribution q over groups, + each round upweight whichever group has the highest loss, and fit the model + on q-reweighted examples. `robustness` is the uncertainty-set knob - larger + means q chases the worst group harder.""" + q = {"older": 0.5, "young": 0.5} + frac = {g: (gtr == g).mean() for g in q} + for _ in range(n_rounds): + w = np.array([q[g] / frac[g] for g in gtr], dtype=float) + w *= len(w) / w.sum() + model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w) + proba = model.predict_proba(Xtr)[:, 1] + gl = {g: log_loss(ytr[gtr == g], proba[gtr == g], labels=[0, 1]) for g in q} + for g in q: + q[g] *= np.exp(robustness * gl[g]) + total = sum(q.values()) + q = {g: v / total for g, v in q.items()} + return model, q + + +if __name__ == "__main__": + X, y, g = load_german_credit() + Xtr, Xte, ytr, yte, gtr, gte = train_test_split( + X, y, g, test_size=0.2, random_state=42, stratify=y) + mean, std = Xtr.mean(), Xtr.std().replace(0, 1) + Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy() + + erm = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr) + tbl, worst, acc = per_group(erm, Xte_s, yte, gte) + print("=== ERM baseline (minimise average loss) ===") + print(tbl.to_string()) + print(f"overall accuracy : {acc:.4f}") + print(f"worst-group log_loss: {worst:.4f}\n") + + print("=== Group DRO: sweep the robustness knob ===") + print(f"{'robustness':>10} | {'q(young)':>8} | {'worst-group log_loss':>20} | {'overall acc':>11}") + for r in [0.0, 0.5, 1.0, 2.0, 5.0]: + model, q = fit_group_dro(Xtr_s, ytr, gtr, robustness=r) + _, w, a = per_group(model, Xte_s, yte, gte) + print(f"{r:>10.1f} | {q['young']:>8.2f} | {w:>20.4f} | {a:>11.4f}") +``` + +### Script Execution Output + +``` +=== ERM baseline (minimise average loss) === + n accuracy log_loss +older 121.0 0.8099 0.4285 +young 79.0 0.7089 0.5880 +overall accuracy : 0.7700 +worst-group log_loss: 0.5880 + +=== Group DRO: sweep the robustness knob === +robustness | q(young) | worst-group log_loss | overall acc + 0.0 | 0.50 | 0.5910 | 0.7700 + 0.5 | 0.62 | 0.5988 | 0.7750 + 1.0 | 0.63 | 0.5991 | 0.7700 + 2.0 | 0.63 | 0.5991 | 0.7700 + 5.0 | 0.63 | 0.5991 | 0.7700 +``` + +(Deterministic with these library versions; a solver's last digits can shift across BLAS backends.) + +Three things this shows: + +**DRO reliably redirects training weight to the worst group.** ERM weights groups by their size. As the robustness knob goes up, `q(young)` climbs from 0.50 to about 0.63 and then saturates - the min-max objective has found the worst group and is pushing on it. That part works exactly as designed. + +**On this problem, redirecting the weight buys no worst-group generalization.** The young group's held-out `log_loss` is 0.588 under ERM and does not improve under any robustness setting - it drifts slightly *up*, to ~0.599. Overall accuracy stays flat at 0.77. A linear model on 800 training rows is capacity-limited, not distribution-limited: the young group is genuinely harder to predict here, and reweighting the objective toward it cannot manufacture signal that is not in the features. + +**This is the paper's own headline finding, not a bug in the loop.** Sagawa et al. (2020) title their paper *"On the Importance of Regularization for Worst-Case Generalization"* precisely because plain group DRO increases worst-group training influence without improving worst-group test loss unless the model is strongly regularized (and, in their setting, overparameterized). The robustness knob is a real dial with a real cost - turn it up and `q` over-hedges toward an implausible worst case, shaving average-case accuracy - but it is not a guarantee of a fairer model. + +--- + +## Detection and Implementation Code + +`fit_group_dro` above is the implementation. The helper below reports whether a DRO run actually moved worst-group loss and what it cost average accuracy - the trade you are actually making. + +```python +def dro_report(erm_model, dro_model, X, y, groups): + """Compare an ERM model against a group-DRO model on held-out data.""" + def summarise(model): + pred, proba = model.predict(X), model.predict_proba(X)[:, 1] + per = {} + for g in sorted(set(groups)): + m = groups == g + from sklearn.metrics import log_loss + per[g] = {"acc": round(float((pred[m] == y[m]).mean()), 4), + "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)} + return per, round(float((pred == y).mean()), 4) + + erm_per, erm_acc = summarise(erm_model) + dro_per, dro_acc = summarise(dro_model) + erm_worst = max(v["log_loss"] for v in erm_per.values()) + dro_worst = max(v["log_loss"] for v in dro_per.values()) + return { + "erm_per_group": erm_per, + "dro_per_group": dro_per, + "worst_group_log_loss_before": erm_worst, + "worst_group_log_loss_after": dro_worst, + "worst_group_improved": dro_worst < erm_worst, + "overall_accuracy_delta": round(dro_acc - erm_acc, 4), + } +``` + +--- + +## Limitations + +### 1. The uncertainty-set size is a free parameter with no data-driven default + +`robustness` (equivalently, the radius of `U`) has to be picked. Too small and DRO collapses to ERM. Too large and the model hedges against reweightings that will never occur, trading real average-case accuracy for robustness to a fantasy worst case. There is no held-out quantity that tells you the "right" size - it encodes how much distribution shift you believe is plausible, which is a judgment call. + +### 2. It still needs group labels at training time + +The group-DRO form used here requires knowing each training example's group to maintain `q`. It removes the need to pick a *parity metric* up front, and it does not need group labels at prediction time, but it is not label-free. (Hashimoto et al. 2018's version drops the training-time group labels too, at the cost of hedging against *all* low-probability subpopulations, not just the ones you care about.) + +### 3. Worst-case training loss is not worst-case test loss + +As the example shows, pushing `q` toward the worst training group does not by itself improve that group's generalization. Without strong regularization or capacity control, group DRO can overfit the worst group's training set - the central caveat in Sagawa et al. (2020). + +### 4. "Worst group" is only defined once you fix the loss and the grouping + +DRO over groups defined by `age` says nothing about groups defined by `job` or by an `age x foreign_worker` intersection. And the worst group under log-loss, under 0-1 error, and under false-negative rate can differ. The uncertainty set is only as good as the grouping and loss you put into it. + +--- + +## Related Concepts + +- [Mitigation Strategies](mitigation-strategies.md) - this repo's S3 `ExponentiatedGradient` in-processing strategy, the constraint-based method DRO is contrasted against. +- [What Is Max-Min (Rawlsian) Fairness?](maxmin-fairness.md) - the same reweighting loop, framed as an objective on group outcomes rather than robustness to distribution shift. +- [What Is Distribution Shift?](distribution-shift.md) - the deployment-time phenomenon whose worst case DRO's uncertainty set is meant to bound. +- [What Is the Fairness-Accuracy Trade-off?](fairness-accuracy-tradeoff.md) - the average-case accuracy DRO spends when the robustness knob is turned up. +- [What Is Class Imbalance?](class-imbalance.md) - why average-loss training underserves the smaller, harder group DRO reweights toward. + +--- + +## Related Projects in This Repo + +- [`German Credit Lending/`](../German%20Credit%20Lending/) - Audit 03, the dataset and `age` attribute used above. +- [`faircode/strategies.py`](../faircode/strategies.py) - the S0-S4 implementation, including the S3 `ExponentiatedGradient` strategy DRO is compared with. +- [`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/) - the frozen S0/S3 `demographic_parity_diff` figures cited above. + +--- + +## Further Reading + +- [Sagawa, Koh, Hashimoto & Liang (2020): Distributionally Robust Neural Networks for Group Shifts: On the Importance of Regularization for Worst-Case Generalization, ICLR 2020](https://arxiv.org/abs/1911.08731) - the group-DRO algorithm used here, and the finding that worst-case generalization needs strong regularization. +- [Hashimoto, Srivastava, Namkoong & Liang (2018): Fairness Without Demographics in Repeated Loss Minimization, ICML 2018, PMLR 80:1929-1938](https://proceedings.mlr.press/v80/hashimoto18a.html) - a DRO objective that bounds the minority group's risk without any group labels. +- [Duchi & Namkoong (2021): Learning Models with Uniform Performance via Distributionally Robust Optimization, Annals of Statistics 49(3), 1378-1406](https://doi.org/10.1214/20-AOS2004) - the statistical foundations of the uncertainty-set formulation. +- [Agarwal, Beygelzimer, Dudik, Langford & Wallach (2018): A Reductions Approach to Fair Classification, ICML 2018](https://proceedings.mlr.press/v80/agarwal18a.html) - the `ExponentiatedGradient` constraint-based method this repo uses at S3, for contrast. + +--- + +*Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* diff --git a/faircode/_explainers/maxmin-fairness.md b/faircode/_explainers/maxmin-fairness.md index 4dd9354..40498e8 100644 --- a/faircode/_explainers/maxmin-fairness.md +++ b/faircode/_explainers/maxmin-fairness.md @@ -44,7 +44,7 @@ There is no equality constraint. The objective is a `min` of a `max`: push down Two standard ways to approximate it without a bespoke solver: -1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. +1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what [group distributionally robust optimization](distributionally-robust-optimization.md) does. 2. **Distributionally robust optimization (DRO).** Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk *without needing group labels*, which is why max-min and DRO are usually discussed together. --- @@ -220,7 +220,7 @@ Minimizing the maximum loss is usually improved by *raising* the worst group, bu ## Related Concepts - [Why Fairness Metrics Conflict](fairness-metric-conflicts.md) - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis. -- Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here. +- [What Is Distributionally Robust Optimization (DRO) for Fairness?](distributionally-robust-optimization.md) - the worst-case-loss training method that is the online form of the reweighting loop here. - [Mitigation Strategies](mitigation-strategies.md) - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss. - [What Is Class Imbalance?](class-imbalance.md) - why the smaller, higher-base-rate group is the one average-loss training underserves. - [What Is Demographic Parity?](demographic-parity.md) - the parity objective max-min is being contrasted against. diff --git a/llms-full.txt b/llms-full.txt index 1e14bdb..341f0f4 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -12294,7 +12294,7 @@ There is no equality constraint. The objective is a `min` of a `max`: push down Two standard ways to approximate it without a bespoke solver: -1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what group distributionally robust optimization (DRO) does. +1. **Iterative group reweighting.** Train, measure each group's loss, upweight whichever group is currently worst, retrain. Repeat. The training objective drifts from "average loss" toward "worst-group loss." This is the approach in the code below, and the online form of it is what [group distributionally robust optimization](distributionally-robust-optimization.md) does. 2. **Distributionally robust optimization (DRO).** Minimize the worst-case loss over a set of reweightings of the data. Hashimoto et al. (2018) show a DRO objective controls the minority group's risk *without needing group labels*, which is why max-min and DRO are usually discussed together. --- @@ -12470,7 +12470,7 @@ Minimizing the maximum loss is usually improved by *raising* the worst group, bu ## Related Concepts - [Why Fairness Metrics Conflict](fairness-metric-conflicts.md) - conflicts among parity metrics; the parity-vs-max-min conflict is a separate axis. -- Distributionally Robust Optimization (DRO) for Fairness (companion explainer) - the worst-case-loss training method that is the online form of the reweighting loop here. +- [What Is Distributionally Robust Optimization (DRO) for Fairness?](distributionally-robust-optimization.md) - the worst-case-loss training method that is the online form of the reweighting loop here. - [Mitigation Strategies](mitigation-strategies.md) - this repo's S0-S4 ladder, all of which target demographic parity rather than worst-group loss. - [What Is Class Imbalance?](class-imbalance.md) - why the smaller, higher-base-rate group is the one average-loss training underserves. - [What Is Demographic Parity?](demographic-parity.md) - the parity objective max-min is being contrasted against. @@ -12496,3 +12496,256 @@ Minimizing the maximum loss is usually improved by *raising* the worst group, bu *Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* +--- + +# What Is Distributionally Robust Optimization (DRO) for Fairness? +URL: https://www.thefaircode.xyz/explainers/distributionally-robust-optimization.html +Summary: Learn how DRO trains a model to minimize its worst-case loss over an uncertainty set of subgroup distributions - an in-processing alternative to constraint-based methods like ExponentiatedGradient that needs neither a chosen fairness metric nor group labels at prediction time. Worked on the Audit 03 German Credit data: online group DRO reliably shifts training weight onto the worst group, but on a capacity-limited linear model it buys no worst-group generalization - Sagawa et al.'s point about regularization. + +# What Is Distributionally Robust Optimization (DRO) for Fairness? + +> *A constraint-based fix needs you to name the fairness metric and the group up front. DRO instead trains against the worst subgroup your data might be under-representing - whichever one that turns out to be.* + +--- + +## The One-Sentence Definition + +**Distributionally Robust Optimization (DRO)** is an in-processing fairness technique that trains a model to minimize its *worst-case* loss over a set of plausible reweightings of the training distribution, rather than minimizing average loss or constraining a specific parity metric. + +--- + +## Why It Matters + +This repo's S3 in-processing strategy ([mitigation-strategies.md](mitigation-strategies.md)) is Fairlearn's `ExponentiatedGradient`: optimize accuracy subject to a fairness constraint, such as demographic parity, on a named protected attribute. That works well when you already know which metric matters and which group is at risk. It has two requirements baked in: + +- **You choose the fairness metric before training.** The constraint is demographic parity, or equalized odds, or one specific thing. A gap on a metric you did not constrain is not touched. +- **You choose the group before training.** The constraint is defined on `race`, or `age`, or a fixed intersection. A subgroup you did not think to check is invisible to it. + +DRO relaxes both. Instead of a constraint on one metric for one group, it hedges against the worst subgroup the training data might be under-representing - without committing to a parity metric at all, and (in the Hashimoto et al. 2018 form) without needing group labels. If the group actually being harmed is not the one you would have constrained, DRO can still help; `ExponentiatedGradient` cannot. + +The cost is a free parameter and no guarantee, both covered below. + +--- + +## Core Concept: The Min-Max Objective + +`ExponentiatedGradient` solves a **constrained** problem: + +``` +minimise average loss L(theta) +subject to |demographic_parity_gap(theta)| <= epsilon +``` + +Group DRO solves a **min-max** problem instead: + +``` +minimise over theta of max over Q in U of E_Q[ loss(theta) ] +``` + +where `U` is an *uncertainty set*: a ball of distributions around the empirical training distribution. `E_Q[loss]` is the expected loss if the data were reweighted to `Q`. The model is trained so that even the least favourable reweighting in `U` still has low loss. + +When `U` is "all reweightings that shift mass between predefined groups", this is **group DRO** (Sagawa et al. 2020), and it has a simple online algorithm: + +1. Keep a probability vector `q` over groups. +2. Each step, raise `q` for whichever group currently has the highest loss. +3. Fit the model on `q`-reweighted examples. +4. Repeat until `q` stabilizes. + +The size of `U` - how far `q` is allowed to move from the group base rates - is the **robustness knob**. Bigger `U` means the model hedges against more extreme reweightings. + +This is the same reweighting loop as iterative [max-min (Rawlsian) fairness](maxmin-fairness.md); the difference is framing. Max-min asks "make the worst group's *outcome* as good as possible." DRO asks "stay robust to a *distribution shift* that inflates the worst group" - and the uncertainty set `U` is the explicit statement of which shifts you are hedging against. + +--- + +## Concrete Example: German Credit Lending - Audit 03 + +Audit 03 ([`German Credit Lending/`](../German%20Credit%20Lending/)) predicts bad credit risk on 1,000 real loan records; the protected attribute is `age`, with "young" meaning under 30. + +For reference, this repo's frozen benchmark ([`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/), the earlier snapshot - see [CLAUDE.md](../CLAUDE.md)) records the S3 `in_processing` strategy driving the baseline logistic-regression `demographic_parity_diff` on `age` from **-0.129 (p = 0.05) at S0 to +0.023 (p = 0.71) at S3** - the constraint closes the parity gap it was given. Group DRO does not target that metric at all; it targets worst-group loss. + +The script below runs an ERM (average-loss) baseline, then online group DRO, sweeping the robustness knob. Features drop `age` and `personal_status`; inputs are standardized; the split is 80/20 stratified at `random_state=42`. + +```python +import numpy as np +import pandas as pd +from sklearn.linear_model import LogisticRegression +from sklearn.metrics import log_loss +from sklearn.model_selection import train_test_split + + +def load_german_credit(path="German Credit Lending/credit_customers.csv"): + df = pd.read_csv(path) + df["y_bad"] = (df["class"] == "bad").astype(int) + df["age_group"] = np.where(df["age"] < 30, "young", "older") + drop = {"class", "y_bad", "age", "age_group", "personal_status"} + feats = [c for c in df.columns if c not in drop] + return pd.get_dummies(df[feats], drop_first=True), df["y_bad"].to_numpy(), df["age_group"].to_numpy() + + +def per_group(model, X, y, groups): + pred, proba = model.predict(X), model.predict_proba(X)[:, 1] + rows = {} + for g in ["older", "young"]: + m = groups == g + rows[g] = {"n": int(m.sum()), + "accuracy": round(float((pred[m] == y[m]).mean()), 4), + "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)} + worst = max(rows["older"]["log_loss"], rows["young"]["log_loss"]) + return pd.DataFrame(rows).T, round(float(worst), 4), round(float((pred == y).mean()), 4) + + +def fit_group_dro(Xtr, ytr, gtr, robustness=1.0, n_rounds=100): + """Online group DRO (Sagawa et al. 2020): keep a distribution q over groups, + each round upweight whichever group has the highest loss, and fit the model + on q-reweighted examples. `robustness` is the uncertainty-set knob - larger + means q chases the worst group harder.""" + q = {"older": 0.5, "young": 0.5} + frac = {g: (gtr == g).mean() for g in q} + for _ in range(n_rounds): + w = np.array([q[g] / frac[g] for g in gtr], dtype=float) + w *= len(w) / w.sum() + model = LogisticRegression(max_iter=5000).fit(Xtr, ytr, sample_weight=w) + proba = model.predict_proba(Xtr)[:, 1] + gl = {g: log_loss(ytr[gtr == g], proba[gtr == g], labels=[0, 1]) for g in q} + for g in q: + q[g] *= np.exp(robustness * gl[g]) + total = sum(q.values()) + q = {g: v / total for g, v in q.items()} + return model, q + + +if __name__ == "__main__": + X, y, g = load_german_credit() + Xtr, Xte, ytr, yte, gtr, gte = train_test_split( + X, y, g, test_size=0.2, random_state=42, stratify=y) + mean, std = Xtr.mean(), Xtr.std().replace(0, 1) + Xtr_s, Xte_s = ((Xtr - mean) / std).to_numpy(), ((Xte - mean) / std).to_numpy() + + erm = LogisticRegression(max_iter=5000).fit(Xtr_s, ytr) + tbl, worst, acc = per_group(erm, Xte_s, yte, gte) + print("=== ERM baseline (minimise average loss) ===") + print(tbl.to_string()) + print(f"overall accuracy : {acc:.4f}") + print(f"worst-group log_loss: {worst:.4f}\n") + + print("=== Group DRO: sweep the robustness knob ===") + print(f"{'robustness':>10} | {'q(young)':>8} | {'worst-group log_loss':>20} | {'overall acc':>11}") + for r in [0.0, 0.5, 1.0, 2.0, 5.0]: + model, q = fit_group_dro(Xtr_s, ytr, gtr, robustness=r) + _, w, a = per_group(model, Xte_s, yte, gte) + print(f"{r:>10.1f} | {q['young']:>8.2f} | {w:>20.4f} | {a:>11.4f}") +``` + +### Script Execution Output + +``` +=== ERM baseline (minimise average loss) === + n accuracy log_loss +older 121.0 0.8099 0.4285 +young 79.0 0.7089 0.5880 +overall accuracy : 0.7700 +worst-group log_loss: 0.5880 + +=== Group DRO: sweep the robustness knob === +robustness | q(young) | worst-group log_loss | overall acc + 0.0 | 0.50 | 0.5910 | 0.7700 + 0.5 | 0.62 | 0.5988 | 0.7750 + 1.0 | 0.63 | 0.5991 | 0.7700 + 2.0 | 0.63 | 0.5991 | 0.7700 + 5.0 | 0.63 | 0.5991 | 0.7700 +``` + +(Deterministic with these library versions; a solver's last digits can shift across BLAS backends.) + +Three things this shows: + +**DRO reliably redirects training weight to the worst group.** ERM weights groups by their size. As the robustness knob goes up, `q(young)` climbs from 0.50 to about 0.63 and then saturates - the min-max objective has found the worst group and is pushing on it. That part works exactly as designed. + +**On this problem, redirecting the weight buys no worst-group generalization.** The young group's held-out `log_loss` is 0.588 under ERM and does not improve under any robustness setting - it drifts slightly *up*, to ~0.599. Overall accuracy stays flat at 0.77. A linear model on 800 training rows is capacity-limited, not distribution-limited: the young group is genuinely harder to predict here, and reweighting the objective toward it cannot manufacture signal that is not in the features. + +**This is the paper's own headline finding, not a bug in the loop.** Sagawa et al. (2020) title their paper *"On the Importance of Regularization for Worst-Case Generalization"* precisely because plain group DRO increases worst-group training influence without improving worst-group test loss unless the model is strongly regularized (and, in their setting, overparameterized). The robustness knob is a real dial with a real cost - turn it up and `q` over-hedges toward an implausible worst case, shaving average-case accuracy - but it is not a guarantee of a fairer model. + +--- + +## Detection and Implementation Code + +`fit_group_dro` above is the implementation. The helper below reports whether a DRO run actually moved worst-group loss and what it cost average accuracy - the trade you are actually making. + +```python +def dro_report(erm_model, dro_model, X, y, groups): + """Compare an ERM model against a group-DRO model on held-out data.""" + def summarise(model): + pred, proba = model.predict(X), model.predict_proba(X)[:, 1] + per = {} + for g in sorted(set(groups)): + m = groups == g + from sklearn.metrics import log_loss + per[g] = {"acc": round(float((pred[m] == y[m]).mean()), 4), + "log_loss": round(float(log_loss(y[m], proba[m], labels=[0, 1])), 4)} + return per, round(float((pred == y).mean()), 4) + + erm_per, erm_acc = summarise(erm_model) + dro_per, dro_acc = summarise(dro_model) + erm_worst = max(v["log_loss"] for v in erm_per.values()) + dro_worst = max(v["log_loss"] for v in dro_per.values()) + return { + "erm_per_group": erm_per, + "dro_per_group": dro_per, + "worst_group_log_loss_before": erm_worst, + "worst_group_log_loss_after": dro_worst, + "worst_group_improved": dro_worst < erm_worst, + "overall_accuracy_delta": round(dro_acc - erm_acc, 4), + } +``` + +--- + +## Limitations + +### 1. The uncertainty-set size is a free parameter with no data-driven default + +`robustness` (equivalently, the radius of `U`) has to be picked. Too small and DRO collapses to ERM. Too large and the model hedges against reweightings that will never occur, trading real average-case accuracy for robustness to a fantasy worst case. There is no held-out quantity that tells you the "right" size - it encodes how much distribution shift you believe is plausible, which is a judgment call. + +### 2. It still needs group labels at training time + +The group-DRO form used here requires knowing each training example's group to maintain `q`. It removes the need to pick a *parity metric* up front, and it does not need group labels at prediction time, but it is not label-free. (Hashimoto et al. 2018's version drops the training-time group labels too, at the cost of hedging against *all* low-probability subpopulations, not just the ones you care about.) + +### 3. Worst-case training loss is not worst-case test loss + +As the example shows, pushing `q` toward the worst training group does not by itself improve that group's generalization. Without strong regularization or capacity control, group DRO can overfit the worst group's training set - the central caveat in Sagawa et al. (2020). + +### 4. "Worst group" is only defined once you fix the loss and the grouping + +DRO over groups defined by `age` says nothing about groups defined by `job` or by an `age x foreign_worker` intersection. And the worst group under log-loss, under 0-1 error, and under false-negative rate can differ. The uncertainty set is only as good as the grouping and loss you put into it. + +--- + +## Related Concepts + +- [Mitigation Strategies](mitigation-strategies.md) - this repo's S3 `ExponentiatedGradient` in-processing strategy, the constraint-based method DRO is contrasted against. +- [What Is Max-Min (Rawlsian) Fairness?](maxmin-fairness.md) - the same reweighting loop, framed as an objective on group outcomes rather than robustness to distribution shift. +- [What Is Distribution Shift?](distribution-shift.md) - the deployment-time phenomenon whose worst case DRO's uncertainty set is meant to bound. +- [What Is the Fairness-Accuracy Trade-off?](fairness-accuracy-tradeoff.md) - the average-case accuracy DRO spends when the robustness knob is turned up. +- [What Is Class Imbalance?](class-imbalance.md) - why average-loss training underserves the smaller, harder group DRO reweights toward. + +--- + +## Related Projects in This Repo + +- [`German Credit Lending/`](../German%20Credit%20Lending/) - Audit 03, the dataset and `age` attribute used above. +- [`faircode/strategies.py`](../faircode/strategies.py) - the S0-S4 implementation, including the S3 `ExponentiatedGradient` strategy DRO is compared with. +- [`paper/results-frozen/results_fairness.csv`](../paper/results-frozen/) - the frozen S0/S3 `demographic_parity_diff` figures cited above. + +--- + +## Further Reading + +- [Sagawa, Koh, Hashimoto & Liang (2020): Distributionally Robust Neural Networks for Group Shifts: On the Importance of Regularization for Worst-Case Generalization, ICLR 2020](https://arxiv.org/abs/1911.08731) - the group-DRO algorithm used here, and the finding that worst-case generalization needs strong regularization. +- [Hashimoto, Srivastava, Namkoong & Liang (2018): Fairness Without Demographics in Repeated Loss Minimization, ICML 2018, PMLR 80:1929-1938](https://proceedings.mlr.press/v80/hashimoto18a.html) - a DRO objective that bounds the minority group's risk without any group labels. +- [Duchi & Namkoong (2021): Learning Models with Uniform Performance via Distributionally Robust Optimization, Annals of Statistics 49(3), 1378-1406](https://doi.org/10.1214/20-AOS2004) - the statistical foundations of the uncertainty-set formulation. +- [Agarwal, Beygelzimer, Dudik, Langford & Wallach (2018): A Reductions Approach to Fair Classification, ICML 2018](https://proceedings.mlr.press/v80/agarwal18a.html) - the `ExponentiatedGradient` constraint-based method this repo uses at S3, for contrast. + +--- + +*Part of [The Fair Code Project](https://instagram.com/thefaircodeproject) - exposing and fixing algorithmic bias with real data and open code.* + diff --git a/sitemap.xml b/sitemap.xml index 6187c07..bde2c28 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -252,4 +252,8 @@ https://www.thefaircode.xyz/explainers/maxmin-fairness.html 2026-09-10 + + https://www.thefaircode.xyz/explainers/distributionally-robust-optimization.html + 2026-09-10 +