Cómo implementar la optimización del descenso de gradientes desde cero

Última actualización el 27 de abril de 2021

Descenso de gradiente es un algoritmo de optimización que sigue el gradiente negativo de una función objetivo para localizar el mínimo de la función.

Es una técnica simple y efectiva que se puede implementar con solo unas pocas líneas de código. También proporciona la base para muchas extensiones y modificaciones que pueden resultar en un mejor rendimiento. El algoritmo también proporciona la base para la extensión ampliamente utilizada llamada descenso de gradiente estocástico, que se utiliza para entrenar redes neuronales de aprendizaje profundo.

En este tutorial, descubrirá cómo implementar la optimización del descenso de gradientes desde cero.


Recomendado: ¿Qué es el Big data?.


Después de completar este tutorial, sabrá:

  • El descenso de gradiente es un procedimiento general para optimizar una función objetivo diferenciable.
  • Cómo implementar el algoritmo de descenso de gradientes desde cero en Python.
  • Cómo aplicar el algoritmo de descenso de gradiente a una función objetivo.

Empecemos.

Cómo implementar la optimización del descenso de gradientes desde cero

Cómo implementar la optimización del descenso de gradientes desde cero
Foto de Bernd Thaller, algunos derechos reservados.

Descripción general del tutorial

Este tutorial se divide en tres partes; ellos son:

  1. Descenso de gradiente
  2. Algoritmo de descenso de gradiente
  3. Ejemplo resuelto de descenso de gradiente

Optimización del descenso de gradientes

El descenso de gradientes es un algoritmo de optimización.

Técnicamente se le conoce como un algoritmo de optimización de primer orden, ya que hace uso explícito de la derivada de primer orden de la función objetivo de destino.

Los métodos de primer orden se basan en la información del gradiente para ayudar a dirigir la búsqueda de un mínimo …

– Página 69, Algoritmos de optimización, 2019.

La derivada de primer orden, o simplemente la “derivada”, es la tasa de cambio o pendiente de la función objetivo en un punto específico, p. Ej. para una entrada específica.

Si la función de destino toma múltiples variables de entrada, se denomina función multivariante y las variables de entrada se pueden considerar como un vector. A su vez, la derivada de una función objetivo multivariante también se puede tomar como un vector y se denomina generalmente “gradiente”.

  • Degradado: Derivada de primer orden para una función objetivo multivariante.

La derivada o el gradiente apunta en la dirección del ascenso más pronunciado de la función de destino para una entrada.

El gradiente apunta en la dirección del ascenso más pronunciado del hiperplano tangente …

– Página 21, Algoritmos de optimización, 2019.

Específicamente, el signo del gradiente le dice si la función de destino está aumentando o disminuyendo en ese punto.

  • Gradiente positivo: La función está aumentando en ese punto.
  • Gradiente negativo: La función está disminuyendo en ese punto.

El descenso de gradiente se refiere a un algoritmo de optimización de minimización que sigue el negativo del gradiente cuesta abajo de la función objetivo para localizar el mínimo de la función.

De manera similar, podemos referirnos al ascenso de gradiente para la versión de maximización del algoritmo de optimización que sigue el gradiente cuesta arriba hasta el máximo de la función objetivo.

  • Descenso de gradiente: Optimización de minimización que sigue el negativo del gradiente al mínimo de la función objetivo.
  • Ascenso en gradiente: Optimización de maximización que sigue el gradiente al máximo de la función objetivo.

Un aspecto fundamental de los algoritmos de descenso de gradientes es la idea de seguir el gradiente de la función objetivo.

Por definición, el algoritmo de optimización solo es apropiado para funciones de destino donde la función derivada está disponible y se puede calcular para todos los valores de entrada. Esto no se aplica a todas las funciones de destino, solo a las llamadas funciones diferenciables.

El principal beneficio del algoritmo de descenso de gradiente es que es fácil de implementar y eficaz en una amplia gama de problemas de optimización.

Los métodos de degradado son simples de implementar y, a menudo, funcionan bien.

– Página 115, Introducción a la optimización, 2001.

El descenso de gradiente se refiere a una familia de algoritmos que utilizan la derivada de primer orden para navegar al óptimo (mínimo o máximo) de una función objetivo.

Hay muchas extensiones del enfoque principal que normalmente reciben el nombre de la característica agregada al algoritmo, como el descenso de gradiente con impulso, el descenso de gradiente con gradientes adaptativos, etc.

El descenso de gradiente también es la base del algoritmo de optimización utilizado para entrenar redes neuronales de aprendizaje profundo, denominado descenso de gradiente estocástico o SGD. En esta variación, la función objetivo es una función de error y el gradiente de la función se aproxima a partir del error de predicción en muestras del dominio del problema.

Ahora que estamos familiarizados con una idea de alto nivel de la optimización del descenso de gradientes, veamos cómo podríamos implementar el algoritmo.

Algoritmo de descenso de gradiente

