Descenso del Gradiente¶
Estudiante: Alexander Oviedo Fadul
Asignatura: NRC-198 Deep Learning - Semana 3
Fecha: Marzo 2026
Introducción¶
El descenso del gradiente es un algoritmo de optimización fundamental en Deep Learning. Se utiliza para minimizar la función de error (pérdida) ajustando iterativamente los pesos y sesgos de una red neuronal.
Concepto clave: Cuando inicializamos una red neuronal, los pesos y el bias se asignan de forma aleatoria, lo que produce un error inicial alto. El descenso del gradiente calcula la derivada de la función de error respecto a cada peso, y usa esa pendiente para actualizar los pesos en la dirección que reduce el error.
Instrucciones del notebook¶
- Cargue las librerías que necesite: numpy, matplotlib.pyplot, etc.
- Para cargar las librerías utilice
importseguido del nombre de la librería y de un alias (as). Ejemplo:import pandas as pd - Ejecute de forma secuencial de arriba hacia abajo las celdas de código dando click al botón play o ctrl+enter
- Al ejecutar la celda debe marcar un check en color verde al lado izquierdo de la celda de código
- El símbolo
#es para comentarios dentro del código - Para subir un archivo a Google Colab se puede hacer click en la parte izquierda, donde aparece una carpeta
El descenso del gradiente se utiliza para alcanzar el mínimo de la función de error (se produce al dar los valores iniciales a los pesos de las neuronas de la red neuronal y el bias), para actualizar los pesos y el bias o sesgo en el entrenamiento de la red neuronal profunda artificial.
# Importamos los módulos a usar.
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
Parte 1: Función base y su gráfica¶
Definimos la función f(x) = x² + 1 y la visualizamos para entender su comportamiento. Esta es una parábola con vértice en (0, 1), lo que significa que el mínimo global está en x = 0.
# Creamos la función a optimizar y la graficamos.
# lambda es un tipo de función llamada anónima.
funcion = lambda x: x**2 + 1
# Se crean 200 valores espaciados entre x=-10 y x=10
x = np.linspace(-10, 10, 200)
# Graficamos la función.
plt.figure(figsize=(8, 5))
plt.plot(x, funcion(x), color='steelblue', linewidth=2)
plt.title('Función f(x) = x² + 1', fontsize=14)
plt.xlabel('x', fontsize=12)
plt.ylabel('f(x)', fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()
Parte 2: Implementación del Descenso del Gradiente¶
La derivada de f(x) = x² + 1 es f'(x) = 2x. Esta derivada nos indica la pendiente en cada punto, y la usamos para movernos hacia el mínimo.
# Creamos la función de la derivada de x.
def derivada(x):
x_derivada = 2*x
return x_derivada # Regresa la derivada de la función inicial
# Creamos la función del gradiente
def gradiente(x_inicio, precision, l_r):
"""
Implementa el descenso del gradiente.
Parámetros:
- x_inicio: valor inicial de x desde donde comienza la búsqueda
- precision: criterio de parada (diferencia mínima entre iteraciones)
- l_r: learning rate (tasa de aprendizaje)
"""
x_list, y_list = [x_inicio], [funcion(x_inicio)]
while True:
d_x = -derivada(x_inicio)
x_inicio = x_inicio + (l_r * d_x)
x_list.append(x_inicio)
y_list.append(funcion(x_inicio))
if abs(x_list[-1] - x_list[-2]) <= precision:
break
print("El mínimo local se produce en: " + str(x_inicio))
print("Valor de f(x) en el mínimo: " + str(funcion(x_inicio)))
print("Número de pasos: " + str(len(x_list)))
plt.figure(figsize=(8, 5))
plt.scatter(x_list, y_list, c='b', zorder=5, label='Pasos del gradiente')
plt.plot(x_list, y_list, c='b', alpha=0.5)
plt.plot(x, funcion(x), c='y', label='f(x) = x² + 1')
plt.title('Gráfico del descenso del gradiente paso a paso')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
Llamamos la función gradiente pasando los parámetros: punto inicial de x, precisión y learning rate
# Prueba 1
gradiente(-8, 0.0001, 0.05)
El mínimo local se produce en: -0.000835965410654226 Valor de f(x) en el mínimo: 1.0000006988381678 Número de pasos: 88
Variemos los 3 valores del Descenso del Gradiente
gradiente(-8, 0.01, 0.03)
El mínimo local se produce en: -0.15250097396828877 Valor de f(x) en el mínimo: 1.0232565470612767 Número de pasos: 65
gradiente(5, 0.01, 0.03)
El mínimo local se produce en: 0.15636149203779828 Valor de f(x) en el mínimo: 1.0244489161922865 Número de pasos: 57
Ejercicio: Descenso del Gradiente para f(x) = x² + 5x + 6¶
Análisis teórico:
- La función f(x) = x² + 5x + 6 es una parábola.
- Su derivada es f'(x) = 2x + 5.
- El mínimo se encuentra donde f'(x) = 0, es decir x = -2.5
- El valor mínimo es f(-2.5) = 6.25 - 12.5 + 6 = -0.25
Desde mi experiencia automatizando procesos en la Rama Judicial, este tipo de optimización es análogo a ajustar parámetros de un modelo de clasificación de tutelas: se parte de valores arbitrarios y se refina hasta encontrar el mínimo error.
# Definimos la nueva función: f(x) = x² + 5x + 6
funcion_nueva = lambda x: x**2 + 5*x + 6
# Derivada: f'(x) = 2x + 5
def derivada_nueva(x):
return 2*x + 5
def gradiente_nuevo(x_inicio, precision, l_r):
"""
Descenso del gradiente para f(x) = x² + 5x + 6
"""
x_list, y_list = [x_inicio], [funcion_nueva(x_inicio)]
while True:
d_x = -derivada_nueva(x_inicio)
x_inicio = x_inicio + (l_r * d_x)
x_list.append(x_inicio)
y_list.append(funcion_nueva(x_inicio))
if abs(x_list[-1] - x_list[-2]) <= precision:
break
print('='*50)
print(f'Punto de inicio: x = {x_list[0]}')
print(f'Precisión: {precision}')
print(f'Learning rate: {l_r}')
print('-'*50)
print(f'El mínimo se alcanza en x = {x_inicio:.6f}')
print(f'Valor de f(x) en el mínimo: y = {funcion_nueva(x_inicio):.6f}')
print(f'Número de pasos: {len(x_list)}')
print('='*50)
x_rango = np.linspace(-10, 10, 200)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Gráfico 1: Función completa
axes[0].plot(x_rango, funcion_nueva(x_rango), c='green', linewidth=2, label='f(x) = x²+5x+6')
axes[0].axhline(y=0, color='gray', linestyle='--', alpha=0.5)
axes[0].axvline(x=-2.5, color='red', linestyle=':', alpha=0.7, label='Mínimo teórico (x=-2.5)')
axes[0].set_title('Función f(x) = x² + 5x + 6', fontsize=12)
axes[0].set_xlabel('x')
axes[0].set_ylabel('f(x)')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Gráfico 2: Descenso del gradiente
axes[1].plot(x_rango, funcion_nueva(x_rango), c='green', alpha=0.5, linewidth=2, label='f(x)')
axes[1].scatter(x_list, y_list, c='red', s=30, zorder=5, label='Pasos')
axes[1].plot(x_list, y_list, c='red', alpha=0.4, linestyle='--')
axes[1].scatter(x_list[-1], y_list[-1], c='darkred', s=100, marker='*', zorder=6, label='Mínimo encontrado')
axes[1].set_title('Descenso del Gradiente - Paso a Paso', fontsize=12)
axes[1].set_xlabel('x')
axes[1].set_ylabel('f(x)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Prueba 1: desde x=8
gradiente_nuevo(8, 0.0001, 0.05)
================================================== Punto de inicio: x = 8 Precisión: 0.0001 Learning rate: 0.05 -------------------------------------------------- El mínimo se alcanza en x = -2.499111 Valor de f(x) en el mínimo: y = -0.249999 Número de pasos: 90 ==================================================
# Prueba 2: desde x=-8
gradiente_nuevo(-8, 0.01, 0.03)
================================================== Punto de inicio: x = -8 Precisión: 0.01 Learning rate: 0.03 -------------------------------------------------- El mínimo se alcanza en x = -2.651977 Valor de f(x) en el mínimo: y = -0.226903 Número de pasos: 59 ==================================================
# Prueba 3: desde x=0, mayor learning rate
gradiente_nuevo(0, 0.001, 0.1)
================================================== Punto de inicio: x = 0 Precisión: 0.001 Learning rate: 0.1 -------------------------------------------------- El mínimo se alcanza en x = -2.496131 Valor de f(x) en el mínimo: y = -0.249985 Número de pasos: 30 ==================================================
Análisis de resultados¶
Convergencia al mínimo: El algoritmo converge al mínimo teórico en x = -2.5, donde f(-2.5) = -0.25.
Efecto del learning rate: Un learning rate más alto (0.1) llega al mínimo en menos pasos. Sin embargo, un valor demasiado grande podría causar divergencia. Con l_r = 0.03 la convergencia es más estable pero requiere más iteraciones.
Efecto de la precisión: Mayor precisión (0.0001) implica más iteraciones para una respuesta más exacta.
Aplicación en redes neuronales: Este principio se aplica en el entrenamiento de redes neuronales, donde variantes como SGD, Adam y RMSprop ajustan los pesos para minimizar la función de pérdida.
Conclusiones¶
El descenso del gradiente es un algoritmo iterativo que encuentra el mínimo de una función usando la derivada. Es la base de cómo las redes neuronales aprenden.
Los tres parámetros (punto inicial, precisión y learning rate) impactan directamente la velocidad y calidad de la solución.
La visualización gráfica permite entender cómo el algoritmo se mueve hacia el mínimo, análogo a lo que ocurre internamente al entrenar modelos con TensorFlow o PyTorch.
En aplicaciones reales como el sistema MARDUK para análisis de tutelas judiciales, el descenso del gradiente opera sobre miles de parámetros simultáneamente, buscando minimizar el error de predicción.
Referencias¶
- Fandango, A. (2018). Mastering TensorFlow 1.x, pp. 73-111.
- Galea, A., y Capelo, L. (2018). Applied Deep Learning with Python, pp. 6-61 y pp. 186-194.
- Hodnett, M., y Wiley, J. (2018). R Deep Learning Essentials, pp. 60-96.