logo

Dot CSV

Inteligencia Artificial, Tecnología, Ciencia y Futuro! Bienvenidos a la 4ª Revolución Industrial 🚀 Inteligencia Artificial, Tecnología, Ciencia y Futuro! Bienvenidos a la 4ª Revolución Industrial 🚀

Transcribed podcasts: 213
Time transcribed: 5d 22h 50m 3s

This graph shows how many times the word ______ has been mentioned throughout the history of the program.

Redes neuronales. Redes neuronales. Redes neuronales. Como una red neuronal. Redes neuronales. Redes neuronales.
Vale, vale, vale, vale. Sí, hemos hablado muchísimo sobre redes neuronales.
Hemos aprendido qué es y cómo funcionan intuitivamente, e incluso hace unos meses programamos desde cero nuestra propia red neuronal funcional.
Paso a paso y línea a línea, implementamos lo que era una red neuronal sencilla, encargada de resolver un problema de clasificación binaria, pues también sencillo.
Ok, genial. Pero, ¿significa esto que cada vez que quiera programar una red neuronal, tengo que programarla desde cero? Pues como podrás imaginarte, no.
De hecho, la realidad es que cuentas con numerosas herramientas que te dan la posibilidad de sacarle todo el partido a los algoritmos de deep learning sin costarte muchos quebraderos de cabeza.
Herramientas como Keras, TensorFlow, PyTorch y otras tantas deberían de ser herramientas que has escuchado más de una vez.
Pero, hey, que a lo mejor no tienes claro qué te aporta cada una y qué no. Es por eso que hoy veremos tres maneras diferentes de programar a una red neuronal.
¿Estás listo? Perfecto. Porque empezamos en nada, pero antes quiero que sepas que si este vídeo te interesa, posiblemente lo que te voy a contar ahora también te va a interesar.
Porque hoy tenemos al primer sponsor del canal, Academy.ai, que es el primer bootcamp dedicado específicamente a la inteligencia artificial en España,
con graduados que han ido a estudiar a Harvard, trabajan para Google o Microsoft, entre otros.
Lo interesante aquí es que el próximo bootcamp de AI Engineering que se va a celebrar empieza el 8 de julio en Barcelona,
con más de 400 horas de formación intensiva dedicada a la inteligencia artificial, el machine learning y el deep learning.
Todo esto durante 10 semanas.
Si has seguido los vídeos de este canal ya tienes el nivel suficiente para poder empezar, pues lo único que vas a necesitar es tener una buena base de Python.
Este curso te va a dar todas las herramientas y prácticas para impulsar tus conocimientos al siguiente nivel y poder trabajar como AI Engineer o Data Scientist.
Además, la buena noticia es que si aplicas a través del siguiente enlace vas a obtener un descuento del 15% exclusivo para los miembros de este canal.
Tienes toda la información para consultarlo en la descripción de este vídeo.
Y ahora sí, vamos a programar unas cuantas redes neuronales. ¿Por dónde empezamos?
Pues como en todo edificio debemos empezar a construir por los cimientos.
Esto es algo muy común dentro del campo de la informática, donde muchas de las herramientas que utilizamos se construyen encima de otras herramientas anteriores.
Vamos cimentando capa por capa, abstrayendo funcionalidades complejas en herramientas cada vez más sencillas de utilizar.
Es decir, si trabajamos con deep learning, pues cada nivel que añadimos sumará una capa de simplicidad en el uso de las redes neuronales,
pero que al mismo tiempo nos restará flexibilidad en el diseño de las arquitecturas.
Como he dicho antes, en este canal nuestro comienzo trabajando con redes neuronales ha sido desde los cimientos,
implementando de T0 todos los pasos que hacen funcionar a una red neuronal.
Capas de neuronas que calculan las sumas ponderadas, funciones de activación, propagación de la información hacia adelante,
el cálculo del error, la propagación del error hacia detrás, encadenando derivadas con la chain rule,
tal y como derivamos en todos los vídeos de teoría, y optimizando los parámetros usando el descenso del gradiente.
Todo esto está muy bien explicado paso a paso en el IA Notebook número 4, que te recomiendo hacer si todavía no lo has hecho.
En el vídeo de hoy, este será nuestro punto de partida.
Si recuerdas, el problema que trabajamos en aquel vídeo era un problema de clasificación binaria, este de aquí,
donde teníamos a dos círculos concéntricos de puntos que queríamos separar en dos clases diferentes.
Si te fijas, todo nuestro código implementado aquí sirve para diseñar una red neuronal con esta arquitectura de acá.
Funciona, perfecto, pero ¿y si quisiera experimentar con el diseño de la arquitectura?
¿Y si quisiera colocar una conexión que fuera de esta capa de aquí a esta otra de aquí, una skip connection?
Pues claro, en nuestro código ya la propagación hacia adelante habría que implementarla de nuevo.
Y peor aún, el código del backpropagation también.
Y claro, esto significa que tenemos que volver a coger papel y boli y recalcular todas las derivadas parciales
que implementaban al algoritmo de backpropagation.
Lo cual, pues, creo que es un motivo suficiente para dejarlo todo e irse a jugar con la realidad virtual.
No, debe de haber una forma más sencilla.
Si al menos existiera una forma en la que yo pudiera definir qué operaciones quiero que se ejecuten dentro de mi red
y que de manera automática el código fuera capaz de calcular todas las derivadas parciales que necesito
para hacer el algoritmo de backpropagation, pues tendría muchos quebraderos de cabeza resueltos.
Sería muy interesante contar con una herramienta así, ¿verdad?
Pues la buena noticia es que si tomamos el ascensor y subimos a la planta uno de nuestro edificio,
entraremos en la zona de las librerías de diferenciación automática.
En esta planta nos vamos a encontrar con diferentes herramientas donde, posiblemente,
TensorFlow y PyTorch sean las librerías de Python más representativas de este nivel.
Como su nombre bien indica, las librerías de diferenciación automática
se van a encargar de la tarea de calcular automáticamente
las derivadas parciales necesarias para optimizar cualquier arquitectura que diseñes.
Internamente, para conseguir esta diferenciación automática,
estas librerías representan todas las operaciones que han de ejecutarse en nuestra arquitectura como un grafo,
donde cada nodo puede representar o una variable de entrada o una operación a realizar.
Por ejemplo, este grafo de aquí representaría la multiplicación de una variable por 3 y la suma de 2.
A este grafo que hemos construido se le conoce como grafo computacional
y es la estructura ideal para iniciar el proceso de diferenciación automática.
En nuestro caso, como ejemplo de librería autodiferenciable,
nos centraremos en la versión 1 de TensorFlow para ver cómo podemos implementar a nuestra red neuronal
y así solucionar el mismo problema de clasificación de la última vez.
Si quieres, puedes parar el video y copiar el siguiente código
que te servirá como punto de partida para cargar los datos y visualizar los resultados.
¿Lo tienes copiado ya?
Guay. O bueno, también puedes encontrar el código en un notebook que he preparado abajo en la descripción.
Oh, ¿por qué siento que debería haber dicho esto antes?
Bueno, da igual.
Fíjate, como hemos mencionado antes,
el punto de partida de todo código en TensorFlow pasa por definir explícitamente
qué operaciones forman parte de nuestro grafo computacional.
Es decir, cómo los datos van a ir fluyendo y transformándose de principio a fin.
En TensorFlow utilizamos los playholders para definir aquellos puntos de entrada
donde nosotros vamos a insertar datos en nuestra red.
En este caso, las variables Ix y Y son dos playholders que utilizaremos
para introducir nuestro dato de entrada, la matriz X y nuestro vector de salida, la Y.
Para ello, los definimos diciendo que el tipo de datos que va a llegar será de tipo Float,
decimal, y cuáles van a ser sus dimensiones.
A partir de este momento, ya podemos empezar a añadir las operaciones que definirán a nuestra red neuronal.
Ya sabes que en una red neuronal, en cada capa se produce una suma ponderada,
que es equivalente a una multiplicación matricial,
después la suma del parámetro de vallas,
y luego lo pasamos por una función de activación como podría ser la función relu.
Pues fíjate qué sencillo es hacer esto con TensorFlow.
Lo primero que tenemos que hacer es definir aquellos parámetros que van a ser optimizados dentro de nuestra red,
cosa que en TensorFlow se define como variable, o variable, o bueno, con esta línea de código,
donde vamos a indicar que sus valores van a ser inicializados aleatoriamente y que tendrá estas dimensiones.
Una vez lo tenemos, simplemente tenemos que indicar que las operaciones de la capa 1
van a ser la de multiplicar nuestro input Ix con la matriz de parámetros W1,
sumar el parámetro de vallas B1 y pasarlo todo por la función de activación relu.
Fíjate cómo en unas pocas líneas de código ya hemos creado lo equivalente a una capa de una red neuronal con TensorFlow.
¿Qué pasa? ¿Qué quieres más capas?
Pues simplemente tenemos que copiar y pegar las mismas líneas de código.
Esta de aquí sería la capa 2 y esta de aquí sería la capa 3.
En este caso, como esta capa 3 va a ser la última capa, la capa de salida,
le pondremos la función de activación sigmoide y no la relu,
para que así el output quede acotado entre los valores de 0 y 1.
Perfecto. Una vez hemos terminado de definir la arquitectura,
a lo mejor te ves tentado de decir, bueno, pues ya estaría.
Ahora hago un print de esto de aquí y obtengo el resultado de las predicciones, ¿verdad?
Pues no exactamente.
Cuando hagas print verás que te sale este mensaje,
lo que viene a indicar algo así como...
Mira, mira, yo sé que esto es un tensor con estas dimensiones,
pero todavía no me has pasado datos, así que no te puedo decir qué valor es.
Claro, recuerda que aquí solo hemos definido el grafo de operaciones, nada más.
Todavía nos faltaría pasarle los datos e iniciar el proceso de entrenamiento.
La ventaja es que si bien lo que hemos hecho hasta ahora no se diferencia mucho
del código original de programarlo desde cero,
es en el proceso de backpropagation y de diferenciación automática,
donde estas librerías nos mostrarán todo el potencial.
Porque mira, una vez tenemos nuestro tensor de salida, las predicciones,
¿qué haremos con él?
Pues si te acuerdas, lo que tenemos que hacer es coger nuestra función de coste,
por ejemplo el error cuadrático medio,
y comparar nuestras predicciones con el vector de salida original,
que en este caso vendrá introducido por el placeholder Y.
Con esto evaluaremos los resultados,
y ¿qué queremos conseguir?
Pues utilizar este error para optimizar a toda la red,
es decir, que el error se minimice.
Pues dicho y hecho.
Con esta línea de aquí es con la que vamos a definir dentro de nuestro grafo de operaciones
que se produzca este proceso de optimización,
utilizando el descenso del gradiente para minimizar el coste.
Con esto ya tendríamos planteadas todas nuestras operaciones,
y solo nos faltaría introducir los datos
y dejar que TensorFlow inicia el proceso de autodiferenciación y entrenamiento,
algo que vamos a hacer con la siguiente línea de código.
Para ejecutar nuestro grafo computacional,
lo primero que tenemos que hacer es iniciar una session, una sesión,
y con esta empezaremos a ejecutar, ahora sí, las operaciones de nuestro grafo.
La primera operación que vamos a ejecutar es esta de aquí,
que servirá para inicializar todos los parámetros que hemos creado anteriormente,
todas las matrices de pesos y vallas.
Esto será de la manera en la que lo hayamos especificado,
que en este caso, tal y como hemos definido, viene a ser de manera aleatoria.
Esto es algo así como si estuviéramos arrancando por primera vez toda la máquina que hemos creado.
A partir de ahí, esta es la línea de código más importante,
porque es aquí donde vamos a evaluar por fin aquello que queremos ejecutar.
En este caso, le estamos pidiendo a Tensorflow que nos calcule estos tensores de aquí,
para lo cual necesitaremos insertar datos a aquellos placeholders
que hemos declarado como punto de entrada de nuestro grafo.
Estos datos se los podemos inyectar de manera sencilla en esta misma línea de código
con el parámetro feed dict.
Es aquí donde pasaremos nuestra matriz de entrada y nuestro vector de salida.
Con esto, estos tensores de aquí se van a calcular,
y como este tensor es el optimizador que hemos definido arriba,
cada vez que se evalúe, nuestra red estará siendo optimizada de manera automática por Tensorflow.
Es decir, estará aprendiendo.
Esta línea nos devolverá por tanto el resultado del optimizador, que lo descartaremos,
y el resultado de la función de coste y el vector de predicciones
tras una iteración de entrenamiento.
Si dejamos que esto se repita de manera iterativa durante un número determinado de veces,
el resultado que obtendremos será el siguiente.
La red ha aprendido a resolver nuestro problema de clasificación.
Como ves, el código que hemos escrito es bastante sencillo,
y no debería de costarte entender el resto de líneas que en realidad solo sirven para obtener métrica durante el entrenamiento.
Tienen todo el código comentado en el notebook que he compartido en la descripción.
La gran ventaja de trabajar con Tensorflow, dejando aparte la optimización para GPU,
la ejecución distribuida, la optimización del código,
es que si tú ahora quisieras modificar algún aspecto de la arquitectura de tu red,
solo tendrías que hacer cambios en la primera parte del código,
y el resto, el proceso de autodiferenciación y entrenamiento, se computará automágicamente.
Si te das cuenta, lo que Tensorflow nos permite hacer
es construir la arquitectura de redes neuronales que queramos a nivel de operaciones.
Es decir, que el diseño de redes neuronales ahora pasa a ser el de juntar diferentes operaciones
como si bloques de LEGO se tratasen cómodo y para toda la familia.
¿Qué te parece? ¿Mola, verdad?
Pues me alegro que te guste, porque vamos a seguir subiendo en el ascensor de las cosas fáciles.
Y es que sí, Tensorflow es una herramienta cómoda, pero si te fijas podría serlo más.
Por ejemplo, antes, cuando hemos querido diseñar a nuestra red neuronal,
para definir a cada capa de la red hemos tenido que escribir tres líneas de código,
declarando los parámetros de la capa y cuáles eran cada una de las operaciones que se producían en ella,
algo que se vuelve muy redundante cuando seguimos añadiendo más capas a nuestra red.
¿No habría una forma más cómoda de hacer esto? Pues en realidad sí.
Todas estas líneas de código las podríamos abstraer hasta el nivel de capa.
Es decir, en vez de trabajar combinando distintas operaciones,
lo que podemos hacer es combinar diferentes tipos de capas para conformar así nuestra red neuronal.
En realidad, en el campo del deep learning, la gran mayoría de arquitecturas pueden ser construidas
a partir de combinar unos pocos tipos de capas que son comunes a todas estas arquitecturas.
Capas fully connected, convolucionales, deconvolucionales, celdas LSTM, etc.
Visto así, tiene sentido plantearse el diseño de redes neuronales de otra manera,
desde el punto de vista de las APIs de alto nivel,
donde pasaremos a ver a las redes neuronales como combinaciones de capas.
Un nivel donde desde la rama de TensorFlow nos encontramos con herramientas como Keras,
que ha pasado a ser la interfaz de alto nivel de facto de la librería TensorFlow,
tras ser integrada como uno de sus módulos.
De manera muy simplificada, lo que consigue Keras es plantear una interfaz sencilla, a nivel de capa,
de todas las funcionalidades que nos aporta TensorFlow.
Visto así, vamos a ver cómo podemos resolver el mismo problema de antes, pero utilizando Keras.
Ojo, porque esta vez va a ser más rápido.
Y es que en Keras comenzamos creando nuestra red neuronal a nuestro modelo, llamando a la función sequential,
que es una forma de decirle a Keras que queremos crear un modelo conformado por una secuencia de capas.
A partir de este momento ya podemos empezar a añadir capas y capas a nuestra red neuronal.
En este caso, las capas fully connected, donde cada neurona tiene una conexión con la capa anterior,
son denominadas capas dens, densas, a las cuales las tenemos que especificar cuantas neuronas contiene
y qué tipo de función de activación va a ejecutar.
Una vez tenemos definida la capa que queremos añadir, la podemos incluir en nuestro modelo
simplemente llamando a la función model.add.
Con esto ya Keras sabrá qué operaciones debe de incluir en nuestro grafo computacional
para implementar a esta capa. Muy, muy sencillo.
Si ahora quisiéramos continuar añadiendo capas, pues tendríamos que repetir la línea de código anterior.
Añadimos una segunda capa y ahora una tercera capa, la de salida.
Una vez estamos satisfechos con el diseño de nuestra red, en Keras el entrenamiento se vuelve mucho más sencillo.
Lo primero que tenemos que hacer es compilar el modelo para así comunicar al backend de TensorFlow
que estamos listos para entrenar.
Esto lo hacemos con la función compile del objeto del modelo.
Y será aquí donde especificemos qué función de costo utilizaremos para optimizar
y qué optimizador utilizaremos.
De manera complementaria también podemos añadir otras métricas que serán calculadas y visualizadas durante el entrenamiento.
Una vez tenemos el modelo compilado, ahora solo queda entrenarlo.
Y en este caso esto se consigue llamando a la función.fit.
Será aquí donde le diremos a Keras cuáles son nuestro dato de entrada, X, y nuestro dato de salida, Y.
De igual forma podremos controlar otros parámetros del proceso de entrenamiento
como el tamaño del batch o el número de épocas a entrenar.
Cuando estemos listos lo ejecutamos y veremos inmediatamente cómo empezará el proceso de entrenamiento.
¿Acaso no es bello?
Comparativamente el código programado con Keras es muchísimo más compacto
y orientado al prototipado rápido de arquitectura de deep learning.
Como ves, tal y como dijimos en un principio,
entre más alto subimos en los niveles de abstracción de estas herramientas,
mayor es su simplicidad de uso, en detrimento o sí de la flexibilidad en el diseño.
Para ilustrar esto podemos subir un último nivel en nuestro ascensor,
donde pasaremos a tratar a las redes neuronales desde un punto de vista de capas a un punto de vista de modelo.
En este nivel las cosas se vuelven bastante sencillas, la verdad,
ya que miramos a las redes neuronales como un todo, como un modelo cuya arquitectura ya nos dan predefinida
y que podemos controlar de manera limitada ajustando unos pocos hiperparámetros.
Como ejemplo de este nivel podemos encontrarnos con librerías como SKLearn,
APIs en plataforma de Machine Learning en la nube o paquetes avanzados en herramientas de Eofimática.
Estas herramientas suelen ofrecerte un catálogo de modelos desde los más tradicionales
como KNN, K-means, modelos logísticos o lineales,
hasta cosas más avanzadas como modelos de redes neuronales.
En este nivel es importante saber qué herramientas ofrece soporte para GPU
si lo que se quiere es buscar una solución que escale bien con el volumen de datos.
Para nuestro problema, bastante sencillo,
SKLearn será suficiente para poder implementar a nuestra red con una mínima cantidad de código.
Aquí podemos usar los modelos dentro del módulo Neural Network
y elegir al Multilayer Perceptron Regressor como el modelo a utilizar.
En este caso utilizo al regresor en vez del clasificador
para seguir obteniendo la probabilidad de pertenencia a una de las clases.
Lo bueno de SKLearn es que todos los modelos seguían una interfaz similar,
donde primero se crea el objeto del modelo, luego se entren a llamando a la función.fit
y finalmente se realizarán predicciones con la función.predict.
Bastante sencillo.
La dificultad aquí reside en la de encontrar aquellos hiperparámetros que mejor se ajusten a tu problema.
Podemos definir a estos hiperparámetros cuando creamos el objeto del modelo.
Por ejemplo aquí podemos seleccionar el tipo de optimizador, el learning rate inicial,
el número de neuronas para cada capa oculta o la política detrás del número de iteraciones a entrenar.
Cuando esté listo veremos que estas dos líneas de código son suficientes para solucionar a nuestro problema.
La ventaja de SKLearn, como ya he dicho,
es la sencillez para probar otros tipos de modelo diferente,
puesto que solo requeriría cambiar esta línea de código por el modelo que queramos
al mantenerse igual la interfaz del resto de funciones.
Con este último ejemplo espero que haya quedado clara mi intención con este video,
que es la de mostrar el gradiente de herramientas con las que contamos en el campo del Machine Learning
si nos movemos en el eje de simplicidad versus flexibilidad.
Hay que comentar que este gradiente de herramientas cada vez se vuelve más difuso
con la evolución de cada una de estas librerías,
al combinarse por ejemplo TensorFlow con Keras o cuando herramientas como PyTorch
añaden cada vez más funciones de alto nivel.
Igualmente a nivel horizontal, las diferencias entre herramientas tan importantes como TensorFlow o PyTorch
se van igualando, cuando, bueno, TensorFlow se inspira en su nueva versión
en alguna de las bondades que ofrece PyTorch.
Pero bueno, esto ya lo dejaremos para otro video.
En nuestro caso quiero que este video sirva como punto de partida
para los próximos EA Notebooks que llegarán al canal,
donde nos moveremos entre la planta 1 y 2 de nuestro edificio,
trabajando con TensorFlow y Keras.
Si quieres estar alerta de estos tutoriales te recomiendo suscribirte
y darle a la campana si aún no lo has hecho,
y si de verdad te interesa el mundo del Machine Learning
y quieres profesionalizarte en este sector,
no dejes de consultar la información de los cursos de Academy.ai
y beneficiarte así del descuento dado a los suscriptores de este canal.
Tenéis toda la información en la descripción.
Por mi parte nada más, adiós.