Curso de ML EN

Capítulo 12 de 37 · intermedio

Evaluar un regresor: error y R²

Qué cubre este capítulo

El capítulo pasado ajustó una recta y citó un R² de 0.585 como si significara algo. En este capítulo nos ganamos ese número, y los otros cinco que vas a usar junto a él. Un modelo de regresión te entrega una columna de predicciones; la verdad es otra columna al lado; cada métrica de aquí es una forma distinta de comprimir esas dos columnas en un solo número que dice qué tan equivocado está el modelo. El detalle, y la razón entera de que esto sea un capítulo propio, es que esos números no son intercambiables — viven en unidades distintas, premian cosas distintas, y uno de ellos te va a mentir en cuanto aparezca un outlier.

Construimos las seis desde cero en NumPy: MSE, su raíz cuadrada RMSE, MAE, R², R² ajustada y MAPE. Luego comparamos cada una contra sklearn.metrics y coinciden hasta el último decimal. La pregunta que nos acompaña todo el camino es la que nadie hace lo suficiente: ¿en qué unidad está este número? MSE está en las unidades del target al cuadrado, y por eso no lo puedes leer. RMSE y MAE vuelven a las unidades propias del target, así que sí puedes. R² es adimensional a propósito. MAPE es un porcentaje. Mantén esa pregunta en la cabeza y la mitad de estas métricas dejan de ser intercambiables y empiezan a ser herramientas con trabajos distintos.

Las predicciones que juzgamos son las del capítulo de regresión lineal: el mismo modelo de vivienda de California, el mismo split 80/20, sus estimaciones sobre los 4,128 bloques reservados. El target es el valor mediano de la vivienda en unidades de $100,000, así que cada cifra en dólares de aquí abajo se remonta a esa unidad.

Un poco de historia

El error cuadrático es el más viejo de todos por dos siglos, y llegó como criterio de optimización mucho antes de que a alguien se le ocurriera usarlo como calificación. Adrien-Marie Legendre publicó los mínimos cuadrados en 1805; Carl Friedrich Gauss publicó su propia versión en 1809 y la ligó a la distribución normal, argumentando que minimizar la suma de residuales al cuadrado es lo correcto cuando tu ruido es gaussiano. Esa suma de cuadrados, dividida entre n, es el MSE. Así que el MSE no nació como número de boleta de calificaciones — nació como la cosa que minimizas, y sigue siendo el mejor del grupo en ese trabajo. RMSE y MAE son simplemente las dos formas obvias de producir un error que sí puedas interpretar: sacar la raíz, o dejar de elevar al cuadrado y usar el valor absoluto, una idea anterior a Gauss que se remonta a Roger Boscovich en los 1750s.

R² es mucho más joven y viene de la genética, no de la astronomía. Sewall Wright, desarrollando el análisis de senderos para desenredar cuánta de la variación de un rasgo se debía a cada causa, introdujo el coeficiente de determinación en 1921 — la fracción de la varianza que una cosa explica de otra. Aterrizó en la misma década en que Ronald Fisher construía el análisis de varianza, y las dos ideas son la misma idea: partir la variación total en la parte que tu modelo explica y la parte que no, y reportar el cociente. Por eso R² es adimensional y por eso es el número que la gente cita en problemas completamente distintos — fue diseñado desde el inicio como una proporción, no como una medición.

La intuición

Cada métrica de esta página parte de la misma imagen: la predicción y la verdad, graficadas una contra la otra. Si el modelo fuera perfecto, cada punto caería sobre la línea de 45 grados, donde lo predicho es igual a lo real. No lo es, así que los puntos se dispersan alrededor de esa línea, y la distancia vertical de cada punto a ella es el error de esa predicción — su residual. Cada métrica es una regla distinta para colapsar esa nube de brechas verticales en un solo resumen.

Aquí están las predicciones del modelo sobre los bloques reservados de California, lo real en el eje x y lo predicho en el y, con la línea y = x donde un modelo perfecto pondría todo. Ambos ejes están en unidades de $100,000.

