📈 Optimización y Análisis Geométrico de Funciones en Dos Variables¶
- Objetivo: Aplicar los conceptos del cálculo diferencial multivariable para calcular derivadas parciales de una función de dos variables, determinar el gradiente y construir la matriz Hessiana.
👥 Alumno¶
- 🧑🎓 José Gómez Brizuela
📅 Entrega: Junio 2025
📘 Módulo 3: Sesión N° 4
# Librerías a utilizar
import sympy as sp
from sympy import symbols, diff, hessian, solve, Matrix, lambdify
from IPython.display import display, Math, HTML
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D # Solo para versiones antiguas de matplotlib
from matplotlib.animation import FuncAnimation, PillowWriter
# Función personalizada para mostrar LaTeX
def mostrar_latex(expr, nombre_funcion="f"):
"""
Muestra en pantalla la representación LaTeX de una expresión simbólica.
Parámetros:
- expr: expresión simbólica de SymPy
- nombre_funcion: nombre de etiqueta (por defecto, 'f')
Resultado:
- Muestra la función en formato LaTeX en un notebook de Colab
"""
latex_expr = sp.latex(expr)
display(Math(rf"{nombre_funcion}(x, y) = {latex_expr}"))
1. Definición de la Función:¶
- Definir simbólicamente una función de dos variables 𝑔(𝑥,𝑦) que presente un punto crítico identificable, se usará:
# Definimos variables simbólicas x e y
x, y = sp.symbols('x y')
# Definimos la función g(x, y) de forma simbólica
g = x**2 + 3*y**2 - 4*x + 2*y + 1
# Mostramos la función g(x, y) en formato LaTeX
mostrar_latex(g, "g")
2. Cálculo de Derivadas Parciales, Gradiente y Matriz Hessiana:¶
# Calculamos las derivadas parciales de g(x, y)
# ----------------------------------------------
# Derivada parcial de g con respecto a x
dg_dx = sp.diff(g, x)
# Derivada parcial de g con respecto a y
dg_dy = sp.diff(g, y)
# Mostramos las derivadas parciales en formato LaTeX
mostrar_latex(dg_dx, r"\frac{\partial g}{\partial x}")
mostrar_latex(dg_dy, r"\frac{\partial g}{\partial y}")
# Calculamos el gradiente de g
# ----------------------------------------------
# El gradiente es un vector formado por las derivadas parciales
grad = sp.Matrix([dg_dx, dg_dy])
# Mostramos el gradiente en notación LaTeX
mostrar_latex(grad, r"\nabla g")
# Cálculo del Hessiano
# ----------------------------------------------
# El Hessiano es la matriz de segundas derivadas parciales de una función
# En este caso, se calcula para la función g respecto a las variables x e y
H = sp.hessian(g, (x, y))
# Mostramos el Hessiano en notación LaTeX
mostrar_latex(H, r"H_g")
3. Clasificación del Punto Crítico:¶
Evaluar la matriz Hessiana en el punto crítico encontrado y utilizar sus valores propios para clasificar el punto (mínimo local, máximo local o punto de silla).
# Resolución del sistema para encontrar puntos críticos
# -----------------------------------------------------
# Los puntos críticos ocurren donde el gradiente es igual al vector cero,
# es decir, donde ambas derivadas parciales se anulan.
criticos = sp.solve([dg_dx, dg_dy], (x, y))
# Mostramos los puntos críticos encontrados (si hay más de uno, será una lista de tuplas)
criticos
{x: 2, y: -1/3}
# Clasificación de puntos críticos según la matriz Hessiana
# ----------------------------------------------------------
# Para cada punto crítico se evalúa el Hessiano y se analizan sus autovalores
# para determinar si se trata de un mínimo local, máximo local o punto de silla.
# Si el resultado es un solo punto en forma de diccionario, lo convertimos en lista
if isinstance(criticos, dict):
criticos = [criticos] # Homogeneizamos la estructura para iterar
for punto in criticos:
try:
x_val = punto[x]
y_val = punto[y]
except KeyError:
print(f"No se pudo extraer x, y de: {punto}")
continue
print(f"\nEvaluando en el punto crítico: ({x_val}, {y_val})")
# Evaluar la Hessiana en el punto crítico
H_eval = H.subs({x: x_val, y: y_val})
mostrar_latex(H_eval, r"H_g\big|_{(x,y)}")
# Calcular los valores propios (autovalores)
autovalores_dict = H_eval.eigenvals()
autovalores = list(autovalores_dict.keys())
print("Valores propios:")
for val in autovalores:
print(f" λ = {val}")
# Clasificación según el signo de los autovalores
if all(val > 0 for val in autovalores):
print("Mínimo local")
elif all(val < 0 for val in autovalores):
print("Máximo local")
elif any(val > 0 for val in autovalores) and any(val < 0 for val in autovalores):
print("Punto de silla")
else:
print("Clasificación no concluyente")
Evaluando en el punto crítico: (2, -1/3)
Valores propios: λ = 2 λ = 6 Mínimo local
4. Visualización:¶
# Visualización 3D y mapa de contorno de una función simbólica
# ------------------------------------------------------------
# --- Definir variables simbólicas ---
x, y = symbols('x y')
# --- Definir la función simbólica g(x, y) ---
g = x**2 + 3*y**2 # Puedes reemplazar esta expresión por tu propia función
# --- Derivadas parciales ---
dg_dx = diff(g, x)
dg_dy = diff(g, y)
# --- Resolver puntos críticos: gradiente igual a cero ---
criticos = solve([dg_dx, dg_dy], (x, y))
# --- Extraer un punto crítico para graficar ---
# Si hay un único resultado como diccionario, lo convertimos a tupla
if isinstance(criticos, dict):
punto_critico = (criticos[x], criticos[y])
else:
punto_critico = (criticos[0][x], criticos[0][y]) # toma el primer punto
# --- Clasificación del punto crítico ---
clasificacion = "Mínimo local" # Cambiar si se conoce la naturaleza del punto
# --- Convertir la función simbólica a numérica para evaluar ---
g_num = lambdify((x, y), g, modules=["numpy", "math"])
# --- Crear malla de valores para graficar ---
x_vals = np.linspace(-5, 5, 200)
y_vals = np.linspace(-5, 5, 200)
X, Y = np.meshgrid(x_vals, y_vals)
# --- Evaluar la función en la malla ---
Z = np.array(g_num(X, Y), dtype=np.float64)
# --- Graficar ---
fig = plt.figure(figsize=(12, 6))
# Gráfico 3D de superficie
ax1 = fig.add_subplot(121, projection='3d')
surf = ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8, edgecolor='none')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('g(x, y)')
ax1.set_title('Superficie 3D de g(x, y)')
# Punto crítico en la superficie
px, py = punto_critico
pz = float(g_num(px, py))
ax1.scatter(px, py, pz, color='red', s=100, label=f'Punto crítico\n{clasificacion}')
ax1.legend()
fig.colorbar(surf, ax=ax1, shrink=0.5, aspect=10)
# Mapa de contorno (vista en 2D)
ax2 = fig.add_subplot(122)
contours = ax2.contour(X, Y, Z, levels=50, cmap='viridis')
ax2.clabel(contours, inline=True, fontsize=8)
ax2.scatter(px, py, color='red', s=50, label=f'Punto crítico\n{clasificacion}')
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title('Mapa de contorno de g(x, y)')
ax2.legend()
# Ajuste de diseño
plt.tight_layout()
plt.show()
# Visualización 3D y mapa de contorno de función con punto crítico en (2, -1/3)
# ---------------------------------------------------------------------------
from sympy import symbols, diff, solve, lambdify
import numpy as np
import matplotlib.pyplot as plt
# --- Definir variables simbólicas ---
x, y = symbols('x y')
# --- Definir la función simbólica g(x, y) con términos lineales ---
g = x**2 + 3*y**2 - 4*x + 2*y + 1
# --- Derivadas parciales ---
dg_dx = diff(g, x)
dg_dy = diff(g, y)
# --- Resolver puntos críticos: gradiente igual a cero ---
criticos = solve([dg_dx, dg_dy], (x, y))
# --- Extraer un punto crítico para graficar ---
if isinstance(criticos, dict):
punto_critico = (criticos[x], criticos[y])
else:
punto_critico = (criticos[0][x], criticos[0][y]) # toma el primer punto
# --- Clasificación del punto crítico ---
clasificacion = "Mínimo local" # corresponde al caso para esta función
# --- Convertir la función simbólica a función numérica para evaluar ---
g_num = lambdify((x, y), g, modules=["numpy", "math"])
# --- Crear malla de valores para graficar ---
x_vals = np.linspace(-1, 5, 300)
y_vals = np.linspace(-2, 2, 300)
X, Y = np.meshgrid(x_vals, y_vals)
# --- Evaluar la función en la malla ---
Z = np.array(g_num(X, Y), dtype=np.float64)
# --- Graficar ---
fig = plt.figure(figsize=(14, 7))
# Gráfico 3D de superficie
ax1 = fig.add_subplot(121, projection='3d')
surf = ax1.plot_surface(X, Y, Z, cmap='viridis', alpha=0.85, edgecolor='none')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.set_zlabel('g(x, y)')
ax1.set_title('Superficie 3D de g(x, y)')
# Punto crítico en la superficie
px, py = punto_critico
pz = float(g_num(px, py))
ax1.scatter(px, py, pz, color='red', s=100, label=f'Punto crítico ({px:.2f}, {py:.2f}, {pz:.2f})\n{clasificacion}')
ax1.legend()
ax1.text(px, py, pz, f' ({px:.2f}, {py:.2f}, {pz:.2f})', color='red', fontsize=10)
# Barra de colores para la superficie
fig.colorbar(surf, ax=ax1, shrink=0.5, aspect=10)
# Mapa de contorno (vista 2D)
ax2 = fig.add_subplot(122)
contours = ax2.contour(X, Y, Z, levels=50, cmap='viridis')
ax2.clabel(contours, inline=True, fontsize=8)
ax2.scatter(px, py, color='red', s=70, label=f'Punto crítico ({px:.2f}, {py:.2f})\n{clasificacion}')
ax2.text(px, py, f' ({px:.2f}, {py:.2f})', color='red', fontsize=10)
ax2.set_xlabel('x')
ax2.set_ylabel('y')
ax2.set_title('Mapa de contorno de g(x, y)')
ax2.legend()
plt.tight_layout()
plt.show()
Explicación del Resultado en el Punto Crítico¶
Al evaluar la matriz Hessiana en el punto crítico encontrado (2, -1/3), obtenemos:
$$ H_g \big|_{(2, -\frac{1}{3})} = \begin{bmatrix} 2 & 0 \\ 0 & 6 \end{bmatrix} $$
Los valores propios de esta matriz son λ₁ = 2 y λ₂ = 6, ambos positivos.
La positividad de todos los valores propios indica que la matriz Hessiana es definida positiva en ese punto, lo que implica que:
- La función g(x, y) tiene una curvatura hacia arriba en ambas direcciones x y y.
- Por lo tanto, el punto (2, -1/3) es un mínimo local.
Este resultado confirma que el punto crítico encontrado corresponde a un valle en la superficie de la función, siendo un lugar donde el valor de la función es localmente el más bajo.
Esta interpretación es clave en Machine Learning, donde optimizamos funciones de costo para encontrar mínimos que representan parámetros óptimos del modelo.
5. Relación con Machine Learning¶
El cálculo del gradiente es una herramienta fundamental en el entrenamiento de modelos de Machine Learning. Por ejemplo, en algoritmos como el Descenso de Gradiente, se utiliza el gradiente de la función de costo (o loss function) para ajustar los parámetros del modelo.
La idea es mover los parámetros en la dirección opuesta al gradiente para minimizar dicha función. Este proceso se repite iterativamente hasta alcanzar un punto crítico, que idealmente es un mínimo local (o global si la función es convexa).
La matriz Hessiana, por su parte, permite analizar la curvatura local de la función y puede utilizarse en variantes avanzadas como el Descenso de Newton o métodos de optimización de segundo orden, que ajustan la magnitud y dirección del paso usando información sobre cómo cambia el gradiente.
Por tanto, este análisis simbólico de derivadas, gradiente y puntos críticos representa una base teórica importante para entender cómo aprenden los modelos de Machine Learning mediante optimización matemática.
6.Documentación y Evidencias¶
Informe Resumen: Análisis Simbólico y Visualización de Funciones con Aplicación en Machine Learning¶
Estructura del Código¶
El proyecto está organizado en bloques de código que incluyen:
- Definición simbólica de funciones matemáticas: uso de
sympy
para definir variables y funciones simbólicas. - Cálculo de derivadas parciales y gradiente: obtención del gradiente para identificar la dirección de máximo incremento de la función.
- Resolución de puntos críticos: solución del sistema donde el gradiente es cero, identificando mínimos, máximos o puntos de silla.
- Análisis del Hessiano: cálculo de la matriz de segundas derivadas para clasificar la naturaleza de los puntos críticos.
- Visualización gráfica: generación de gráficos 3D y mapas de contorno usando
matplotlib
. - Animación dinámica: creación de animaciones para rotar y visualizar mejor la superficie y sus características.
- Documentación y explicaciones: inclusión de comentarios, docstrings y un bloque explicativo que relaciona los conceptos matemáticos con Machine Learning.
Metodología Empleada¶
El análisis parte de definir funciones simbólicas y usar herramientas de cálculo simbólico para obtener derivadas y resolver sistemas de ecuaciones. A partir de los puntos críticos obtenidos, se evalúa la matriz Hessiana para determinar la naturaleza de estos puntos.
Posteriormente, se transforma la función simbólica en una función numérica para evaluar y graficar su comportamiento en un rango de valores. Finalmente, se genera una animación que rota la gráfica 3D para facilitar la comprensión visual.
Relevancia en Machine Learning¶
El cálculo del gradiente y la clasificación de puntos críticos son fundamentales en técnicas de optimización usadas en Machine Learning, especialmente en algoritmos como el Descenso de Gradiente. Este método ajusta iterativamente los parámetros del modelo para minimizar una función de costo, moviéndose en dirección opuesta al gradiente.
Además, la matriz Hessiana proporciona información sobre la curvatura de la función de costo, que es usada en métodos avanzados de optimización de segundo orden, como el Descenso de Newton.
Por tanto, entender y visualizar estos conceptos permite comprender mejor el funcionamiento interno del entrenamiento de modelos de Machine Learning y la importancia de la optimización matemática en la mejora del rendimiento de los algoritmos.
Desarrollado por Julián Gómez Brizuela
Bonus: Animación interactiva de superficie 3D y punto crítico¶
# Variables simbólicas
x, y = symbols('x y')
# Función simbólica a analizar.
g = x**2 + 3*y**2 - 4*x + 2*y + 1
# Derivadas parciales y puntos críticos
dg_dx = diff(g, x)
dg_dy = diff(g, y)
criticos = solve([dg_dx, dg_dy], (x, y))
if isinstance(criticos, dict):
punto_critico = (criticos[x], criticos[y])
else:
punto_critico = (criticos[0][x], criticos[0][y])
clasificacion = "Mínimo local"
# Función numérica
g_num = lambdify((x, y), g, modules=["numpy", "math"])
# Meshgrid centrado
x_vals = np.linspace(0, 4, 200)
y_vals = np.linspace(-2, 1, 200)
X, Y = np.meshgrid(x_vals, y_vals)
Z = np.array(g_num(X, Y), dtype=np.float64)
# Figura y eje 3D
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
# Superficie
surf = ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.8, edgecolor='none')
# Punto crítico y etiqueta
px, py = punto_critico
pz = float(g_num(px, py))
pt = ax.scatter(px, py, pz, color='red', s=100, label=f'Punto crítico\n{clasificacion}')
label = ax.text(px, py, pz, f'({px:.2f}, {py:.2f}, {pz:.2f})', color='red', fontsize=10)
ax.legend()
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('g(x, y)')
ax.set_title('Superficie 3D de g(x,y)')
# Animación
frames = 320
pause_frames = 30
def update(frame):
if frame < pause_frames:
azim = 45
elif frame > frames - pause_frames:
azim = 45
else:
azim = 45 + 360 * (frame - pause_frames) / (frames - 2*pause_frames)
ax.view_init(elev=30, azim=azim)
return surf, pt, label
anim = FuncAnimation(fig, update, frames=frames, interval=50, blit=False)
plt.close(fig)
html_anim = anim.to_html5_video()
HTML(html_anim)