En esta sección, veremos más de cerca el algoritmo de descenso de gradiente.

El algoritmo de descenso de gradiente requiere una función objetivo que se está optimizando y la función derivada para la función objetivo.

La función objetivo F() devuelve una puntuación para un conjunto dado de entradas y la función derivada F'() da la derivada de la función objetivo para un conjunto dado de entradas.

  • Función objetiva: Calcula una puntuación para un conjunto determinado de parámetros de entrada.
    Función derivada: Calcula la derivada (gradiente) de la función objetivo para un conjunto de entradas dado.

El algoritmo de descenso de gradiente requiere un punto de partida (X) en el problema, como un punto seleccionado al azar en el espacio de entrada.

Luego se calcula la derivada y se da un paso en el espacio de entrada que se espera que resulte en un movimiento cuesta abajo en la función objetivo, asumiendo que estamos minimizando la función objetivo.

Un movimiento cuesta abajo se realiza calculando primero qué tan lejos moverse en el espacio de entrada, calculado como el tamaño del paso (llamado alfa o la tasa de aprendizaje) multiplicado por el gradiente. Esto luego se resta del punto actual, asegurando que nos movemos contra el gradiente o hacia abajo de la función de destino.

  • x_nuevo = x – alfa * f ‘(x)

Cuanto más pronunciada sea la función objetivo en un punto dado, mayor será la magnitud del gradiente y, a su vez, mayor será el paso dado en el espacio de búsqueda.

El tamaño del paso realizado se escala mediante un hiperparámetro de tamaño de paso.

  • Numero de pie (alfa): Hiperparámetro que controla qué tan lejos moverse en el espacio de búsqueda contra el gradiente en cada iteración del algoritmo.

Si el tamaño del paso es demasiado pequeño, el movimiento en el espacio de búsqueda será pequeño y la búsqueda llevará mucho tiempo. Si el tamaño del paso es demasiado grande, la búsqueda puede rebotar en el espacio de búsqueda y omitir los óptimos.

Tenemos la opción de dar pasos muy pequeños y reevaluar el gradiente en cada paso, o podemos dar pasos grandes cada vez. El primer enfoque da como resultado un método laborioso para llegar al minimizador, mientras que el segundo enfoque puede resultar en un camino más en zigzag hacia el minimizador.

– Página 114, Introducción a la optimización, 2001.

Encontrar un buen tamaño de paso puede requerir algo de prueba y error para la función de destino específica.

La dificultad de elegir el tamaño del paso puede dificultar la búsqueda del óptimo exacto de la función de destino. Muchas extensiones implican adaptar la tasa de aprendizaje a lo largo del tiempo para dar pasos más pequeños o pasos de diferentes tamaños en diferentes dimensiones y así sucesivamente para permitir que el algoritmo se concentre en la función óptima.

El proceso de calcular la derivada de un punto y calcular un nuevo punto en el espacio de entrada se repite hasta que se cumple alguna condición de parada. Esto podría ser un número fijo de pasos o evaluaciones de la función objetivo, una falta de mejora en la evaluación de la función objetivo durante cierto número de iteraciones o la identificación de un área plana (estacionaria) del espacio de búsqueda representada por un gradiente de cero.

  • Condición de parada: Decisión de cuándo finalizar el procedimiento de búsqueda.

Veamos cómo podríamos implementar el algoritmo de descenso de gradientes en Python.

Primero, podemos definir un punto inicial como un punto seleccionado al azar en el espacio de entrada definido por límites.

Los límites se pueden definir junto con una función objetivo como una matriz con un valor mínimo y máximo para cada dimensión. La función rand () NumPy se puede utilizar para generar un vector de números aleatorios en el rango 0-1.

Luego podemos calcular la derivada del punto usando una función llamada derivado().

Y da un paso en el espacio de búsqueda hacia un nuevo punto cuesta abajo del punto actual.

La nueva posición se calcula utilizando el gradiente calculado y la Numero de pie hiperparámetro.

Luego podemos evaluar este punto e informar el desempeño.

Este proceso se puede repetir para un número fijo de iteraciones controladas mediante un nitro hiperparámetro.

Podemos unir todo esto en una función llamada descenso de gradiente().

La función toma el nombre de las funciones objetivo y de gradiente, así como los límites de las entradas de la función objetivo, el número de iteraciones y el tamaño del paso, luego devuelve la solución y su evaluación al final de la búsqueda.

El algoritmo de optimización de descenso de gradiente completo implementado como una función se enumera a continuación.

Ahora que estamos familiarizados con el algoritmo de descenso de gradientes, veamos un ejemplo trabajado.

Ejemplo resuelto de descenso de gradiente

En esta sección, trabajaremos a través de un ejemplo de aplicación de descenso de gradiente a una función de optimización de prueba simple.

Primero, definamos una función de optimización.

Usaremos una función unidimensional simple que cuadra la entrada y define el rango de entradas válidas de -1.0 a 1.0.

La objetivo() función a continuación implementa esta función.

