Muy buenas, me llamo Luis y para hoy les traigo este nuevo post.
Un número cada vez mayor de personas está definiendo las redes de forma procedimental y dependiente de los datos (con bucles y condicionales), lo que les permite cambiar dinámicamente en función de los datos de entrada que se les suministran.
Es muy parecido a un programa normal, excepto que está parametrizado, diferenciado automáticamente y
entrenable / optimizable
.– Yann Lecun, director de la FERIA.
El objetivo general de la programación diferenciable es calcular:
¿Por qué necesitamos poder diferenciar un programa?
Es decir, cuantificar la sensibilidad del programa y sus resultados con respecto a algunos de sus parámetros.
En este artículo, vamos a explicar la Programación diferenciable es mediante el desarrollo desde cero todas las herramientas necesarias para esta nueva clase de programación.
Índice
El panorama
Comencemos con un ejemplo de juguete simple, que muestra cómo escribiríamos un programa para estimar la duración de un viaje en taxi usando un código tradicional independiente de datos:
En este código, calculamos la duración del viaje en taxi utilizando la velocidad media precalculada en Nueva York: alrededor de 30 km / h
. Esta es la forma heredada de hacer un programa, es decir, los datos no influyen en sus parámetros.
Usamos parámetros predefinidos, aquí la velocidad promedio estimada por un experto, multiplicamos la inversa de esta velocidad por la distancia del viaje y obtenemos la duración esperada del viaje.
No importa cuántas veces lo ejecutemos, nunca mejorará. Nunca aprenderá de su error.
Lo que ofrece la Programación Diferenciable es exactamente lo contrario: cada ejecución se puede utilizar para ajustar los parámetros de la aplicación. Veamos cómo se logra esto.
Aprovechar la retroalimentación
Una cosa que vale tanto para las computadoras como para los humanos es que para mejorar, se necesita retroalimentación. E idealmente, necesita una forma de cuantificar sus errores.
En el mundo de la informática, esto se hace fácilmente introduciendo en nuestro código inicial una nueva función que calcula una medida de error relativamente común: el error al cuadrado.
Código aumentado con cálculo de errores.
Una vez que tenga una idea del error, necesita una forma de saber en qué dirección necesita modificar sus parámetros para reducir el error.
Analicemos un ejemplo concreto. Considere un viaje cuya duración fue de 12
minutos y cuya distancia fue de 6 km
. Para predecir con precisión este valor con nuestro modelo, el parámetro correcto para el modelo sería 30 km / h
. Cualquier otro valor resultaría en un error para este viaje.
Echemos un vistazo a la gráfica del error al cuadrado con respecto a nuestro parámetro, la velocidad promedio, para obtener algunas ideas. Todo el código es sencillo:
La curva resultante es:
La curva azul muestra la evolución de la duración del viaje con respecto a la velocidad. Los viajes más rápidos obviamente conducen a viajes de menor duración.
La curva naranja muestra el error como una simple diferencia entre la duración real, aquí 12
minutos, y la duración del viaje dada la velocidad elegida. Este error es cero para la velocidad media real: 30 km / h
.
La curva verde es el error al cuadrado. De forma similar al error, se alcanza cero para una velocidad media de 30 km / h
.
Bajo la colina
Ahora tenemos una forma de cuantificar nuestro error, con respecto al parámetro de entrada de nuestro programa. Lo que necesitamos ahora es alguna indicación de cómo reducir el error.
Digamos que nuestro parámetro, la velocidad media es de 35 km / h
. El error al cuadrado es de alrededor de 2,95
. La pregunta es, ¿debemos reducir la velocidad o aumentarla para reducir el error? Como conocemos el valor óptimo, 30 km / h
, obviamente sabemos que la velocidad debe reducirse. Pero para un problema real, tenemos que descubrirlo.
Una vez más, obtengamos una idea de un gráfico que traza la curva de error al cuadrado, así como la tangente a la curva para una velocidad promedio de 35 km / h
.
Mirando la tangente, está claro que necesitamos seguir su pendiente para disminuir el error. En este caso, esto significa que debemos reducir la velocidad media. Este método se llama descenso de gradiente.
Diferenciación
La forma de saber cómo actualizar nuestro parámetro de manera efectiva es clara: necesitamos calcular la tangente de nuestra función de error y estimar su pendiente. Esto significa que debemos diferenciar la función de error para obtener su gradiente.
Hay varias formas de calcular la derivada de una función. Por ahora, consideraremos sólo dos:
- Diferenciación numérica, utilizando la definición de diferenciación basada en límites
- Cálculo formal, por derivación de la fórmula del error con respecto a la velocidad media.
Veremos una tercera opción muy versátil y robusta más adelante en este artículo.
Aquí está el código utilizado para trazar la curva anterior:
La función speed_error_num_diff
estima el gradiente de error con respecto al error, utilizando la definición de derivada:
Como puede ver, el código es sencillo pero tiene el inconveniente de ser numéricamente inestable y requiere un parámetro adicional: delta.
Dependiendo del valor absoluto del error, se debe ajustar delta.
La alternativa requiere la derivación simbólica de la fórmula del error al cuadrado:
Ambos métodos funcionan y se pueden utilizar para optimizar el parámetro de velocidad promedio. Podemos graficar iteraciones del método de descenso de gradiente y observar que el método converge como se esperaba a 30 km / h
:
El descenso de gradiente se implementa con un bucle y un coeficiente arbitrario constante que especifica el tamaño del paso:
Código utilizado para optimizar el parámetro de velocidad media mediante descenso de gradiente.
Diferenciación automática
Como se muestra arriba, estos dos métodos funcionan, pero no siempre son manejables. El formal implica la derivación simbólica de la fórmula del error, que generalmente no es tan simple como en nuestro ejemplo.
El numérico se utiliza a veces pero adolece de inestabilidad numérica debido a errores de redondeo.
Y ambos requirieron la construcción manual del gradiente.
Afortunadamente, existe un método que construye automáticamente todas las derivadas necesarias: Diferenciación automática.
Este método aprovecha dos hechos:
- En cualquier programa, terminamos haciendo operaciones aritméticas simples (
+ - * /
) y llamando a algunas funciones fundamentales comosen
,cos
,exp
,log
,…
. - La regla de la cadena se puede utilizar para diferenciar expresiones matemáticas complejas.
Para ilustrar cómo funciona este método, vamos a desarrollar una implementación rudimentaria que solo admite operaciones aritméticas. Como recordatorio, aquí está la lista de reglas de derivación para las cuatro operaciones aritméticas:
Hay dos implementaciones principales de diferenciación automática: los modos de avance y retroceso. Como el modo directo es más sencillo de implementar, elegimos este.
1.0
.
Este par de flotadores se llama número dual y matemáticamente hablando constituye un álgebra en la que podemos realizar operaciones estándar.
Todo lo que tenemos que hacer es crear una clase para DualFloat, que sobrecargue los operadores aritméticos y use las reglas de derivación anteriores:
Ahora, tomemos el ejemplo del polinomio f (x) = 3x²
y calculemos su derivada para x = 4
usando la diferenciación automática manualmente.
3
, siendo constante, se inicializa con (3, 0)
, y x
, la variable, se inicializa con (4, 1
). Usando el cálculo recién definido para la multiplicación de números duales, obtenemos:
Y de hecho, la derivada de f (x) es 6x
, por lo tanto, cuando x = 4
, f '(x) = 24
.
Ahora tenemos herramientas para reescribir el descenso del gradiente usando la diferenciación automática:
Tenga en cuenta que ya no tenemos que preocuparnos más por diferenciar nuestro cálculo. Todos los cálculos necesarios se realizan bajo el capó, utilizando números duales. Llamar a derivative ()
en el error proporciona automáticamente el gradiente.
Ahora tenemos herramientas para reescribir el descenso del gradiente usando la diferenciación automática:
Tenga en cuenta que ya no tenemos que preocuparnos más por diferenciar nuestro cálculo. Todos los cálculos necesarios se realizan bajo el capó, utilizando números duales. Llamar a derivative ()
en el error proporciona automáticamente el gradiente.
Programación diferenciable
Hemos desarrollado en este artículo todas las herramientas matemáticas y programáticas para hacer Programación Diferenciable. Como has visto, se basa en conceptos matemáticos sencillos y se puede introducir en cualquier programa con bastante facilidad.
Una vez que haya identificado estos parámetros y haya definido un error, puede calcular la derivada de sus parámetros con respecto a este error. Luego, cada vez que use nuevos datos, puede beneficiarse del cálculo gratuito de gradientes para mejorar sus parámetros.
Con la Programación Diferenciable, cualquier programa puede beneficiarse de todas las herramientas matemáticas disponibles para funciones diferenciables, lo que les permite mejorarse aprendiendo de los datos.
Espero que te haya sido de utilidad. Gracias por leer este post.
Añadir comentario