Newton vs Gradiente
- • GD: usa solo gradiente (1er orden)
- • Newton: usa Hessiana (2do orden)
- • Newton converge en menos pasos pero cada paso es más caro
- • GD es más escalable a altas dimensiones (ML)
Capítulo 4
Aproximamos una función suave alrededor de un punto usando sus derivadas:
Hasta primer orden: (aproximación lineal). Hasta segundo orden: .
Para :
donde es la matriz Hessiana de segundas derivadas:
import numpy as np
# Hessiana de f(x,y) = x^2 + y^2 + xy
def hessian(xy):
x, y = xy
return np.array([[2, 1], # d²f/dx², d²f/dxdy
[1, 2]]) # d²f/dydx, d²f/dy²
# Verificar con numérico
from scipy.optimize import approx_fprime
def grad_f(xy):
return np.array([2*xy[0] + xy[1], 2*xy[1] + xy[0]])
xy = np.array([1.0, 2.0])
H_num = approx_fprime(xy, lambda p: grad_f(p)[0], 1e-6)
print("Hessiana:
", hessian(xy))
print("Eigenvalores:", np.linalg.eigvalsh(hessian(xy)))
# Si todos > 0 → mínimo localEl método de Newton usa la expansión de segundo orden para optimizar:
import numpy as np
# Newton vs GD para f(x) = x^2 + sin(3x)*0.5
def f(x):
return x**2 + 0.5*np.sin(3*x)
def df(x):
return 2*x + 1.5*np.cos(3*x)
def d2f(x):
return 2 - 4.5*np.sin(3*x)
x_gd, x_nt = 2.0, 2.0
lr = 0.1
for i in range(10):
x_gd -= lr * df(x_gd)
x_nt -= df(x_nt) / d2f(x_nt) # Newton: H^{-1} * grad
print(f"Paso {i+1}: GD x={x_gd:.4f} | Newton x={x_nt:.4f}")
print(f"
GD final: {x_gd:.6f}, f={f(x_gd):.6f}")
print(f"Newton final: {x_nt:.6f}, f={f(x_nt):.6f}")En muchos problemas de ML, linealizamos modelos no lineales alrededor de una estimación inicial (ej: filtro de Kalman, sistema de recomendación).
# Linearización de un modelo no lineal
import numpy as np
# Modelo: y = exp(x) + ruido
x0 = 0.0
# Taylor 1er orden alrededor de x0: exp(x) ≈ 1 + x
# Modelo linealizado: y ≈ 1 + x
# Para funciones vectoriales, la Jacobiana es la linearización
# f(x) = [exp(x), sin(x)] ≈ f(x0) + J(x0) * (x - x0)
x0 = np.array([0.0])
J = np.array([[np.exp(x0[0])],
[np.cos(x0[0])]])
print("Jacobiana en x=0:
", J)
print("f(0) ≈", np.array([np.exp(0), np.sin(0)]))
print("f(0.1) ≈", np.array([np.exp(0), np.sin(0)]) + J.flatten() * 0.1)