Desmitificando optimizaciones para el aprendizaje automático

Pendiente de gradiente para aprendizaje automático

La optimización es el ingrediente más esencial en la receta de algoritmos de aprendizaje automático. Comienza con la definición de algún tipo de función de pérdida / función de costo y finaliza minimizando el uso de una u otra rutina de optimización. La elección del algoritmo de optimización puede marcar la diferencia entre obtener una buena precisión en horas o días. Las aplicaciones de optimización no tienen límites y es un tema ampliamente investigado en la industria y el mundo académico. En este artículo analizaremos varios algoritmos de optimización utilizados en el ámbito del aprendizaje profundo. (Puede pasar por este artículo para comprender los conceptos básicos de las funciones de pérdida)

Gradiente de gradiente estocástico

Gradiente de gradiente estocástico (SGD) es el algoritmo de optimización más simple utilizado para encontrar parámetros que minimizan la función de costo dada. Aparentemente, para que el descenso del gradiente converja al mínimo óptimo, la función de costo debe ser convexa . Para el propósito de demostración, imagine la siguiente representación gráfica para la función de costo.

Ilustración del descenso del gradiente

Comenzamos definiendo algunos valores iniciales aleatorios para los parámetros. El objetivo del algoritmo de optimización es encontrar valores de parámetros que correspondan al valor mínimo de la función de costo. Específicamente, el descenso de gradiente comienza con el cálculo de gradientes (derivadas) para cada uno de los parámetros w.r.t función de costo. Esos gradientes nos dan un ajuste numérico que debemos hacer a cada parámetro para minimizar la función de costo. Este proceso continúa hasta que lleguemos al mínimo local / global (la función de costo es mínima cuando se trata de valores circundantes). Matemáticamente,

 para i en rango (iterations_count):           
     param  _  gradientes = evaluate_gradients (loss_function, 
 data, 
 params) 
     params - = learning_rate * param_gradients [19659012] Efecto de la velocidad de aprendizaje en el descenso del gradiente

La ​​tasa de aprendizaje define cuánto deben cambiar los parámetros en cada iteración. En otras palabras, controla qué tan rápido o lento debemos convergir al mínimo. Por un lado, la pequeña tasa de aprendizaje puede llevar a las iteraciones a converger, una gran tasa de aprendizaje puede sobrepasar el mínimo como se puede ver en la figura anterior.

Aunque es bastante fácil de aplicar en la práctica, tiene bastantes desventajas cuando se trata de redes neuronales ya que estas redes tienen un gran número de parámetros para encajar. Para ilustrar los problemas con el descenso de gradientes, supongamos que tenemos una función de costo con solo dos parámetros. Suponga que la función de costo es muy sensible a los cambios en uno de los parámetros, por ejemplo, en dirección vertical y menos a otro parámetro, es decir, dirección horizontal (Esto significa que la función de costo tiene un alto número de condición).

Movimiento en zig zag con pendiente descendente

Si ejecutamos pendiente de gradiente estocástica en esta función, obtenemos un tipo de comportamiento en zigzag. En esencia, SGD está progresando lentamente hacia una dirección menos sensible y más hacia uno altamente sensible y, por lo tanto, no se alinea en la dirección de mínimo. En la práctica, la red neuronal profunda podría tener millones de parámetros y, por lo tanto, millones de direcciones para acomodar los ajustes de gradiente y agravar el problema.

Otro problema con SGD es el problema del mínimo local o puntos de silla de montar . Los puntos de silla son puntos donde el gradiente es cero en todas las direcciones. En consecuencia, nuestro SGD estará atascado allí solamente. Por otro lado, los mínimos locales son puntos que son mínimos alrededor, pero no mínimos. Como el gradiente será cero en el mínimo local, nuestro descenso de gradiente lo reportará como valor mínimo cuando el mínimo global esté en otro lugar.

Para rectificar los problemas con el descenso del gradiente de vainilla, se desarrollaron varios algoritmos avanzados de optimización en los últimos años. Vamos a ver a través de ellos uno por uno.