Dos cosas saltan a la vista, y ambas son cosas que una métrica tendrá que resumir. La nube está inclinada a lo largo de la línea, lo cual es bueno — las predicciones altas van en su mayoría con valores reales altos. Pero mira el borde derecho: una pared vertical de puntos en actual = 5.0 donde el modelo predice de todo, porque el target está topado en $500,000 y el modelo, sin saber del tope, sigue estimando más alto. Esos son residuales grandes. Una métrica que los eleva al cuadrado se va a obsesionar con ellos; una que no, se va a encoger de hombros. La misma nube, veredictos distintos, y esa diferencia es todo el capítulo.

La matemática

Escribe yiy_i para el valor verdadero del bloque ii, y^i\hat{y}_i para la predicción del modelo, yˉ\bar{y} para la media de los valores verdaderos, y nn para el número de bloques. Cada métrica es una función de los residuales yiy^iy_i - \hat{y}_i.

El error cuadrático medio promedia los residuales al cuadrado. Elevar al cuadrado hace dos trabajos: mata el signo para que los errores no se cancelen, y hace que un error grande cuente mucho más que uno pequeño.

MSE=1ni=1n(yiy^i)2\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} \left( y_i - \hat{y}_i \right)^2

Como eleva al cuadrado, su unidad es la unidad del target al cuadrado — dólares al cuadrado, un área. La raíz del error cuadrático medio deshace eso, y te regresa a las unidades propias del target:

RMSE=MSE=1ni=1n(yiy^i)2\text{RMSE} = \sqrt{\text{MSE}} = \sqrt{\frac{1}{n} \sum_{i=1}^{n} \left( y_i - \hat{y}_i \right)^2}

El error absoluto medio toma el otro camino: no elevar al cuadrado para nada, solo tomar el valor absoluto. Mismas unidades que RMSE, pero cada residual cuenta en proporción a su tamaño, ni más ni menos.

MAE=1ni=1nyiy^i\text{MAE} = \frac{1}{n} \sum_{i=1}^{n} \left| y_i - \hat{y}_i \right|

R² es un cociente de dos sumas de cuadrados. La suma de cuadrados residual es el error cuadrático total de tu modelo; la suma de cuadrados total es el error que cometerías prediciendo la media yˉ\bar{y} para cada bloque:

SSres=i=1n(yiy^i)2,SStot=i=1n(yiyˉ)2\text{SS}_{\text{res}} = \sum_{i=1}^{n} \left( y_i - \hat{y}_i \right)^2, \qquad \text{SS}_{\text{tot}} = \sum_{i=1}^{n} \left( y_i - \bar{y} \right)^2 R2=1SSresSStotR^2 = 1 - \frac{\text{SS}_{\text{res}}}{\text{SS}_{\text{tot}}}

Ambas sumas están en las unidades del target al cuadrado, así que el cociente es adimensional — ese es el punto. Un R² de 1 es perfecto, 0 es no mejor que adivinar la media, y negativo significa que te habría ido mejor con la media. La R² ajustada es el mismo número con una penalización por el número de features pp, para que agregar una columna inútil no pueda inflarlo:

Radj2=1(1R2)n1np1R^2_{\text{adj}} = 1 - \left( 1 - R^2 \right) \frac{n - 1}{n - p - 1}

El error porcentual absoluto medio divide cada residual entre la verdad antes de promediar, que es lo que lo hace independiente de la escala — y lo que lo hace explotar si algún yiy_i está cerca de cero:

MAPE=100ni=1nyiy^iyi\text{MAPE} = \frac{100}{n} \sum_{i=1}^{n} \left| \frac{y_i - \hat{y}_i}{y_i} \right|

Qué premia cada una, y qué castiga

