Redes Neuronales

En esta práctica implementarás una red neuronal para predicción, seleccionando las variables de entrada más importantes, entrenando el modelo y validando su rendimiento. Es importante verificar que se tienen instalados los paquetes necesarios para la práctica, en caso de que alguna marque error será necesaria su instalación antes de intentar volver a ejecutar el código.

Paso 1 - Instalar y cargar las bibliotecas necesarias

# Cargar las bibliotecas necesarias
#install.packages("nnet") 
#install.packages("randomForest") 
#Descomentar las 2 lineas anteriores en caso de no tener instalados los paquetes randomForest y nnet o puedes instalarlos previamente desde el ménu de Tools-->Intall packages. 
library(lattice)
library(ggplot2)
library(nnet)
library(caret)

Paso 2 - Cargar datos para análisis y entrenamiento

Continuaremos usando la misma base de datos de la semana pasada, la base de datos Boston para predecir el coste de la vivienda.

# Cargar la base de datos
data("Boston", package = "MASS")

# Ver primeros registros de la base de datos
head(Boston)
##      crim zn indus chas   nox    rm  age    dis rad tax ptratio  black lstat
## 1 0.00632 18  2.31    0 0.538 6.575 65.2 4.0900   1 296    15.3 396.90  4.98
## 2 0.02731  0  7.07    0 0.469 6.421 78.9 4.9671   2 242    17.8 396.90  9.14
## 3 0.02729  0  7.07    0 0.469 7.185 61.1 4.9671   2 242    17.8 392.83  4.03
## 4 0.03237  0  2.18    0 0.458 6.998 45.8 6.0622   3 222    18.7 394.63  2.94
## 5 0.06905  0  2.18    0 0.458 7.147 54.2 6.0622   3 222    18.7 396.90  5.33
## 6 0.02985  0  2.18    0 0.458 6.430 58.7 6.0622   3 222    18.7 394.12  5.21
##   medv
## 1 24.0
## 2 21.6
## 3 34.7
## 4 33.4
## 5 36.2
## 6 28.7

Si no recuerdas el significado de cada columna y la información contenida en esta base de datos puedes consultar su descripción en: http://www.cs.toronto.edu/~delve/data/boston/bostonDetail.html

Paso 3 División del conjunto de datos

Dividimos el conjunto de datos en un 70% para entrenamiento de la red y un 30% para realizar pruebas.

# Fijar semilla para reproducibilidad
set.seed(123)

# Dividir los datos en entrenamiento (70%) y prueba (30%)
indice <- createDataPartition(Boston$medv, p = 0.7, list = FALSE)
entrenamiento <- Boston[indice, ]
prueba <- Boston[-indice, ]

Paso 4 - Selección de las variables a emplear

Utilizaremos la función rfe (Recursive Feature Elimination) del paquete caret para seleccionar las variables más importantes.

# Selección de las variables más importantes usando Recursive Feature Elimination
control <- rfeControl(functions = rfFuncs, method = "cv", number = 20)
#Otras opciones que puedes emplear en method son:
#cv para usar validación cruzada
#boot para usar Bootstrap 
#boot632 para usar Bootstrap .632
#repeatedcv para usar validación cruzada repetida
#LOOCV Leave-One-Out Cross-Validation para usar Validación cruzada de dejar uno fuera
#LGOCV": Leave-Group-Out Cross-Validation para usar Validación cruzada de dejar fuera grupos 
#none para no usar remuestreo
rfeModelo <- rfe(entrenamiento[, -14], entrenamiento$medv, sizes = c(1:13), rfeControl = control)

# Imprime la salida del metodo RFE
print(rfeModelo)
## 
## Recursive feature selection
## 
## Outer resampling method: Cross-Validated (20 fold) 
## 
## Resampling performance over subset size:
## 
##  Variables  RMSE Rsquared   MAE RMSESD RsquaredSD  MAESD Selected
##          1 6.186   0.5358 4.636  1.489     0.1942 0.8911         
##          2 4.206   0.7726 3.089  1.356     0.1474 0.7552         
##          3 3.784   0.8145 2.715  1.259     0.1259 0.7329         
##          4 3.653   0.8367 2.581  1.399     0.1153 0.7107         
##          5 3.595   0.8485 2.554  1.482     0.1115 0.6949         
##          6 3.331   0.8607 2.348  1.286     0.1037 0.5825         
##          7 3.356   0.8625 2.341  1.350     0.1068 0.5900         
##          8 3.326   0.8638 2.328  1.392     0.1125 0.6094         
##          9 3.254   0.8671 2.284  1.375     0.1070 0.6134         
##         10 3.235   0.8701 2.261  1.377     0.1061 0.6127         
##         11 3.259   0.8694 2.277  1.405     0.1085 0.6073         
##         12 3.223   0.8706 2.244  1.352     0.1059 0.6219         
##         13 3.193   0.8734 2.227  1.352     0.1039 0.6159        *
## 
## The top 5 variables (out of 13):
##    rm, lstat, crim, nox, ptratio

