05 — gradiente

Gradiente Descendente

Algoritmo de optimización iterativa basado en el cálculo de la derivada de la función de pérdida. Permite encontrar los parámetros del modelo cuando no existe o no es práctico usar una solución analítica.

Motivación: limitaciones de la fórmula cerrada

La solución analítica de mínimos cuadrados es exacta para regresión lineal simple, pero presenta restricciones que la hacen impráctica en escenarios más generales:

El gradiente descendente ofrece una alternativa general: en lugar de calcular la solución exacta, la busca iterativamente.

La derivada como guía

Imagina que estás parado en una montaña con niebla y quieres bajar al valle. No puedes ver el panorama completo, pero puedes sentir la inclinación del terreno bajo tus pies. Si siempre das pasos en la dirección de mayor pendiente descendente, eventualmente llegarás al punto más bajo.

La derivada de la función de pérdida respecto a un parámetro indica exactamente eso: en qué dirección (positiva o negativa) se mueve el error cuando cambiamos ese parámetro.

Concepto clave

Si la derivada \(\frac{\partial ECM}{\partial m} > 0\), el ECM aumenta cuando aumentamos m, así que debemos disminuir m. Si es negativa, debemos aumentarla. En ambos casos, nos movemos en la dirección opuesta al gradiente.

El algoritmo

El gradiente descendente sigue estos pasos de forma repetida hasta converger:

  1. Inicializar los parámetros con algún valor (por ejemplo, 0 o valores aleatorios)
  2. Calcular las predicciones con los parámetros actuales
  3. Calcular el error (ECM)
  4. Calcular el gradiente — la derivada del ECM respecto a cada parámetro
  5. Actualizar cada parámetro restándole el gradiente multiplicado por el learning rate
  6. Repetir desde el paso 2

Regla de actualización

Para cada parámetro \(\theta\) (que puede ser m o b):

\[ \theta_{t+1} = \theta_t - \alpha \cdot \frac{\partial ECM}{\partial \theta} \]

Donde \(\alpha\) (alpha) es el learning rate — un número positivo pequeño que controla el tamaño del paso.

Learning rate

El learning rate es el hiperparámetro más crítico del gradiente descendente:

Valores típicos de prueba: 0.1, 0.01, 0.001. Se elige mediante experimentación.

ECM en función del valor de la pendiente con b fijo
ECM según el valor de la pendiente \(m\) (con \(b = 40\) fijo). La curva tiene un único mínimo: el gradiente apunta hacia él desde cualquier punto. Un learning rate demasiado grande saltaría de un lado al otro.

Derivación del gradiente para regresión lineal

Para el ECM con modelo \(\hat{y} = mx + b\), las derivadas parciales son:

\[ \frac{\partial ECM}{\partial m} = \frac{2}{n} \sum_{i=1}^{n} x_i(\hat{y}_i - y_i) = \frac{2}{n} \sum x_i (mx_i + b - y_i) \]
\[ \frac{\partial ECM}{\partial b} = \frac{2}{n} \sum_{i=1}^{n} (\hat{y}_i - y_i) \]

En la práctica se omite el factor 2 (no cambia la dirección del gradiente) y con NumPy la implementación es directa:

Implementación

import numpy as np
import pandas as pd

# Funciones del módulo utils.py
def calcular_y(x, m, b):
    return x * m + b

def ecm(y_real, y_est):
    return ((y_real - y_est) ** 2).mean()

def gradiente_m(m, alpha, b, x, y_real):
    """Actualiza m usando el gradiente del ECM."""
    gradiente = (2 * (x * (m * x + b - y_real))).mean()
    return m - alpha * gradiente

def gradiente_b(b, alpha, m, x, y_real):
    """Actualiza b usando el gradiente del ECM."""
    gradiente = (2 * (m * x + b - y_real)).mean()
    return b - alpha * gradiente

# --- Entrenamiento ---
df = pd.read_csv("data/csvs/examenes.csv").sample(100)
x = np.array(df["study_hours"])
y = np.array(df["exam_score"])

# Inicializar parámetros
m = 0.0
b = 0.0
alpha = 0.01
n_iter = 1000

for i in range(n_iter):
    m = gradiente_m(m, alpha, b, x, y)
    b = gradiente_b(b, alpha, m, x, y)
    if i % 100 == 0:
        print(f"Iteración {i:4d} — ECM: {ecm(y, calcular_y(x, m, b)):.2f}")

print(f"\nResultado: m={m:.4f}, b={b:.4f}")
Scatter plot de horas de estudio vs calificación del examen
Datos reales: horas de estudio vs calificación del examen. Este es el punto de partida del gradiente descendente — buscamos la recta que mejor atraviesa esta nube de puntos.

Convergencia y diagnóstico

Para verificar que el algoritmo converge, se grafica el ECM en función del número de iteraciones. La curva debe descender y aplanarse:

import matplotlib.pyplot as plt

m, b = 0.0, 0.0
alpha = 0.01
historial_ecm = []

for i in range(1000):
    m = gradiente_m(m, alpha, b, x, y)
    b = gradiente_b(b, alpha, m, x, y)
    historial_ecm.append(ecm(y, calcular_y(x, m, b)))

fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(historial_ecm)
ax.set_xlabel("Iteración")
ax.set_ylabel("ECM")
ax.set_title("Convergencia del gradiente descendente")
plt.show()
Curva de convergencia del ECM y recta ajustada por gradiente descendente
Izquierda: el ECM disminuye en cada iteración y se estabiliza cuando el algoritmo converge. Derecha: la recta ajustada resultante sobre los datos reales.
Profundización Variantes del gradiente descendente

Existen tres variantes principales según cuántos datos se usan en cada actualización:

  • Batch Gradient Descent: usa todos los datos en cada iteración. Estable pero lento en datasets grandes.
  • Stochastic Gradient Descent (SGD): usa un solo dato por iteración. Rápido pero ruidoso.
  • Mini-batch Gradient Descent: usa un subconjunto (lote) de datos. Equilibrio entre estabilidad y velocidad. Es el estándar en deep learning.