La división que más importa es cuadrático contra absoluto. MSE y RMSE elevan el residual al cuadrado, así que les importan los errores grandes fuera de toda proporción — un residual del doble de tamaño cuenta cuatro veces más. Eso es exactamente lo que quieres al entrenar un modelo, porque hace que el ajuste trabaje más duro donde está más equivocado, y es exactamente lo que no quieres al reportar, porque un solo outlier puede dominar todo el número. MAE pondera linealmente, así que reporta el error típico y se mantiene tranquilo cuando unos cuantos puntos se descontrolan. Ninguno es más correcto; responden preguntas distintas. "Qué tan malo es mi peor comportamiento" es RMSE. "Qué tan equivocado estoy en un día normal" es MAE.

R² y MAPE son las dos que te dejan comparar entre problemas, y ambas lo pagan. R² es independiente de la escala porque se mide contra la varianza del propio target, lo cual es una fortaleza — te dice cuánto mejor que la adivinanza tonta de la media lo estás haciendo — y una trampa, porque un R² alto sobre un target que apenas varía es fácil, y un R² bajo sobre uno tremendamente variable podría ser excelente. MAPE es independiente de la escala porque es un porcentaje, lo que la convierte en la única métrica que puedes decir en voz alta en una junta, pero está sesgada: explota cerca de targets en cero y penaliza menos la sobre-predicción que la sub-predicción, así que un modelo que sistemáticamente estima de menos puede sacar una calificación sospechosamente buena. La jugada honesta es nunca reportar solo una.

Las predicciones que estamos juzgando

Los números de abajo salen todos de un archivo: las predicciones del modelo lineal sobre los 4,128 bloques reservados de California, guardadas junto a sus valores verdaderos en test_predictions.csv. El modelo se ajustó con los otros 16,512 bloques — el mismo ajuste OLS, el mismo split con semilla 0 del capítulo de regresión lineal — y usa ocho features, que es la pp que la R² ajustada nos va a cobrar. El target es el valor mediano de la vivienda en unidades de $100,000, y va de 0.15 hasta el 5.0 topado, lo cual importa por partida doble: no hay ceros, así que MAPE está bien definido, y está ese techo, que es de donde salen los residuales gordos.

Cargarlas es el único I/O del archivo desde cero — cada métrica arranca desde aquí:

def mse(y_true, y_pred):
    """Mean squared error. UNIT: target units, SQUARED.

    Average of the squared residuals. If the target is in $100k, this number is
    in ($100k)^2 — an area, not a price, which is exactly why you can't read it
    off as "the model is off by X." Squaring makes every miss positive and makes
    a big miss count far more than a small one, so a single outlier moves this a
    lot. Great to optimize, hard to interpret.
    """
    resid = y_true - y_pred
    return float(np.mean(resid ** 2))

Ese es MSE, y vale la pena decir qué va a ser su número antes de siquiera correrlo: lo que salga está en $100k al cuadrado, un área, y no deberías intentar imaginártelo. Es una gran cosa que minimizar y una cosa terrible que interpretar, que es el tema recurrente.

Constrúyelas, una métrica a la vez

RMSE es MSE con la interpretación reatornillada. Una raíz cuadrada y el número vuelve a estar en las unidades del target — una cifra que puedes poner junto al precio de una casa.

def rmse(y_true, y_pred):
    """Root mean squared error. UNIT: target units.

    The square root of the MSE, which undoes the squaring and lands you back in
    the target's own units — dollars, not dollars-squared. That's the whole
    reason it exists: it's the MSE made readable. Still outlier-sensitive (the
    squaring happened before the root), but now it's a number you can compare to
    the scale of what you're predicting.
    """
    return float(np.sqrt(mse(y_true, y_pred)))

MAE es el otro resumen de los residuales, y el robusto. Valor absoluto en lugar de cuadrado, así que las unidades son las del target desde el principio y ningún punto solo puede apalancar el total:

def mae(y_true, y_pred):
    """Mean absolute error. UNIT: target units.

    Average of the absolute residuals. Same units as RMSE — the target's units —
    but it weights every miss linearly instead of squaring it, so one wild point
    contributes its size and no more. This is the robust one: the typical error,
    unimpressed by outliers.
    """
    return float(np.mean(np.abs(y_true - y_pred)))

