ia machine learning python gradiente descendente regresión lineal

De la parábola a la derivada: cómo el gradiente descendente encuentra el mínimo

Visualizá la curva de error J(w), entendé la derivada como brújula matemática y seguí paso a paso cómo el gradiente descendente reduce el error hasta que la pendiente es casi cero.

Por Compujuy · ·
- vistas
Gradiente descendente, parábola y derivada en Machine Learning con Python

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:

  1. Primero dibujamos la curva de error J(w)J(w) y la interpretamos.
  2. Después usamos la derivada para mover w y 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:

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:

y^=wx+b\hat{y} = wx + b

Función de costo (solo depende de w):

J(w)=1ni=1n(yi(wxi+b))2J(w) = \frac{1}{n}\sum_{i=1}^{n}(y_i-(wx_i+b))^2

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

Curva de error J(w)

Interpretación de la Parte 1


Parte 2 — Aplicar derivada y ver cómo w se acerca al mínimo

La derivada:

dJdw=2mean[x(yy^)]\frac{dJ}{dw} = -2\,\text{mean}\left[x(y-\hat{y})\right]

Regla de actualización:

wnuevo=wactualαdJdww_{nuevo} = w_{actual} - \alpha\frac{dJ}{dw}

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ónwJ(w)dJ/dw
0-0.500039.083333-48.333333
11.91679.88194424.166667
20.70832.581597-12.083333
51.16150.1861711.510417
101.10950.148185-0.047201
201.11110.148148-0.000046
251.11110.1481480.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()

Recorrido del gradiente descendente

Interpretación de la Parte 3


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()

Pendiente por iteración

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.


Más sobre ia Probar Yagware gratis