Paso 5 - Entrenamiento de la red

# Usar las variables seleccionadas para entrenar el modelo de red neuronal
variables_seleccionadas <- predictors(rfeModelo)
print(variables_seleccionadas)
##  [1] "rm"      "lstat"   "crim"    "nox"     "ptratio" "age"     "dis"    
##  [8] "tax"     "indus"   "black"   "rad"     "chas"    "zn"
entrenamiento_ajustado <- entrenamiento[, c(variables_seleccionadas, "medv")]

# Entrenar la red neuronal
set.seed(123)
modelo_nnet <- nnet(medv ~ ., data = entrenamiento_ajustado, size = 5, linout = TRUE, trace = FALSE)
summary(modelo_nnet)
## a 13-5-1 network with 76 weights
## options were - linear output units 
##   b->h1  i1->h1  i2->h1  i3->h1  i4->h1  i5->h1  i6->h1  i7->h1  i8->h1  i9->h1 
##   -0.30    0.40   -0.13    0.54    0.62   -0.64    0.04    0.55    0.07   -0.06 
## i10->h1 i11->h1 i12->h1 i13->h1 
##    0.64   -0.07    0.25    0.10 
##   b->h2  i1->h2  i2->h2  i3->h2  i4->h2  i5->h2  i6->h2  i7->h2  i8->h2  i9->h2 
##   -0.56    0.56   -0.36   -0.64   -0.24    0.64    0.55    0.27    0.20    0.69 
## i10->h2 i11->h2 i12->h2 i13->h2 
##    0.22    0.29    0.06    0.13 
##   b->h3  i1->h3  i2->h3  i3->h3  i4->h3  i5->h3  i6->h3  i7->h3  i8->h3  i9->h3 
##   -0.32   -0.67    0.33    0.55    0.25   -0.11   -2.23   -0.17  -10.01   -0.67 
## i10->h3 i11->h3 i12->h3 i13->h3 
##  -11.25   -0.50   -0.50   -0.79 
##   b->h4  i1->h4  i2->h4  i3->h4  i4->h4  i5->h4  i6->h4  i7->h4  i8->h4  i9->h4 
##   -0.12   -0.19   -0.49   -0.51   -0.37   -0.06   -0.37    0.50   -0.80   -0.09 
## i10->h4 i11->h4 i12->h4 i13->h4 
##    0.11   -0.53    0.09   -0.41 
##   b->h5  i1->h5  i2->h5  i3->h5  i4->h5  i5->h5  i6->h5  i7->h5  i8->h5  i9->h5 
##   -0.52    0.35    0.55   -0.18    0.23   -0.57   -0.16   -0.32    0.44   -0.07 
## i10->h5 i11->h5 i12->h5 i13->h5 
##    0.43    0.44    0.41   -0.08 
##   b->o  h1->o  h2->o  h3->o  h4->o  h5->o 
##   5.86   5.68   5.80   1.27   0.27   5.11

Paso 5 - Validación del modelo

Para validar que tan bueno es nuestro modelo nuevamente calcularemos el MAE y el MSE.

# Predecir los valores en el conjunto de prueba
prueba_ajustada <- prueba[, c(variables_seleccionadas, "medv")]
predicciones <- predict(modelo_nnet, prueba_ajustada[, -ncol(prueba_ajustada)])

# Calcular MAE y MSE
mae <- mean(abs(predicciones - prueba_ajustada$medv))
mse <- mean((predicciones - prueba_ajustada$medv)^2)

# Imprimir los resultados
cat("Error Absoluto Medio (MAE):", mae, "\n")
## Error Absoluto Medio (MAE): 6.750682
cat("Error Cuadrático Medio (MSE):", mse, "\n")
## Error Cuadrático Medio (MSE): 92.28911

Paso 6 - Evaluación del modelo graficamente

En este gráfico esperariamos que todos los puntos estén sobre la diagonal, si esto fuese así nuestro modelo hubiese predicho todos los valores a la perfección, pero sabemos que esto será muy poco probable, con lo que se desea que todos los puntos estén más lo más cerca posible de la línea roja.