R² compara tu error cuadrático contra el error cuadrático de adivinar la media. Nota las dos sumas que la matemática definió — ss_res es la del modelo, ss_tot es la de la media — y que el cociente sale adimensional porque ambas están en las mismas unidades cuadradas:

def r2(y_true, y_pred):
    """Coefficient of determination. UNIT: none (a fraction, <= 1).

    R^2 = 1 - SS_res / SS_tot, where
      SS_res = sum of squared residuals (our model's squared error), and
      SS_tot = sum of squared deviations from the mean of y (the error you'd get
               predicting the mean for everything).
    So it's the fraction of the target's variance the model explains. 1.0 is
    perfect, 0.0 is no better than always guessing the mean, negative is worse
    than the mean. Unitless by construction — a ratio of two things in the same
    (squared) units — which is what lets you compare it across datasets.
    """
    ss_res = float(np.sum((y_true - y_pred) ** 2))
    ss_tot = float(np.sum((y_true - np.mean(y_true)) ** 2))
    return 1.0 - ss_res / ss_tot

La R² ajustada envuelve eso con la penalización por features. Necesita saber cuántos predictores entraron al modelo, porque el punto entero es hacer que cada uno pague renta:

def adjusted_r2(y_true, y_pred, n_features):
    """Adjusted R^2. UNIT: none.

    Plain R^2 never goes down when you add a feature, even a useless one, so it
    rewards fatter models. Adjusted R^2 charges rent for each feature: it
    discounts R^2 by the number of predictors `p` relative to the sample size
    `n`, and only rises when a new feature earns more than it costs.

        1 - (1 - R^2) * (n - 1) / (n - p - 1)
    """
    n = len(y_true)
    r2_val = r2(y_true, y_pred)
    return 1.0 - (1.0 - r2_val) * (n - 1) / (n - n_features - 1)

Y MAPE, el porcentaje. La división entre y_true es lo que lo hace independiente de la escala y también lo que lo hace frágil — es la única línea de este archivo que puede dividir entre cero, y la única razón de que aquí no lo haga es que los valores de las casas nunca son cero:

def mape(y_true, y_pred):
    """Mean absolute percentage error. UNIT: percent.

    Average of |residual / true value|, times 100. Because it divides by the
    truth, it's scale-free — a $5k miss on a $50k house (10%) counts the same as
    a $50k miss on a $500k house (10%). That makes it easy to explain to a
    non-technical audience, but it has two teeth: it blows up when any true value
    is near zero, and it punishes over-prediction less than under-prediction.
    Undefined if any `y_true` is exactly zero (California's target never is).
    """
    return float(np.mean(np.abs((y_true - y_pred) / y_true)) * 100.0)

Corre las seis sobre las predicciones del conjunto de prueba y esto es lo que el modelo realmente saca, cada barra en su propia unidad porque no comparten una. Lee cada una con su etiqueta — una barra de MSE y una barra de MAE lado a lado es un error de categoría, y la gráfica está dibujada para que lo sientas:

Los cuatro números para llevarte, cada uno con su unidad: RMSE es 0.727 $100k — digamos un error típico de unos $72,700. MAE es 0.529 $100k, más o menos $52,900, notablemente menor que RMSE, y esa brecha es información en sí misma: RMSE supera a MAE exactamente cuando los errores son desiguales, cuando una minoría de errores grandes (los bloques topados) está jalando la métrica cuadrática hacia arriba. MSE es 0.528 $100k al cuadrado, un número que genuinamente no puedo interpretar y no voy a intentarlo. R² es 0.585, adimensional — el modelo explica alrededor del 59% de la varianza del valor de la vivienda, y la R² ajustada apenas lo mueve a 0.584 porque ocho features contra 4,128 puntos de prueba es una penalización del tamaño de un error de redondeo. MAPE es 32.06%, lo que significa que el modelo falla por aproximadamente una tercera parte del valor verdadero en un bloque típico, lo cual suena peor que el R² y es un recordatorio justo de que "explica el 59% de la varianza" y "falla por una tercera parte" son verdad al mismo tiempo.

