From a6591e48a553aa559773549e586556d6009d9522 Mon Sep 17 00:00:00 2001 From: propcgamer20-png Date: Thu, 10 Sep 2026 15:22:28 +0530 Subject: [PATCH] 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 +