← Explore

Learn by tinkering

Bias & Fairness Sandbox

Two groups, equally qualified on average in reality. Adjust how much of the training data comes from each group, and how much historical bias has crept into one group's recorded scores, then watch a real classifier, trained live in your browser, treat them differently.

How this works: this runs an actual logistic regression, fit by gradient descent on simulated data, right now in your browser, nothing here is scripted or faked. Group A and Group B are drawn from the exact same underlying distribution of "true qualification," so any gap you see between them is caused entirely by the sliders below, not by a real difference in ability.

Held-out evaluation (fixed, balanced 50/50)

Group A, truly qualified Group B, truly qualified Not truly qualified Ring = classifier predicted "no"
MetricGroup AGroup B
What am I actually looking at?

Historical bias and proxy variables

The slider doesn't touch anyone's real qualification -- it only shifts what gets recorded. That's exactly what Lesson 10 calls historical bias: a real, systematic skew in the data a model is trained on, which the model learns as if it were a meaningful, causal signal.

Two fairness metrics, and why they can conflict

Selection rate (demographic parity) asks: are the two groups approved at the same rate? True-positive rate (equal opportunity) asks: among people who are genuinely qualified, does each group get approved equally often? At zero bias, both stay close between groups. Push the bias slider up and watch Group B's true-positive rate collapse -- the classifier isn't being "unfair" on purpose, it's faithfully reproducing a biased proxy.

Composition makes it worse

Shrinking Group B's share of the training set compounds the effect: the classifier sees even fewer of Group B's genuinely qualified people during training, so it has even less evidence to correct for the bias in what it does see.

Does "seeing group directly" fix it?

Try checking the box at a high bias setting. Group B's numbers usually recover -- because the classifier can now learn a separate effective cutoff per group, explicitly correcting for a known, uniform shift. But notice what that actually is: a deliberate policy decision to treat groups differently in order to reach a fairness goal, not a neutral default. It also only works because the bias here happens to be a simple, uniform offset -- Lesson 10's "proxy variable" problem is that in the real world, removing or adding a group feature rarely lines up this cleanly, and there's no single definition of fairness that resolves every case.