Míralo trabajar

Esta es la demostración que hace que la división cuadrático-contra-absoluto deje de ser abstracta. Veinte puntos de juguete, una recta de ajuste fija dibujada a través de ellos, y un punto — el naranja — que arrastramos hacia arriba, cuadro por cuadro, de modo que su residual crece de nada a doce unidades. Las predicciones nunca cambian; solo se mueve ese valor verdadero. Debajo, tres barras: MSE, RMSE y MAE, recalculadas sobre los veinte puntos en cada cuadro, compartiendo un eje para que las veas divergir.

Dale play. La línea naranja punteada es el residual creciente del punto que estamos arrastrando.

Observa lo que hacen las barras. Al inicio, con el punto pegado a la recta, las tres métricas están cerca — MSE 0.17 unidades², RMSE 0.42 unidades, MAE 0.35 unidades, todas en el mismo vecindario aproximado. Luego arrastra el punto hasta un residual de doce unidades y se separan violentamente. MSE se dispara a 7.72 unidades² — sube por un factor de cuarenta y cinco a cuenta de un solo punto, porque ese residual se eleva al cuadrado antes de promediarse. RMSE trepa a 2.78 unidades, un factor de alrededor de siete, siguiendo el cuadrado del outlier pero suavizado por la raíz. Y MAE avanza a gatas de 0.35 a 0.95 unidades, sin llegar a triplicarse, porque para un promedio lineal un punto desviado doce unidades es solo un punto desviado doce unidades. Un outlier, tres historias completamente distintas.

Esta es toda la lección práctica del capítulo en una sola animación. Si tus datos tienen outliers que no puedes limpiar — un target topado, una distribución de colas pesadas, unos cuantos desastres de captura de datos — MSE y RMSE van a reportar tu modelo como dramáticamente peor de lo que es sobre el grueso de los datos, porque están respondiendo "qué tan malo es lo peor de esto", y lo peor es un outlier. MAE te va a seguir hablando del bloque típico. Cuál quieres depende enteramente de si esos errores grandes son lo que te importa o lo que quieres ignorar, y esa es una decisión de criterio que la métrica no puede tomar por ti — pero ni siquiera puedes tomarla si solo miras un número.

La implementación completa

El archivo entero, sin librería, de arriba a abajo — seis métricas y un cargador, cada una en su unidad declarada. Este es el código que realmente corrió cada número de arriba y cada barra de la animación:

"""Regression metrics, built from scratch in pure NumPy.

Every function here answers the same question — "how wrong is this regressor?" —
and every one answers it in a different unit. That unit is the whole point of
the chapter, so each docstring states it plainly.

The inputs are always the same: `y_true`, the real targets, and `y_pred`, the
model's predictions, both 1-D arrays of the same length. None of these functions
know or care where the predictions came from; they judge a column of numbers
against another column of numbers. We feed them the test-set predictions of the
linear model from the linear-regression chapter (California housing, target in
units of $100,000).

Pure NumPy — no ML library in this file. The `# region:` markers are what the
book's include directives pull in, one metric at a time.
"""

import numpy as np
import pandas as pd


# region: mse
def mse(y_true, y_pred):
    """Mean squared error. UNIT: target units, SQUARED.

    Average of the squared residuals. If the target is in $100k, this number is
    in ($100k)^2 — an area, not a price, which is exactly why you can't read it
    off as "the model is off by X." Squaring makes every miss positive and makes
    a big miss count far more than a small one, so a single outlier moves this a
    lot. Great to optimize, hard to interpret.
    """
    resid = y_true - y_pred
    return float(np.mean(resid ** 2))
# endregion


