Introducción
El deep learning es una disciplina que se engloba en lo que se conoce como representation learning y esta a su vez está contenida en el archi-conocido machine learning.
Pero, ¿de qué se trata?, ahora todo el mundo habla de deep learning, de la cantidad de cosas que se pueden hacer con este método y de lo maravilloso que es.
En el mundo de la tecnología añadir la etiqueta deep learning a un producto se está convirtiendo en lo mismo que hace unos años añadir IoT, web semántica, cloud o mucho más lejos en el tiempo AJAX.
Actualmente trabajo con deep learning para construir modelos que permitan perfilar a un usuario en base al comportamiento que tiene en una web, este tipo de información te permite conocer un poco mejor a tu usuario: como utiliza el ratón y el teclado, desde donde se conecta, cuando se conecta, que acciones se supone que va a realizar después, etc. Dado que trabajo en el sector de la seguridad informática, esto es por ejemplo útil para tratar de determinar si un usuario es quien dice ser, o por el contrario ha sido suplantado y hay que prestar mayor atención a sus acciones, por ejemplo a la hora de realizar una transferencia bancaria.
La motivación para escribir este articulo es la escasa información que existe en lengua Cervantina sobre el tema, esto es algo normal, puesto que es algo novedoso y está en pleno crecimiento.
Además, me voy a permitir la licencia de construir la casa por el tejado, en vez de explicar las bases sobre las que se asienta el deep learning voy a explicar a nivel muy general como funciona y más adelante entraré en detalles concretos.
Quiero construir una serie de artículos que demuestre de forma práctica para que vale el deep learning y como nos está ayudando en nuestro día a día sin que lo sepamos. Para conseguirlo contruiré un modelo de deep learning utilizando tensorflow que permita predecir el valor que va a tomar bitcoin en base a sus valores anteriores y a lo que la gente comenta en twitter. Esto será utilizando únicamente deep learning. Para ello me apoyaré en un repositorio de github donde iré desarrollando cada parte con las explicaciones pertinentes.
Para los que no lo sepáis, tensorflow es la herramienta de Google para la creación de modelos de deep learning, la cual se está convirtiendo en un estandar de facto con el paso del tiempo, pero hay otrás que han sido y son muy populares.
Deep learning
Y ahora vayamos al lío, ¿qué es el deep learning?. El deep learning es un método que nos va a permitir generalmente clasificar cosas, es cierto que nos puede ayudar en otros áreas como por ejemplo: predicciones, sistemas de compresión, generación automática de contenidos, traducciones, etc. Pero estas áreas no van a dejar de ser un subconjunto del problema de clasificación.
El deep learning se sustenta en el uso de redes neuronales, aunque podéis leer muchas cosas por ahí, estas redes no se basan en el funcionamiento del cerebro, básicamente porque desconocemos el funcionamiento exacto del cerebro. Este hecho es muy importante en cuanto a terminología, una red neuronal no está formada de neuronas, está formada de unidades.
De momento veamos lo que es una red neuronal:
Una red neuronal puede ser representada básicamente por la ilustración superior. Es una función que acepta una serie parámetros (x1, x2, ..., xn) y devuelve uno o más valores (y1, y2, ..., ym).
La particularidad que tiene esta función es que normalmente es suficientemente compleja como para adaptarse a una clasificación que utilizaríamos en la vida real, como por ejemplo etiquetar una imagen con el valor del número que contiene.
Veamos un ejemplo práctico utilizando un conjunto de datos público llamado MNIST, este conjunto de datos es muy popular para validar modelos y contiene 70000 números escritos a mano y etiquetados. Estar etiquetado significa que existe metainformación que indica que número contiene la imagen correspondiente, la resolución de las imágenes es de 28x28x1 (ancho x alto x profundidad de color):
Para transformar la imagen en algo entendible por la red tenemos que preguntarnos qué es una imagen del MNIST. Una imagen del MNIST es una matriz cuadrada (o tensor de orden 2) que contiene valores de 0 a 255, siendo 0 los pixeles de color negro más oscuro y 255 el color blanco más intenso, como ejemplo la siguiente imagen (tensorflow.rstudio.com/tensorflow/articles/tutorial_mnist_beginners.ht):
En este caso los valores están normalizados entre 0 y 1 una imagen de MNIST únicamente tenemos que dividir la matriz entre el escalar 255. La normalización es un tema de gran importancia en el deep learning pero lo trataremos más adelante. Los colores también están invertidos, pero para este caso nos resulta indiferente.
Ahora tenemos una matriz cuadrada y normalizada de 28x28, pero necesitamos convertirla en un vector (tensor de orden 1), el proceso para conseguirlo es muy sencillo, simplemente concatenaremos todas las filas una detrás de otra para acabar teniendo un tensor de rango 1 que tiene 784 componentes.
Por otro lado tenemos la salida, otro tensor de orden 1 con 10 componentes, cada componente representa una categoría, así por ejemplo y1 representa la categoría del número 0, y2 representa la categoría del número 1, y3 del 2, etc.
Lo que vamos a tratar de buscar, es que cuando metamos una imagen que represente ún número, la componente de la salida que representa la categoría de la entrada (en este caso 4) valga un valor muy cercano a 1 y el resto de componentes su valor sea cercano al 0.
En el caso de la imagen de arriba, donde se introduce un 4 a la red neuronal, vemos que la salida es perfecta: todas las componentes valen 0 excepto y5 que representa la categoría 4 y vale exactamente 1. Lamentablemente esta exactitud es muy dificil que suceda en la vida real.
Aunque no es estrictamente necesario, lo habitual es que la suma de todas las componentes del tensor de salida sumen 1, para conseguir esto el modelo suele aplicar una función que se llama softmax como última función.
Modelo
Bueno, ya tenemos un modelo que podría llegar a predecir algo, pero por desgracia, si le pasaramos una imagen convertida en un tensor únicamente acertaríamos un 10% de las veces, como si la clasificación fuera aleatoria. Si resulta que a vosotros os acierta nada más construir el modelo, dejad esta tontería del deep learning e idos a echar la lotería inmediatamente, eso sí, echad un boleto por mi y mandádmelo.
¿Por qué sucede esto?, bueno, esto sucede por como funciona internamente una red neuronal. Hemos visto como funciona a nivel general, pero una red neuronal está formado por pequeños componentes llamados unidades, estas unidades son funciones más pequeñas que aportan cierto valor a la salida de la red neuronal.
Observemos la imagen siguiente:
Cada circulo pequeño representa una unidad de la red neuronal, como podéis ver en la imagen, están agrupados en capas, y cada capa está completamente conectada a la siguiente. ¿Qué significa estar completamente conectadas?, significa únicamente que la salida de cada unidad es la entrada de cada unidad de la siguiente capa.
Vamos a ir un poquito más allá para entender como se conectan las unidades entre sí, pa ello veremos a detalle el funcionamiento de una unidad:
Como podéis ver, la imagen de arriba representa la función:
y = f(x1*w1 + x2*w2 + x3*w3 + x4*w4 + x5*w5)
Siendo f la función de activación, xn los valores de entrada de la unidad y w los pesos. Normalmente a la función se le añade una variable más llamada peso de sesgo que permitirá activar o desactivar la unidad, por lo que la función quedaría:
y = f(x1*w1 + x2*w2 + x3*w3 + x4*w4 + x5*w5 + b)
Siendo es este caso b el peso de sesgo (bias) y que no está representado en la ilustración superior.
La función de activación (f) que se suele aplicar varía dependiendo de lo que queremos conseguir, pueden ser funciones lineales o no lineales. Habitualmente se usan alguna de las siguientes funciones: sigmoide, relu o leaky relu o la tangente hiperbólica.
Perfecto, esta última parte ha sido un poco más intensa que la anterior, pero era necesaria para explicar lo que viene a continuación. Lo único que nos tiene que quedar claro es que hay un montón de pesos (w) y sesgos (b), y estas variables influyen en el valor de salida de cada unidad y por ende el valor de salida de la red neuronal.
Forward pass
El forward pass es el proceso por el cual dada una entrada a la red neuronal, por ejemplo la imagen con el número 4, obtenemos una salida (0, 0, 0, 0, 1, 0, 0, 0, 0, 0), esto básicamente se consigue multiplicando, sumando y aplicando funciones de activación de manera encadenada hasta que llegamos a la salida de la red neuronal. Es decir, es aplicar la función red neuronal a una entrada.
Para que la salida sea la que esperamos, antes debemos ajustar los pesos (w) y sesgos (b) para que cuando se hagan todos los cálculos el resultado que obtengamos sea el que esperamos.
¿Los podemos ajustar a mano?, bueno, todo depende de tu paciencia, pero en general ajustarlos a mano únicamente vale para aproximar funciones sencillas como por ejemplo la función AND, OR, XOR, etc. Para todo lo demás deberemos utilizar otro método.
Hay que tener en cuenta que los modelos pueden tener decenas de capas, con cientos de unidades en cada capa y cuya salida influye en el resto de unidades que están por delante, ajustar todo a ojo sería lo más cercano al infierno que puede existir.
Backward pass
El backward pass es lo que el común de los mortales suele llamar entrenamiento, el objetivo de esta fase es ir ajustando poco a poco las variables de peso (w) y sesgo (b) para que ajusten la salida de la red neuronal a lo que nosotros esperamos, para ello necesitaremos bastantes datos de entrada y el valor que esperamos obtener como salida en la red neuronal, en el caso del MNIST tendremos un montón de imágenes con números y el valor del número de la imagen.
Vamos a introducir otra función llamada función de coste o función de perdida, esta función va a tomar como entrada lo que devuelve la red neuronal y va a devolver cuanto nos hemos confundido, algo tal que así:
Nuestro objetivo es minimizar el error, es decir, loss debe valer el menor valor posible, aunque no quiero entrar en mucho detalle, seguro que recordaréis con cariño vuestras clases de cálculo encontrando mínimos en funciones. Pues esto es lo mismo pero con una función con miles de variables: nuestros amigos los pesos (w) y sesgos (b).
La imagen superior pertenece a: www.deeplearningbook.org/
No voy a entrar en como calcular el mínimo de una función, esto convertiría el artículo en un tostón, especialmente para mi que no me atrae mucho el cálculo (soy más de álgebra), pero si que destacaré un par de cosas, calcular mínimos en funciones sencillas es algo fácil, el problema es que cuando tu función es gigantesca calcular el mínimo implica un coste computacional inasumible, es decir, el tiempo que vas a tardar en calcular los valores de w y b se dispara de tal forma que se vuelve inaceptable.
Por eso, existen distintas estrategias para aproximar las funciones a su mínimo, simplemente mencionaré algunas: descenso del gradiente, descenso del gradiente estocástico, Adagrad y Adam. Existen muchos otros, estos son los más comunes y además ya tenéis por donde empezar a leer.
Conclusiones
Una vez hemos realizado los suficientes backward passes en nuestra red neuronal los valores de los pesos y sesgos deberían estar suficientemente bien ajustados como para que la predicción de nuestro modelo sea lo suficientemente buena como para usarlo en la vida, en el siguiente artículo escribiré un pequeño código que permita clasificar con una buena tasa de acierto los números del conjunto de datos del MNIST.
Lo que acabo de contar simplemente es un esbozo del deep learning, existen muchas formas de preprocesar datos, estrategias para no consumir memoria, formas de conectar las unidades (capas convolucionales, RRN, LSTM, GRU), distintas formas de calcular la pérdida (MSE, cross entropy,), reutilización de modelos ya entrenados, etc. Pero lo que si que es cierto es que esto son las bases para iniciarse en el estudio del deep learning.