Descenso de gradiente estocástico con impulso

Para entender la dinámica detrás de las optimizaciones avanzadas, primero tenemos que entender el concepto de promedio ponderado exponencialmente . Supongamos que recibimos datos de las temperaturas por día de cualquier ciudad en particular durante los 365 días del año. Al trazarlo, obtenemos un gráfico en la esquina superior izquierda.

Demostración del promedio ponderado exponencialmente

Ahora, si deseamos calcular la temperatura promedio local a lo largo del año procederíamos de la siguiente manera. [19659009] NOTA: –
alpha = 0.9

 es peso elegido al azar. 
      t (i)  es la temperatura en el día. 
      v (i)  es la temperatura promedio para el día con un promedio de  1 / (1 - alfa)  días. [19659026] v (0) = 0 
 v (1) = 0.9 * v (0) + 0.1 * t (1) 
 v (2) = 0.9 * v (1) + 0.1 * t (2) 
 ... 
 v (i) = alfa * v (i-1) + (1 - alfa) * t (i)

Cada día calculamos el promedio ponderado de las temperaturas del día anterior y temperatura actual del día. El diagrama para el cálculo anterior se muestra en la esquina superior derecha. Esta gráfica está promediando la temperatura en los últimos 10 días (alfa = 0.9). La parte inferior izquierda (línea verde) muestra los datos del promediado de la gráfica durante los últimos 50 días (alfa = 0,98).

Un punto importante a notar aquí es que promediamos más días de la parcela que será menos sensible a los cambios en temperatura. Por el contrario, si promediamos durante menos días, la trama será más sensible a los cambios de temperatura y, por lo tanto, al comportamiento irregular.

Este aumento en la latencia se debe al hecho de que damos más peso a las temperaturas del día anterior que la temperatura actual del día.

Hasta ahora todo bien, pero la pregunta es qué nos compra todo esto. De manera similar, al promediar gradientes en relación con valores pasados, tendemos a reducir las oscilaciones en una dirección más sensible y hacer que converja más rápido.

En la práctica, los algoritmos de optimización basados ​​en ímpetu son casi siempre más rápidos que el descenso en gradiente de vainilla. Matemáticamente,

 momento = 0 
 para i en rango (iterations_count):           
     param  _  gradientes = evaluate_gradients (loss_function, 
 data, 
 params) 
 momento = gamma * momento +   param  _  gradientes 
     params   + = momento_preparación * momento 
 (donde  momento  está construyendo promedio móvil de gradientes. 
                  ] gamma  da tipo de fricción = 0.9 o 0.99). 

AdaGrad Optimization

La idea es que, para cada parámetro, almacenemos la suma de cuadrados de todos sus gradientes históricos. Esta suma se usa luego para escalar la tasa de aprendizaje.

Observe que, a diferencia de las optimizaciones anteriores, aquí tenemos una tasa de aprendizaje diferente para cada parámetro.

 squared_gradients = 0 
 para i en rango (iterations_count ):           
     param  _  gradientes = evaluate_gradients (loss_function, 
 data, 
 params) 
 squared_gradients + = param  _  gradientes * param  _  gradientes 
 params - = learning_rate * param  _  gradientes / 
 (np.sqrt (squared_gradients) + 1e-8) 
                            {1e-8 es para evitar dividir por cero }  

¿Ahora la pregunta es cómo nos ayuda esta escala cuando tenemos un número de condición muy alto para nuestra función de pérdida?

Para parámetros con valores de gradiente alto, el término cuadrado será grande y por lo tanto se dividirá con grande término sería haz que el gradiente acelere lentamente en esa dirección. De manera similar, los parámetros con gradientes bajos producirán términos cuadrados más pequeños y por lo tanto el gradiente se acelerará más rápido en esa dirección.

Sin embargo, observe que, como el gradiente se cuadra en cada paso, la estimación móvil crecerá monótonamente con el transcurso del tiempo y, por lo tanto, El tamaño del paso que nuestro algoritmo tardará en converger al mínimo se haría cada vez más pequeño.

