Valores atípicos y robustez

最終更新: 4 分で読めます このページを編集

Los valores atípicos (outliers) son observaciones que se desvían fuertemente del resto. Qué es un outlier depende del problema, la distribución y la escala del objetivo. Aquí comparamos mínimos cuadrados (pérdida cuadrática) con la pérdida de Huber usando datos con un punto extremo.

import japanize_matplotlib
import matplotlib.pyplot as plt
import numpy as np

1. Por qué OLS es sensible a outliers #

OLS minimiza la suma de cuadrados de los residuos $$ \text{RSS} = \sum_{i=1}^n (y_i - \hat y_i)^2. $$ Al elevar al cuadrado los residuos, un solo punto extremo puede dominar la pérdida y arrastrar la recta ajustada hacia el outlier.


2. Pérdida de Huber: compromiso entre cuadrada y absoluta #

La pérdida de Huber usa cuadrática para residuos pequeños y absoluta para grandes. Para r = y - \hat y y umbral $\delta > 0$:

$$ \ell_\delta(r) = \begin{cases} \dfrac{1}{2}r^2, & |r| \le \delta \ \delta\left(|r| - \dfrac{1}{2}\delta\right), & |r| > \delta. \end{cases} $$

La derivada (influencia) es $$ \psi_\delta(r) = \frac{d}{dr}\ell_\delta(r) = \begin{cases} r, & |r| \le \delta \ \delta,\mathrm{sign}(r), & |r| > \delta, \end{cases} $$ por lo que el gradiente se satura ante residuos grandes (outliers).

Nota: en HuberRegressor de scikit-learn, el umbral es epsilon (corresponde a $\delta$).


3. Visualizar las formas de las pérdidas #

def huber_loss(r: np.ndarray, delta: float = 1.5):
    half_sq = 0.5 * np.square(r)
    lin = delta * (np.abs(r) - 0.5 * delta)
    return np.where(np.abs(r) <= delta, half_sq, lin)

delta = 1.5
r_vals = np.arange(-2, 2, 0.01)
h_vals = huber_loss(r_vals, delta=delta)

plt.figure(figsize=(8, 6))
plt.plot(r_vals, np.square(r_vals), "red",   label=r"cuadrática $r^2$")
plt.plot(r_vals, np.abs(r_vals),    "orange",label=r"absoluta $|r|$")
plt.plot(r_vals, h_vals,            "green", label=fr"Huber ($\delta={delta}$)")
plt.axhline(0, color="k", linewidth=0.8)
plt.grid(True, alpha=0.3)
plt.legend()
plt.xlabel("residuo $r$")
plt.ylabel("pérdida")
plt.title("Cuadrática vs absoluta vs Huber")
plt.show()

png


4. ¿Qué pasa con un outlier? (datos) #

Creamos un problema lineal con 2 variables y inyectamos un outlier extremo en y.

np.random.seed(42)

N = 30
x1 = np.arange(N)
x2 = np.arange(N)
X = np.c_[x1, x2]                      # (N, 2)
epsilon = np.random.rand(N)            # ruido en [0, 1)
y = 5 * x1 + 10 * x2 + epsilon * 10

y[5] = 500  # un outlier muy grande

plt.figure(figsize=(8, 6))
plt.plot(x1, y, "ko", label="datos")
plt.xlabel("$x_1$")
plt.ylabel("$y$")
plt.legend()
plt.title("Conjunto con un outlier")
plt.show()

png


5. Comparar OLS vs Ridge vs Huber #

  • OLS: muy sensible a outliers.
  • Ridge (L2): encoge coeficientes; algo más estable, pero aún afectado.
  • Huber: satura la influencia de outliers; la recta se arrastra menos.
from sklearn.linear_model import HuberRegressor, Ridge, LinearRegression

plt.figure(figsize=(8, 6))

# Huber: epsilon=3 para reducir la influencia de outliers
huber = HuberRegressor(alpha=0.0, epsilon=3.0)
huber.fit(X, y)
plt.plot(x1, huber.predict(X), "green", label="Huber")

# Ridge (L2). Con alpha≈0, se parece a OLS
ridge = Ridge(alpha=1.0, random_state=0)
ridge.fit(X, y)
plt.plot(x1, ridge.predict(X), "orange", label="Ridge (α=1.0)")

# OLS
lr = LinearRegression()
lr.fit(X, y)
plt.plot(x1, lr.predict(X), "r-", label="OLS")

# datos brutos
plt.plot(x1, y, "kx", alpha=0.7)

plt.xlabel("$x_1$")
plt.ylabel("$y$")
plt.legend()
plt.title("Efecto de un outlier en las rectas ajustadas")
plt.grid(alpha=0.3)
plt.show()

png

Interpretación:

  • OLS (rojo) es fuertemente arrastrado por el outlier.
  • Ridge (naranja) mitiga un poco pero sigue afectado.
  • Huber (verde) reduce la influencia del outlier y sigue mejor la tendencia global.

6. Parámetros: epsilon y alpha #

  • epsilon (umbral $\delta$):
    • Mayor → más cercano a OLS; menor → más cercano a pérdida absoluta.
    • Depende de la escala de residuos; estandarice o use escalado robusto.
  • alpha (penalización L2):
    • Estabiliza coeficientes; útil con colinealidad.

Sensibilidad a epsilon:

from sklearn.metrics import mean_squared_error

for eps in [1.2, 1.5, 2.0, 3.0]:
    h = HuberRegressor(alpha=0.0, epsilon=eps).fit(X, y)
    mse = mean_squared_error(y, h.predict(X))
    print(f"epsilon={eps:>3}: MSE={mse:.3f}")

7. Notas prácticas #

  • Escalado: si las escalas de variables/objetivo difieren, cambia el significado de epsilon; estandarice o use escalado robusto.
  • Puntos de alto apalancamiento: Huber es robusta a outliers verticales en y, no necesariamente a extremos en X.
  • Elegir umbrales: ajuste epsilon y alpha (por ejemplo, con GridSearchCV).
  • Evalúe con CV: no se guíe solo por el ajuste en entrenamiento.

8. Resumen #

  • OLS es sensible a outliers; el ajuste puede ser arrastrado.
  • Huber usa cuadrática para errores pequeños y absoluta para grandes, saturando gradientes ante outliers.
  • Ajuste epsilon y alpha para balancear robustez y ajuste.
  • Cuidado con puntos de apalancamiento; combine con inspección y preprocesamiento si es necesario.