En el presente documento se explica el proceso para hacer redes neuronales con R. Dichas redes se aplican dentro de las siguientes categorías generales:
Los pasos a seguir para la creación de las redes neuronales son:
A continuación el código para cargar los datos:
file <- "https://raw.githubusercontent.com/fhernanb/datos/master/propelente"
datos <- read.table(file=file, header=TRUE)
head(datos) # Muestra las 6 primeras filas
Para crear un diagrama de dispersión que muestre la relación entre las variables usamos las siguientes instrucciones:
#install.packages("ggplot2", dependencies = TRUE) # Se instala una sola vez
library(ggplot2) # Se carga cada session
ggplot(datos, aes(x=Edad, y=Resistencia)) + geom_point()
Se usa la función neuralnet del paquete neuralnet para crear la red neuronal y el paquete ggplot2 para crear el gráfico de dispersión. Se instala y se cargan los paquetes, así:
#install.packages("neuralnet", dependencies = TRUE) # Se instala una sola vez
library(neuralnet) # Se carga cada session
La función neuralnet tiene la siguiente estructura:
Argumentos:
formula:
una descripción simbólica del modelo que se va a instalar.data:
un marco de datos que contiene las variables especificadas en la fórmula.hidden:
un vector de enteros que especifica el número de neuronas ocultas (vértices) en cada capa.threshold:
un valor numérico que especifica el umbral para los derivados parciales de la función de error como criterio de parada.stepmax:
los pasos máximos para el entrenamiento de la red neural. Alcanzar este máximo conduce a una interrupción del proceso de entrenamiento de la red neural.rep:
el número de repeticiones para el entrenamiento de la red neural.startweights:
un vector que contiene los valores iniciales de las ponderaciones. Se establece en NULL para la inicialización aleatoria.learningrate.limit:
un vector o una lista con el límite inferior y superior del ritmo de aprendizaje. Utilizado sólo para RPROP y GRPROP.learningrate.factor:
un vector o una lista que contenga los factores de multiplicación para la velocidad de aprendizaje superior e inferior. Utilizado sólo para RPROP y GRPROP.learningrate:
un valor numérico que especifica la velocidad de aprendizaje utilizada por la retropaginación tradicional. Se utiliza sólo para la retropropagación tradicional.lifesign:
una cadena que especifica cuánto imprimirá la función durante el cálculo de la red neuronal. ninguna“,”mínima" o “completa”.lifesign.step:
un número entero que especifica el tamaño del paso para imprimir el umbral mínimo en modo de signo de vida completo.algorithm:
una cadena que contiene el tipo de algoritmo para calcular la red neuronal. Los siguientes tipos son posibles: ‘backprop’, ‘rprop+’, ‘rprop-’, ‘sag’, o ‘slr’. backprop" se refiere a la retropagación, “rprop+” y “rprop-” se refieren a la retropagación elástica con y sin retroceso del peso, mientras que “sag” y “slr” inducen el uso del algoritmo de convergencia global modificado (grprop). Vea Detalles para más información.err.fct:
una función diferenciable que se utiliza para el cálculo del error. Alternativamente, se pueden utilizar las cadenas ‘sse’ y ‘ce’, que significan la suma de los errores cuadrados y la cruzentropía.act.fct:
una función diferenciable que se utiliza para suavizar el resultado del producto cruzado del covariante o las neuronas y los pesos. Adicionalmente las cadenas, ‘logístico’ y ‘tanh’ son posibles para la función logística e hiperbólica tangencial.linear.output:
lógico. Si act.fct no debe aplicarse a la salida, las neuronas establecen la salida lineal como VERDADERA, de lo contrario como FALSA.exclude:
un vector o una matriz que especifique los pesos que se excluyen del cálculo. Si se indica como vector, deben conocerse las posiciones exactas de las pesas. Una matriz con n filas y 3 columnas excluirá n pesos, donde la primera columna representa la capa, la segunda la neurona de entrada y la tercera la neurona de salida del peso.constant.weights:
un vector que especifica los valores de los pesos que se excluyen del proceso de entrenamiento y se tratan como fijos.likelihood:
lógico. Si la función de error es igual a la función de verosimilitud negativa, se calcularán los criterios de información AIC y BIC. Además, el uso de confidence.interval es significativo.Para conocer en detalle la función se recomienda al lector escribir en la consola de R help(neuralnet).
Antes de crear la red es necesario escalar las variables para evitar el efecto de la escala de las variables. Existen varias formas de escalar pero se usará una transformación para pasar los valores de las variables al intervalo (0,1).
Con el siguiente código se va convertir los datos originales a datos escalados y se almacenarán en el objeto scaled.
maxs <- apply(datos, 2, max) # Máximo valor de las variables
mins <- apply(datos, 2, min) # Mínimo valor de las variables
scaled <- as.data.frame(scale(datos, center=mins, scale=maxs-mins))
A continuación, se comparan las primeras 6 filas de datos y de scaled para ver lo que sucedió.
head(cbind(datos, scaled))
En la siguiente figura se muestra el diagrama de dispersión para las variables escaladas. Al comparar ambos gráficos de dispersión (con datos y con scaled) se observa el mismo patrón en la nube de puntos, la única diferencia es que ahora los valores de las variables (Y y X) están en (0,1).
#install.packages("gridExtra", dependencies = TRUE) # Se instala una sola vez
require(gridExtra) # Se carga cada session
plot1 <- ggplot(datos, aes(x=Edad, y=Resistencia)) + geom_point()
plot2 <- ggplot(scaled, aes(x=Edad, y=Resistencia)) + geom_point()
grid.arrange(plot1, plot2, ncol=2)
Ahora bien, la primera red neuronal a considerar se llamará mod1 y tendrá una capa con un solo nodo. El código para crear la red es el siguiente.
mod1 <- neuralnet(Resistencia ~ Edad, data=scaled, hidden=c(1), threshold=0.01)
Recuerde que:
hidden:
es un vector de enteros que especifica el número de neuronas ocultas (vértices) en cada capa.threshold:
un valor numérico que especifica el umbral para los derivados parciales de la función de error como criterio de parada.Además, se puede construir un dibujo con la red ajustada usando la función plot sobre el objeto mod1, véase:
plot(mod1, rep="best")
El objeto mod1 tiene varios elementos en su interior, éstos se pueden ver usando el código siguiente:
names(mod1)
## [1] "call" "response" "covariate"
## [4] "model.list" "err.fct" "act.fct"
## [7] "linear.output" "data" "exclude"
## [10] "net.result" "weights" "generalized.weights"
## [13] "startweights" "result.matrix"
El elemento act.fct es la función de activación (logística por defecto) y el elemento weights contiene los pesos mostrados en la anterior figura. Estos dos elementos serán útiles más adelante. A continuación el código para explorar lo que hay dentro de estos dos elementos.
mod1$act.fct # Activation function
## function (x)
## {
## 1/(1 + exp(-x))
## }
## <bytecode: 0x00000000207626d8>
## <environment: 0x0000000020765c10>
## attr(,"type")
## [1] "logistic"
unlist(mod1$weights) # Obtener en formas de vector los weigths=pesos
## [1] 0.1698825 -1.5879861 -0.4231497 2.4697846
En este ejemplo la base de datos tiene solo 20 observaciones y por esta razón el conjunto de entrenamiento y conjunto de prueba son el mismo.
En el código mostrado a continuación se crea el conjunto de prueba test solo con la covariable Edad proveniente de la base scaled. La función compute permite predecir los valores Resistencia para la informacion disponible en test teniendo como referencia una red neuronal entrenada, en este caso vamos a usar mod1.
test <- data.frame(Edad = scaled$Edad)
myprediction <- compute(x=mod1, covariate=test)
El objeto myprediction tiene varios elementos y uno de ellos es $net.result que contiene las predicciones. A continuación vamos a explorar los 5 primeros valores.
myprediction$net.result[1:5]
## [1] 0.36266075 0.09269394 0.66160083 0.30824542 0.76744902
El elemento $net.result del objeto myprediction tiene la respuesta estimada pero en la forma escalada, por esta razón es necesario aplicar la transformación inversa para obtener el resultado en la escala original. A continuación el código necesario para retornar a la escala original.
yhat_red <- myprediction$net.result * (max(datos$Resistencia)-min(datos$Resistencia))+min(datos$Resistencia)
datos$yhat_red <- yhat_red
yhat_red[1:5] # Para ver los primeros 5 valores estimados
## [1] 2032.125 1768.624 2323.905 1979.013 2427.219
Para comparar los resultados obtenidos con la red neuronal podemos dibujar los valores observados y contra los valores estimados de la variable respuesta. A continuación, se muestra el código para crear el gráfico de dispersión al cual se le agrega un línea recta a 45 grados como referencia; entre más cerca este un punto de la línea, significa que la respuesta estimada y son cercanos.
ggplot(datos, aes(x=Resistencia, y=yhat_red)) + geom_point() +
geom_abline(intercept=0, slope=1, color="blue", linetype="dashed", size=1.5)
Del gráfico anterior podemos ver que las estimaciones y estimado son cercanas a los verdaderos y, adicionalmente el coeficiente de correlación lineal calculado es de 0.9469494 lo cual es un valor alto.
mod2 <- lm(Resistencia ~ Edad, data=datos)
coef(mod2)
## (Intercept) Edad
## 2627.82236 -37.15359
Los valores estimados para la resistencia con el modelo lineal simple se pueden obtener de la siguiente manera.
yhat_mls <- fitted(mod2)
yhat_mls[1:5] # Para ver los primeros 5 valores estimados
## 1 2 3 4 5
## 2051.942 1745.425 2330.594 1996.211 2423.478
Para comparar la red neuronal con el modelo lineal simple vamos a usar el Error Cuadrático Medio, así:
ecm_red <- mean((datos$Resistencia - yhat_red)^2)
ecm_red
## [1] 8820.333
ecm_rls <- mean((datos$Resistencia - yhat_mls)^2)
ecm_rls
## [1] 8312.743
Como ilustración vamos a construir una segunda red con dos capas, la primera con 2 nodos y la segunda con 3 nodos, a continuación el código.
mod3 <- neuralnet(Resistencia ~ Edad, data=scaled,
hidden=c(2, 3), threshold=0.01)
Nuevamente podemos dibujar la red construída.
plot(mod3, rep="best")
Hernández, F. “Redes neuronales con neuralnet”. Consultado el 31 de mayo de 2019.
Wikipedia. “Red neuronal artificial”. Consultado el 7 de junio de 2019.