¿Es tu modelo realmente mejor? Una guía en lenguaje sencillo sobre la significancia estadística
La última vez, Dev construyó un disparador de reentrenamiento para su recomendador — un sistema de doble señal que se dispara cuando el rendimiento del modelo cae por debajo de un umbral, o cuando la distancia de Wasserstein en las variables de entrada cruza un umbral de deriva. Una noche, la señal de deriva se activa. La distancia de Wasserstein en session_duration supera 2.0, el script nocturno registra “ALARMA: Deriva detectada”, y el pipeline de Airflow inicia un trabajo de reentrenamiento con datos nuevos. Por la mañana, Dev tiene un nuevo modelo candidato en su sistema de seguimiento de experimentos. El disparador le indicó cuándo reentrenar — pero no si el reentrenamiento funcionó. Así que ahora necesita saber, de manera rigurosa y estadística, si el candidato realmente supera al modelo actual antes de desplegarlo para diez millones de usuarios.
El problema del ‘acierto por suerte’
Dev tiene dos versiones de su recomendador para comparar. El Modelo A —el que está en producción— alcanza un 85% de precisión en el conjunto de holdout. El Modelo B, recientemente reentrenado, alcanza un 86%. Es comprensible el impulso de desplegar el Modelo B de inmediato. Pero, ¿es el Modelo B realmente más inteligente, o simplemente obtuvo una división favorable en el conjunto de prueba?
Este es el problema de la “Adivinación Afortunada”: error de muestreo, en términos de ciencia de datos. Probamos los modelos en una porción del mundo real, por lo que las puntuaciones que observamos son estimaciones. Si mezclas los datos y pruebas de nuevo, los números podrían invertirse.
Piénsalo como lanzar una moneda. Lánzala 10 veces, saca 6 caras —no concluirías que la moneda está trucada. Asumirías que esos 10 lanzamientos simplemente favorecieron a las caras por casualidad. La misma lógica aplica aquí: esa ventaja de 1% podría ser estructural, o podría ser ruido. Veamos qué sucede cuando examinamos la incertidumbre alrededor de estas puntuaciones.
import numpy as np
from sklearn.metrics import accuracy_score
from scipy import stats
# Let's simulate 1000 predictions for two models
# Model A is our baseline, Model B is slightly 'better'
np.random.seed(42)
y_true = np.random.randint(0, 2, 1000)
# Model A gets 850 right
pred_a = y_true.copy()
mask_a = np.random.choice([True, False], size=1000, p=[0.15, 0.85])
pred_a[mask_a] = 1 - pred_a[mask_a]
# Model B gets 865 right
pred_b = y_true.copy()
mask_b = np.random.choice([True, False], size=1000, p=[0.135, 0.865])
pred_b[mask_b] = 1 - pred_b[mask_b]
acc_a = accuracy_score(y_true, pred_a)
acc_b = accuracy_score(y_true, pred_b)
print(f"Model A Accuracy: {acc_a:.3f}")
print(f"Model B Accuracy: {acc_b:.3f}")
# Calculate a simple 95% Confidence Interval for Model A
stderr = np.sqrt((acc_a * (1 - acc_a)) / 1000)
ci_low, ci_high = acc_a - 1.96 * stderr, acc_a + 1.96 * stderr
print(f"Model A 95% CI: [{ci_low:.3f}, {ci_high:.3f}]")
Esta es la simulación que Dev ejecuta para poner barras de incertidumbre alrededor de las puntuaciones de precisión de sus dos recomendadores —la misma comparación del Modelo A (incumbente) frente al Modelo B (candidato reentrenado) que necesita hacer antes de enviar a producción.
np.random.seed(42)— bloquea el generador de números aleatorios para que la simulación sea reproducible. Sin esto, cada ejecución produciría datos sintéticos diferentes y números de precisión diferentes.y_true = np.random.randint(0, 2, 1000)— genera 1,000 etiquetas de verdad fundamental (ground truth), cada una aleatoriamente 0 o 1. En el pipeline real de Dev, estas serían las etiquetas del conjunto de holdout —¿el usuario hizo clic en el producto recomendado o no?pred_a = y_true.copy()— comienza las predicciones del Modelo A como una copia perfecta de la verdad fundamental. La siguiente línea inyectará errores para simular una precisión realista de ~85%.mask_a = np.random.choice([True, False], size=1000, p=[0.15, 0.85])— crea una máscara booleana donde ~15% de las entradas sonTrue. Estas son las filas donde el Modelo A cometerá un error en la predicción.pred_a[mask_a] = 1 - pred_a[mask_a]— invierte la predicción dondequiera que la máscara seaTrue. Si la etiqueta verdadera era 1, se convierte en 0, y viceversa. Esto simula un modelo que acierta ~85%.mask_b = np.random.choice([True, False], size=1000, p=[0.135, 0.865])— la máscara de error del Modelo B usa una tasa de error de 13.5%, simulando un modelo ligeramente mejor (~86.5% de precisión).acc_a = accuracy_score(y_true, pred_a)— calcula la precisión del Modelo A comparando las predicciones con la verdad fundamental. La fracción de predicciones correctas.stderr = np.sqrt((acc_a * (1 - acc_a)) / 1000)— el error estándar de una proporción. Para una métrica de precisión binomial, el error estándar es donde es la precisión y es el tamaño de la muestra. Esto te indica cuánto variaría la precisión si extrajeras un conjunto de prueba diferente del mismo tamaño.ci_low, ci_high = acc_a - 1.96 * stderr, acc_a + 1.96 * stderr— construye un intervalo de confianza del 95% usando la aproximación normal. El valor 1.96 es el z-score que captura el 95% central de una distribución normal estándar. La interpretación: si repitieras este experimento muchas veces con diferentes conjuntos de prueba, aproximadamente el 95% de esos intervalos contendrían la precisión verdadera del modelo.
Intervalo de confianza para una proporción (precisión del Modelo A):
Estadístico de la prueba de McNemar (con corrección de continuidad):
Donde es el recuento de filas donde el Modelo A es correcto y el Modelo B está equivocado, y es el recuento donde el Modelo B es correcto y el Modelo A está equivocado. El es la corrección de continuidad (corrección de Yates) que ajusta por la naturaleza discreta de los datos de conteo.
| Lenguaje sencillo | Símbolo estadístico | Equivalente en Python |
|---|---|---|
| Precisión observada del modelo | acc_a | |
| Número de muestras de prueba | 1000 / len(y_true) | |
| Error estándar de la precisión | np.sqrt((acc_a * (1 - acc_a)) / 1000) | |
| z-score del 95% (distribución normal) | 1.96 | |
| Límite inferior del IC | acc_a - 1.96 * stderr | |
| Límite superior del IC | acc_a + 1.96 * stderr | |
| A-correcto, B-equivocado (celda discordante) | tb[0, 1] | |
| B-correcto, A-equivocado (celda discordante) | tb[1, 0] | |
| Estadístico de la prueba de McNemar | chi2 de mcnemar(ary=tb, corrected=True) | |
| Valor p de la prueba de McNemar | p de mcnemar(ary=tb, corrected=True) |
la salida muestra que, si bien el Modelo B parece mejor, el rendimiento del Modelo A podría situarse en cualquier punto entre 82.8% y 87.2%. El 86.5% del Modelo B se encuentra justo dentro de ese rango, por lo que no podemos estar seguros de que sea realmente mejor. Es por esto que un único número es una base arriesgada para tomar decisiones de negocio.
Conoce la prueba de McNemar: el ‘desempate’ para clasificadores
Para saber si el Modelo B es realmente mejor, las puntuaciones finales no bastan. Necesitamos los desacuerdos.
Imagina un marcador de errores. Las filas donde ambos modelos aciertan no nos dicen nada sobre cuál es mejor — lo mismo ocurre con las filas donde ambos fallan. Las únicas filas que importan son aquellas donde el Modelo A acierta y el Modelo B falla, o viceversa.
Este es el Test de McNemar. Construye una ‘tabla de contingencia’ a partir de esos desacuerdos. Si el Modelo B le gana al Modelo A con la misma frecuencia con la que el Modelo A le gana al Modelo B, los modelos son esencialmente iguales. Una gran inclinación en una dirección señala un ganador.
from mlxtend.evaluate import mcnemar
from mlxtend.evaluate import contingency_table
# Create the table of disagreements
# Row 0, Col 0: Both correct
# Row 0, Col 1: A correct, B wrong
# Row 1, Col 0: B correct, A wrong
# Row 1, Col 1: Both wrong
tb = contingency_table(y_true, pred_a, pred_b)
print("Contingency Table:")
print(tb)
chi2, p = mcnemar(ary=tb, corrected=True)
print(f"p-value from McNemar: {p:.4f}")
Esta es la tabla de contingencia que Dev construye para sus dos recomendadores — el mismo Modelo A vs. Modelo B de la simulación de precisión anterior, ahora desglosada por en dónde discrepan en lugar de solo por puntuación agregada.
from mlxtend.evaluate import mcnemar— importa la implementación del test de McNemar desdemlxtend, una biblioteca complementaria de scikit-learn que proporciona utilidades de evaluación adicionales. Dev también podría implementar el test a mano (es un cálculo de chi-cuadrado de una sola línea), peromlxtendmaneja los casos extremos y las correcciones.from mlxtend.evaluate import contingency_table— importa el helper que construye la matriz de desacuerdos 2×2 a partir de las etiquetas ground-truth y las predicciones de los dos modelos.tb = contingency_table(y_true, pred_a, pred_b)— construye la tabla 2×2. Las cuatro celdas son:- Fila 0, Col 0 — ambos modelos correctos (acuerdo, ignorado por McNemar)
- Fila 0, Col 1 — Modelo A correcto, Modelo B incorrecto (el Modelo A “gana” esta fila)
- Fila 1, Col 0 — Modelo B correcto, Modelo A incorrecto (el Modelo B “gana” esta fila)
- Fila 1, Col 1 — ambos modelos incorrectos (acuerdo, ignorado por McNemar)
chi2, p = mcnemar(ary=tb, corrected=True)— ejecuta el test de McNemar con corrección de continuidad (corrección de Yates). La corrección resta 1 de antes de elevar al cuadrado, lo que evita que el test sea demasiado agresivo con tamaños de muestra pequeños. La salidachi2es el estadístico del test;pes el valor p. Un valor p pequeño (típicamente < 0.05) significa que los desacuerdos están significativamente sesgados — un modelo está superando sistemáticamente al otro, no simplemente intercambiando victorias por azar.
¿Qué método de comparación de modelos debería usar Dev?
| Método | Qué te dice | Qué pasa por alto | Cuándo usarlo |
|---|---|---|---|
| Evaluar la diferencia de precisión a ojo | Qué modelo obtuvo mayor puntuación en el conjunto de prueba | Si la diferencia es real o ruido; no da ninguna estimación de incertidumbre; una brecha de 1% en 500 filas no tiene sentido | Verificaciones rápidas en problemas de juguete donde los riesgos son bajos y solo necesitas un ranking aproximado |
| Intervalos de confianza | El rango de valores plausibles para la precisión de cada modelo — ¿cuánto variaría la puntuación con un conjunto de prueba diferente? | Que los IC se superpongan no demuestra equivalencia — dos modelos pueden tener IC superpuestos y aun así ser significativamente diferentes. “La estimación puntual de B cae dentro del IC de A” es una regla general, no una prueba de hipótesis formal | Cuando necesitas una estimación rápida de incertidumbre y no quieres configurar una prueba por pares formal; útil para dashboards y monitoreo |
| Test de McNemar | Si los desacuerdos entre dos clasificadores están sesgados sistemáticamente — la comparación por pares rigurosa que usa la estructura de qué filas cada modelo falla | Solo funciona para clasificación (no regresión); requiere que ambos modelos predigan sobre exactamente el mismo conjunto de prueba; no te dice la magnitud de la mejora (para eso necesitas el tamaño del efecto) | Cuando estás tomando una decisión de despliegue y necesitas saber si el nuevo modelo es estadísticamente mejor, no solo numéricamente mejor — exactamente la situación de Dev con el recomendador |
El compromiso clave: Evaluar a ojo es rápido pero no te dice nada sobre la incertidumbre. Los intervalos de confianza te dan un rango, pero “la estimación puntual del otro modelo cae dentro de mi IC” es una heurística, no una prueba de significancia formal — puede pasar por alto diferencias reales porque ignora la correlación entre los errores de los dos modelos (ambos se prueban en las mismas filas). El test de McNemar es el más riguroso de los tres porque usa la estructura de los desacuerdos — ¿en qué filas específicas discreparon los modelos? — en lugar de simplemente comparar puntuaciones agregadas. Para la decisión de despliegue de Dev, donde un falso positivo significa desplegar un modelo que no es realmente mejor y un falso negativo significa dejar un modelo mejor en el estante, el rigor vale las tres líneas de código adicionales.
En la práctica, estamos ignorando los empates y enfocándonos en los casos donde los modelos discreparon. Cuando los números de la esquina superior derecha e inferior izquierda en nuestra tabla están cerca, el test reporta que no hay diferencia real.
La parte más difícil: lo que realmente significa un valor p
Aquí está la parte más difícil de la comparación de modelos para asimilar: el valor p. La mayoría de la gente piensa que un valor p de 0.05 significa que hay un 95% de probabilidad de que su modelo sea mejor. Eso es incorrecto.
Piense en el valor p como un ‘Medidor de Sorpresa’. Comenzamos con la Hipótesis Nula, que es simplemente el supuesto de ‘Nada Especial’: asumimos que ambos modelos son idénticos.
Un valor p de 0.03 significa: “Si estos modelos fueran realmente idénticos, veríamos un resultado tan extremo solo el 3% de las veces por pura suerte”.
Mide qué tan extraños se ven nuestros datos bajo el supuesto de que nada ha cambiado. Cuando el valor p cae lo suficiente (generalmente por debajo de 0.05), decimos “Bien, esto es demasiado raro para ser una casualidad” y rechazamos el supuesto de ‘Nada Especial’.
def interpret_p(p_val):
threshold = 0.05
if p_val < threshold:
return f"p={p_val:.4f}: The difference is likely real (Statistically Significant)."
else:
return f"p={p_val:.4f}: The difference could just be luck (Not Significant)."
print(interpret_p(p))
Esta es la función auxiliar que Dev escribe para convertir el valor p crudo de la prueba de McNemar en un veredicto legible por humanos: la misma función que llamará al comparar su recomendador reentrenado contra el actual.
def interpret_p(p_val):— una función simple que toma un valor p y devuelve una cadena en lenguaje sencillo. Dev llama a esto justo después de ejecutarmcnemar()para que la salida sea inmediatamente interpretable en su reporte nocturno.threshold = 0.05— el nivel de significancia convencional (alfa). Esto significa “Estoy dispuesto a aceptar un 5% de probabilidad de equivocarme, de declarar que los modelos son diferentes cuando en realidad son iguales”. Dev podría ajustar esto a 0.01 para decisiones de despliegue de alto riesgo o relajarlo a 0.10 para verificaciones exploratorias.if p_val < threshold:— si el valor p está por debajo del umbral, el patrón de desacuerdo observado es poco probable bajo la hipótesis nula (ambos modelos idénticos). Dev concluye que la diferencia probablemente es real.return f"p={p_val:.4f}: The difference is likely real (Statistically Significant)."— el veredicto “significativo”, con el valor p real impreso hasta 4 decimales para que Dev pueda ver qué tan significativo es: un valor p de 0.049 apenas pasa la línea, mientras que un valor p de 0.0001 es abrumador.return f"p={p_val:.4f}: The difference could just be luck (Not Significant)."— el veredicto “no significativo”. Importante: esto no significa que los modelos sean iguales. Significa que la evidencia no es lo suficientemente fuerte para descartar la suerte. La diferencia podría ser real pero pequeña, o el conjunto de prueba podría ser demasiado pequeño para detectarla.print(interpret_p(p))— llama a la función conp, el valor p de la prueba de McNemar del bloque de código anterior. Para la comparación del recomendador de Dev, esta es la última línea en su notebook de evaluación de modelos: el veredicto estadístico sobre si el Modelo B es realmente mejor que el Modelo A.
Si tu valor p es 0.40, hay un 40% de probabilidad de que la diferencia que ves sea solo ruido aleatorio. No apostarías un presupuesto de marketing de un millón de dólares a una casualidad del 40%, ¿verdad?
Comparando regresiones con la prueba de Diebold-Mariano
¿Qué pasa si no estás haciendo clasificación? Si estás prediciendo precios de viviendas o tendencias del mercado de valores, no hay “correcto” ni “incorrecto”—solo errores. Residuos, para ser más precisos.
Para comparar dos modelos de regresión, usamos el test de Diebold-Mariano. Aquí no hay tabla de contingencia. En su lugar, observamos la “serie de errores”—restando el error del Modelo B del error del Modelo A para cada predicción. Si el Modelo B es realmente mejor, sus errores deberían ser consistentemente más pequeños.
Así es como verificamos si un modelo más complejo—por ejemplo, una red neuronal masiva—realmente vale la computación adicional respecto a una regresión lineal simple.
def diebold_mariano_test(y_true, p1, p2):
# Calculate errors (Absolute Error)
e1 = np.abs(y_true - p1)
e2 = np.abs(y_true - p2)
# The difference in errors
d = e1 - e2
# Mean difference divided by its standard deviation
# This is a simplified version of the DM statistic
dm_stat = np.mean(d) / (np.std(d, ddof=1) / np.sqrt(len(d)))
p_value = stats.norm.cdf(-np.abs(dm_stat)) * 2
return dm_stat, p_value
# Simulate regression errors
y_real = np.linspace(100, 1000, 100)
reg_a = y_real + np.random.normal(0, 50, 100)
reg_b = y_real + np.random.normal(0, 45, 100)
stat, p_reg = diebold_mariano_test(y_real, reg_a, reg_b)
print(f"DM Statistic: {stat:.4f}, p-value: {p_reg:.4f}")
Esta es el test de Diebold-Mariano que Dev usaría si su recomendador fuera evaluado con una métrica de regresión (como la probabilidad de clic predicha) en lugar de clasificación binaria — la misma pregunta “¿el Modelo B es realmente mejor?”, adaptada para errores continuos.
def diebold_mariano_test(y_true, p1, p2):— define la función del test DM.y_truees el valor real,p1yp2son las predicciones de los dos modelos. En el caso de Dev, estos serían las probabilidades de clic predichas del modelo incumbente y del modelo reentrenado.e1 = np.abs(y_true - p1)— errores absolutos para el Modelo A: qué tan lejos está cada predicción del valor real. El test DM también puede usar errores cuadrados ((y_true - p1)**2) u otras funciones de pérdida; el error absoluto es un valor predeterminado robusto.e2 = np.abs(y_true - p2)— errores absolutos para el Modelo B.d = e1 - e2— la serie de diferencias de errores. Para cada predicción, esto es positivo cuando el error del Modelo A es mayor (el Modelo B es mejor en esa fila) y negativo cuando el error del Modelo B es mayor. Si el Modelo B es consistentemente mejor,dserá predominantemente positivo.dm_stat = np.mean(d) / (np.std(d, ddof=1) / np.sqrt(len(d)))— el estadístico del test DM: la diferencia media de errores dividida por su error estándar. Esto es estructuralmente idéntico a un estadístico t de una muestra — mide cuántas desviaciones estándar está la diferencia media de errores desde cero.ddof=1usa la desviación estándar muestral (dividiendo por en lugar de ).p_value = stats.norm.cdf(-np.abs(dm_stat)) * 2— calcula un valor p de dos colas a partir de la distribución normal estándar.np.abs(dm_stat)toma el valor absoluto del estadístico (por lo que el test es simétrico — no importa cuál modelo sea mejor, solo si uno lo es).stats.norm.cdf(-...)da la probabilidad de la cola izquierda, y* 2la duplica para un test de dos colas. Un valor p pequeño significa que la diferencia media de errores es demasiado grande para ser explicada solo por variación aleatoria.y_real = np.linspace(100, 1000, 100)— genera 100 valores reales uniformemente espaciados de 100 a 1000. Esto simula un objetivo de regresión con una tendencia suave.reg_a = y_real + np.random.normal(0, 50, 100)— predicciones del Modelo A: valores reales más ruido gaussiano con desviación estándar 50. El Modelo A tiene errores más grandes.reg_b = y_real + np.random.normal(0, 45, 100)— predicciones del Modelo B: mismos valores reales pero con ruido de desviación estándar 45. El Modelo B es ligeramente mejor — sus errores son consistentemente ~10% más pequeños.stat, p_reg = diebold_mariano_test(y_real, reg_a, reg_b)— ejecuta el test. Unstatpositivo con unp_regbajo significa que los errores del Modelo B son significativamente más pequeños que los del Modelo A — la mejora es real, no ruido.
Un estadístico DM positivo con un valor p bajo significa que los errores del Modelo B son significativamente más pequeños. Si el valor p es alto, la “mejora” solo es fluctuación en el ruido.
Cuándo detenerse: la regla de ‘lo suficientemente bueno’
La significancia estadística no es lo mismo que la importancia práctica.
Con 10 millones de filas, una mejora de 0.0001% se registrará como estadísticamente significativa. El valor p sale en 0.000001. ¿Importa? Si el nuevo modelo cuesta $50,000 en servidores y ahorra $5 en pérdidas por fraude, es un mal negocio.
Revisa el Tamaño del Efecto. Mide la magnitud de la mejora. El valor d de Cohen es un enfoque estándar — cuenta cuántas desviaciones estándar mejor se desempeña el nuevo modelo.
def cohens_d(x, y):
nx, ny = len(x), len(y)
var_x, var_y = np.var(x, ddof=1), np.var(y, ddof=1)
pooled_sd = np.sqrt(((nx - 1) * var_x + (ny - 1) * var_y) / (nx + ny - 2))
return (np.mean(x) - np.mean(y)) / pooled_sd
# Comparing the errors of our regression models
d_val = cohens_d(np.abs(y_real - reg_a), np.abs(y_real - reg_b))
print(f"Effect Size (Cohen's d): {d_val:.4f}")
Esta es la verificación del tamaño del efecto que Dev ejecuta después de la prueba de significancia — la pregunta de “está bien, es estadísticamente real, pero ¿es lo suficientemente grande para importar?” que determina si el recomendador reentrenado vale la pena enviar a producción.
def cohens_d(x, y):— define el valor d de Cohen, la métrica de tamaño del efecto más común.xyyson las dos muestras que se comparan — en el caso de Dev, los errores absolutos del Modelo A y el Modelo B. Undpositivo significa que el Modelo A tiene errores mayores (el Modelo B es mejor); undnegativo significa lo opuesto.nx, ny = len(x), len(y)— los tamaños de muestra. Ambos son 100 en esta simulación. El valor d de Cohen contempla tamaños de muestra desiguales mediante la desviación estándar agrupada.var_x, var_y = np.var(x, ddof=1), np.var(y, ddof=1)— las varianzas muestrales de los errores de cada modelo.ddof=1significa dividir entre (varianza muestral, estimador insesgado) en lugar de (varianza poblacional). Esta es la elección estándar al calcular tamaños de efecto a partir de datos muestrales.pooled_sd = np.sqrt(((nx - 1) * var_x + (ny - 1) * var_y) / (nx + ny - 2))— la desviación estándar agrupada, que combina las varianzas de ambas muestras en una sola estimación de dispersión, ponderada por el tamaño de muestra. La fórmula es . Este es el denominador del valor d de Cohen — estandariza la diferencia de medias para que el resultado esté en una escala de “desviaciones estándar”.return (np.mean(x) - np.mean(y)) / pooled_sd— el valor d de Cohen: la diferencia de medias dividida entre la desviación estándar agrupada. Indica la mejora en términos de desviaciones estándar — un número adimensional comparable entre conjuntos de datos y métricas.d_val = cohens_d(np.abs(y_real - reg_a), np.abs(y_real - reg_b))— llama a la función con los errores absolutos del Modelo A comoxy los del Modelo B comoy. Si el Modelo B es mejor (errores menores),d_valserá positivo — la media de los errores del Modelo A menos la media de los errores del Modelo B, dividida entre su dispersión agrupada.print(f"Effect Size (Cohen's d): {d_val:.4f}")— imprime el tamaño del efecto. Dev revisa este número junto con el valor p: un valor p significativo con un d minúsculo significa que la mejora es real pero prácticamente despreciable — probablemente no valga el riesgo de desplegarla.
- Un valor d de Cohen de 0.2 es pequeño.
- 0.5 es mediano.
- 0.8 o mayor es grande.
Un valor p significativo con un tamaño del efecto de 0.01 significa que has encontrado una diferencia real que es efectivamente inútil.
Lo que cubrimos:
- Una puntuación más alta podría ser solo una ‘conjetura afortunada’ debido al error de muestreo.
- La Prueba de McNemar verifica si los desacuerdos del clasificador están equilibrados.
- El valor p es un ‘medidor de sorpresa’ — te indica qué tan probable es que un resultado sea casualidad.
- La Prueba de Diebold-Mariano verifica si las reducciones de error en regresión son consistentes.
- El Tamaño del Efecto te indica si un hallazgo ‘significativo’ le importa al resultado final.
La próxima vez que veas una tabla de clasificación, no te limites a mirar el número de arriba. Pide el valor p.
Dev ejecuta los números con sus dos recomendadores. Un valor p de 0.03, con un tamaño del efecto modesto pero real — el modelo reentrenado es genuinamente mejor, no solo suerte. Sin embargo, ha visto modelos ganar sin conexión y tropezar en producción. Así que no activará el cambio para los diez millones de usuarios de golpe. Primero va el tráfico en paralelo, luego despliegues canario.
Comprueba tu comprensión
Las preguntas a continuación van desde el recuerdo simple hasta el diseño abierto, siguiendo aproximadamente la Taxonomía de Bloom.
Recordar ¿En qué se centra la prueba de McNemar y por qué ignora las filas donde ambos modelos coinciden?
Comprender Con tus propias palabras, explica qué significa realmente un valor p de 0.03, utilizando el encuadre del “Medidor de Sorpresa” del artículo—y contra qué error de interpretación común advierte el artículo.
Aplicar
Utilizando la lógica de la función interpret_p del artículo (umbral=0.05), ¿un valor p de 0.049 sería clasificado como “Estadísticamente Significativo” o “No Significativo”?
Analizar La sección “Regla de ‘Suficientemente Bueno’” del artículo dice que con 10 millones de filas, incluso una mejora del 0.0001% puede ser estadísticamente significativa. Explica por qué el tamaño de la muestra específicamente hace que los valores p disminuyan para un tamaño de efecto fijo, y por qué esto hace que el tamaño del efecto (como la d de Cohen) sea una métrica complementaria necesaria en lugar de un extra opcional.
Evaluar El ejemplo del intervalo de confianza del Modelo A/B del artículo muestra que el IC del Modelo A [82.8%, 87.2%] contiene la precisión del 86.5% del Modelo B, concluyendo “no podemos estar seguros de que sea realmente mejor.” Critica este razonamiento específico: ¿acaso “la estimación puntual del otro modelo cae dentro de mi intervalo de confianza” es realmente la prueba correcta para determinar si dos modelos difieren significativamente, o confunde dos preguntas estadísticas diferentes?
Crear Diseña un informe de comparación de modelos para un stakeholder: el nuevo modelo de fraude de tu equipo tiene p=0.001 (altamente significativo) pero una d de Cohen de 0.03 (efecto minúsculo) en comparación con el modelo de producción actual. Escribe la recomendación de un párrafo que darías, utilizando la distinción del artículo entre significancia estadística e importancia práctica.
Artículos relacionados
- ¿Cuándo deberías reentrenar? Creación de un disparador de reentrenamiento sencillo — El disparador de reentrenamiento de Dev se activó y produjo un modelo candidato. Este artículo muestra cómo verifica si ese candidato es realmente mejor antes de enviarlo a producción.
- Pruebas A/B de modelos desplegados: despliegues en la sombra y más — Las estadísticas indican que el nuevo recomendador es una mejora real. Pero Dev no activará el interruptor para los 10 millones de usuarios a la vez — así es como lo despliega de manera segura con tráfico en la sombra, despliegues canario y pruebas A/B controladas.
Referencias y lecturas adicionales
- McNemar, Q. (1947). “Note on the sampling error of the difference between correlated proportions or percentages.” Psychometrika, 12(2), 153–157. — el artículo original que introduce la prueba de McNemar para comparar dos clasificadores evaluados en el mismo conjunto de prueba. Esta es la base de la comparación estadística que Dev utiliza para decidir si su recomendador reentrenado es genuinamente mejor que el modelo actual.
- Dietterich, T. G. (1998). “Approximate Statistical Tests for Comparing Supervised Classification Learning Algorithms.” Neural Computation, 10(7), 1345–1365. — la referencia canónica sobre pruebas de significancia estadística para la comparación de modelos de ML, que abarca la prueba de McNemar, la prueba t pareada con validación cruzada de 5×2, y orientación práctica sobre cuándo es apropiada cada prueba para tomar una decisión de lanzamiento.
Aplica lo que aprendiste
Tema: Cómo el error de muestreo genera incertidumbre en las puntuaciones del modelo, y el pipeline de pruebas estadísticas (McNemar para clasificación, Diebold-Mariano para regresión) y tamaño del efecto (Cohen’s d) que convierte una diferencia bruta de precisión en una decisión defendible de desplegar/no desplegar.
Dibuja un mapa mental (papel, Excalidraw, Miro — cualquier medio) con al menos estos nodos:
- Error de muestreo (problema de la “Adivinación Afortunada”)
- Intervalo de confianza (95% CI con z = 1.96)
- Prueba de McNemar (tabla de contingencia, celdas discordantes b y c)
- Prueba de Diebold-Mariano (serie de diferencias de errores para regresión)
- Valor p (“Medidor de Sorpresa” bajo la hipótesis nula)
- Tamaño del efecto (Cohen’s d: 0.2 pequeño, 0.5 mediano, 0.8 grande)
- Importancia práctica (Regla del “Suficientemente Bueno”: significancia ≠ valor para el negocio)
con al menos estas aristas:
- Error de muestreo → Intervalo de confianza
- Prueba de McNemar → Valor p
- Valor p → Tamaño del efecto
Rúbrica: todos los 7 nodos nombrados presentes; las 3 aristas requeridas dibujadas; una arista extra propia con una justificación de una oración sobre por qué la agregaste.
Tu líder de equipo revisa el recomendador reentrenado de Dev y dice: “El Modelo A alcanza 85% con un 95% CI de [82.8%, 87.2%], y el Modelo B alcanza 86.5% — eso cae dentro del CI de A, así que no podemos concluir que B es mejor.” Escribe un memorándum para stakeholders de 200–400 palabras que critique o defienda este razonamiento.
Entregable: Un memorándum (texto plano, 200–400 palabras) dirigido a tu líder de equipo, tomando una posición clara sobre si “la estimación puntual del Modelo B dentro del CI del Modelo A” es evidencia suficiente para bloquear el despliegue.
Rúbrica:
- Cita los números específicos del artículo: Modelo A al 85%, CI [82.8%, 87.2%], Modelo B al 86.5%, n = 1000 filas de prueba
- Identifica el defecto estadístico: la superposición de CI es una heurística, no una prueba de hipótesis por pares adecuada — trata las puntuaciones de ambos modelos como independientes cuando ambas se evalúan en el mismo conjunto de prueba de 1000 filas
- Nombra la prueba de McNemar como la alternativa adecuada y explica qué hace: compara las celdas discordantes b (A correcto, B incorrecto) y c (B correcto, A incorrecto) en lugar de las puntuaciones agregadas
- Caracteriza correctamente lo que un valor p no significa — no es “la probabilidad de que el Modelo B sea mejor” ni “95% de probabilidad de que la diferencia sea real”; es la probabilidad de observar datos tan extremos si los modelos fueran idénticos
- Toma una posición clara (defender o criticar el razonamiento del líder de equipo) y la respalda con al menos un argumento específico de la comparación de “Alternativas y Compromisos” del artículo
El disparador de reentrenamiento de Dev se activa y produce un modelo candidato cada vez que la distancia de Wasserstein sobre session_duration cruza 2.0. Necesita un servicio reutilizable de comparación de modelos que cualquier job nocturno de Airflow pueda invocar para obtener un veredicto estructurado de desplegar/no desplegar — no solo “B obtuvo una puntuación más alta” sino una decisión estadísticamente defendible con filtrado por tamaño del efecto.
Punto de partida: projects/is-your-model-actually-better-a-plain-english-guide-to-statistical-significance/model_comparison.py
Entregable: Completa el stub de model_comparison.py de modo que cada cuerpo de función con # TODO esté completado y el smoke test de __main__ se ejecute de principio a fin sin errores. El módulo debe exportar dos puntos de entrada: ship_decision(y_true, pred_a, pred_b) para clasificación y diebold_mariano_test(y_true, pred_a, pred_b) para regresión.
Rúbrica:
-
compute_confidence_interval(0.85, 1000)retorna aproximadamente(0.828, 0.872)— usa la aproximación normal con z = 1.96 ysqrt(p*(1-p)/n) -
build_contingency_tableproduce un arreglo 2×2 donde[0,1]= A-correcto/B-incorrecto y[1,0]= B-correcto/A-incorrecto (las celdas discordantes sobre las que opera la prueba de McNemar) -
run_mcnemarinvocamcnemar(ary=tb, corrected=True)— la corrección de continuidad (de Yates) se aplica, no se omite -
interpret_p_valueretorna “Statistically Significant” cuando p < 0.05 y “Not Significant” en caso contrario, y no retorna ninguna cadena que afirme “95% de probabilidad de que el modelo sea mejor” -
compute_cohens_dusa la desviación estándar agrupada conddof=1y retorna un flotante adimensional interpretable en la escala 0.2 / 0.5 / 0.8 -
ship_decisionretorna un dict con las clavesaccuracy_a,accuracy_b,ci_a,p_value,effect_size, yverdict— dondeverdictes"SHIP"solo cuando tanto p < 0.05 y |Cohen’s d| ≥ 0.2,"DO NOT SHIP — negligible effect"cuando p < 0.05 pero d < 0.2, y"INCONCLUSIVE"cuando p ≥ 0.05 -
diebold_mariano_testse ejecuta en la simulación de regresión (y_real, reg_a, reg_b) y produce un estadístico DM positivo con un valor p de dos colas < 0.05, confirmando que los errores del Modelo B son significativamente menores que los del Modelo A
Esta traducción fue generada automáticamente y puede contener errores. Si el idioma inglés es tu preferencia, puedes leer el artículo original en inglés .
Artículos relacionados
- Estadística En revisión
Intervalos de confianza: lo que realmente significa '95% de confianza' (sin dolores de cabeza con las matemáticas)
Aprende qué significa realmente el 95% de confianza —describe el proceso, no tu intervalo específico— con código en Python, ejemplos de pruebas A/B y una intuición clara.
- Estadística En revisión
Pearson vs Spearman vs Kendall: Cómo elegir la correlación adecuada para tus datos
Aprende cuándo usar la correlación de Pearson, Spearman o Kendall en Python — y por qué un puntaje bajo puede significar que simplemente elegiste la herramienta incorrecta para tus datos.
- Estadística En revisión
Referencia: Prueba de hipótesis
Una referencia completa sobre la prueba de hipótesis: valores p, tipos de error, potencia, correcciones para comparaciones múltiples y cómo elegir la prueba adecuada con ejemplos en Python.
- Estadística En revisión
Piensa como un bayesiano: una guía para frecuentistas que odian las fórmulas
Aprende cómo los a priori bayesianos, los a posteriori y las tasas base producen conclusiones más claras y accionables que los métodos frecuentistas — con ejemplos intuitivos en Python.
¿Buscas otra cosa?
Busca en todos los artículos por título, resumen o tema.