Cómo crear una red neuronal en R con Tensorflow

Share on linkedin
Share on twitter
Share on email

Por qué crear una red neuronal con Tensorflow

Tensorflow es una de las herramientas más usadas para crear redes neuronales. Está muy extendida en Python, pero no tanto en R. Así que hoy, os voy a explicar cómo crear una red neuronal en R con Tensorflow.

¿Y por qué usar Tensorflow si se puede programar desde cero? Pues porque aún se pueda hacer todo desde cero aquí te explico cómo, la razón es sencilla: no es práctico. ¿Para qué iba yo a dedicarme a hacer algo desde cero si lo puedo de hacer de forma más rápida y segura de otro modo?

De hecho, como curiosidad, en el post de cómo programar una red neuronal desde 0 en R, a pesar de todo el tiempo que le dediqué, la red tenía una errata. Y sí, incluso viéndolo entre dos personas (gracias Luis por la ayuda;) nos costó encontrar el problema. Que, por cierto, era que en la extracción de una lista, vez de un 1, debí poner un 2…

Por tanto, sabiendo lo horroroso que puede ser programar una red neuronal desde cero, vamos a aprender a programar nuestras redes neuronales en R usando tensorflow. ¡Vamos a ello!

Cómo crear una red neuronal con Tensorflow en R

Cómo funciona Tensorflow

Antes de ponernos a programar, vamos a aprender la lógica que hay detrás de Tensorflow. Así, podremos entender mejor lo que hagamos después.

La idea de Tensoflow es que el usuario diseñe cuál será la estructura de la red neuronal, también llamada grafo computacional. Este grafo incluye: las capas, el número de neuronas en cada una de ellas, las funciones de activación, la función de coste y el optimizador.

Solo con eso, Tensorflow se encargará del resto: crear las capas, inicializarlas, hacer las conexiones, calcular de forma automática las derivadas parciales de la red neuronal, entrenar el modelo… todo. Además, tenemos que tener en cuenta que Tensorlow:

  • Tiene una gran comunidad por detrás donde encontrarás respuesta a tus dudas, encontrarás datasets y modelos entrenados… de todo.
  • Está disponible en distintos lenguajes (aunque el más común es usarlo en Python).
  • Ofrece muchas otras herramientas que te hacen la programación mucho más sencilla, como Tensorboard, que te permite ver cómo aprende tu red neuronal de una forma sencilla.

En definitiva, programar una red neuronal con Tensorflow es mucho más eficiente que hacerlo desde cero. De ahí su éxito. De hecho, para que lo veáis, cuando creamos la red neuronal desde 0 usé más de 90 líneas de código, mientras que con Tensorflow he necesitado menos de 20 líneas.

Como véis es mucho más fácil y rápido en Tensorlow. Además, es más sencillo. Así que, ahora que ya estás convencido, vamos a aprender a programar una red neuronal en R con Tensorflow. Al lío.

Programando una red neuronal en R con Tensorflow

Creando el Grafo Computacional

Lo primero que tenemos que hacer es crear el grafo computacional. En este caso, usaremos la misma estructura que usamos en el post anterior, que consiste en una red de 4 capas con 2 vectores de entrada, 4 y 8 neuronas en las capas ocultas, respectivamente y un vector de salida.

neuronas = as.integer(c(2,4,8,1)) #Estos son el número de neuronas por capa

Además, usaremos el mismo problema de clasificación del post anterior, que por si no acordáis consiste en clasificar entre puntos rojos y azules:

library(ggplot2)
ggplot(datos,aes(x,y, col = as.factor(Y))) + geom_point()

Dicho esto, vamos a ello;)

Empezamos creando nuestro vector de entrada, conocido como placeholder en Tensorflow. Para ello, tenemos que decirle qué tipo de dato tendrá y sus dimensiones.

library(tensorflow)

X <- tf$placeholder("float" , shape = list(300,ncol(Xr)))
Y <- tf$placeholder("float", shape = list(300,1))

Una vez hecho esto, vamos creando las capas una a una. Para ello, debemos:

  • Crear la matriz de pesos W, que tendrá tantas filas como el número de capas en la capa anterior y tantas columnas como el número de neuronas en esa capa.
  • Crear el vector bias b, que tendrá las mismas dimensiones que el número de neuronas en la capa.
  • Calcular el resultado de la neurona. Para lo cual tenemos que multiplicar matricialmente el resultado de la capa anterior (o el vector de entrada si es la priemra capa) con W1, sumar el bias y aplicar la función de activación.

IMPORTANTE: en R el shape de un Tensor se define con una lista.

¿Cómo se hace eso? Pues en la primera capa será así:

# Capa 1
W1 = tf$Variable(tf$random_normal(shape = list(neuronas[1],neuronas[2])))
b1 = tf$Variable(tf$random_normal(shape = list(neuronas[2])))
l1 = tf$nn$relu(tf$add(tf$matmul(X,W1),b1))

Como véis, las variables se definen con la función Variable. En el primer fron propagation tenemos que inicializar los valores, por lo que usamos la función random_normal, que inicializa los datos ya normalizados (media 0, desviación típica 1).

