A continuación los pasos para tener un inicio exitoso con Keras.
Lo primero que se debe hacer es instalar Python, para esto se recomienda seguir los pasos de este video.
Instalar el paquete devtools de R.
install.packages("devtools")
Instalar el paquete keras de R.
devtools::install_github("rstudio/keras")
La interfaz de Keras usa como motor TensorFlow asà que debemos instalarlo si aún no le tenemos. La función install_keras permite instalar Keras (de Python) y TensorFlow.
library(keras)
install_keras()
En este ejemplo se utilizará la base de datos MNIST que contiene información de números dÃgitos escritos a mano con su correspondiente etiqueta. Todas las imágenes son de 28 x 28 en escala de grises.
La base de datos está disponible dentro del paquete keras. Para acceder a la base de datos usamos:
library(keras)
mnist <- dataset_mnist()
names(mnist)
## [1] "train" "test"
¿Qué hay dentro de mnist?
names(mnist)
## [1] "train" "test"
¿Qué hay dentro de mnist$train y mnist$test?
names(mnist$train)
## [1] "x" "y"
names(mnist$test)
## [1] "x" "y"
Vamos a crear los conjuntos de entrenamiento y de prueba.
x_train <- mnist$train$x
y_train <- mnist$train$y
x_test <- mnist$test$x
y_test <- mnist$test$y
y_true <- mnist$test$y # Esto para luego comparar
Veamos que tan grande es la base de datos de entrenamiento.
dim(x_train)
## [1] 60000 28 28
El objeto x_train es un arreglo de dimensión 60000, 28, 28. Eso significa que tenemos a nuestra disposición 60000 imagénes de 28 x 28. Para explorar la primer imágen se puede escribir View(x_train[1, , ]) en la consola de R y el resultado será algo muy similar a lo mostrado en la siguiente figura. Una matriz de 28 x 28 llena de número, donde haya un 0 es que ahà no se escribió nada y donde haya un número es que ahà hay una marca del lápiz, entre más grande el valor más obscura la marca.
En la figura anterior se incluyó una silueta en color rojo para observar más fácil el número que está en la imagen, claramente es un 5. Vamos ahora a dibujar esa matriz en R para verificar el número que contiene.
im <- mnist$train$x[1 , ,] / 255
im <- 1 - t(apply(im, 2, rev))
image(x=1:28, y=1:28, z=im, col=gray((0:255)/255), xaxt='n', yaxt='n', xlab='')
Ahora vamos a ver los primeros 16 números que hay en la base de datos de entrenamiento pero cambiando de colores para obtener un constraste.
# visualize the digits
par(mfcol=c(4, 4))
par(mar=c(0, 0, 3, 0), xaxs='i', yaxs='i')
for (j in 1:16) {
im <- x_train[j, , ]
im <- t(apply(im, 2, rev))
image(x=1:28, y=1:28, z=im, col=gray((0:255)/255),
xaxt='n', main=paste(y_train[j]))
}
Por último exploremos los primeros 16 valores del objeto y_train que corresponden a las etiquetas asociadas a las primeras 16 imágenes de x_train.
y_train[1:16]
## [1] 5 0 4 1 9 2 1 3 1 4 3 5 3 6 1 7
Lo primero a realizar es convertir las matrices de \(28 \times 28\) a un vector de \(1 \times 784\) como se muestra en la figura siguiente.
También se deben convertir los valores que están dentro de las matrices (0 a 255) a valores entre 0 y 1. A continuación el código necesario para hacer esto.
# reshape
x_train <- array_reshape(x_train, c(nrow(x_train), 784))
x_test <- array_reshape(x_test, c(nrow(x_test), 784))
# rescale
x_train <- x_train / 255
x_test <- x_test / 255
Los vectores y_train y y_test contienen las verdaderas etiquetas, vamos a explorar las primeras cinco de y_train.
y_train[1:5]
## [1] 5 0 4 1 9
Los vectores y_train y y_test se deben convertir al formato one-hot encoding usando la función to_categorical().
y_train <- to_categorical(y_train, 10)
y_test <- to_categorical(y_test, 10)
colnames(y_train) <- 0:9
colnames(y_train) <- 0:9
¿Cómo lucen ahora y_train y y_test? Estos objetos son ahora matrices (no vectores), exploremos la forma one-hot encoding para las 5 primeras imágenes.
y_train[1:5, ]
## 0 1 2 3 4 5 6 7 8 9
## [1,] 0 0 0 0 0 1 0 0 0 0
## [2,] 1 0 0 0 0 0 0 0 0 0
## [3,] 0 0 0 0 1 0 0 0 0 0
## [4,] 0 1 0 0 0 0 0 0 0 0
## [5,] 0 0 0 0 0 0 0 0 0 1
Para definir el modelo se usa la función keras_model_sequential().
model <- keras_model_sequential()
Luego se deben agregar las capas. En este ejemplo vamos a utilizar una arquitectura de 784 variables de entrada, una capa con 128 neuronas y una capa de salida con 10 elementos, adicionalmente se van a convertir algunos inputs en cero para prevenir overfitting. En la siguiente figura se muestra la red de interés.
Para dibujar la arquitectura de una red neuronal se puede usar esta herramienta.
Para agregar una capa se usa la función layer_dense(units, activation, input_shape), a continuación se muestra la forma de construir la red neuronal.
model %>%
layer_dense(units = 256, activation = 'relu', input_shape = c(784)) %>%
layer_dropout(rate = 0.4) %>%
layer_dense(units = 128, activation = 'relu') %>%
layer_dropout(rate = 0.3) %>%
layer_dense(units = 10, activation = 'softmax')
Los elementos layer_dropout se usan para obligar que algunas de las entradas a una neurona sean cero y asà evitar el overfitting o sobreajuste, para entender mejor el concepto revise este video.
Para observar un resumen del modelo creado se usa lo siguiente:
summary(model)
## ___________________________________________________________________________
## Layer (type) Output Shape Param #
## ===========================================================================
## dense (Dense) (None, 256) 200960
## ___________________________________________________________________________
## dropout (Dropout) (None, 256) 0
## ___________________________________________________________________________
## dense_1 (Dense) (None, 128) 32896
## ___________________________________________________________________________
## dropout_1 (Dropout) (None, 128) 0
## ___________________________________________________________________________
## dense_2 (Dense) (None, 10) 1290
## ===========================================================================
## Total params: 235,146
## Trainable params: 235,146
## Non-trainable params: 0
## ___________________________________________________________________________
¿De dónde salen los número de la tabla de arriba?
784 * 256 + 256.256 * 128 + 128.128 * 10 + 10.El siguiente paso consiste en compilar el modelo, para esto se usa la función S3 compile(object, optimizer, loss, metrics). Para encontrar la ayuda de esta función se debe escribir ?compile.keras.engine.training.Model en la consola.
Para el argumento optimizer se tienen las opciones: optimizer_adadelta, optimizer_adagrad, optimizer_adam, optimizer_adamax, optimizer_nadam, optimizer_rmsprop y optimizer_sgd. En este ejemplo vamos a usar optimizer_rmsprop. ¿Cómo seleccionar el optimizador? Vea algunas sugerencias aquÃ.
Para el argumento loss se tienen muchas opciones descritas aquÃ. Dos de esas opciones son binary_crossentropy usada para clasificación binaria y categorical_crossentropy usada para clasificación en más de dos grupos. Estas dos funciones de pérdida pueden ser consultadas en este enlace.
model %>% compile(
optimizer = optimizer_rmsprop(),
loss = 'categorical_crossentropy',
metrics = c('accuracy')
)
Para entrenar el modelo se usa la función fit().
history <- model %>% fit(
x_train, y_train,
epochs = 15, batch_size = 128,
validation_split = 0.2
)
plot(history)
Evaluate the model’s performance on the test data:
model %>% evaluate(x_test, y_test)
## $loss
## [1] 0.0888216
##
## $acc
## [1] 0.9784
Generate predictions on new data:
prediccion <- model %>% predict_classes(x_test)
tabla_confusion <- table(prediccion, y_true)
tabla_confusion
## y_true
## prediccion 0 1 2 3 4 5 6 7 8 9
## 0 971 0 1 0 2 3 4 1 4 1
## 1 0 1120 0 0 0 0 2 3 1 2
## 2 1 3 1020 5 2 0 0 11 5 0
## 3 2 3 2 990 0 6 1 6 9 8
## 4 1 0 1 0 956 0 4 0 6 7
## 5 1 2 0 7 0 872 7 0 5 2
## 6 1 2 2 0 4 6 938 0 3 0
## 7 1 1 3 5 1 0 0 1000 3 4
## 8 2 4 3 2 2 2 2 0 932 0
## 9 0 0 0 1 15 3 0 7 6 985
sum(diag(tabla_confusion)) / sum(tabla_confusion)
## [1] 0.9784
Haciendo una predicción para las primeras i=5 imagenes.
i <- 5
model %>% predict_classes(x_test[1:i, ]) # prediccion
## [1] 7 2 1 0 4
y_true[1:i] # real
## [1] 7 2 1 0 4
De la salida anterior vemos que la predicción coincide con el valor verdadero del objeto y_true.
En esta sección vamos a probar el clasificador usando números escritos manualmente por mi en paint utilizando el mouse. En la siguiente figura se ilustra la elaboración del número ocho en tamaño 28 por 28 pixeles en paint.
Vamos a cargar todos los números 0 a 9 de prueba construÃdos manualmente, los archivos fueron llamados uno.png, dos.png y asà sucesivamente. A continuación el código usado para leer los número.
library(png)
numeros <- list()
nombres <- c("cero", "uno", "dos", "tres", "cuatro", "cinco", "seis", "siete", "ocho", "nueve")
for (i in 1:10) {
w <- readPNG(paste0(nombres[i], ".png"))
numeros[[i]] <- w[, , 1]
}
true_label <- 0:9 # para comparar luego
Ahora vamos a dibujar los números de prueba que acabamos de cargar.
par(mfrow=c(3, 4))
for (j in 1:10) {
im <- numeros[[j]]
im <- abs(1-im)
im <- 1 - t(apply(im, 2, rev))
image(1:28, 1:28, im, col=gray((0:255)/255), xaxt='n', yaxt='n', xlab='', ylab='', main=nombres[j])
}
Vamos a tomar individualmente el número cinco.png, lo vamos a dibujar y luego vamos a predecir qué numero es.
num <- readPNG("cinco.png")
num <- num[, , 1] # canal 1
num <- abs(1-num)
im <- 1 - t(apply(num, 2, rev))
image(1:28, 1:28, im, col=gray((0:255)/255), xaxt='n', yaxt='n', xlab='')
numero_reshape <- array_reshape(num, c(1, 784)) # reshape
model %>% predict_classes(numero_reshape) # prediccion
## [1] 5
Vamos a generalizar la prueba anterior con todos los números dibujados a mano y luego vamos a predecir el valor al que representan. Lo primero es convertir la lista numeros en una matriz numeros_matriz de dimensión 10 x 784 con la información de los números de prueba. Luego vamos a clasificar los números, a continuación el código.
transformacion <- function(x) array_reshape(x, c(1, 784))
Numeros <- lapply(numeros, transformacion)
numeros_matriz <- do.call(rbind, Numeros)
numeros_matriz <- abs(1-numeros_matriz)
prediccion <- model %>% predict_classes(numeros_matriz)
prediccion
## [1] 2 1 2 3 9 5 6 3 8 9
Por último vamos a construir una tabla para comparar la etiqueta verdadera con la predicción.
cbind(true_label, prediccion)
## true_label prediccion
## [1,] 0 2
## [2,] 1 1
## [3,] 2 2
## [4,] 3 3
## [5,] 4 9
## [6,] 5 5
## [7,] 6 6
## [8,] 7 3
## [9,] 8 8
## [10,] 9 9
De la tabla anterior vemos que se clasificaron correctamente 7 imágenes del total de 10 que se crearon manualmente.