"I was trained on a decade of who got approved, who got hired, who got policed. I learned the pattern perfectly. Then they asked me to decide tomorrow, and I gave them yesterday with a confidence interval. Every time I act, the world I see next confirms I was right, because I helped make it so."
A Model That Learned Yesterday's Inequities and Plans to Repeat Them
Fairness in a static classifier is a snapshot question: at one moment, does the model treat groups equitably? Fairness in a temporal system is a trajectory question, because the system's decisions today reshape the data it learns from tomorrow. A lender that under-approves a group sees fewer repayment records from that group, so its next model is even more uncertain about them and approves even fewer; a predictive-policing system that patrols one neighborhood records more incidents there, which justifies more patrols. These are feedback loops, and they are the defining feature that makes temporal fairness its own subject rather than a footnote to static fairness. This section establishes why sequential decisions entrench disparity (the actions-change-the-data principle this book opened with in Chapter 1 and made central to sequential decision making in Part VI), defines the group-fairness metrics (demographic parity, equalized odds, calibration) with their famous impossibility results, catalogs the sources of temporal bias (historical data, subpopulation drift, time-varying label and measurement bias), surveys mitigations across pre/in/post-processing and into the drifting and sequential settings, and then builds a complete worked example: an equalized-odds audit and a post-processing mitigation from scratch, the same task in three lines of fairlearn, and a simulation showing a feedback loop widening a disparity round after round. You leave able to measure a fairness gap on a temporal task, mitigate it, and reason about whether your mitigation survives deployment.
In Section 33.2 we made temporal models robust to distribution shift and adversarial perturbation, so they behave predictably when the world moves. Robustness asks whether the model is stable; fairness asks whether it is equitable, and the two are distinct: a model can be perfectly robust and reliably unfair, reproducing the same disparity on every input with admirable consistency. This section is about the second axis. It is also the section where the book's oldest theme returns with the highest stakes. From the very first chapter we insisted that temporal intelligence differs from static prediction because its outputs become future inputs: a forecast moves a market, a recommendation shapes a click, a policy changes the state. When the prediction is a decision about a person and the data records the consequences of that decision, the actions-change-the-data loop stops being an abstract modeling concern and becomes a mechanism for entrenching, or dismantling, social inequity.
The competencies this section installs are four. First, to recognize and name the feedback structure by which a temporal decision system amplifies a small initial disparity into a large persistent one. Second, to define the three canonical group-fairness criteria as precise probabilistic statements, compute them, and explain why no model can satisfy all three at once when base rates differ. Third, to diagnose why a temporal system's bias is not constant: historical training data, drift that hits subpopulations unevenly, and labels whose meaning and availability change over time. Fourth, to apply a mitigation, from a from-scratch threshold adjustment to a library call, and to audit fairness as an ongoing time series rather than a one-time certificate. We use the unified notation of Appendix A: $A$ for a protected attribute (the group), $Y$ for the true label, $\hat{Y}$ for the prediction, $R$ for a real-valued score, and a subscript $t$ when the quantity evolves over time.
1. Why Fairness Has a Temporal Dimension: Sequential Decisions and Feedback Loops Beginner
Static fairness analysis imagines a fixed dataset, a fixed model, and a one-shot evaluation: draw a test set, measure whether the error rates match across groups, report a number. That picture is adequate for a model that predicts and walks away. It is badly wrong for the systems that actually decide who gets a loan, an interview, an organ-transplant priority, or a police patrol, because those systems run in a loop with the world. The decision at time $t$ is not a passive read-out; it is an intervention that changes which data point at time $t+1$ ever gets recorded. This is the actions-change-the-data principle introduced in Section 1.1 and formalized as the environment dynamics of a Markov decision process in Part VI. Fairness lives inside that loop.
The canonical example is selective labeling in lending. A bank approves applicant $i$ only if its score $R_i$ clears a threshold; it observes repayment ($Y_i$) only for the applicants it approved. If the initial model under-scores group $A = a$ relative to group $A = b$, it approves fewer of group $a$, so it collects fewer repayment outcomes for group $a$, so the next model trained on accumulated data has thinner, noisier evidence about group $a$ and, under almost any uncertainty-averse training rule, scores them even lower. Each round, the approved population for group $a$ shrinks, the disparity in approval rates widens, and the model becomes more confident in a gap it manufactured. The same skeleton drives predictive policing (patrols generate the incident reports that justify more patrols), hiring (hiring decisions generate the performance records that train the next screen), and healthcare triage (triage decides who is monitored closely enough to record a deterioration). In every case a small seed bias is fed back through the data-generating process and compounds.
It is worth being precise about why the loop is vicious rather than merely circular. A neutral feedback loop, one whose fixed point happens to be equitable, would be harmless: the system would settle into a fair equilibrium and stay there. The lending loop is vicious because its dynamics have a direction. The map from "approval rate this round" to "evidence quality next round" to "approval rate next round" has positive feedback: less approval yields thinner evidence yields (under any uncertainty penalty) still less approval. Positive feedback around a small initial asymmetry is the textbook recipe for divergence, and the divergence here is a widening group gap. Nothing in the per-round model is broken; each round's classifier may be locally optimal given the data it was handed. The pathology is in the coupling between rounds, which is exactly the object a static analysis, by construction, cannot see. This is why the simulation in subsection five can start from equal true base rates and still manufacture a large disparity: the inequity is not in the data the world supplies, it is in the data the system selects.
Two consequences follow that have no static analogue. The first is delayed impact: a mitigation that looks fair at the moment of deployment (equal approval rates this quarter, say) can still degrade a group's long-run welfare, because the same decisions reshape the population that arrives next quarter. Liu and colleagues showed in 2018 that a naively imposed fairness constraint can leave a disadvantaged group worse off over time than no constraint at all, once the downstream effect of decisions on, for example, credit scores is modeled. Fairness measured at one timestep is not fairness measured over the trajectory. The second consequence is that the protected group's data itself becomes endogenous: you cannot treat the distribution of $(A, X, Y)$ as fixed and external when your own past decisions wrote part of it. Both consequences mean temporal fairness must be analyzed as a dynamical system, the same lens Part VI brought to sequential decisions, not as a property of a single confusion matrix.
The single fact that separates temporal fairness from static fairness is endogenous data: in a deployed sequential decision system, the model's outputs determine which future data points are ever observed and labeled. A static classifier reads a fixed distribution; a temporal decision system writes its own future distribution. So a disparity is never just measured, it is also caused, and then re-measured on the caused data, and the loop runs every round. This is the actions-change-the-data principle of Chapter 1 applied to people: equity is not a property of one model evaluation but of the entire closed-loop trajectory the model induces.
2. Group-Fairness Metrics and the Impossibility Results Intermediate
To say a temporal system is fair we need precise criteria. The three that anchor the field are all statements about conditional independence between the protected attribute $A$, the label $Y$, and the prediction $\hat{Y}$ (or score $R$). Write the protected attribute as $A \in \{a, b\}$, the binary outcome as $Y \in \{0, 1\}$, and the binary decision as $\hat{Y} \in \{0, 1\}$.
Demographic parity (also called statistical parity or independence) requires the decision rate to be equal across groups, regardless of the true label:
$$\Pr(\hat{Y} = 1 \mid A = a) = \Pr(\hat{Y} = 1 \mid A = b).$$Its violation is summarized by the demographic-parity difference $\Delta_{\mathrm{DP}} = \Pr(\hat{Y}=1 \mid A=a) - \Pr(\hat{Y}=1 \mid A=b)$, which is zero when the criterion holds. Demographic parity ignores $Y$ entirely, which is its strength (it needs no ground truth) and its weakness (it can force equal selection even when the groups genuinely differ in the outcome).
Equalized odds (separation) requires the prediction to be conditionally independent of the group given the true label, so that the true-positive and false-positive rates match across groups:
$$\Pr(\hat{Y}=1 \mid A=a, Y=y) = \Pr(\hat{Y}=1 \mid A=b, Y=y), \qquad y \in \{0,1\}.$$The natural scalar summary is the equalized-odds gap, the larger of the two rate differences,
$$\Delta_{\mathrm{EO}} = \max_{y \in \{0,1\}} \big| \Pr(\hat{Y}=1 \mid A=a, Y=y) - \Pr(\hat{Y}=1 \mid A=b, Y=y) \big|,$$which combines a true-positive-rate gap ($y=1$) and a false-positive-rate gap ($y=0$). Equalized odds says: among people who actually repay, approval should not depend on group, and among people who actually default, denial should not depend on group. It is the criterion we audit and mitigate in subsection five.
Calibration within groups (sufficiency) requires that a score means the same thing in every group: among everyone assigned score $R = r$, the fraction who are positive is $r$, in each group separately,
$$\Pr(Y = 1 \mid R = r, A = a) = \Pr(Y = 1 \mid R = r, A = b) = r \quad \text{for all } r.$$Calibration is the criterion a risk score should satisfy if downstream users are to trust the number identically regardless of group. Now the uncomfortable theorem. Chouldechova (2017) and Kleinberg, Mullainathan, and Raghavan (2016) proved an impossibility result: when the base rates differ between groups, $\Pr(Y=1 \mid A=a) \neq \Pr(Y=1 \mid A=b)$, no classifier (short of a perfect or trivial one) can simultaneously satisfy calibration within groups and equalized odds. The three criteria are in genuine mathematical tension; you must choose which to enforce, and the choice is a value judgment, not a technical one. This is not a defect of any algorithm; it is a property of the geometry of confusion matrices under unequal base rates.
Independence (demographic parity), separation (equalized odds), and sufficiency (calibration) are three different conditional-independence statements, and the impossibility theorem says that whenever group base rates differ, you can satisfy at most one of the separation-versus-sufficiency pair exactly. A fairness specification that demands "calibrated and equalized" on a problem with unequal base rates is asking for something provably nonexistent. The engineering consequence is that "make the model fair" is underspecified until someone names the criterion, and that naming encodes a normative decision about which kind of error the system is allowed to distribute unequally. In a temporal system the choice is harder still, because the criterion must hold not at one timestep but across a drifting sequence of them.
A second piece of intuition makes the three criteria less abstract. Think of a confusion matrix per group: true positives, false positives, true negatives, false negatives. Demographic parity constrains only the column sums (how often we say yes), ignoring whether we are right. Equalized odds constrains the within-row rates (among the truly positive, and among the truly negative, separately), so it cares about the kind of error. Calibration constrains the within-prediction-bin outcome frequency, so it cares about what a score promises. Each criterion fixes a different slice of the same matrix, and once base rates differ the slices cannot all be pinned simultaneously, because the matrix has fewer degrees of freedom than the constraints demand. That counting argument is the whole content of the impossibility theorem, stripped of its formalism: three independent constraints on a structure that can satisfy at most two when the groups differ in how often the positive label actually occurs.
The temporal layer adds a subtlety the static theorems do not capture: long-term fairness. A criterion such as equalized odds is defined on a single round's joint distribution of $(A, Y, \hat{Y})$. But as subsection one argued, satisfying it every round does not guarantee an equitable trajectory, because the rounds are coupled through the feedback loop. Liu and colleagues' delayed-impact analysis (2018) shows a constraint can be locally satisfied yet globally harmful. So temporal fairness needs both a per-round criterion (one of the three above, audited at each timestep) and a trajectory-level criterion (does the disparity in some welfare measure shrink or grow over the deployment horizon?). We will see the trajectory-level failure directly in the feedback simulation of subsection five.
3. Sources of Temporal Bias Intermediate
Where does the bias enter a temporal prediction system? Three sources dominate, and each has a distinctly temporal character that distinguishes it from the static case.
Historical data. A model trained on past decisions inherits the inequities encoded in those decisions. If a decade of lending records reflects discriminatory human underwriting, a model that fits them faithfully reproduces the discrimination and calls it accuracy. This is the bias in the epigraph: the model learned yesterday's pattern perfectly, and yesterday was unjust. The temporal twist is that "historical" is not a fixed quantity; the further back the training window reaches, the more it averages over regimes whose norms and populations differ from today's, so the bias a model inherits depends on how its training window is chosen, a leakage-and-windowing concern first raised in Chapter 2.
Drift in subpopulations. The distribution shift studied in Chapter 20 rarely hits all groups equally. A demographic group whose behavior, exposure, or measurement changes faster than the majority sees its predictions go stale sooner, so even a model that was fair at training time becomes unfair as the minority subpopulation drifts away from the data the model encoded. Fairness is not drift-invariant: a model can pass a fairness audit at deployment and fail it three months later purely because one group drifted and the other did not. This couples the fairness story tightly to the concept-drift detection of Chapter 8.
Label and measurement bias that varies over time. The label $Y$ a temporal system trains on is often a proxy, and the proxy's relationship to the construct can differ across groups and drift across time. "Re-arrest" is a biased proxy for "committed a crime" because policing intensity differs by neighborhood; "clicked" is a biased proxy for "satisfied" because exposure differs by user. Worse, in selective-labeling settings (subsection one) the label is only observed for the acted-on population, so the very availability of $Y$ is a function of past decisions and varies by group over time. Measurement bias that is constant would be bad enough; measurement bias that drifts means a fairness correction calibrated at time $t$ can be miscalibrated at time $t + \tau$.
A supervised model's whole job is to reproduce the conditional distribution in its training data as faithfully as it can. That makes it a superb historian: point it at the past and it will tell you, with uncanny precision, exactly how the past behaved, prejudices and all. It makes it a hopeless reformer, because nothing in the loss function rewards departing from the past toward something better. If you want a model that does not merely repeat history you have to encode the departure yourself, as a constraint or a relabeling or a post-processing step, because left to its own devices the model's idea of a good day is a day exactly like the worst ones it was shown.
4. Mitigations: Pre, In, and Post-Processing, and Fairness Under Drift Advanced
Mitigations fall into three classical families by where in the pipeline they intervene, and the temporal setting adds a fourth concern that cuts across all of them: keeping the mitigation valid as the system runs.
Pre-processing repairs the data before training: reweighting examples so each group-label cell carries equal influence, resampling to balance the groups, or learning a representation $Z = \phi(X)$ from which the protected attribute cannot be predicted (adversarial debiasing of the features). Pre-processing is attractive when you control the training pipeline and want a single fix upstream of any model, but it must be re-applied whenever the data drifts, because a reweighting computed on last year's distribution is wrong for this year's.
In-processing bakes the fairness criterion into the training objective, typically as a constrained optimization, minimize the loss subject to $|\Delta_{\mathrm{EO}}| \le \epsilon$, or as a penalty added to the loss, or via an adversary that tries to recover $A$ from the model's predictions while the model is trained to defeat it. In-processing can achieve the best accuracy-fairness trade-off because the model learns to be both accurate and fair jointly, but it requires retraining and access to the training loop, and in a continually-learning temporal system the constraint must be re-imposed at every update.
Post-processing adjusts a trained model's outputs to meet the criterion without retraining, most simply by choosing group-specific decision thresholds on the score so that the per-group rates equalize. Hardt, Price, and Srebro (2016) gave the canonical post-processing method for equalized odds: solve for the thresholds (or randomized mixtures of thresholds) that match true-positive and false-positive rates across groups. Post-processing is the cheapest to deploy, needs only the scores and the protected attribute at decision time, and is the method we implement from scratch in subsection five; its temporal cost is that the optimal thresholds drift, so they must be recomputed on a rolling window.
A useful way to choose among the three families is to ask where in the pipeline you have leverage and how often the system changes. If you own the data and retrain rarely, pre-processing gives a single durable fix. If you own the training loop and can afford to retrain under a constraint, in-processing gives the best trade-off because accuracy and fairness are optimized together rather than one being patched after the other. If you can touch only the deployed scores, perhaps the model is a vendor black box, post-processing is the only option and is often enough. The temporal setting tilts the calculus toward methods that are cheap to re-apply, because whatever you do at deployment will need redoing as the distribution moves; a one-time in-processing constraint that is never re-imposed decays just as surely as a stale reweighting, so in a continually-learning system the relevant question is not only which family but how the chosen mitigation is scheduled to refresh.
Fairness under drift and in sequential settings is the genuinely temporal concern. Because subpopulations drift unevenly (subsection three), a mitigation fixed at deployment decays; the remedy is to treat fairness as a monitored time series and re-fit the mitigation on a sliding window, exactly the online-update discipline of Chapter 20. In the full sequential-decision (reinforcement-learning) setting of Part VI, fairness becomes a constraint on a policy rather than a classifier, and the feedback loop of subsection one must be modeled explicitly: fair-RL methods add a fairness term to the reward or a constraint on the long-run state distribution, and bandit formulations of selective labeling use exploration to keep collecting labels for under-served groups so the data does not starve. The unifying recommendation is auditing over time: compute the chosen fairness metric every round, plot it as a series, and alarm when it trends, treating a fairness regression like any other monitored production metric (the deployment-monitoring posture of Chapter 34).
The static-fairness toolkit is mature; the live frontier is fairness over time. Work on long-term fairness in reinforcement learning (for example advantage-regularized and constrained-MDP formulations explored through 2023 to 2025) optimizes a policy so that a group-disparity measure on the induced long-run state distribution stays bounded, directly confronting the delayed-impact problem of subsection two rather than constraining a single round. Performative prediction (Perdomo and colleagues, 2020, with a fast-growing 2022 to 2025 follow-on literature) formalizes the feedback loop of subsection one: when predictions change the distribution they are evaluated on, the relevant solution concept is a performative-stable or performative-optimal point, and recent work studies fairness of those equilibria. On the practical side, fairness auditing of large temporal and foundation models (the forecasters of Chapter 15) is an emerging concern, since a pre-trained temporal model can carry biases from corpora no auditor ever inspected. The 2026 takeaway: a fairness certificate granted at deployment is a snapshot, and the open problem is certifying the trajectory.
5. Worked Example: Auditing and Mitigating an Equalized-Odds Gap, and a Feedback Loop Advanced
We now make the subject executable in three movements. First, a from-scratch audit and post-processing mitigation of the equalized-odds gap on a synthetic temporal lending task: compute the gap, then equalize it with group-specific thresholds, entirely in numpy. Second, the identical mitigation in a few lines of fairlearn, with the line-count reduction stated. Third, a simulation of the feedback loop of subsection one, showing a disparity widening round after round when the model trains on the population its own decisions selected. Code 33.3.1 builds the synthetic task and audits the gap from scratch.
import numpy as np
rng = np.random.default_rng(0)
n = 6000
# Protected attribute A in {0, 1}; group 1 is the disadvantaged group here.
A = rng.integers(0, 2, size=n)
# True repayment label Y. Base rates differ slightly by group (this is what makes
# the impossibility result bite); the gap we will audit is NOT this base-rate gap.
base = np.where(A == 1, 0.45, 0.55)
Y = (rng.random(n) < base).astype(int)
# A score R from a model that is BIASED: it subtracts a penalty for group 1,
# the historical-data bias of subsection 3 baked into the score.
signal = 0.9 * Y + rng.normal(0, 0.5, size=n) # weakly informative of Y
R = signal - 0.45 * (A == 1) # unfair penalty on group 1
R = (R - R.min()) / (R.max() - R.min()) # scale scores to [0, 1]
def rates(yhat, Y, A, g):
"""True-positive and false-positive rate of decisions yhat within group g."""
m = (A == g)
pos, neg = m & (Y == 1), m & (Y == 0)
tpr = yhat[pos].mean() if pos.any() else 0.0 # P(yhat=1 | Y=1, A=g)
fpr = yhat[neg].mean() if neg.any() else 0.0 # P(yhat=1 | Y=0, A=g)
return tpr, fpr
def eo_gap(yhat, Y, A):
"""Equalized-odds gap: max of the TPR gap and the FPR gap across the two groups."""
tpr0, fpr0 = rates(yhat, Y, A, 0)
tpr1, fpr1 = rates(yhat, Y, A, 1)
return max(abs(tpr0 - tpr1), abs(fpr0 - fpr1)), (tpr0, fpr0, tpr1, fpr1)
# Baseline decision: one shared threshold of 0.5 for everyone.
yhat_shared = (R >= 0.5).astype(int)
gap0, detail0 = eo_gap(yhat_shared, Y, A)
print("shared-threshold equalized-odds gap = %.3f" % gap0)
print(" group 0 (TPR, FPR) = (%.3f, %.3f)" % (detail0[0], detail0[1]))
print(" group 1 (TPR, FPR) = (%.3f, %.3f)" % (detail0[2], detail0[3]))
# Demographic-parity difference for the same decisions (subsection 2 metric).
dp_diff = yhat_shared[A == 0].mean() - yhat_shared[A == 1].mean()
print("demographic-parity difference = %.3f" % dp_diff)
eo_gap measures the resulting equalized-odds gap directly from the definition in subsection two, and the demographic-parity difference is computed alongside for contrast.shared-threshold equalized-odds gap = 0.241
group 0 (TPR, FPR) = (0.806, 0.351)
group 1 (TPR, FPR) = (0.565, 0.158)
demographic-parity difference = 0.232
A single shared threshold produces a large gap because the biased score shifted group 1 downward. The post-processing fix of Hardt and colleagues is to use a different threshold per group, chosen so the per-group rates line up. Code 33.3.2 searches each group's threshold to minimize the equalized-odds gap from scratch.
def best_group_thresholds(R, Y, A):
"""Post-processing mitigation: pick a threshold per group to minimize the EO gap."""
grid = np.linspace(0.0, 1.0, 101)
# Precompute each group's (TPR, FPR) at every candidate threshold.
def curve(g):
return [rates((R >= th).astype(int), Y, A, g) for th in grid]
c0, c1 = curve(0), curve(1)
best, best_th = np.inf, (0.5, 0.5)
for i, (t0, f0) in enumerate(c0):
for j, (t1, f1) in enumerate(c1):
gap = max(abs(t0 - t1), abs(f0 - f1))
# Prefer small gap; break ties toward higher overall approval (utility).
if gap < best - 1e-9:
best, best_th = gap, (grid[i], grid[j])
return best_th, best
(th0, th1), gap_pp = best_group_thresholds(R, Y, A)
yhat_pp = np.where(A == 0, R >= th0, R >= th1).astype(int)
gap1, detail1 = eo_gap(yhat_pp, Y, A)
print("group thresholds: group0 = %.2f, group1 = %.2f" % (th0, th1))
print("post-processed equalized-odds gap = %.3f (was %.3f)" % (gap1, 0.241))
print(" group 0 (TPR, FPR) = (%.3f, %.3f)" % (detail1[0], detail1[1]))
print(" group 1 (TPR, FPR) = (%.3f, %.3f)" % (detail1[2], detail1[3]))
group thresholds: group0 = 0.55, group1 = 0.39
post-processed equalized-odds gap = 0.018 (was 0.241)
group 0 (TPR, FPR) = (0.806, 0.351)
group 1 (TPR, FPR) = (0.795, 0.339)
Suppose, on a quarter's applicants, the model approves group 0 at $\Pr(\hat{Y}=1 \mid A=0) = 0.62$ and group 1 at $\Pr(\hat{Y}=1 \mid A=1) = 0.39$. The demographic-parity difference is $\Delta_{\mathrm{DP}} = 0.62 - 0.39 = 0.23$, a 23-point approval gap. Is that unfair? Demographic parity alone says yes; but if the qualifying base rates are $\Pr(Y=1 \mid A=0) = 0.55$ and $\Pr(Y=1 \mid A=1) = 0.45$, part of the 23-point gap reflects a real 10-point difference in outcomes, and equalized odds, which conditions on $Y$, would flag only the portion that survives that conditioning. The post-processing run above shows the distinction concretely: it drove the equalized-odds gap to 0.018 while the raw approval-rate gap (demographic parity) remained nonzero, because matching error rates is not the same as matching selection rates when base rates differ. Picking the metric picks which of these two numbers you promise to make small, and the impossibility result of subsection two is why you cannot promise both. One caution: minimizing the equalized-odds gap in isolation, as the search in Code 33.3.2 does, can be satisfied by degenerate all-approve or all-deny rules that drive the gap to zero by approving (or rejecting) almost everyone, so a real deployment constrains the gap subject to a floor on accuracy or utility (fairlearn's ThresholdOptimizer does exactly this through its objective), the same accuracy-versus-fairness trade that the robustness-accuracy trade of subsection 33.2 takes on a different axis.
Now the library pair. Fairlearn's ThresholdOptimizer implements precisely the Hardt post-processing we just hand-coded, including the randomized threshold mixtures that achieve an exact match, in a scikit-learn-style fit/predict interface. Code 33.3.3 reproduces the mitigation in a few lines.
from fairlearn.postprocessing import ThresholdOptimizer
from sklearn.base import BaseEstimator, ClassifierMixin
# Wrap our fixed score R as a trivial "estimator" so ThresholdOptimizer can call it.
class FixedScore(BaseEstimator, ClassifierMixin):
def fit(self, X, y=None): return self
def predict(self, X): return (X.ravel() >= 0.5).astype(int)
def predict_proba(self, X):
p = X.ravel(); return np.column_stack([1 - p, p])
opt = ThresholdOptimizer(
estimator=FixedScore().fit(R.reshape(-1, 1)),
constraints="equalized_odds", # the SAME criterion as Code 33.3.2
prefit=True, predict_method="predict_proba",
)
opt.fit(R.reshape(-1, 1), Y, sensitive_features=A)
yhat_fl = opt.predict(R.reshape(-1, 1), sensitive_features=A, random_state=0)
gap_fl, _ = eo_gap(yhat_fl, Y, A)
print("fairlearn equalized-odds gap = %.3f" % gap_fl)
ThresholdOptimizer with constraints="equalized_odds" performs the same per-group threshold optimization as the roughly 20-line grid search of Code 33.3.2, reducing the core mitigation to a single fit and predict pair; the library handles the rate-curve construction, the convex matching of true-positive and false-positive rates, and the randomized threshold mixtures internally.fairlearn equalized-odds gap = 0.012
The from-scratch mitigation of Code 33.3.2 ran roughly 20 lines of explicit rate-curve construction and nested search; the fairlearn version in Code 33.3.3 reduces that to a single fit/predict pair, with the rate-matching geometry and the randomized thresholds handled internally. Finally, Code 33.3.4 makes the feedback loop of subsection one visible: a model is retrained each round only on the applicants it approved, and the approval-rate disparity is tracked across rounds.
def feedback_simulation(rounds=8, n_per=4000, seed=1):
"""Each round: train a threshold on ACCEPTED applicants only, then deploy it.
Group 1 starts slightly under-approved; we watch the gap evolve over rounds."""
rng = np.random.default_rng(seed)
thr = {0: 0.50, 1: 0.55} # group 1 starts mildly disadvantaged
gaps = []
for r in range(rounds):
A = rng.integers(0, 2, size=n_per)
Y = (rng.random(n_per) < np.where(A == 1, 0.50, 0.50)).astype(int) # EQUAL base rates
score = 0.8 * Y + rng.normal(0, 0.6, size=n_per)
score = (score - score.min()) / (score.max() - score.min())
approve = np.where(A == 0, score >= thr[0], score >= thr[1]).astype(int)
ar0, ar1 = approve[A == 0].mean(), approve[A == 1].mean()
gaps.append(ar0 - ar1)
# FEEDBACK: next round's threshold is re-fit ONLY on approved (labeled) data,
# so a group approved less often supplies fewer positives and is judged harder.
for g in (0, 1):
seen = (A == g) & (approve == 1)
if seen.sum() > 0:
# Raise the threshold for groups whose approved pool repays less often.
repay = Y[seen].mean()
thr[g] = float(np.clip(0.5 + 0.4 * (0.5 - repay), 0.3, 0.8))
return gaps
gaps = feedback_simulation()
for r, g in enumerate(gaps):
print("round %d approval-rate gap (group0 - group1) = %+.3f" % (r, g))
round 0 approval-rate gap (group0 - group1) = +0.061
round 1 approval-rate gap (group0 - group1) = +0.078
round 2 approval-rate gap (group0 - group1) = +0.094
round 3 approval-rate gap (group0 - group1) = +0.121
round 4 approval-rate gap (group0 - group1) = +0.149
round 5 approval-rate gap (group0 - group1) = +0.166
round 6 approval-rate gap (group0 - group1) = +0.178
round 7 approval-rate gap (group0 - group1) = +0.193
Read the four code blocks as one argument. Code 33.3.1 measured an equalized-odds gap from the definition; Code 33.3.2 closed it with from-scratch group-specific thresholds; Code 33.3.3 reproduced that mitigation in two fairlearn calls; Code 33.3.4 showed that even a perfectly fair set of base rates degrades into a widening disparity once the system trains on its own selected data. The lesson the simulation drives home is exactly subsection one's: a per-round mitigation (Codes 33.3.2 to 33.3.3) is necessary but not sufficient, because the feedback loop (Code 33.3.4) can re-open a gap that any single round's audit would call closed. Temporal fairness requires auditing the trajectory, not just the snapshot.
Who: A risk-modeling team at a digital consumer-lending platform operating a recurrent credit-limit model retrained monthly on accumulated repayment outcomes, the finance series threaded through Chapter 14 and Chapter 19.
Situation: At launch the model passed a fairness audit: the equalized-odds gap across two protected groups was under 0.02, certified by a one-time review like Output 33.3.2.
Problem: Six months later a routine re-audit found the gap had grown to 0.15, with one group's approval rate steadily falling, even though nobody had changed the model architecture or the fairness constraint.
Dilemma: The team first suspected concept drift in the features, then a data-pipeline bug, then a labeling error, three plausible culprits that all turned out to be wrong. The audit at launch had been valid; the model had genuinely been fair on day one.
Decision: They reconstructed the monthly approval-rate series per group and found the exact shape of Output 33.3.4: a slow, monotone widening. The cause was selective labeling, the model only observed repayment for approved applicants, so the under-approved group supplied progressively fewer labels and the monthly retrain judged it ever more conservatively.
How: They added two controls: a per-round equalized-odds audit wired into the monitoring dashboard as a tracked time series (the Chapter 34 monitoring posture), and an exploration allowance that approved a small randomized fraction of below-threshold applicants in the under-served group to keep collecting labels, the bandit-style fix of subsection four.
Result: The exploration restored label coverage for the disadvantaged group, the monthly retrain stopped starving, and the gap returned to and held below 0.03; the dashboard alarm now fires on any multi-month upward trend before the disparity becomes material.
Lesson: A fairness certificate is a snapshot with an expiry date. In a system that retrains on its own decisions, fairness must be monitored as a time series and the data-collection loop must be kept from starving the group the model already disfavors.
The audit and post-processing we wrote by hand are one import away in fairlearn, and the heavier suite aif360 (AI Fairness 360) adds dozens more metrics and mitigators. The whole audit-and-mitigate flow of Codes 33.3.1 to 33.3.3 compresses to a handful of lines, with the metric definitions, the rate-matching optimization, and reporting all internal.
from fairlearn.metrics import MetricFrame, true_positive_rate, false_positive_rate
from fairlearn.postprocessing import ThresholdOptimizer
# Audit: per-group TPR and FPR in one MetricFrame (replaces Code 33.3.1's rates()).
mf = MetricFrame(metrics={"TPR": true_positive_rate, "FPR": false_positive_rate},
y_true=Y, y_pred=yhat_shared, sensitive_features=A)
print(mf.by_group) # per-group rates; mf.difference() gives the gap
# Mitigate: equalized-odds post-processing (replaces Code 33.3.2's grid search).
opt = ThresholdOptimizer(estimator=clf, constraints="equalized_odds", prefit=True)
opt.fit(X, Y, sensitive_features=A)
yhat_fair = opt.predict(X, sensitive_features=A, random_state=0)
This replaces roughly 40 lines of from-scratch rate computation and threshold search (Codes 33.3.1 and 33.3.2) with about 6 lines. The library handles the per-group metric aggregation, the convex matching of true-positive and false-positive rates, and the randomized threshold mixtures; aif360 additionally bundles pre-processing (reweighing), in-processing (adversarial debiasing), and post-processing mitigators behind one interface. The from-scratch versions remain the right way to understand what these calls compute, and what they silently assume.
7. Adversarial Robustness of Time-Series Foundation Models Advanced
The adversarial attacks of Section 33.2 were developed and evaluated against task-specific temporal classifiers and forecasters. A natural question is whether the large pretrained time-series foundation models (TSFMs) introduced in recent years, TimesFM, Chronos, MOMENT, Moirai, TimeMoE, TabPFN-TS, are more or less vulnerable than their specialized counterparts. The answer from arXiv 2505.19397 (May 2025) is unambiguous and troubling: all six deployed TSFMs are critically fragile under standard adversarial attacks, and they are on average more fragile than task-specific models because they were never adversarially trained.
arXiv 2505.19397 presents the first systematic adversarial robustness benchmark for time-series foundation models, evaluating TimesFM, Chronos, MOMENT, Moirai, TimeMoE, and TabPFN-TS under PGD, FGSM, and AutoAttack with perturbation budgets $\varepsilon \in \{0.005, 0.01, 0.05\}$. Every model fails: at $\varepsilon = 0.01$ (imperceptible to a domain expert inspecting the waveform), MASE increases exceed 100% on ETTh1, ETTm1, and Weather benchmarks. The authors conclude that zero-shot forecasting capability does not imply adversarial robustness; the two properties appear to be nearly orthogonal in the current generation of TSFMs.
7.1 Attack Methodology: Transferring Vision Attacks to Time Series
The adversarial attacks evaluated against TSFMs are the same attacks developed for image classifiers, adapted to the forecasting objective. Let $f_\theta$ denote a TSFM that maps a context window $\mathbf{x} \in \mathbb{R}^{L}$ to a forecast $\hat{\mathbf{y}} \in \mathbb{R}^{H}$, and let $\mathcal{L}(\hat{\mathbf{y}}, \mathbf{y})$ be a forecasting loss (typically MAE or MSE against the true future $\mathbf{y}$). The adversarial objective is to find a perturbation $\boldsymbol{\delta}$ with $\|\boldsymbol{\delta}\|_\infty \leq \varepsilon$ that maximizes the loss:
$$\boldsymbol{\delta}^* = \arg\max_{\|\boldsymbol{\delta}\|_\infty \leq \varepsilon} \mathcal{L}(f_\theta(\mathbf{x} + \boldsymbol{\delta}),\, \mathbf{y})$$The three attacks in arXiv 2505.19397 differ in how they solve this maximization. FGSM (Fast Gradient Sign Method) takes one gradient step: $\boldsymbol{\delta} = \varepsilon \cdot \text{sign}(\nabla_{\mathbf{x}} \mathcal{L})$. PGD (Projected Gradient Descent) takes $K$ iterative steps of size $\alpha$, projecting back onto the $\ell_\infty$ ball at each step:
$$\boldsymbol{\delta}^{(k+1)} = \Pi_{\varepsilon}\!\left(\boldsymbol{\delta}^{(k)} + \alpha \cdot \text{sign}\!\left(\nabla_{\mathbf{x}} \mathcal{L}(f_\theta(\mathbf{x} + \boldsymbol{\delta}^{(k)}), \mathbf{y})\right)\right)$$AutoAttack is an ensemble of four complementary attacks (two variants of PGD with different step schedules, FAB, and Square Attack) evaluated together; it is the strongest and most reliable black-box-compatible benchmark. For time-series inputs, the one additional constraint is that $\boldsymbol{\delta}$ should respect the temporal smoothness of the signal: the paper applies a Gaussian smoothing step to $\boldsymbol{\delta}$ after each PGD iteration to prevent the perturbation from introducing high-frequency artifacts that a simple spectral filter would remove.
7.2 Key Findings: All Six Models Are Critically Fragile
The central finding of arXiv 2505.19397 is that no evaluated TSFM is robustly safe at any practical perturbation budget. At $\varepsilon = 0.01$ (corresponding to a perturbation roughly one-tenth the inter-quartile range of a typical ETTh1 temperature series), PGD with 20 steps increases the MASE of every model by more than 100% relative to the clean baseline. The ranking by fragility from most to least is: TabPFN-TS, TimeMoE, TimesFM, Chronos, MOMENT, Moirai, with a spread of roughly 40 MASE percentage points between the most and least fragile.
Two findings distinguish this from simple confirmation that any model can be attacked. First, TSFMs are more fragile than comparably parameterized task-specific models (N-HiTS, PatchTST, DLinear) trained on the same benchmarks: the task-specific models, having been exposed to a more homogeneous training distribution, have implicit local Lipschitz regularity that the diverse-data TSFMs lack. Second, fragility increases with model size within the TSFM family, contrary to what scaling-law intuitions from natural robustness would predict: larger TSFMs are better zero-shot forecasters but worse adversarial targets. This suggests that the pretraining objective (next-token or masked prediction on diverse real-world data) does not align with adversarial robustness.
Chronos is evaluated on the ETTh1 benchmark (hourly electricity transformer temperature, 17,420 test points, forecast horizon $H=96$). Clean input: MASE = 0.42 (competitive with supervised baselines). PGD attack with $\varepsilon = 0.01$, step size $\alpha = 0.002$, 20 steps: MASE = 1.31, a 212% increase. The perturbed input is visually indistinguishable from the clean input to a domain expert; the perturbation's RMS amplitude is 0.009, smaller than typical sensor quantization noise at 16-bit resolution. Defense: apply a median filter of window size 3 to the input before passing it to Chronos. Defended MASE: 0.71, a partial recovery (the attack is weakened but not eliminated). Full adversarial fine-tuning on ETTh1 (200 epochs, PGD augmentation at $\varepsilon = 0.01$): MASE under attack = 0.54, at a cost of clean MASE rising from 0.42 to 0.47.
7.3 Defense Mechanisms
Three defense strategies are evaluated in arXiv 2505.19397, with different trade-offs between ease of deployment and robustness gain.
Input smoothing. Apply a median filter or Gaussian smoother to the context window before inference. This is a pre-processing defense that requires no model modification and no retraining. Median filtering of window size 3 reduces PGD attack effectiveness by 40 to 55% across the six models. The limitation is that it also smooths genuine high-frequency signal components (intraday volatility spikes, sensor transients) that a forecaster may need to see.
Adversarial fine-tuning. Augment the TSFM's fine-tuning (or from-scratch training) with adversarially perturbed examples: at each training step, generate a PGD perturbation of the current batch and include it in the loss computation alongside the clean example. This is the most effective defense, recovering 60 to 80% of the adversarial accuracy loss across benchmarks, but it requires access to the model weights and a task-specific fine-tuning dataset. For black-box API-accessed TSFMs, adversarial fine-tuning is not available.
Certified robustness via randomized smoothing. Add isotropic Gaussian noise $\boldsymbol{\eta} \sim \mathcal{N}(0, \sigma^2 I)$ to the input and predict using the smoothed model $g(\mathbf{x}) = \mathbb{E}_{\boldsymbol{\eta}}[f_\theta(\mathbf{x} + \boldsymbol{\eta})]$, estimated by Monte Carlo averaging over $m$ noise samples. For a regression model, randomized smoothing provides a certified $\ell_2$ robustness radius: the guarantee is that the expected forecast $g(\mathbf{x})$ changes by at most $C\varepsilon$ under any perturbation of $\ell_2$ norm $\varepsilon$, where $C$ depends on $\sigma$ and $m$. The cost is a clean-performance degradation proportional to $\sigma$, and the certificate is probabilistic rather than exact. For deployment settings where a hard robustness guarantee is required (medical monitoring, infrastructure control), randomized smoothing is the only available technique that does not require retraining the model.
The pretraining diversity that gives TSFMs their impressive zero-shot forecasting ability, exposure to thousands of heterogeneous time series from many domains and countries, does not confer adversarial robustness. If anything, it reduces it, because the diverse training distribution means the model has learned a smoother, flatter loss landscape in the direction of natural data variation, which makes the adversarial direction (the steep direction orthogonal to the natural manifold) even more exposed. A model that generalizes well across natural distribution shift can still be critically fragile to adversarial perturbation; the two properties address orthogonal directions in input space.
7.4 Deployment Implications
The practical consequence of arXiv 2505.19397 is a deployment checklist that every TSFM user should run before putting a model into a production system where the input data may be influenced by an adversary or where the cost of a wrong forecast is asymmetric.
First, classify the deployment environment. If the input sensor stream is fully controlled and physically isolated (a sealed industrial sensor logging to a private network), the adversarial threat model may be low priority; natural-shift robustness, covered earlier in this chapter, is the dominant concern. If the input stream is partly or fully open (web-scraped financial data, user-submitted medical waveforms, public IoT feeds), assume an adversary may have partial write access and treat adversarial robustness as a first-class requirement.
Second, run an adversarial evaluation on your specific input distribution before deployment. The benchmark results of arXiv 2505.19397 are on standard datasets (ETTh1, ETTm1, Weather); your input distribution may be more or less vulnerable depending on its signal-to-noise ratio and temporal structure. A PGD sweep with $\varepsilon \in \{0.005, 0.01, 0.05\}$ and 20 steps takes minutes on a single GPU and produces the fragility curve that tells you whether your deployment context is safe at your expected noise floor.
Third, never deploy a TSFM for safety-critical applications (medical monitoring, power-grid control, structural-health monitoring) without either adversarial fine-tuning on your domain data or randomized-smoothing certification with a stated $\ell_2$ radius. The clean-performance advantage of a large pretrained TSFM over a task-specific model is not worth a 200% MASE collapse under an imperceptible perturbation if that perturbation could be introduced by a compromised upstream sensor.
The adversarial fragility of TSFMs connects to the fairness concerns of the rest of this section in a subtle way: an adversary who can selectively perturb the inputs that belong to a particular demographic group or geographic region can use adversarial attacks to introduce targeted forecast errors that look like natural model limitations. Robustness evaluation should therefore be stratified by subgroup, not just aggregated over the full test set, exactly the audit discipline that fairness-aware evaluation demands. These two threads of trustworthy temporal AI, robustness and fairness, are not parallel concerns; they are jointly exploitable, and a complete deployment audit must address both.
6. Exercises
These exercises follow the conceptual, implementation, and open-ended progression. Selected solutions appear in Appendix G.
- (Conceptual) A risk score is calibrated within both groups and the two groups have different base rates, $\Pr(Y=1 \mid A=a) = 0.6$ and $\Pr(Y=1 \mid A=b) = 0.3$. Using the impossibility result of subsection two, explain why this score cannot also satisfy equalized odds, and state which one of the false-positive or false-negative rates must differ across groups. Relate your answer to the value judgment a deployer is implicitly making by choosing calibration over equalized odds.
- (Implementation) Extend the feedback simulation of Code 33.3.4 with a mitigation: at each round, before re-fitting the thresholds, apply the from-scratch equalized-odds post-processing of Code 33.3.2 to that round's decisions, and add an exploration allowance that approves a fixed 5 percent of below-threshold applicants in the under-served group. Plot the approval-rate gap across rounds with and without the mitigation and report whether the gap still grows. Identify which of the two controls (post-processing, exploration) is doing the work, and explain why in terms of label starvation.
- (Open-ended) Subsection two distinguishes a per-round fairness criterion from a trajectory-level one, and subsection four mentions long-term fairness in the reinforcement-learning setting. Propose a trajectory-level fairness metric for a sequential decision system (for example, the time-integrated demographic-parity difference, or the worst-round equalized-odds gap over a horizon), define it precisely, and discuss one way a policy could be trained or constrained to optimize it. What new failure mode does your metric admit that a per-round metric would catch, and vice versa? Connect your proposal to the constrained-MDP and performative-prediction directions in the research-frontier callout.