# Gráfico de predicciones vs. valores reales
plot(prueba_ajustada$medv, predicciones, 
     main = "Predicciones vs Valores Reales",
     xlab = "Valores Reales",
     ylab = "Predicciones",
     col = "blue", pch = 19)
abline(0, 1, col = "red")

Paso 7 ¿Qué pasó? ¿No debería ser mejor que mi regresión de la semana pasada?

Usualmente las redes neuronales requieren que los datos estén normalizados para que nos ofrezca mejores resultados, esto se puedo haber hecho antes del paso 3, sin embargo en esta práctica se decide hacer hasta este punto para retomar la importancia de la limpieza y tratamiento de los datos, ya que hasta este punto casi no hemos puesto en práctica esto. Por lo que procedemos a normalizar la información y a realizar practicamente todo el procedimiento, esto para poder comparar como mejora el rendimiento de nuestra red.

# Escalar los datos de entrenamiento y prueba
preproc <- preProcess(entrenamiento_ajustado[, -ncol(entrenamiento_ajustado)], method = c("center", "scale"))
entrenamiento_escalado <- predict(preproc, entrenamiento_ajustado)
prueba_escalada <- predict(preproc, prueba_ajustada)

# Entrenar la red neuronal con datos escalados
set.seed(123)
modelo_nnet_escalado <- nnet(medv ~ ., data = entrenamiento_escalado, size = 5, linout = TRUE, trace = FALSE)

predicciones_escaladas <- predict(modelo_nnet_escalado, prueba_escalada[, -ncol(prueba_escalada)])

mae <- mean(abs(predicciones_escaladas - prueba_escalada$medv))
mse <- mean((predicciones - prueba_escalada$medv)^2)

# Imprimir los resultados
cat("Error Absoluto Medio (MAE):", mae, "\n")
## Error Absoluto Medio (MAE): 4.006047
cat("Error Cuadrático Medio (MSE):", mse, "\n")
## Error Cuadrático Medio (MSE): 92.28911
plot(prueba_escalada$medv, predicciones_escaladas, 
     main = "Predicciones vs Valores Reales (Escalado)",
     xlab = "Valores Reales",
     ylab = "Predicciones",
     col = "blue", pch = 19)
abline(0, 1, col = "red")

Después de normalizar los datos vemos que nuestro MAE mejoró y gráficamente los puntos se ajustan de mejor manera a la diagonal, sin embargo, el MSE aún tiene un valor grande, con lo que podemos concluir que en la mayoría de las predicciones se acerca bastante bien, sin embargo existen valores que si se alejan de esta predcción, esto se puede observar en los puntos del extremo del gráfico.

Paso 8 - ¿Cómo mejoramos el modelo?

Si comparamos los valores del MAE y MSE obtenidos con la semana pasada(regresión polinomial), nuestros valores actuales se acercan, pero no los hemos podido mejorar, aquí tenemos varias opciones para intentar mejorar estos valores, entre los cuales se encuentra:
1 - Hacer transformaciones logaritmicas o de raíz, justo lo que hicimos la semana pasada
2 - Considerar un modelo polinomial, lo que nos dió mejores resultados
3 - Eliminar valores átipicos o extremos
4 - Aumentar el número de redes neuronales, esto variando el parametro size
5 - Aumetar el número de capas ocultas, cosa que no podemos realizar,ya que la biblioteca que estamos empleando solo nos permite tener una capa oculta.
6 - Ocupar bibliotecas más avanzadas que permitan trabajar modelos flexibles, los cuales pueden genera mejores ajustes, esto lo estaremos realizando la próxima semana con la biblioteca neuralnet, con la cual estaremos alcanzado un MAE de 2.5 y un MSE de 15.6. 7- Inteta mejorar estos valores haciendo ajustes en el código, modificando los parámetros al hacer el entrenamiento de la red.

Conclusiones

nnet es una opción sencilla y eficiente para tareas donde una sola capa oculta es suficiente, y es útil para pequeños conjuntos de datos. Sin embargo, si necesitas entrenar redes neuronales más profundas o más flexibles, sería recomendable utilizar paquetes más avanzados como , neuralnet,keras o tensorflow, que ofrecen mayor control y potencia.Finalmente, el parámetro linout en TRUE es últil cuando tu modelo es una regresión, esta se ajustará a una regresión lineal, pero si tu problema es de clasificación, por ejemplo, el caso de Titanic, este valor debe ser FALSE.