Luego, podemos muestrear todas las entradas en el rango y calcular el valor de la función objetivo para cada una.

Finalmente, podemos crear una gráfica de línea de las entradas (eje x) versus los valores de la función objetivo (eje y) para obtener una intuición de la forma de la función objetivo que buscaremos.

El siguiente ejemplo une esto y proporciona un ejemplo de cómo trazar la función de prueba unidimensional.

La ejecución del ejemplo crea un gráfico de líneas de las entradas de la función (eje x) y la salida calculada de la función (eje y).

Podemos ver la familiar forma de U llamada parábola.

Gráfico de línea de función unidimensional simple

Gráfico de línea de función unidimensional simple

A continuación, podemos aplicar el algoritmo de descenso de gradiente al problema.

Primero, necesitamos una función que calcule la derivada de esta función.

La derivada de x ^ 2 es x * 2 y la derivado() La función implementa esto a continuación.

Luego podemos definir los límites de la función objetivo, el tamaño del paso y el número de iteraciones del algoritmo.

Usaremos un tamaño de paso de 0.1 y 30 iteraciones, ambas encontradas después de un poco de experimentación.

Uniendo esto, el ejemplo completo de aplicación de la optimización del descenso de gradiente a nuestra función de prueba unidimensional se enumera a continuación.

La ejecución del ejemplo comienza con un punto aleatorio en el espacio de búsqueda y luego aplica el algoritmo de descenso de gradiente, informando el rendimiento a lo largo del camino.

Nota: Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o las diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y compare el resultado promedio.

En este caso, podemos ver que el algoritmo encuentra una buena solución después de aproximadamente 20-30 iteraciones con una evaluación de función de aproximadamente 0.0. Tenga en cuenta que el óptimo para esta función está en f (0.0) = 0.0.

Ahora, permítanos sentir la importancia de un buen tamaño de paso.

Establezca el tamaño del paso en un valor grande, como 1.0, y vuelva a ejecutar la búsqueda.

Ejecute el ejemplo con el tamaño de paso más grande e inspeccione los resultados.

Nota: Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o las diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y compare el resultado promedio.

Podemos ver que la búsqueda no encuentra los óptimos, sino que rebota alrededor del dominio, en este caso entre los valores 0,64820935 y -0,64820935.

Ahora, pruebe con un tamaño de paso mucho más pequeño, como 1e-8.

Nota: Sus resultados pueden variar dada la naturaleza estocástica del algoritmo o procedimiento de evaluación, o las diferencias en la precisión numérica. Considere ejecutar el ejemplo varias veces y compare el resultado promedio.

Al volver a ejecutar la búsqueda, podemos ver que el algoritmo se mueve muy lentamente por la pendiente de la función objetivo desde el punto de partida.

Estos dos ejemplos rápidos resaltan los problemas al seleccionar un tamaño de paso que es demasiado grande o demasiado pequeño y la importancia general de probar muchos valores de tamaño de paso diferentes para una función objetivo determinada.

Por último, podemos volver a cambiar la tasa de aprendizaje a 0,1 y visualizar el progreso de la búsqueda en un gráfico de la función objetivo.

Primero, podemos actualizar el descenso de gradiente() función para almacenar todas las soluciones y su puntuación encontradas durante la optimización como listas y devolverlas al final de la búsqueda en lugar de la mejor solución encontrada.

Se puede llamar a la función y podemos obtener las listas de las soluciones y sus puntuaciones encontradas durante la búsqueda.

Podemos crear una gráfica lineal de la función objetivo, como antes.

Finally, we can plot each solution found as a red dot and connect the dots with a line so we can see how the search moved downhill.

Tying this all together, the complete example of plotting the result of the gradient descent search on the one-dimensional test function is listed below.

Running the example performs the gradient descent search on the objective function as before, except in this case, each point found during the search is plotted.

Nota: Your results may vary given the stochastic nature of the algorithm or evaluation procedure, or differences in numerical precision. Consider running the example a few times and compare the average outcome.

In this case, we can see that the search started about halfway up the left part of the function and stepped downhill to the bottom of the basin.

We can see that in the parts of the objective function with the larger curve, the derivative (gradient) is larger, and in turn, larger steps are taken. Similarly, the gradient is smaller as we get closer to the optima, and in turn, smaller steps are taken.

This highlights that the step size is used as a scale factor on the magnitude of the gradient (curvature) of the objective function.

Plot of the Progress of Gradient Descent on a One Dimensional Objective Function

Plot of the Progress of Gradient Descent on a One Dimensional Objective Function

Further Reading

This section provides more resources on the topic if you are looking to go deeper.

Books

APIs

Articles

Summary

In this tutorial, you discovered how to implement gradient descent optimization from scratch.

Specifically, you learned:

  • Gradient descent is a general procedure for optimizing a differentiable objective function.
  • How to implement the gradient descent algorithm from scratch in Python.
  • How to apply the gradient descent algorithm to an objective function.

Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.