Una vez definimos W1 y b1, calculamos el resultado de la primera capa, para lo cual hemos usado la función matmul para hacer las multiplicaciones matriciales entre X y W1, add para sumar b al resultado anterior y relu para aplicar al resultado anterior la función de activación sigmoide.

¿Cómo serían las siguientes capas? Pues copiando y pegando el código anterior, cambiando los índices del vector neuronas y cambiando la función de activación, si es que hace falta. En nuestro caso usamos la función relu para las capas ocultas y la sigmoide en la capa de salida.

# Capa 2
W2 = tf$Variable(tf$random_normal(shape = list(neuronas[2],neuronas[3])))
b2 = tf$Variable(tf$random_normal(shape = list(neuronas[3])))
l2 = tf$nn$relu(tf$add(tf$matmul(l1,W2),b2))

# Capa 3
W3 = tf$Variable(tf$random_normal(shape = list(neuronas[3],neuronas[4])))
b3 = tf$Variable(tf$random_normal(shape = list(neuronas[4])))

# Capa 4 - Vector de Salida 
Yp = tf$nn$sigmoid(tf$add(tf$matmul(l2,W3),b3))

Estructura creada. Como véis, Tensorflow incluye funciones para la gran mayoría de funciones que tenemos que implementar, desde la multiplicación matricial hasta las funciones de activación. Fácil, ¿verdad? Pues aún queda lo mejor;)

Backprop y Gradient Descent en Tensorflow R

Ahora que ya hemos creado la estructura de nuestra red neuronal, nos toca definir cuál será la función de coste y la función de optimización que usaremos para entrenar el modelo.

En nuestro caso, siguiendo la red neuronal que hicimos, usaremos el error cuadrático medio como función de coste y como optimización usaremos gradient descent.

Si os acordáis (y sino lo visitáis jeje), programar esta parte desde cero fue un poco complicado. En Tensorflow sin embargo lo haremos de una forma súper sencilla, ya que Tensorflow tiene funciones predefinidas para esa función de coste y modelo de optimización (y para muchos otros).

coste = tf$compat$v1$losses$mean_squared_error(Yp, Y) 
optimizador = tf$compat$v1$train$GradientDescentOptimizer(
  learning_rate = 0.1)$minimize(coste)

Como véis, con estas dos funciones hemos dicho:

  • Cuál es nuestra función de coste y cómo calcularla. No tenemos que crear la función de coste ni nada. Elegimos una de las muchas que hay, y listo.
  • Cuál es nuestro optimizador, definimos su learning rate y qué debe optimizar. De nuevo, muchos optimizadores posibles y sin tener que programar nada.

Y te estarás preguntado, ¿con esto ya estaría? Pues no. De hecho, de momento no hemos hecho “nada”, más que definir el grafo computacional. Vamos, que Tensorflow no se ha puesto a trabajar.

Para ello, tenemos que:

  • Iniciar una sesión en Tensorflow con la función tf$Session.
  • Inicializar los valores de la red con la función tf$global_variables_initializer.
  • Entrenar la red neuronal mediante la función sess$run (en nuestro caso sess porque así hemos llamadoa la sesión).
i = 1
j = 1
pasos = 1000

sess <- tf$compat$v1$Session()
sess$run(tf$global_variables_initializer())
coste_acum <- list()
error <- vector()

for (i in seq(1,pasos)){
 coste_acum[[i]] <- sess$run(c(optimizador, coste,Yp), 
                        feed_dict = dict(X = Xr,Y = Yr))
 if(i %%25 == 0){
   j = i/25
   error[j] = mean(1-round(coste_acum[[i]][[3]]) == Yr)
 }
}

¡Ya estaría! Alucinante, ¿verdad? Veamos cómo ha ido aprendiendo la red a cada 25 pasos:

library(ggplot2)
library(dplyr)

iteraciones <- seq(25,1000,25)
grafico <- data.frame(it = iteraciones, err = error)

ggplot(grafico, aes(it, err)) + geom_line() + theme_minimal() +
  labs(title = "Aprendizaje de la Red Neuronal",
       subtitle ="Error de la red por iteración",
       x = "Iteración", y = "Error") + scale_x_continuous(breaks = seq(0,1000,50))

Como véis, la red neuronal ha ido aprendiendo poco a poco. Al principio la red falla mucho. De hecho, falla más que prediciendo de forma aleatoria. Pero, poco a poco va aprendiendo. Para la ronda 75 ya había mejorado su error por debajo del 20%. Así, poco a poco ha ido bajando hasta apenas fallar. De hecho, el error de la red en la última iteración es del 0.08333. Vamos, que nuestra red neuronal hecha en R con Tensorflow a penas falla.

En definitiva, saber cómo programar una red neuronal desde cero está bien, pero es mucho más rápido, fácil y seguro hacerlo con otras herramientas como Tensorflow. Espero que este tutorial os haya servido como orientación para aprender a programar redes neuronales con Tensorflow en R. Cualquier duda, coméntame por LinkedIn y… ¡nos vemos en el siguiente post!