# region: rmse
def rmse(y_true, y_pred):
    """Root mean squared error. UNIT: target units.

    The square root of the MSE, which undoes the squaring and lands you back in
    the target's own units — dollars, not dollars-squared. That's the whole
    reason it exists: it's the MSE made readable. Still outlier-sensitive (the
    squaring happened before the root), but now it's a number you can compare to
    the scale of what you're predicting.
    """
    return float(np.sqrt(mse(y_true, y_pred)))
# endregion


# region: mae
def mae(y_true, y_pred):
    """Mean absolute error. UNIT: target units.

    Average of the absolute residuals. Same units as RMSE — the target's units —
    but it weights every miss linearly instead of squaring it, so one wild point
    contributes its size and no more. This is the robust one: the typical error,
    unimpressed by outliers.
    """
    return float(np.mean(np.abs(y_true - y_pred)))
# endregion


# region: r2
def r2(y_true, y_pred):
    """Coefficient of determination. UNIT: none (a fraction, <= 1).

    R^2 = 1 - SS_res / SS_tot, where
      SS_res = sum of squared residuals (our model's squared error), and
      SS_tot = sum of squared deviations from the mean of y (the error you'd get
               predicting the mean for everything).
    So it's the fraction of the target's variance the model explains. 1.0 is
    perfect, 0.0 is no better than always guessing the mean, negative is worse
    than the mean. Unitless by construction — a ratio of two things in the same
    (squared) units — which is what lets you compare it across datasets.
    """
    ss_res = float(np.sum((y_true - y_pred) ** 2))
    ss_tot = float(np.sum((y_true - np.mean(y_true)) ** 2))
    return 1.0 - ss_res / ss_tot
# endregion


# region: adjusted_r2
def adjusted_r2(y_true, y_pred, n_features):
    """Adjusted R^2. UNIT: none.

    Plain R^2 never goes down when you add a feature, even a useless one, so it
    rewards fatter models. Adjusted R^2 charges rent for each feature: it
    discounts R^2 by the number of predictors `p` relative to the sample size
    `n`, and only rises when a new feature earns more than it costs.

        1 - (1 - R^2) * (n - 1) / (n - p - 1)
    """
    n = len(y_true)
    r2_val = r2(y_true, y_pred)
    return 1.0 - (1.0 - r2_val) * (n - 1) / (n - n_features - 1)
# endregion


# region: mape
def mape(y_true, y_pred):
    """Mean absolute percentage error. UNIT: percent.

    Average of |residual / true value|, times 100. Because it divides by the
    truth, it's scale-free — a $5k miss on a $50k house (10%) counts the same as
    a $50k miss on a $500k house (10%). That makes it easy to explain to a
    non-technical audience, but it has two teeth: it blows up when any true value
    is near zero, and it punishes over-prediction less than under-prediction.
    Undefined if any `y_true` is exactly zero (California's target never is).
    """
    return float(np.mean(np.abs((y_true - y_pred) / y_true)) * 100.0)
# endregion


def load_predictions(path="../data/test_predictions.csv"):
    """Load the committed (y_true, y_pred) pair for the real test set.

    These are the linear model's predictions on the held-out California housing
    blocks, in units of $100,000. Returns (y_true, y_pred) as float arrays.
    """
    df = pd.read_csv(path)
    return df["y_true"].to_numpy(float), df["y_pred"].to_numpy(float)

La versión de librería

Nunca las escribirías a mano en la práctica; sklearn.metrics las tiene todas, e ir por la librería es la decisión correcta. La única razón de construirlas una vez es que sepas exactamente qué está calculando la librería, porque un par de sus decisiones te van a morder si no lo sabes. root_mean_squared_error es una función separada de mean_squared_error — sklearn solía meterla en un flag squared=False y luego lo deprecó, así que en cualquier versión reciente llamas la función dedicada. Y mean_absolute_percentage_error devuelve una fracción, no un porcentaje: te da 0.32, y si lo imprimes como "0.32% de error" estás equivocado por dos órdenes de magnitud. Multiplicamos por 100 para alinearlo con el nuestro.

