From 1f58fd2b9da24d3ee942ab88c489104fcd456b67 Mon Sep 17 00:00:00 2001
From: propcgamer20-png
Date: Thu, 10 Sep 2026 01:32:06 +0530
Subject: [PATCH] docs: correct COMPAS reduction figure repo-wide (71% -> 82%)
+ Benefits Denial proxy count
#545: the COMPAS "Reduction" figure was the raw percentage-point gap
(86.77 - 15.69 = 71.08 -> "71%"), not the relative reduction every other
audit reports:
(86.77 - 15.69) / 86.77 = 81.9% -> "82%"
The other six audits' rows all check out against the relative formula
(97.3%, 73.6%, ~60%, ~72%, 53%). "71%" was COMPAS's number not just in
the two summary tables but in nine explainers and CHANGELOG.md, so
fixing only the tables would have left the repo internally inconsistent.
Updated every "71% reduction" / "71% of the gap" COMPAS reference in
source (COMPAS/README.md, root README.md x3, CHANGELOG.md,
PULL_REQUEST_TEMPLATE.md example, and explainers ai-objectivity-myth,
confounding-variable, data-leakage, equalized-odds,
how-ai-detects-patterns, ml-bias, proxy-variables, reinforcement-learning)
and regenerated the explainer pages / package mirror / llms-full.txt.
The underlying gap numbers (86.77%, 15.69%) are unchanged.
#546: Benefits Denial's audit.yaml lists five proxy_features
(marital.status, occupation, relationship, hours.per.week, fnlwgt) and
fair.py removes all five with documented reasoning, but both READMEs
listed only four. Added fnlwgt to Benefits Denial/README.md and the root
README (summary row, the "four proxy variables" -> "five" counts, and the
fair.py code-block comment list).
Closes #545
Closes #546
---
.github/PULL_REQUEST_TEMPLATE.md | 2 +-
Benefits Denial/README.md | 2 +-
CHANGELOG.md | 2 +-
COMPAS/README.md | 2 +-
README.md | 11 ++++----
explainers/ai-objectivity-myth.html | 2 +-
explainers/ai-objectivity-myth.md | 2 +-
explainers/confounding-variable.html | 6 ++---
explainers/confounding-variable.md | 6 ++---
explainers/data-leakage.html | 2 +-
explainers/data-leakage.md | 2 +-
explainers/equalized-odds.html | 2 +-
explainers/equalized-odds.md | 2 +-
explainers/how-ai-detects-patterns.html | 2 +-
explainers/how-ai-detects-patterns.md | 2 +-
explainers/ml-bias.html | 2 +-
explainers/ml-bias.md | 2 +-
explainers/proxy-variables.html | 4 +--
explainers/proxy-variables.md | 4 +--
explainers/reinforcement-learning.html | 6 ++---
explainers/reinforcement-learning.md | 6 ++---
faircode/_explainers/ai-objectivity-myth.md | 2 +-
faircode/_explainers/confounding-variable.md | 6 ++---
faircode/_explainers/data-leakage.md | 2 +-
faircode/_explainers/equalized-odds.md | 2 +-
.../_explainers/how-ai-detects-patterns.md | 2 +-
faircode/_explainers/ml-bias.md | 2 +-
faircode/_explainers/proxy-variables.md | 4 +--
.../_explainers/reinforcement-learning.md | 6 ++---
llms-full.txt | 26 +++++++++----------
30 files changed, 62 insertions(+), 61 deletions(-)
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 529deaf..d9690a5 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -31,7 +31,7 @@
**After fairness gap:**
-**Reduction:**
+**Reduction:**
**Protected attribute(s):**
diff --git a/Benefits Denial/README.md b/Benefits Denial/README.md
index f8e01ee..52a1e9a 100644
--- a/Benefits Denial/README.md
+++ b/Benefits Denial/README.md
@@ -19,7 +19,7 @@ python3 "Benefits Denial/fair.py" # mitigated (protected attribute + proxies
## What the audit controls
- Protected attribute(s): Sex, Race, Origin, Age
-- Proxy feature(s) removed in `fair.py`: Relationship, Marital Status, Hours, Occupation
+- Proxy feature(s) removed in `fair.py`: Relationship, Marital Status, Hours, Occupation, fnlwgt (census sampling weight)
- Fairness metric: Demographic Parity (difference in positive-prediction rate between groups)
## Expected result (published, paper-aligned)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d7ed6e..85e5e3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -717,7 +717,7 @@ First release since **v1.1.0** (9 Jun 2026). The headline is the **Open Dataset
### Added
- Explainer: Reinforcement Learning - `reinforcement-learning.md` created by evanjain-dot (PR #48, commit a785ea95), added to `index.html`, `README.md`, and `CONTRIBUTING.md` (commit e3928af7)
- Full explainer covering the three-part RL loop (state → action → reward → policy), reward function design as a political act, reward hacking, and the credit assignment problem
- - Real-world proof using COMPAS as an RL-adjacent system: biased policy produces 86.77% Black/White fairness gap; removing race + `CustodyStatus` proxy reduces gap to 15.69% (71% reduction)
+ - Real-world proof using COMPAS as an RL-adjacent system: biased policy produces 86.77% Black/White fairness gap; removing race + `CustodyStatus` proxy reduces gap to 15.69% (82% reduction)
- Results table: biased policy vs. race-only removal vs. race + proxy removal
- Second case: YouTube recommendation engine using watch time as reward signal - documents asymmetric demographic consequences and outrage optimisation
- `fairness_gap()` detection code with chi-squared proxy check for state representation audit
diff --git a/COMPAS/README.md b/COMPAS/README.md
index 7bfa48d..6eb2493 100644
--- a/COMPAS/README.md
+++ b/COMPAS/README.md
@@ -26,6 +26,6 @@ python3 "COMPAS/fair.py" # mitigated (protected attribute + proxies dropped)
| Group | Gap, biased (`unfair.py`) | Gap, mitigated (`fair.py`) | Reduction |
|-------|--------------------------:|---------------------------:|----------:|
-| Race | 86.77% | 15.69% | 71% |
+| Race | 86.77% | 15.69% | 82% |
These match the "Results at a Glance" table in the [main README](../README.md#results-at-a-glance) and the frozen snapshot in `paper/results-frozen/`. The scripts are deterministic at `random_state=42`, so a correct local run reproduces them exactly. If your numbers differ, check the seed, the split, and your package versions before opening an issue - and never edit the frozen numbers to match a local run (see [CLAUDE.md](../CLAUDE.md)).
diff --git a/README.md b/README.md
index dc5a409..9faa093 100644
--- a/README.md
+++ b/README.md
@@ -68,12 +68,12 @@ Each audit ships as both a pair of Python scripts (`unfair.py` / `fair.py`) for
| # | Domain | Protected Attribute | Proxies Removed | Gap Before → After | Reduction |
|:-:|--------|--------------------|-----------------|--------------------|:---------:|
-| 01 | [Criminal Justice](#01--compas--criminal-justice-bias) | Race | Custody Status | 86.77% → 15.69% | **71%** |
+| 01 | [Criminal Justice](#01--compas--criminal-justice-bias) | Race | Custody Status | 86.77% → 15.69% | **82%** |
| 02 | [Hiring](#02--ai-fair-recruitment--hiring-bias) | Gender | Age | 4.51% → 0.12% | **97.3%** |
| 03 | [Lending](#03--german-credit-lending--lending-bias) | Age | Employment Tenure | 7.16% → 1.89% | **73.6%** |
| 04 | [Healthcare](#04--insurance-denial--healthcare-bias) | Age, Gender | BMI, Smoker, Diabetic | Age: 7.93% → 3.18% | **60%** |
| ↳ | | | | Gender: 5.44% → 1.54% | **72%** |
-| 05 | [Welfare](#05--benefits-denial--welfare-eligibility-bias) | Sex, Race, Origin, Age | Relationship, Marital Status, Hours, Occupation | Sex: 18.00% → 8.52% | **53%** |
+| 05 | [Welfare](#05--benefits-denial--welfare-eligibility-bias) | Sex, Race, Origin, Age | Relationship, Marital Status, Hours, Occupation, fnlwgt | Sex: 18.00% → 8.52% | **53%** |
| ↳ | | | | Race: 12.75% → 6.90% | **46%** |
| ↳ | | | | Origin: 4.40% → 0.52% | **88%** |
| 06 | [Healthcare Readmission](#06--healthcare-readmission--clinical-bias) | Race, Gender, Age | Payer Code, Discharge Disposition, Medical Specialty, Prior Inpatient | Gender: 0.02% → 0.04% | **+100% ↑** |
@@ -371,7 +371,7 @@ X = pd.get_dummies(df[[
| White Defendants | 69.02% |
| **New Fairness Gap** | **15.69%** |
-**Result: 71% reduction in the fairness gap.**
+**Result: 82% reduction in the fairness gap.**
> **Key insight:** Removing race alone isn't enough. Proxy variables like custody status carry the same racial signal because of historical over-policing of Black communities. Both the protected attribute *and* its proxies must be removed.
@@ -546,7 +546,7 @@ Automated welfare and benefits systems use income-prediction models to screen ap
#### The Problem - `unfair.py`
-Trained with sex, race, age, and national origin directly, plus four proxy variables that reconstruct those attributes even after the protected columns are removed.
+Trained with sex, race, age, and national origin directly, plus five proxy variables that reconstruct those attributes even after the protected columns are removed.
| Group | Ineligibility Flag Rate |
|-------|:-----------------------:|
@@ -568,7 +568,7 @@ Trained with sex, race, age, and national origin directly, plus four proxy varia
#### The Fix - `fair.py`
-Dropped all four protected attributes and all four proxy variables. Retained only the features a means-tested programme can legitimately consult under equality law.
+Dropped all four protected attributes and all five proxy variables. Retained only the features a means-tested programme can legitimately consult under equality law.
```python
# THE FIX: Policy-defined economic signals only
@@ -586,6 +586,7 @@ features = [
# marital.status removed ✓ (proxy: encodes sex via spousal status)
# hours.per.week removed ✓ (proxy: encodes sex via caregiving gap)
# occupation removed ✓ (proxy: encodes race via occupational segregation)
+ # fnlwgt removed ✓ (proxy: census sampling weight, no causal link)
]
```
diff --git a/explainers/ai-objectivity-myth.html b/explainers/ai-objectivity-myth.html
index 4d923f3..1047a98 100644
--- a/explainers/ai-objectivity-myth.html
+++ b/explainers/ai-objectivity-myth.html
@@ -211,7 +211,7 @@ Concrete Example: COMPAS - Audit 01
-
Removing race alone barely moves the number, because CustodyStatus - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after race and CustodyStatus are both dropped does the gap fall to 15.69%, a 71% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
+Removing race alone barely moves the number, because CustodyStatus - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after race and CustodyStatus are both dropped does the gap fall to 15.69%, an 82% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
Detection Code
This function checks whether a model's outcome rates differ across protected groups by more than a configurable threshold, and flags features that correlate with the protected attribute strongly enough to explain the gap on their own.
import pandas as pd
diff --git a/explainers/ai-objectivity-myth.md b/explainers/ai-objectivity-myth.md
index e98cbbe..ee9c242 100644
--- a/explainers/ai-objectivity-myth.md
+++ b/explainers/ai-objectivity-myth.md
@@ -40,7 +40,7 @@ model.fit(X_train, y_train)
# Fairness Gap: 86.77%
```
-Removing `race` alone barely moves the number, because `CustodyStatus` - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after `race` *and* `CustodyStatus` are both dropped does the gap fall to 15.69%, a 71% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
+Removing `race` alone barely moves the number, because `CustodyStatus` - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after `race` *and* `CustodyStatus` are both dropped does the gap fall to 15.69%, an 82% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
## Detection Code
diff --git a/explainers/confounding-variable.html b/explainers/confounding-variable.html
index 420cb4d..b488406 100644
--- a/explainers/confounding-variable.html
+++ b/explainers/confounding-variable.html
@@ -222,7 +222,7 @@ Concrete Example: COMPAS and
Both arrows are caused by the same systemic factor. CustodyStatus is associated with the recidivism label not because custody history is a reliable individual risk signal, but because the same structural forces that produce elevated custody records also produce more recidivism label events - through monitoring, not through behavior.
Removing both race and CustodyStatus breaks this path:
# fair.py: features include only priors_count + charge_degree + age
-# Black/White fairness gap: 15.69% - 71% reduction
+# Black/White fairness gap: 15.69% - 82% reduction
The residual 15.69% reflects other confounding paths (differential bail rates, charge severity distributions, surveillance-driven label noise) that require changes upstream of the model to eliminate entirely.
Detection Code
@@ -315,7 +315,7 @@ Limitations
1. Stratified analysis cannot distinguish confounding from effect modification. If a feature has a genuinely different causal effect on the outcome across strata - not just a different baseline - that is effect modification, not confounding. The two require different handling. Conflating them produces wrong adjustments.
2. You can only condition on observed confounders. If the confounder is unmeasured - historical policing intensity, neighbourhood-level surveillance, differential healthcare access - no statistical adjustment removes its effect. Causal inference methods (instrumental variables, propensity score matching, difference-in-differences) can partially address unmeasured confounding but require strong, often untestable assumptions about the causal structure.
3. Conditioning on a collider opens new bias. A collider is a variable caused by both the feature and the outcome - the reverse of a confounder. Controlling for a collider introduces a spurious association rather than removing one. Correctly distinguishing confounders from colliders requires a causal graph (a DAG), not statistical testing alone. Chi-squared tests cannot tell you which direction the arrows point.
-4. Confounder removal reduces but does not eliminate bias. Removing CustodyStatus from COMPAS cuts the fairness gap from 86.77% to 15.69% - a 71% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
+4. Confounder removal reduces but does not eliminate bias. Removing CustodyStatus from COMPAS cuts the fairness gap from 86.77% to 15.69% - an 82% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
5. Adjustment can introduce its own distortions. Propensity score methods and inverse probability weighting reduce confounding but amplify variance, especially in small subgroups. In high-stakes settings, an overcorrected model may perform worse for the groups it was adjusted to protect.
@@ -329,7 +329,7 @@ Feedback Loop Bias
When a confounded model is deployed and its outputs influence future labels - recidivism surveillance, credit monitoring, healthcare resource allocation - the confounding strengthens across retraining cycles. The model's outputs become part of the data-generating process, reinforcing the spurious association with each iteration. See feedback-loop-bias.md.
-COMPAS/ - the primary worked example. CustodyStatus confounds the race→recidivism path, driving 71% of the Black/White fairness gap. Removing it alongside race reduces the gap from 86.77% to 15.69%.Benefits Denial/ - relationship and marital-status act as confounders for sex: historical gender norms independently elevated male-coded relationship statuses and income levels in the census data, creating a spurious association the model amplifies.Healthcare Readmission/ - payer_code is confounded by race: differential insurance access is caused by structural factors that also independently predict readmission risk, not only by individual health status.
+COMPAS/ - the primary worked example. CustodyStatus confounds the race→recidivism path, driving 82% of the Black/White fairness gap. Removing it alongside race reduces the gap from 86.77% to 15.69%.Benefits Denial/ - relationship and marital-status act as confounders for sex: historical gender norms independently elevated male-coded relationship statuses and income levels in the census data, creating a spurious association the model amplifies.Healthcare Readmission/ - payer_code is confounded by race: differential insurance access is caused by structural factors that also independently predict readmission risk, not only by individual health status.
Further Reading
- Pearl, J. (2009). Causality: Models, Reasoning and Inference (2nd ed.). Cambridge University Press. - the foundational text on causal graphs, the do-calculus, and the formal definitions of confounders, mediators, and colliders that underpin modern causal fairness work.
- Obermeyer, Z., Powers, B., Vogeli, C., & Mullainathan, S. (2019). Dissecting racial bias in an algorithm used to manage the health of populations. Science, 366(6464), 447–453. - a documented case of confounding-driven racial bias: healthcare cost (the proxy label) was confounded by differential access, making Black patients appear healthier than white patients with the same conditions, and the algorithm allocated less care as a result.
- VanderWeele, T. J., & Shpitser, I. (2013). On the definition of a confounder. Annals of Statistics, 41(1), 196–220. - a rigorous definition of confounding that distinguishes it from colliders and mediators, resolving long-standing disagreements in the epidemiological and statistical literature that carry directly into algorithmic fairness auditing.
diff --git a/explainers/confounding-variable.md b/explainers/confounding-variable.md
index 46ad0b5..94586e7 100644
--- a/explainers/confounding-variable.md
+++ b/explainers/confounding-variable.md
@@ -69,7 +69,7 @@ Removing both `race` and `CustodyStatus` breaks this path:
```python
# fair.py: features include only priors_count + charge_degree + age
-# Black/White fairness gap: 15.69% - 71% reduction
+# Black/White fairness gap: 15.69% - 82% reduction
```
The residual 15.69% reflects other confounding paths (differential bail rates, charge severity distributions, surveillance-driven label noise) that require changes upstream of the model to eliminate entirely.
@@ -181,7 +181,7 @@ For continuous features, replace `chi2_contingency` with a Pearson correlation o
3. **Conditioning on a collider opens new bias.** A collider is a variable caused by both the feature and the outcome - the reverse of a confounder. Controlling for a collider introduces a spurious association rather than removing one. Correctly distinguishing confounders from colliders requires a causal graph (a DAG), not statistical testing alone. Chi-squared tests cannot tell you which direction the arrows point.
-4. **Confounder removal reduces but does not eliminate bias.** Removing `CustodyStatus` from COMPAS cuts the fairness gap from 86.77% to 15.69% - a 71% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
+4. **Confounder removal reduces but does not eliminate bias.** Removing `CustodyStatus` from COMPAS cuts the fairness gap from 86.77% to 15.69% - an 82% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
5. **Adjustment can introduce its own distortions.** Propensity score methods and inverse probability weighting reduce confounding but amplify variance, especially in small subgroups. In high-stakes settings, an overcorrected model may perform worse for the groups it was adjusted to protect.
@@ -205,7 +205,7 @@ When a confounded model is deployed and its outputs influence future labels - re
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - the primary worked example. `CustodyStatus` confounds the race→recidivism path, driving 71% of the Black/White fairness gap. Removing it alongside `race` reduces the gap from 86.77% to 15.69%.
+- [`COMPAS/`](../COMPAS/) - the primary worked example. `CustodyStatus` confounds the race→recidivism path, driving 82% of the Black/White fairness gap. Removing it alongside `race` reduces the gap from 86.77% to 15.69%.
- [`Benefits Denial/`](../Benefits%20Denial/) - `relationship` and `marital-status` act as confounders for sex: historical gender norms independently elevated male-coded relationship statuses and income levels in the census data, creating a spurious association the model amplifies.
- [`Healthcare Readmission/`](../Healthcare%20Readmission/) - `payer_code` is confounded by race: differential insurance access is caused by structural factors that also independently predict readmission risk, not only by individual health status.
diff --git a/explainers/data-leakage.html b/explainers/data-leakage.html
index 4dc1ab9..4b9fc35 100644
--- a/explainers/data-leakage.html
+++ b/explainers/data-leakage.html
@@ -210,7 +210,7 @@ Train-Test Contamination
Concrete Example
The COMPAS audit in this repository demonstrates a proxy form of target leakage. CustodyStatus - a feature describing whether a defendant is currently in custody - correlates with race at p < 0.001 and with the recidivism label at p < 0.001. It is not a neutral operational variable. It encodes the outcome of a prior criminal justice interaction, which itself encodes historical over-policing of Black communities. Including it in the model achieves an 86.77% Black/White fairness gap in positive prediction rates.
This is the structural form of leakage: a feature that looks like a legitimate input but whose predictive power comes from downstream effects of the very outcome being predicted.
-Removing CustodyStatus alongside the protected race attribute reduces the gap to 15.69% - a 71% reduction.
+Removing CustodyStatus alongside the protected race attribute reduces the gap to 15.69% - an 82% reduction.
import os
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
diff --git a/explainers/data-leakage.md b/explainers/data-leakage.md
index f11f86c..bb9a366 100644
--- a/explainers/data-leakage.md
+++ b/explainers/data-leakage.md
@@ -47,7 +47,7 @@ The COMPAS audit in this repository demonstrates a proxy form of target leakage.
This is the structural form of leakage: a feature that looks like a legitimate input but whose predictive power comes from downstream effects of the very outcome being predicted.
-Removing `CustodyStatus` alongside the protected race attribute reduces the gap to 15.69% - a 71% reduction.
+Removing `CustodyStatus` alongside the protected race attribute reduces the gap to 15.69% - an 82% reduction.
```python
import os
diff --git a/explainers/equalized-odds.html b/explainers/equalized-odds.html
index 86d5ddb..5b50a14 100644
--- a/explainers/equalized-odds.html
+++ b/explainers/equalized-odds.html
@@ -287,7 +287,7 @@ Proxy Variables
Even after correcting for Equalized Odds, models can still encode protected attributes through proxy variables - features like zip code, custody status, or employment tenure that correlate with race, age, or class in the training data. Equalized Odds doesn't detect these; proxy variable auditing does. See the proxy variables explainer.
-COMPAS/ - Full COMPAS analysis: biased model → fair model → 71% gap reduction. Equalized Odds violations visible in the raw model outputs.explainers/proxy-variables.md - Why AI stays biased even after you remove protected attributesexplainers/shap-values.md - How to see exactly what drove an AI decision - and use that to catch bias
+COMPAS/ - Full COMPAS analysis: biased model → fair model → 82% gap reduction. Equalized Odds violations visible in the raw model outputs.explainers/proxy-variables.md - Why AI stays biased even after you remove protected attributesexplainers/shap-values.md - How to see exactly what drove an AI decision - and use that to catch bias
Further Reading
diff --git a/explainers/equalized-odds.md b/explainers/equalized-odds.md
index 50cbeb0..706d756 100644
--- a/explainers/equalized-odds.md
+++ b/explainers/equalized-odds.md
@@ -184,7 +184,7 @@ Even after correcting for Equalized Odds, models can still encode protected attr
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 71% gap reduction. Equalized Odds violations visible in the raw model outputs.
+- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 82% gap reduction. Equalized Odds violations visible in the raw model outputs.
- [`explainers/proxy-variables.md`](proxy-variables.md) - Why AI stays biased even after you remove protected attributes
- [`explainers/shap-values.md`](shap-values.md) - How to see exactly what drove an AI decision - and use that to catch bias
diff --git a/explainers/how-ai-detects-patterns.html b/explainers/how-ai-detects-patterns.html
index 40a7920..43c1f50 100644
--- a/explainers/how-ai-detects-patterns.html
+++ b/explainers/how-ai-detects-patterns.html
@@ -219,7 +219,7 @@ Concrete Example: COMPAS - Audit 01
-When race is dropped, the gap barely moves - CustodyStatus alone reconstructs most of the pattern, because it correlates with race at 0.31 importance even on its own. Only when both race and CustodyStatus are removed does the gap fall to 15.69%, a 71% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
+When race is dropped, the gap barely moves - CustodyStatus alone reconstructs most of the pattern, because it correlates with race at 0.31 importance even on its own. Only when both race and CustodyStatus are removed does the gap fall to 15.69%, an 82% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
Detection Code
The following functions inspect what patterns a trained model relied on, and flag features whose importance may be inflated by correlation with a protected attribute.
import pandas as pd
diff --git a/explainers/how-ai-detects-patterns.md b/explainers/how-ai-detects-patterns.md
index 229b1bc..30b73da 100644
--- a/explainers/how-ai-detects-patterns.md
+++ b/explainers/how-ai-detects-patterns.md
@@ -52,7 +52,7 @@ model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
```
-When `race` is dropped, the gap barely moves - `CustodyStatus` alone reconstructs most of the pattern, because it correlates with `race` at 0.31 importance even on its own. Only when both `race` and `CustodyStatus` are removed does the gap fall to 15.69%, a 71% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
+When `race` is dropped, the gap barely moves - `CustodyStatus` alone reconstructs most of the pattern, because it correlates with `race` at 0.31 importance even on its own. Only when both `race` and `CustodyStatus` are removed does the gap fall to 15.69%, an 82% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
## Detection Code
diff --git a/explainers/ml-bias.html b/explainers/ml-bias.html
index c19e448..8def3cb 100644
--- a/explainers/ml-bias.html
+++ b/explainers/ml-bias.html
@@ -228,7 +228,7 @@ Concrete Example: COMPAS - Audit 01Labels: The label is is_recid - whether a defendant reoffended within two years. Reoffending is measured by re-arrest, not by actual criminal behaviour. In a jurisdiction where Black defendants are more likely to be stopped, searched, and arrested for equivalent behaviour, re-arrest is a biased label. The model trains to predict re-arrest and produces a proxy for policing intensity, not criminal propensity.
Proxy variable: Custody status correlates with race because pretrial detention patterns reflect unequal bail access, which reflects income inequality, which is racially structured. Removing race while leaving custody status in the model leaves most of the racial signal intact.
Feedback loop: COMPAS scores influence bail and sentencing decisions. A defendant flagged as high-risk is more likely to be detained pretrial. Pretrial detention increases the probability of conviction and reoffending (due to job loss, housing instability, and network effects). The model produces the outcome it predicted, and the outcome enters the next training set as evidence the prediction was correct.
-The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. A 71% reduction from one targeted intervention on one proxy variable.
+The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. An 82% reduction from one targeted intervention on one proxy variable.
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
diff --git a/explainers/ml-bias.md b/explainers/ml-bias.md
index b3ac0f2..5ae0b0b 100644
--- a/explainers/ml-bias.md
+++ b/explainers/ml-bias.md
@@ -76,7 +76,7 @@ The COMPAS audit is the sharpest illustration of how all four entry points conve
**Feedback loop:** COMPAS scores influence bail and sentencing decisions. A defendant flagged as high-risk is more likely to be detained pretrial. Pretrial detention increases the probability of conviction and reoffending (due to job loss, housing instability, and network effects). The model produces the outcome it predicted, and the outcome enters the next training set as evidence the prediction was correct.
-The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. A 71% reduction from one targeted intervention on one proxy variable.
+The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. An 82% reduction from one targeted intervention on one proxy variable.
```python
import pandas as pd
diff --git a/explainers/proxy-variables.html b/explainers/proxy-variables.html
index fabf1c8..0a7551a 100644
--- a/explainers/proxy-variables.html
+++ b/explainers/proxy-variables.html
@@ -230,7 +230,7 @@ What We Did
Results:
| Group | High-Risk Flag Rate |
|---|
| Black defendants | 84.71% |
| White defendants | 69.02% |
| Fairness gap | 15.69% |
Summary
-| Approach | Fairness Gap | Reduction |
|---|
| Biased model | 86.77% | - |
| Remove race only | ~80%+ | Minimal |
| Remove race + proxy | 15.69% | 71% |
+| Approach | Fairness Gap | Reduction |
|---|
| Biased model | 86.77% | - |
| Remove race only | ~80%+ | Minimal |
| Remove race + proxy | 15.69% | 82% |
Removing the protected attribute alone is not enough. You must audit every feature for correlation with protected attributes.
How to Detect Proxy Variables
@@ -264,7 +264,7 @@ The Bigger Picture
This is why algorithmic auditing is not optional. It is a prerequisite for deployment.
-COMPAS/ - Full COMPAS analysis: biased model → fair model → 71% gap reductionAI Fair Recruitment/ - AI recruitment bias: 97.3% gap reduction after feature audit- Coming soon: Facial recognition bias, HMDA loan bias, healthcare AI
+COMPAS/ - Full COMPAS analysis: biased model → fair model → 82% gap reductionAI Fair Recruitment/ - AI recruitment bias: 97.3% gap reduction after feature audit- Coming soon: Facial recognition bias, HMDA loan bias, healthcare AI
Further Reading
diff --git a/explainers/proxy-variables.md b/explainers/proxy-variables.md
index 3d4e935..e9582b8 100644
--- a/explainers/proxy-variables.md
+++ b/explainers/proxy-variables.md
@@ -89,7 +89,7 @@ X = pd.get_dummies(df[[
|---|---|---|
| Biased model | 86.77% | - |
| Remove race only | ~80%+ | Minimal |
-| Remove race + proxy | 15.69% | **71%** |
+| Remove race + proxy | 15.69% | **82%** |
**Removing the protected attribute alone is not enough. You must audit every feature for correlation with protected attributes.**
@@ -138,7 +138,7 @@ Data doesn't exist in a vacuum. It reflects the society that generated it. A mod
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 71% gap reduction
+- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 82% gap reduction
- [`AI Fair Recruitment/`](../AI%20Fair%20Recruitment/) - AI recruitment bias: 97.3% gap reduction after feature audit
- Coming soon: Facial recognition bias, HMDA loan bias, healthcare AI
diff --git a/explainers/reinforcement-learning.html b/explainers/reinforcement-learning.html
index 8e295df..f379eb9 100644
--- a/explainers/reinforcement-learning.html
+++ b/explainers/reinforcement-learning.html
@@ -411,7 +411,7 @@ Step 3 - Remove Proxies and Retrain
model_fair.fit(X_train_f, y_train_f)
Results - mitigated policy (race + proxy removed):
| Group | High-Risk Flag Rate |
|---|
| Black defendants | 53.43% |
| White defendants | 37.74% |
| New fairness gap | 15.69% |
-| Approach | Fairness Gap | Reduction |
|---|
| Biased policy | 86.77% | - |
| Remove race only | ~60% | Partial |
| Remove race + custody_status | 15.69% | 71% |
+| Approach | Fairness Gap | Reduction |
|---|
| Biased policy | 86.77% | - |
| Remove race only | ~60% | Partial |
| Remove race + custody_status | 15.69% | 82% |
The policy architecture did not change. The training procedure did not change. Only the state representation changed - and most of the discriminatory behaviour disappeared.
Second Case: Recommendation Systems and Feedback Loops
@@ -443,13 +443,13 @@ Second Case: Reco
Limitations
RL is rarely deployed explicitly in high-stakes settings. The COMPAS case is RL-adjacent, not pure RL. The patterns - reward misspecification, credit assignment failure, proxy exploitation - appear in supervised systems too. The explainer uses the RL frame because it makes these failure modes most legible, not because RL is uniquely responsible.
-Removing proxies reduces but does not eliminate the gap. The 71% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
+Removing proxies reduces but does not eliminate the gap. The 82% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
Defining the reward function is unavoidably political. There is no neutral reward signal for a parole decision. Choosing to penalise false releases more than wrongful holds is a value judgment about whose safety matters more. This explainer cannot resolve that question. It can only make it visible.
Credit assignment failure is not fully solvable. No discounting scheme correctly attributes a 6-month outcome to the exact decision that caused it when hundreds of intervening variables - housing, employment, family, neighbourhood - all contribute. This is a fundamental limit of sequential decision-making under delayed feedback, not an implementation flaw.
Watch time as a proxy for value is increasingly contested. Platforms have introduced secondary signals (surveys, explicit ratings) to supplement watch time. Whether these corrections are sufficient, or whether they introduce new biases, is an open empirical question.
-feedback-loop-bias.md - How retraining on RL-generated decisions amplifies bias across cyclesproxy-variables.md - Why the state representation is where most RL bias enterslabel-bias.md - How the reward signal inherits bias from historical outcomesneural-networks.md - How the policy function learns from state-reward pairsCOMPAS/ - Full audit of the COMPAS dataset: 71% gap reduction after removing race + custody_status proxy
+feedback-loop-bias.md - How retraining on RL-generated decisions amplifies bias across cyclesproxy-variables.md - Why the state representation is where most RL bias enterslabel-bias.md - How the reward signal inherits bias from historical outcomesneural-networks.md - How the policy function learns from state-reward pairsCOMPAS/ - Full audit of the COMPAS dataset: 82% gap reduction after removing race + custody_status proxy
Further Reading
diff --git a/explainers/reinforcement-learning.md b/explainers/reinforcement-learning.md
index 146f71c..37d5f2d 100644
--- a/explainers/reinforcement-learning.md
+++ b/explainers/reinforcement-learning.md
@@ -309,7 +309,7 @@ model_fair.fit(X_train_f, y_train_f)
|---|---|---|
| Biased policy | 86.77% | - |
| Remove race only | ~60% | Partial |
-| Remove race + custody_status | 15.69% | **71%** |
+| Remove race + custody_status | 15.69% | **82%** |
The policy architecture did not change. The training procedure did not change. Only the state representation changed - and most of the discriminatory behaviour disappeared.
@@ -353,7 +353,7 @@ The demographic consequences of this reward function are asymmetric: recommendat
**RL is rarely deployed explicitly in high-stakes settings.** The COMPAS case is RL-adjacent, not pure RL. The patterns - reward misspecification, credit assignment failure, proxy exploitation - appear in supervised systems too. The explainer uses the RL frame because it makes these failure modes most legible, not because RL is uniquely responsible.
-**Removing proxies reduces but does not eliminate the gap.** The 71% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
+**Removing proxies reduces but does not eliminate the gap.** The 82% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
**Defining the reward function is unavoidably political.** There is no neutral reward signal for a parole decision. Choosing to penalise false releases more than wrongful holds is a value judgment about whose safety matters more. This explainer cannot resolve that question. It can only make it visible.
@@ -369,7 +369,7 @@ The demographic consequences of this reward function are asymmetric: recommendat
- [`proxy-variables.md`](proxy-variables.md) - Why the state representation is where most RL bias enters
- [`label-bias.md`](label-bias.md) - How the reward signal inherits bias from historical outcomes
- [`neural-networks.md`](neural-networks.md) - How the policy function learns from state-reward pairs
-- [`COMPAS/`](../COMPAS/) - Full audit of the COMPAS dataset: 71% gap reduction after removing race + custody_status proxy
+- [`COMPAS/`](../COMPAS/) - Full audit of the COMPAS dataset: 82% gap reduction after removing race + custody_status proxy
---
diff --git a/faircode/_explainers/ai-objectivity-myth.md b/faircode/_explainers/ai-objectivity-myth.md
index e98cbbe..ee9c242 100644
--- a/faircode/_explainers/ai-objectivity-myth.md
+++ b/faircode/_explainers/ai-objectivity-myth.md
@@ -40,7 +40,7 @@ model.fit(X_train, y_train)
# Fairness Gap: 86.77%
```
-Removing `race` alone barely moves the number, because `CustodyStatus` - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after `race` *and* `CustodyStatus` are both dropped does the gap fall to 15.69%, a 71% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
+Removing `race` alone barely moves the number, because `CustodyStatus` - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after `race` *and* `CustodyStatus` are both dropped does the gap fall to 15.69%, an 82% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
## Detection Code
diff --git a/faircode/_explainers/confounding-variable.md b/faircode/_explainers/confounding-variable.md
index 46ad0b5..94586e7 100644
--- a/faircode/_explainers/confounding-variable.md
+++ b/faircode/_explainers/confounding-variable.md
@@ -69,7 +69,7 @@ Removing both `race` and `CustodyStatus` breaks this path:
```python
# fair.py: features include only priors_count + charge_degree + age
-# Black/White fairness gap: 15.69% - 71% reduction
+# Black/White fairness gap: 15.69% - 82% reduction
```
The residual 15.69% reflects other confounding paths (differential bail rates, charge severity distributions, surveillance-driven label noise) that require changes upstream of the model to eliminate entirely.
@@ -181,7 +181,7 @@ For continuous features, replace `chi2_contingency` with a Pearson correlation o
3. **Conditioning on a collider opens new bias.** A collider is a variable caused by both the feature and the outcome - the reverse of a confounder. Controlling for a collider introduces a spurious association rather than removing one. Correctly distinguishing confounders from colliders requires a causal graph (a DAG), not statistical testing alone. Chi-squared tests cannot tell you which direction the arrows point.
-4. **Confounder removal reduces but does not eliminate bias.** Removing `CustodyStatus` from COMPAS cuts the fairness gap from 86.77% to 15.69% - a 71% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
+4. **Confounder removal reduces but does not eliminate bias.** Removing `CustodyStatus` from COMPAS cuts the fairness gap from 86.77% to 15.69% - an 82% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
5. **Adjustment can introduce its own distortions.** Propensity score methods and inverse probability weighting reduce confounding but amplify variance, especially in small subgroups. In high-stakes settings, an overcorrected model may perform worse for the groups it was adjusted to protect.
@@ -205,7 +205,7 @@ When a confounded model is deployed and its outputs influence future labels - re
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - the primary worked example. `CustodyStatus` confounds the race→recidivism path, driving 71% of the Black/White fairness gap. Removing it alongside `race` reduces the gap from 86.77% to 15.69%.
+- [`COMPAS/`](../COMPAS/) - the primary worked example. `CustodyStatus` confounds the race→recidivism path, driving 82% of the Black/White fairness gap. Removing it alongside `race` reduces the gap from 86.77% to 15.69%.
- [`Benefits Denial/`](../Benefits%20Denial/) - `relationship` and `marital-status` act as confounders for sex: historical gender norms independently elevated male-coded relationship statuses and income levels in the census data, creating a spurious association the model amplifies.
- [`Healthcare Readmission/`](../Healthcare%20Readmission/) - `payer_code` is confounded by race: differential insurance access is caused by structural factors that also independently predict readmission risk, not only by individual health status.
diff --git a/faircode/_explainers/data-leakage.md b/faircode/_explainers/data-leakage.md
index f11f86c..bb9a366 100644
--- a/faircode/_explainers/data-leakage.md
+++ b/faircode/_explainers/data-leakage.md
@@ -47,7 +47,7 @@ The COMPAS audit in this repository demonstrates a proxy form of target leakage.
This is the structural form of leakage: a feature that looks like a legitimate input but whose predictive power comes from downstream effects of the very outcome being predicted.
-Removing `CustodyStatus` alongside the protected race attribute reduces the gap to 15.69% - a 71% reduction.
+Removing `CustodyStatus` alongside the protected race attribute reduces the gap to 15.69% - an 82% reduction.
```python
import os
diff --git a/faircode/_explainers/equalized-odds.md b/faircode/_explainers/equalized-odds.md
index 50cbeb0..706d756 100644
--- a/faircode/_explainers/equalized-odds.md
+++ b/faircode/_explainers/equalized-odds.md
@@ -184,7 +184,7 @@ Even after correcting for Equalized Odds, models can still encode protected attr
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 71% gap reduction. Equalized Odds violations visible in the raw model outputs.
+- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 82% gap reduction. Equalized Odds violations visible in the raw model outputs.
- [`explainers/proxy-variables.md`](proxy-variables.md) - Why AI stays biased even after you remove protected attributes
- [`explainers/shap-values.md`](shap-values.md) - How to see exactly what drove an AI decision - and use that to catch bias
diff --git a/faircode/_explainers/how-ai-detects-patterns.md b/faircode/_explainers/how-ai-detects-patterns.md
index 229b1bc..30b73da 100644
--- a/faircode/_explainers/how-ai-detects-patterns.md
+++ b/faircode/_explainers/how-ai-detects-patterns.md
@@ -52,7 +52,7 @@ model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
```
-When `race` is dropped, the gap barely moves - `CustodyStatus` alone reconstructs most of the pattern, because it correlates with `race` at 0.31 importance even on its own. Only when both `race` and `CustodyStatus` are removed does the gap fall to 15.69%, a 71% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
+When `race` is dropped, the gap barely moves - `CustodyStatus` alone reconstructs most of the pattern, because it correlates with `race` at 0.31 importance even on its own. Only when both `race` and `CustodyStatus` are removed does the gap fall to 15.69%, an 82% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
## Detection Code
diff --git a/faircode/_explainers/ml-bias.md b/faircode/_explainers/ml-bias.md
index b3ac0f2..5ae0b0b 100644
--- a/faircode/_explainers/ml-bias.md
+++ b/faircode/_explainers/ml-bias.md
@@ -76,7 +76,7 @@ The COMPAS audit is the sharpest illustration of how all four entry points conve
**Feedback loop:** COMPAS scores influence bail and sentencing decisions. A defendant flagged as high-risk is more likely to be detained pretrial. Pretrial detention increases the probability of conviction and reoffending (due to job loss, housing instability, and network effects). The model produces the outcome it predicted, and the outcome enters the next training set as evidence the prediction was correct.
-The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. A 71% reduction from one targeted intervention on one proxy variable.
+The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. An 82% reduction from one targeted intervention on one proxy variable.
```python
import pandas as pd
diff --git a/faircode/_explainers/proxy-variables.md b/faircode/_explainers/proxy-variables.md
index 3d4e935..e9582b8 100644
--- a/faircode/_explainers/proxy-variables.md
+++ b/faircode/_explainers/proxy-variables.md
@@ -89,7 +89,7 @@ X = pd.get_dummies(df[[
|---|---|---|
| Biased model | 86.77% | - |
| Remove race only | ~80%+ | Minimal |
-| Remove race + proxy | 15.69% | **71%** |
+| Remove race + proxy | 15.69% | **82%** |
**Removing the protected attribute alone is not enough. You must audit every feature for correlation with protected attributes.**
@@ -138,7 +138,7 @@ Data doesn't exist in a vacuum. It reflects the society that generated it. A mod
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 71% gap reduction
+- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 82% gap reduction
- [`AI Fair Recruitment/`](../AI%20Fair%20Recruitment/) - AI recruitment bias: 97.3% gap reduction after feature audit
- Coming soon: Facial recognition bias, HMDA loan bias, healthcare AI
diff --git a/faircode/_explainers/reinforcement-learning.md b/faircode/_explainers/reinforcement-learning.md
index 146f71c..37d5f2d 100644
--- a/faircode/_explainers/reinforcement-learning.md
+++ b/faircode/_explainers/reinforcement-learning.md
@@ -309,7 +309,7 @@ model_fair.fit(X_train_f, y_train_f)
|---|---|---|
| Biased policy | 86.77% | - |
| Remove race only | ~60% | Partial |
-| Remove race + custody_status | 15.69% | **71%** |
+| Remove race + custody_status | 15.69% | **82%** |
The policy architecture did not change. The training procedure did not change. Only the state representation changed - and most of the discriminatory behaviour disappeared.
@@ -353,7 +353,7 @@ The demographic consequences of this reward function are asymmetric: recommendat
**RL is rarely deployed explicitly in high-stakes settings.** The COMPAS case is RL-adjacent, not pure RL. The patterns - reward misspecification, credit assignment failure, proxy exploitation - appear in supervised systems too. The explainer uses the RL frame because it makes these failure modes most legible, not because RL is uniquely responsible.
-**Removing proxies reduces but does not eliminate the gap.** The 71% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
+**Removing proxies reduces but does not eliminate the gap.** The 82% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
**Defining the reward function is unavoidably political.** There is no neutral reward signal for a parole decision. Choosing to penalise false releases more than wrongful holds is a value judgment about whose safety matters more. This explainer cannot resolve that question. It can only make it visible.
@@ -369,7 +369,7 @@ The demographic consequences of this reward function are asymmetric: recommendat
- [`proxy-variables.md`](proxy-variables.md) - Why the state representation is where most RL bias enters
- [`label-bias.md`](label-bias.md) - How the reward signal inherits bias from historical outcomes
- [`neural-networks.md`](neural-networks.md) - How the policy function learns from state-reward pairs
-- [`COMPAS/`](../COMPAS/) - Full audit of the COMPAS dataset: 71% gap reduction after removing race + custody_status proxy
+- [`COMPAS/`](../COMPAS/) - Full audit of the COMPAS dataset: 82% gap reduction after removing race + custody_status proxy
---
diff --git a/llms-full.txt b/llms-full.txt
index a99002b..e11ad4a 100644
--- a/llms-full.txt
+++ b/llms-full.txt
@@ -103,7 +103,7 @@ X = pd.get_dummies(df[[
|---|---|---|
| Biased model | 86.77% | - |
| Remove race only | ~80%+ | Minimal |
-| Remove race + proxy | 15.69% | **71%** |
+| Remove race + proxy | 15.69% | **82%** |
**Removing the protected attribute alone is not enough. You must audit every feature for correlation with protected attributes.**
@@ -152,7 +152,7 @@ Data doesn't exist in a vacuum. It reflects the society that generated it. A mod
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 71% gap reduction
+- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 82% gap reduction
- [`AI Fair Recruitment/`](../AI%20Fair%20Recruitment/) - AI recruitment bias: 97.3% gap reduction after feature audit
- Coming soon: Facial recognition bias, HMDA loan bias, healthcare AI
@@ -360,7 +360,7 @@ Even after correcting for Equalized Odds, models can still encode protected attr
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 71% gap reduction. Equalized Odds violations visible in the raw model outputs.
+- [`COMPAS/`](../COMPAS/) - Full COMPAS analysis: biased model → fair model → 82% gap reduction. Equalized Odds violations visible in the raw model outputs.
- [`explainers/proxy-variables.md`](proxy-variables.md) - Why AI stays biased even after you remove protected attributes
- [`explainers/shap-values.md`](shap-values.md) - How to see exactly what drove an AI decision - and use that to catch bias
@@ -4413,7 +4413,7 @@ model_fair.fit(X_train_f, y_train_f)
|---|---|---|
| Biased policy | 86.77% | - |
| Remove race only | ~60% | Partial |
-| Remove race + custody_status | 15.69% | **71%** |
+| Remove race + custody_status | 15.69% | **82%** |
The policy architecture did not change. The training procedure did not change. Only the state representation changed - and most of the discriminatory behaviour disappeared.
@@ -4457,7 +4457,7 @@ The demographic consequences of this reward function are asymmetric: recommendat
**RL is rarely deployed explicitly in high-stakes settings.** The COMPAS case is RL-adjacent, not pure RL. The patterns - reward misspecification, credit assignment failure, proxy exploitation - appear in supervised systems too. The explainer uses the RL frame because it makes these failure modes most legible, not because RL is uniquely responsible.
-**Removing proxies reduces but does not eliminate the gap.** The 71% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
+**Removing proxies reduces but does not eliminate the gap.** The 82% reduction in the COMPAS case leaves a 15.69-point fairness gap. Remaining disparity reflects features that correlate with race for legitimate predictive reasons (prior arrests reflect real behaviour differences produced by structural conditions) or proxies not yet identified. Proxy removal is necessary but not sufficient.
**Defining the reward function is unavoidably political.** There is no neutral reward signal for a parole decision. Choosing to penalise false releases more than wrongful holds is a value judgment about whose safety matters more. This explainer cannot resolve that question. It can only make it visible.
@@ -4473,7 +4473,7 @@ The demographic consequences of this reward function are asymmetric: recommendat
- [`proxy-variables.md`](proxy-variables.md) - Why the state representation is where most RL bias enters
- [`label-bias.md`](label-bias.md) - How the reward signal inherits bias from historical outcomes
- [`neural-networks.md`](neural-networks.md) - How the policy function learns from state-reward pairs
-- [`COMPAS/`](../COMPAS/) - Full audit of the COMPAS dataset: 71% gap reduction after removing race + custody_status proxy
+- [`COMPAS/`](../COMPAS/) - Full audit of the COMPAS dataset: 82% gap reduction after removing race + custody_status proxy
---
@@ -4825,7 +4825,7 @@ The COMPAS audit is the sharpest illustration of how all four entry points conve
**Feedback loop:** COMPAS scores influence bail and sentencing decisions. A defendant flagged as high-risk is more likely to be detained pretrial. Pretrial detention increases the probability of conviction and reoffending (due to job loss, housing instability, and network effects). The model produces the outcome it predicted, and the outcome enters the next training set as evidence the prediction was correct.
-The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. A 71% reduction from one targeted intervention on one proxy variable.
+The biased model flags Black defendants as high-risk at 86.77%. The fair model - with race and custody status removed - reduces that gap to 15.69%. An 82% reduction from one targeted intervention on one proxy variable.
```python
import pandas as pd
@@ -5049,7 +5049,7 @@ The COMPAS audit in this repository demonstrates a proxy form of target leakage.
This is the structural form of leakage: a feature that looks like a legitimate input but whose predictive power comes from downstream effects of the very outcome being predicted.
-Removing `CustodyStatus` alongside the protected race attribute reduces the gap to 15.69% - a 71% reduction.
+Removing `CustodyStatus` alongside the protected race attribute reduces the gap to 15.69% - an 82% reduction.
```python
import os
@@ -5289,7 +5289,7 @@ model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
```
-When `race` is dropped, the gap barely moves - `CustodyStatus` alone reconstructs most of the pattern, because it correlates with `race` at 0.31 importance even on its own. Only when both `race` and `CustodyStatus` are removed does the gap fall to 15.69%, a 71% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
+When `race` is dropped, the gap barely moves - `CustodyStatus` alone reconstructs most of the pattern, because it correlates with `race` at 0.31 importance even on its own. Only when both `race` and `CustodyStatus` are removed does the gap fall to 15.69%, an 82% reduction. The pattern the model was detecting was never really about behavior - it was about which neighborhoods got patrolled.
## Detection Code
@@ -5629,7 +5629,7 @@ model.fit(X_train, y_train)
# Fairness Gap: 86.77%
```
-Removing `race` alone barely moves the number, because `CustodyStatus` - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after `race` *and* `CustodyStatus` are both dropped does the gap fall to 15.69%, a 71% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
+Removing `race` alone barely moves the number, because `CustodyStatus` - a record of prior system contact - carries the same signal: communities that were over-policed historically generate more "custody status" records today, independent of actual reoffense risk. Only after `race` *and* `CustodyStatus` are both dropped does the gap fall to 15.69%, an 82% reduction. The model was never neutral. It was a faithful record of an unequal criminal justice system, expressed as a probability.
## Detection Code
@@ -5813,7 +5813,7 @@ Removing both `race` and `CustodyStatus` breaks this path:
```python
# fair.py: features include only priors_count + charge_degree + age
-# Black/White fairness gap: 15.69% - 71% reduction
+# Black/White fairness gap: 15.69% - 82% reduction
```
The residual 15.69% reflects other confounding paths (differential bail rates, charge severity distributions, surveillance-driven label noise) that require changes upstream of the model to eliminate entirely.
@@ -5925,7 +5925,7 @@ For continuous features, replace `chi2_contingency` with a Pearson correlation o
3. **Conditioning on a collider opens new bias.** A collider is a variable caused by both the feature and the outcome - the reverse of a confounder. Controlling for a collider introduces a spurious association rather than removing one. Correctly distinguishing confounders from colliders requires a causal graph (a DAG), not statistical testing alone. Chi-squared tests cannot tell you which direction the arrows point.
-4. **Confounder removal reduces but does not eliminate bias.** Removing `CustodyStatus` from COMPAS cuts the fairness gap from 86.77% to 15.69% - a 71% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
+4. **Confounder removal reduces but does not eliminate bias.** Removing `CustodyStatus` from COMPAS cuts the fairness gap from 86.77% to 15.69% - an 82% reduction. The remaining gap reflects additional confounding paths that cannot be closed by feature removal without changing the label generation process itself.
5. **Adjustment can introduce its own distortions.** Propensity score methods and inverse probability weighting reduce confounding but amplify variance, especially in small subgroups. In high-stakes settings, an overcorrected model may perform worse for the groups it was adjusted to protect.
@@ -5949,7 +5949,7 @@ When a confounded model is deployed and its outputs influence future labels - re
## Related Projects in This Repo
-- [`COMPAS/`](../COMPAS/) - the primary worked example. `CustodyStatus` confounds the race→recidivism path, driving 71% of the Black/White fairness gap. Removing it alongside `race` reduces the gap from 86.77% to 15.69%.
+- [`COMPAS/`](../COMPAS/) - the primary worked example. `CustodyStatus` confounds the race→recidivism path, driving 82% of the Black/White fairness gap. Removing it alongside `race` reduces the gap from 86.77% to 15.69%.
- [`Benefits Denial/`](../Benefits%20Denial/) - `relationship` and `marital-status` act as confounders for sex: historical gender norms independently elevated male-coded relationship statuses and income levels in the census data, creating a spurious association the model amplifies.
- [`Healthcare Readmission/`](../Healthcare%20Readmission/) - `payer_code` is confounded by race: differential insurance access is caused by structural factors that also independently predict readmission risk, not only by individual health status.