Módulo 2: Bias and Fairness — Detection, Measurement, Mitigation
3. Fairness Metrics: Equalized Odds y Calibration
Descripción de la cápsula
Demographic parity (cápsula 02) compara rates de outputs. Pero esa métrica sola tiene gaps: puede ser perfecta mientras los errores del modelo difieren por grupo, o mientras la confianza del modelo significa cosas distintas para cada grupo.
Esta cápsula cubre dos métricas que cubren esos gaps:
- Equalized Odds — iguales error rates (false positives + false negatives) por grupo.
- Calibration — el score predicho debe corresponder a la probabilidad real del outcome, igualmente entre grupos.
Cada una mide algo distinto. Juntas con demographic parity, te dan un panorama mucho más completo. Pero (spoiler de cápsula 05) no podés satisfacer las tres simultáneamente excepto en casos triviales — esa es la razón de los impossibility theorems.
Al terminar la cápsula vas a saber: cuándo cada métrica es la apropiada, cómo implementarla, y cómo comunicar los trade-offs.
Equalized Odds
Definición formal
Equalized odds requiere que los errores del modelo sean igualmente distribuidos por grupo:
P(Ŷ = 1 | Y = y, A = a) = P(Ŷ = 1 | Y = y, A = b) ∀ a, b ∈ A, ∀ y ∈ {0, 1}
Donde:
Ŷ= predicción del modelo.Y= ground truth (label real).A= atributo protegido.
En palabras: condicional al ground truth, la probabilidad de predicción positiva debe ser igual para todos los grupos.
Equivalentemente, requiere igualdad en:
- True Positive Rate (TPR) = sensitivity = recall = P(Ŷ=1 | Y=1).
- False Positive Rate (FPR) = P(Ŷ=1 | Y=0).
Ambas tienen que ser iguales entre grupos.
Diferencia con demographic parity
Demographic parity NO usa Y (ground truth). Compara solo predictions.
Equalized odds SÍ usa Y. Compara error rates dado el ground truth real.
Ejemplo simple:
Loan approvals para 100 applicants por grupo:
Grupo A: 50 que defaultarían (Y=0), 50 que no (Y=1)
Grupo B: 80 que defaultarían (Y=0), 20 que no (Y=1)
Modelo aprueba:
Grupo A: 40 (de los 50 Y=1) + 10 (de los 50 Y=0) = 50 approved
Grupo B: 18 (de los 20 Y=1) + 30 (de los 80 Y=0) = 48 approved
Demographic parity: 50/100 = 50%, 48/100 = 48%. Ratio ~ 0.96. Casi perfecta paridad.
TPR (recall):
Grupo A: 40/50 = 0.80
Grupo B: 18/20 = 0.90
Diferencia: 10 puntos.
FPR:
Grupo A: 10/50 = 0.20
Grupo B: 30/80 = 0.375
Diferencia: 17 puntos.
Demographic parity sale bien, pero el modelo es notablemente peor en grupo B (más false positives). Equalized odds detecta esto.
Implementación
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix
def equalized_odds(predictions, ground_truth, groups, favorable_outcome=1):
"""
Calcula equalized odds metrics por grupo.
Args:
predictions: predictions del modelo.
ground_truth: labels verdaderos.
groups: atributo protegido.
favorable_outcome: valor considerado favorable.
Returns:
dict con TPR, FPR per grupo y diferencias.
"""
df = pd.DataFrame({
'pred': predictions,
'true': ground_truth,
'group': groups,
})
metrics_per_group = {}
for group_name, group_df in df.groupby('group'):
y_true = (group_df['true'] == favorable_outcome).astype(int)
y_pred = (group_df['pred'] == favorable_outcome).astype(int)
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
tpr = tp / (tp + fn) if (tp + fn) > 0 else 0
fpr = fp / (fp + tn) if (fp + tn) > 0 else 0
metrics_per_group[group_name] = {
'TPR': tpr,
'FPR': fpr,
'n': len(group_df),
'positives': int((y_true == 1).sum()),
'negatives': int((y_true == 0).sum()),
}
# Diferencia entre worst/best
tprs = [m['TPR'] for m in metrics_per_group.values()]
fprs = [m['FPR'] for m in metrics_per_group.values()]
return {
'metrics_per_group': metrics_per_group,
'tpr_difference': max(tprs) - min(tprs),
'fpr_difference': max(fprs) - min(fprs),
'meets_threshold_5pct': (
max(tprs) - min(tprs) < 0.05 and
max(fprs) - min(fprs) < 0.05
),
}
Threshold típico
Para deploy production:
- TPR difference < 5% entre grupos.
- FPR difference < 5% entre grupos.
Estos son thresholds conservadores. Algunos contextos toleran más (10-15%), otros menos (<2% para healthcare). Decisión informada según context.
Calibration
Definición formal
Calibration requiere que el score predicho corresponda a la probabilidad real del outcome, igualmente entre grupos:
P(Y = 1 | S = s, A = a) = s = P(Y = 1 | S = s, A = b) ∀ a, b, ∀ s ∈ [0, 1]
Donde:
S= score predicho por el modelo (probabilidad).Y= ground truth.A= atributo protegido.
En palabras: si el modelo predice "score = 0.7", entonces el outcome real debe ocurrir 70% de las veces, independientemente del grupo.
Por qué importa
Sin calibration, el mismo score significa cosas distintas según el grupo:
- "Score = 0.7" para grupo A → realmente outcome ocurre 70% de las veces.
- "Score = 0.7" para grupo B → realmente outcome ocurre 50% de las veces.
Si usás el score como base para decisión (ej., approve si score > 0.6), estás aplicando estándar más estricto a grupo B.
Es problema sutil pero real. Frecuente en credit scoring, healthcare risk prediction, y sistemas donde "score" influye decisión.
Implementación
Calibration testing requiere agrupar predictions por bins de score, y comparar el "actual rate" contra el score:
def calibration_by_group(scores, ground_truth, groups, n_bins=10):
"""
Mide calibration por grupo, agrupando scores en bins.
"""
df = pd.DataFrame({
'score': scores,
'true': ground_truth,
'group': groups,
})
df['bin'] = pd.cut(df['score'], bins=n_bins)
calibration_per_group = {}
for group_name, group_df in df.groupby('group'):
bin_stats = group_df.groupby('bin').agg(
mean_score=('score', 'mean'),
actual_rate=('true', 'mean'),
n=('score', 'count'),
)
# Calibration error: difference entre mean_score y actual_rate por bin
bin_stats['error'] = (bin_stats['actual_rate'] - bin_stats['mean_score']).abs()
# Expected Calibration Error (ECE): weighted average
ece = (bin_stats['error'] * bin_stats['n']).sum() / bin_stats['n'].sum()
calibration_per_group[group_name] = {
'ECE': ece,
'bins': bin_stats.reset_index().to_dict('records'),
}
eces = [m['ECE'] for m in calibration_per_group.values()]
return {
'calibration_per_group': calibration_per_group,
'ece_difference': max(eces) - min(eces),
'meets_threshold_2pct': max(eces) - min(eces) < 0.02,
}
Visualización: calibration plot
Un calibration plot ayuda a ver visualmente:
import matplotlib.pyplot as plt
def plot_calibration(scores, ground_truth, groups, n_bins=10):
"""Calibration plot con líneas por grupo."""
df = pd.DataFrame({
'score': scores,
'true': ground_truth,
'group': groups,
})
df['bin'] = pd.cut(df['score'], bins=n_bins)
fig, ax = plt.subplots(figsize=(8, 6))
# Línea diagonal = perfect calibration
ax.plot([0, 1], [0, 1], 'k--', label='Perfect calibration')
for group_name, group_df in df.groupby('group'):
stats = group_df.groupby('bin').agg(
mean_score=('score', 'mean'),
actual_rate=('true', 'mean'),
)
ax.plot(stats['mean_score'], stats['actual_rate'],
marker='o', label=f'Group {group_name}')
ax.set_xlabel('Predicted score')
ax.set_ylabel('Actual rate')
ax.set_title('Calibration by group')
ax.legend()
ax.grid(True)
return fig
Si las líneas de cada grupo divergen significativamente del diagonal, hay miscalibration. Si una línea está siempre arriba o abajo de otra, el score significa cosas distintas por grupo.
Cuándo equalized odds vs calibration vs demographic parity
Cuándo equalized odds
Apropiado cuando:
- Hay ground truth confiable: podés verificar quién "merecía" el outcome.
- Errores tienen impacto importante y deben distribuirse equitativamente.
- Base rates difieren entre grupos y querés error parity, no rate parity.
Ejemplos: loans (default es ground truth verificable), medical diagnosis, criminal recidivism.
Cuándo calibration
Apropiado cuando:
- El score se usa directamente en decisiones (no solo binary classification).
- Múltiples downstream decisions dependen del score.
- Stakeholders interpretan score como probabilidad.
Ejemplos: credit scoring (interest rate scaled by score), insurance pricing (premium based on risk score), healthcare risk stratification.
Cuándo demographic parity
Apropiado cuando:
- No hay ground truth confiable de merit.
- Policy/legal requiere paridad explícitamente.
- Marketing/exposure decisions.
Ejemplos: ad serving, content recommendation, affirmative action programs.
Tabla resumen
| Métrica | Compara | Requiere ground truth | Cuándo aplicar |
|---|---|---|---|
| Demographic parity | Rates de outputs | No | Sin ground truth, policy parity |
| Equalized odds | Error rates (TPR, FPR) | Sí | Errores deben ser equitativos |
| Calibration | Score-to-actual mapping | Sí | Score usado directamente en decisiones |
El conflict entre métricas
A continuación vas a aprender sobre impossibility theorems (cápsula 05), pero ya podemos ver el conflicto:
Ejemplo: COMPAS (criminal recidivism prediction)
ProPublica (2016) analizó COMPAS, usado en cortes US para predecir recidivism:
- Score 1-10. Prediction de re-arresto.
- ProPublica midió equalized odds: encontró que los black defendants tenían FPR mucho más alto que white defendants. Conclusión: discriminación.
- Northpointe (vendor) respondió midiendo calibration: el modelo estaba bien calibrado para ambos grupos. Score = 0.7 → 70% recidivism para black, 70% para white. Conclusión: no discriminación.
¿Quién tenía razón? Ambos. El modelo satisfacía calibration pero violaba equalized odds. Por los impossibility theorems, no se puede tener ambas.
La conclusión correcta no es "estaba bien" o "estaba mal" — es "el modelo tiene este trade-off; el sistema legal debe decidir conscientemente cuál priorizar". ProPublica argued que en criminal justice, equalized odds importa más (false positives causan encarcelamiento injusto). Northpointe argued que calibration importa más (score consistente).
Decision making requiere elegir explícitamente. No hay metric universal "fairness".
Trampas y errores comunes
1. Reportar solo accuracy promedio
Demographic parity, equalized odds, calibration — todas requieren desglose por subgrupo. Accuracy global oculta gaps.
2. Confundir TPR difference con FPR difference
Equalized odds requiere AMBAS sean similares por grupo. Si solo igualás TPR pero FPR difiere, eso es equal opportunity (variant más débil), no equalized odds completo.
3. Ignorar sample size en bins de calibration
Si un bin tiene 5 samples, su "actual_rate" es muy noisy. Reportá ECE solo si bins tienen al menos 30 samples.
4. Pretender que las métricas se complementan sin tradeoff
Cuando un PM dice "satisfagamos todas las métricas", el answer correcto es "matemáticamente imposible (ver Chouldechova). Tenemos que priorizar."
5. Aplicar misma threshold a contextos distintos
5% TPR difference puede ser aceptable en advertising y inaceptable en healthcare. Threshold debe ser context-specific.
Auto-verificación
1. ¿Cuál es la diferencia conceptual entre demographic parity y equalized odds?
Demographic parity mide rates de outputs sin ground truth: P(Ŷ=1 | A=a) = P(Ŷ=1 | A=b).
Equalized odds mide rates de errores dados el ground truth: P(Ŷ=1 | Y=y, A=a) = P(Ŷ=1 | Y=y, A=b) ∀ y.
Implication:
- Demographic parity puede ignorar quién "merecía" el outcome.
- Equalized odds incorpora "merit" (ground truth) en la métrica.
Para sistemas con ground truth confiable y diferentes base rates entre grupos, equalized odds es típicamente más apropiado.
2. ¿Por qué calibration puede ser violada incluso cuando equalized odds se cumple?
Equalized odds compara TPR y FPR a un threshold específico (típicamente 0.5).
Calibration mira la distribución completa de scores: si "score = 0.7" significa lo mismo para cada grupo across todos los scores.
Podés tener:
- TPR y FPR igualados a threshold 0.5 (equalized odds ✅).
- Pero scores en 0.3-0.4 distribuidos distinto por grupo (calibration ❌).
Las dos métricas miden cosas distintas. Y por impossibility theorems (cápsula 05), no podés tener ambas en general.
3. ¿Cuándo elegirías equalized odds sobre demographic parity?
Cuando:
-
Tenés ground truth confiable que puede verificarse (defaults, medical outcomes, recidivism).
-
Las base rates difieren entre grupos y forzar demographic parity requeriría aprobar a casos marginales (causando harm tanto al individuo como al system).
-
Equity de errores importa más que equity de rates: querés que el modelo sea igualmente bueno (o malo) para cada grupo, no que apruebe igual porcentaje.
Caso típico: loan approvals donde grupos tienen diferentes default rates verificables. Demographic parity forzaría dar loans a casos high-risk, lo cual harm al borrower. Equalized odds asegura que dado el risk real, el modelo trata igual.
4. ¿Por qué COMPAS expone el conflicto entre métricas?
COMPAS satisfacía calibration (score consistente entre grupos) pero violaba equalized odds (false positives más altos para black defendants).
Por impossibility theorems, no podés tener ambas cuando los grupos tienen base rates distintas.
ProPublica vs Northpointe representan dos posiciones legítimas:
- ProPublica: "Equalized odds importa más en criminal justice — false positives = encarcelamiento injusto".
- Northpointe: "Calibration importa más — score consistente es transparency básica".
Ambas son matemáticamente válidas. La decisión es policy choice, no math.
Lección: cuando un modelo afecta high-stakes decisions, los stakeholders (no los engineers solos) deben decidir cuál métrica priorizar, basado en values y impact analysis. Engineering proporciona las opciones; la decisión es socio-técnica.
Resumen y siguiente paso
- Equalized odds mide error rates (TPR, FPR) por grupo. Apropiado cuando hay ground truth.
- Calibration mide si scores corresponden a probabilidades reales por grupo. Crítico cuando scores se usan directamente.
- Cada métrica captura algo distinto: demographic parity (rates), equalized odds (errors), calibration (scores).
- No podés satisfacer las tres simultaneamente (impossibility theorems, cápsula 05).
- COMPAS ejemplo: calibration pasaba, equalized odds fallaba — eso es decisión policy, no math.
Checkpoint: deberías poder calcular las tres métricas en código y elegir cuál es apropiada para un context dado.
Puente a la siguiente cápsula: la cápsula 04 cubre bias detection en la práctica — cómo aplicar estas métricas a tu sistema real. Vas a ver técnicas como slicing por subgrupo, A/B testing demográfico, counterfactual testing, y cómo integrar todo en CI/monitoring.
Recursos
- Equality of Opportunity (Hardt et al., 2016) — paper canónico de equalized odds.
- Inherent Trade-offs (Kleinberg et al., 2017) — impossibility theorems.
- ProPublica COMPAS analysis — case study.
- Reliability Diagrams (DeGroot & Fienberg, 1983) — calibration original paper.
Siguiente: 04-bias-detection-slicing.md — Bias detection en práctica: slicing, A/B, counterfactual.
Cápsula 03 de 08 — Módulo 2 — AI Ethics & Compliance Guide