def sklearn_metrics(y_true, y_pred):
    """Every metric sklearn ships, as a dict, in the same units as impl.py.

    mape is scaled to a percent (sklearn returns a fraction) so it matches our
    `mape`. rmse uses sklearn's dedicated function rather than sqrt-ing the mse.
    """
    return {
        "mse": float(mean_squared_error(y_true, y_pred)),          # target units^2
        "rmse": float(root_mean_squared_error(y_true, y_pred)),    # target units
        "mae": float(mean_absolute_error(y_true, y_pred)),         # target units
        "r2": float(r2_score(y_true, y_pred)),                     # unitless
        "mape": float(mean_absolute_percentage_error(y_true, y_pred) * 100.0),  # percent
    }

No hay helper de R² ajustada en sklearn — esa se queda en nuestro archivo, porque necesita conocer el conteo de features, que es una propiedad de tu modelo, no de las dos columnas de predicciones. Todo lo demás es un one-liner, y es lo que deberías shippear.

Desde cero contra librería

Las mismas predicciones, nuestras seis fórmulas contra las cinco de sklearn, y aterrizan en los mismos números. Cada métrica está dibujada en su propia fila con su propia escala — las dos barras por métrica tienen longitudes idénticas, que es la única forma honesta de mostrar "estas coinciden" cuando las métricas mismas viven en unidades distintas:

La coincidencia es exacta — la diferencia más grande entre cualquiera de nuestros números y los de sklearn es cero a la precisión que la aserción revisa, que es lo que esperarías de fórmulas así de cortas. MSE 0.528 $100k al cuadrado, RMSE 0.727 $100k, MAE 0.529 $100k, R² 0.585 adimensional, MAPE 32.06%, empatados línea por línea. El punto de construirlas desde cero nunca fue que sklearn pudiera estar mal; fue hacer imposible perder de vista las unidades. Cuando llamas mean_squared_error y obtienes 0.528, ahora sabes sin pensarlo que es un área, y que el número que en realidad quieres citar es su raíz.

Conclusiones

Reporta RMSE, y repórtalo en las unidades del target. Es el único número que está a la vez en las unidades que te importan y es sensible a los errores que duelen, así que "el modelo falla por unos $72,700 en un bloque típico" es una frase que un stakeholder entiende y una frase que es verdad. RMSE es mi titular por defecto para un regresor, y las unidades son la mitad de la razón — una métrica a la que no le puedes colgar una unidad es una métrica que no puedes defender en una sala.

Mantén MAE a su lado, y lee la brecha. Cuando RMSE queda bien por encima de MAE — 0.727 contra 0.529 aquí — esa separación te está diciendo que los errores son desiguales, que una minoría de errores grandes está inflando la métrica cuadrática. Si esos errores grandes son outliers que no te importan, cita MAE y sigue adelante; si son las casas topadas en $500,000 que a tu negocio sí le importan, esa brecha es lo más importante de la página. La animación del outlier es el argumento completo: un punto movió MSE 45x y MAE menos de 3x, y solo viendo ambas supiste de qué tipo de equivocado es tu modelo.

Usa R² para la pregunta independiente de la escala — cuánto mejor que adivinar la media — y nunca como tu único número, porque 0.585 y "falla por una tercera parte del valor" son el mismo modelo. Ve por MAPE cuando un porcentaje es lo que la audiencia necesita y ya revisaste que no haya targets cercanos a cero que lo hagan explotar. Y MSE: minimízalo, no lo reportes. Es el mejor objetivo de entrenamiento de este capítulo y el peor número de boleta, porque es la única cifra de aquí de la que no puedes decir en qué unidad está sin detenerte a pensar — dólares al cuadrado — y un número que tienes que traducir antes de poder creerle es un número que tarde o temprano te va a engañar. Esa es la lección recurrente: siempre sabe qué unidad traes en la mano.