Entendiendo el error respecto al peso: de la parábola a la derivada
Este post está pensado en 2 partes, para que se vea claro:
- Primero dibujamos la curva de error y la interpretamos.
- Después usamos la derivada para mover
wy ver cómo llega al mínimo.
Introducción
Cuando entrenamos una regresión lineal, no estamos “adivinando” el mejor peso w: estamos buscando el valor que minimiza el error.
La idea central de este post es muy visual:
- si dejamos
bfijo y cambiamos solow, - el error cuadrático medio toma forma de parábola,
- y el punto más bajo de esa parábola representa el mejor
wpara esos datos.
Primero vamos a construir esa curva para entender el terreno. Luego vamos a usar la derivada como una brújula matemática para bajar por la curva hasta acercarnos al punto donde la pendiente es cero.
Si entiendes esa secuencia (curva → pendiente → actualización), entiendes la base del gradiente descendente.
Parte 0 — Setup mínimo
Modelo lineal con b fijo:
Función de costo (solo depende de w):
import numpy as np
import matplotlib.pyplot as plt
# Datos
x = np.array([2, 4, 5], dtype=float)
y = np.array([3, 5, 7], dtype=float)
b = 1.0 # fijo
def mse_w(w, x, y, b):
y_pred = w * x + b
return np.mean((y - y_pred) ** 2)
def d_mse_dw(w, x, y, b):
y_pred = w * x + b
return -2 * np.mean(x * (y - y_pred))
Parte 1 — Dibujar solo la curva del error y entenderla
w_values = np.linspace(-1, 3, 300)
J_values = np.array([mse_w(w, x, y, b) for w in w_values])
w_min = w_values[np.argmin(J_values)]
J_min = np.min(J_values)
plt.figure(figsize=(9, 4.5))
plt.plot(w_values, J_values, color='royalblue', linewidth=2, label='J(w)')
plt.scatter([w_min], [J_min], color='crimson', s=80, label='mínimo de la curva')
plt.title('Parte 1: Curva de error J(w) con b fijo')
plt.xlabel('w (peso)')
plt.ylabel('MSE')
plt.grid(True)
plt.legend()
plt.show()
print(f"w del mínimo aprox: {w_min:.4f}")
print(f"J(w_min): {J_min:.6f}")
Resultado:
w del mínimo aprox: 1.1137
J(w_min): 0.148250

Interpretación de la Parte 1
- La curva es una parábola convexa.
- A la izquierda y derecha del mínimo, el error sube.
- En el punto más bajo está el mejor
wpara esebfijo. - Aquí todavía no optimizamos, solo observamos el paisaje del error.
Parte 2 — Aplicar derivada y ver cómo w se acerca al mínimo
La derivada:
Regla de actualización:
alpha = 0.05
w = -0.5 # arrancamos lejos del mínimo
history_w = [w]
history_J = [mse_w(w, x, y, b)]
history_grad = [d_mse_dw(w, x, y, b)]
for _ in range(25):
grad = d_mse_dw(w, x, y, b)
w = w - alpha * grad
history_w.append(w)
history_J.append(mse_w(w, x, y, b))
history_grad.append(d_mse_dw(w, x, y, b))
print(f"w inicial: {history_w[0]:.4f}")
print(f"w final: {history_w[-1]:.4f}")
print(f"gradiente inicial: {history_grad[0]:.6f}")
print(f"gradiente final: {history_grad[-1]:.6f}")
Resultado:
w inicial: -0.5000
w final: 1.1111
gradiente inicial: -48.333333
gradiente final: 0.000001
| Iteración | w | J(w) | dJ/dw |
|---|---|---|---|
| 0 | -0.5000 | 39.083333 | -48.333333 |
| 1 | 1.9167 | 9.881944 | 24.166667 |
| 2 | 0.7083 | 2.581597 | -12.083333 |
| 5 | 1.1615 | 0.186171 | 1.510417 |
| 10 | 1.1095 | 0.148185 | -0.047201 |
| 20 | 1.1111 | 0.148148 | -0.000046 |
| 25 | 1.1111 | 0.148148 | 0.000001 |
Parte 3 — Visualizar el recorrido del gradiente sobre la curva
plt.figure(figsize=(9, 4.5))
plt.plot(w_values, J_values, color='royalblue', linewidth=2, label='J(w)')
plt.scatter(history_w, history_J, c=np.arange(len(history_w)), cmap='viridis', s=45, label='pasos GD')
plt.scatter([w_min], [J_min], color='crimson', s=90, label='mínimo')
for i in [0, 1, 2, 5, 10, 20, len(history_w)-1]:
if i < len(history_w):
plt.annotate(str(i), (history_w[i], history_J[i]), textcoords='offset points', xytext=(5, 5), fontsize=8)
plt.title('Parte 3: Cómo la derivada lleva a w al mínimo')
plt.xlabel('w (peso)')
plt.ylabel('MSE')
plt.grid(True)
plt.legend()
plt.show()

Interpretación de la Parte 3
- Si , el update resta un número positivo y
wbaja (se mueve a la izquierda). - Si , al restar un negativo,
wsube (se mueve a la derecha). - En cada paso,
wcae hacia una zona de menor error. - Cerca del mínimo, , entonces casi no se mueve.
Parte 4 — Señal final: la pendiente tiende a cero
plt.figure(figsize=(9, 4))
plt.plot(np.abs(history_grad), marker='o')
plt.title('Magnitud de la pendiente |dJ/dw| por iteración')
plt.xlabel('Iteración')
plt.ylabel('|dJ/dw|')
plt.grid(True)
plt.show()

Interpretación rápida:
El entrenamiento converge cuando la pendiente se vuelve muy pequeña. “Pendiente casi cero” significa “estoy en (o muy cerca de) un mínimo”.
Frase corta para cerrar el post
Primero mirás el mapa (la curva de error). Luego usás la derivada como brújula. Así llegás al punto donde la pendiente es cero.