Y en cierto sentido, esto es beneficioso para problemas convexos ya que se espera que disminuya al mínimo en este caso. Sin embargo, el mismo regalo se convierte en una maldición en caso de problemas de optimización no convexa ya que aumenta la probabilidad de quedar atrapado en los puntos de silla.

Optimización de RMSProp

Esta es una pequeña variación de AdaGrad y funciona mejor en la práctica, ya que aborda los problemas que deja abierta. Similar a AdaGrad, aquí también mantendremos la estimación del gradiente cuadrado, pero en lugar de dejar que esa estimación cuadrada se acumule durante el entrenamiento, más bien dejaremos que esa estimación se desvanezca gradualmente . Para lograr esto, multiplicamos la estimación actual de gradientes cuadrados con la tasa de disminución.

 squared_gradients = 0 
 para i en rango (iterations_count):           
     param  _  gradients = evaluate_gradients (loss_function , 
 data, 
 params) 
 squared_gradients = decay_rate * squared_gradients + (1 - 
 decay_rate) * param  _  gradientes * param  _  gradientes 
 params - = learning_rate * param  _  gradients / 
 (np.sqrt (squared_gradients) + 1e-8) 

Adam

Esto incorpora todas las características agradables de RMSProp y descenso de gradiente con ímpetu.

Específicamente, este algoritmo calcula una media móvil exponencial de gradientes y los gradientes cuadrados, mientras que los parámetros beta_1 y beta_2 controla las tasas de disminución de estos promedios móviles.

 first_moment = 0 
 second_moment = 0 
 para el paso en el rango (iterations_count): 
     param  _  gradients = evaluate_gradients ( loss_function, 
 data, 
 params) 
 first_moment = beta_1 * first_moment + (1 - beta_1) * 
 param  _  gradientes 
 second_moment = beta_2 * second_moment + (1 - beta_2) * 
 param  _  gradientes * param  _  gradientes       
     params - = learning_rate * first_moment / (np.sqrt (second_moment) + 
 1e-8 ) 

Observe que hemos inicializado second_moment a cero. Entonces, al principio, second_moment se calcularía como algo muy cercano a cero. En consecuencia, estamos actualizando los parámetros dividiéndolos con un número muy pequeño y, por lo tanto, haciendo grandes actualizaciones al parámetro. Eso significa inicialmente, el algoritmo haría pasos más grandes . Para rectificar, creamos una estimación imparcial de esos primeros y segundos momentos incorporando el paso actual. Y luego actualizamos los parámetros basados ​​en estas estimaciones insesgadas en lugar del primer y el segundo momento. Matemáticamente,

 first_moment = 0 
 second_moment = 0 
 para el paso en rango (iterations_count): 
 param  _  gradients = evaluate_gradients (loss_function, 
 data, 
 params) 
 first_moment = beta_1 * first_moment + (1 - beta_1) * 
 param  _  gradientes 
 second_moment = beta_2 * second_moment + (1 - beta_2) * 
 param  _  gradientes * param  _  gradientes       
     first_bias_correction  =  first_moment / (1 - beta_1 ** paso) 
 second_bias_correction = second_moment / (1 - beta_2 ** paso) 
 params - = learning_rate * first_bias_correction / 
 (np.sqrt (second_bias_correction) + 1e-8) 

Figura debajo de la demostración prueba el rendimiento de cada uno de los algoritmos de optimización a medida que las iteraciones pasan. Claramente, agregar ímpetu aumenta la precisión. En la práctica, sin embargo, se sabe que Adam funciona muy bien con grandes conjuntos de datos y características complejas.

Medida de rendimiento para optimizaciones

Recursos

RMSprop – Algoritmos de optimización | Coursera

Puede encontrar este para obtener más antecedentes matemáticos. Háganme saber a través de sus comentarios las modificaciones / mejoras que este artículo podría acomodar.


Desmitificación de optimizaciones para el aprendizaje automático se publicó originalmente en Towards Data Science en Medium, donde las personas continúan la conversación resaltando y respondiendo a esta historia.

Dejá un comentario