Monográfico. Paquete de R NNET para modelos de redes neuronales

Quiero introduciros a los modelos de redes neuronales con R, mas concretamente quiero acercaros al módulo nnet de R. Tenemos extensa literatura al respecto de las redes neuronales, personalmente considero de lectura obligatoria este link (y prácticamente toda la documentación de este profesor) El paquete nnet nos permite crear redes neuronales de clasificación monocapa. Las redes neuronales clasifican mediante algoritmos o métodos de entrenamiento, en función de estos métodos podemos tener redes supervisadas y redes no supervisadas. Las primeras buscan un límite de decisión lineal a través de un entrenamiento. Las segundas parten de unos parámetros (pesos) fijos y no requieren entrenamiento porque realizan mecanismos de aprendizaje en función de experiencias anteriores. Como ya os he indicado hay mucha bibliografía al respecto y muchas entradas en Google que pueden ayudaros a conocer mejor estos modelos. En el caso que nos ocupa, y como viene siendo tónica habitual de la bitácora, vamos a darle una visión más práctica (tampoco soy yo el más adecuado para dar esa visión teórica). Trabajamos en una gran Caja española y nuestro responsable nos pide realizar una selección de clientes para un mailing. Tenemos que “colocar” planes de pensiones vitalicios inmediatos. A nosotros se nos ocurre realizar un modelo de redes neuronales para seleccionar aquellos clientes con una puntuación más alta y, por tanto, más propensos a comprar el producto.

Como en anteriores ejemplos partimos de un objeto con datos aleatorios que simula la cartera de una entidad bancaria. Queremos determinar que clientes son mas propensos a la contratación de un plan vitalicio de pensión inmediata para seleccionarlos y lanzar una comunicación comercial sobre ellos. Simulamos una cartera de 20.000 clientes:


clientes=20000
saldo_vista=runif(clientes,0,1)*10000
saldo_ppi=(runif(clientes,0.1,0.2)*rpois(clientes,1))*100000
saldo_fondos=(runif(clientes,0.1,0.9)*(rpois(clientes,1)-1>0))*100000
edad=rpois(clientes,60)
datos_ini<-data.frame(cbind(saldo_vista,saldo_ppi,saldo_fondos,edad))
datos_ini$saldo_ppi=(edad<=68)*datos_ini$saldo_ppi
#Creamos la variable objetivo a partir de un potencial
datos_ini$potencial=runif(1,0,1)+
(log(edad)/(log(68))/100) +
runif(1,0,0.001)*(saldo_vista>5000)+
runif(1,0,0.001)*(saldo_fondos>10000)+
runif(1,0,0.007)*(saldo_ppi>10000)-
runif(1,0,0.2)
datos_ini$pvi=as.factor((datos_ini$potencial>=quantile(datos_ini$potencial,
0.90))*1)

Código más que conocido por todos porque le hemos utilizado en más de un monográfico. El paquete de R que vamos a emplear será nnet. Veamos una propuesta de estructura para el análisis:


#Carga del paquete:
library(nnet)
#Matriz de predictores:
pred=cbind(datos_ini$saldo_vista,datos_ini$saldo_ppi,
datos_ini$saldo_fondos,datos_ini$edad)
summary(pred)
#Matriz de target:
target=as.matrix(datos_ini$pvi)
#Muestra aleatoria del 10% de los clientes para el aprendizaje
select=sample(1:clientes,clientes*0.9)

Para “enseñar” a nuestra red vamos a trabajar con una muestra del 90% de los registros. El 10% restante nos servirá para validar el comportamiento predictor de nuestro modelo de redes neuronales. En realidad sería más adecuado quedarnos con 3 conjuntos de datos: entrenamiento, test y validación; pero los dos últimos en nuestro ejemplo serán uno. Pasamos a realizar el modelo:


redn=nnet(pred[select,],as.numeric(target[select,]), size = 2,
rang =0.1,decay = 5e-4, maxit = 500)

Los parámetros fundamentales son las matrices predictoras y target y de éstos solo seleccionamos una muestra select del 90%. No asignamos pesos con la función weights así que por defecto serán 1 para todos. Con size asignamos el número de capas ocultas de la neurona, en este caso 2, podemos poner de 0 a n capas ocultas entre la neurona de entrada y la neurona de salida. Con rang asignamos los pesos iniciales, con decay establecemos los “weight decay” (pesos decadentes), es una medida para limitar el efecto de los pesos altos. Con maxit limitamos el número máximo de iteraciones del modelo, como no tenemos un gran número de observaciones paramos en 500, por defecto está en 100.

Tras ejecutarlo vemos que no han hecho falta las 500 iteracciones para llegar a la convergencia del modelo. En este punto tenemos que estudiar su comportamiento predictor con las observaciones reservadas a test. Empleamos la función predict para añadir el potencial a todo el conjunto de datos inicial:


prediccion=predict(redn,datos_ini)
names(prediccion)=c("prediccion")
datos_ini.pred=cbind(datos_ini,prediccion)
summary(datos_ini.pred)
tapply(datos_ini.pred$prediccion, list(pvi=datos_ini.pred$pvi),
mean, na.rm=TRUE)

Ahora analizamos como funcionan las predicciones en el 10% de las observaciones reservadas para el testeo:


datos_ini.test=datos_ini.pred[-select,]
library(reshape)
datos_ini.test=sort_df(datos_ini.test,vars='prediccion')
tapply(datos_ini.test$prediccion, list(pvi=datos_ini.test$pvi), mean)

El conjunto de datos de testeo tiene una media de potencial mucho más alta para aquellos que tienen contratado el producto, parece que tiene un comportamiento correcto. Es interesante realizar un pequeño analisis del comportamiento de las variables que han participado en el estudio. Planteo algo muy sencillo. Voy a tramificar las variables cuantitativas en 10 tramos (percentiles) y calcular la media del potencial que hemos obtenido con el modelo en cada tramo.


#Potenciales por percentil de edad:
edad=data.frame(cbind(1,datos_ini.pred$edad,datos_ini.pred$prediccion))
names(edad)=c("acum","edad","prediccion")
edad=sort_df(edad,vars="edad")
edad$tramo=as.factor(ceiling((cumsum(edad[,1])/nrow(edad))*10))
rbind(
tapply(edad$edad, list(edad=edad$tramo), min, na.rm=TRUE),
tapply(edad$edad, list(edad=edad$tramo), max, na.rm=TRUE),
tapply(edad$prediccion, list(edad=edad$tramo), mean, na.rm=TRUE))

Vemos que para edades más alta el potencial es mayor. Este programa habría de parametrizarse y convertirse en una función, pero ese ejercicio os lo dejo a vosotros, vamos que lo he intentado, no he sido capaz y no he insistido… Igualmente debemos hacerlo para todas las variables, por ejemplo:


#Potenciales por percentil de saldo_vista:
saldo_vista=data.frame(cbind(1,datos_ini.pred$saldo_vista,datos_ini.pred$prediccion))
names(saldo_vista)=c("acum","saldo_vista","prediccion")
saldo_vista=sort_df(saldo_vista,vars="saldo_vista")
saldo_vista$tramo=as.factor(ceiling((cumsum(saldo_vista[,1])/nrow(saldo_vista))*10))
rbind(
tapply(saldo_vista$saldo_vista, list(saldo_vista=saldo_vista$tramo), min, na.rm=TRUE),
tapply(saldo_vista$saldo_vista, list(saldo_vista=saldo_vista$tramo), max, na.rm=TRUE),
tapply(saldo_vista$prediccion, list(saldo_vista=saldo_vista$tramo), mean, na.rm=TRUE))

Con esta metodología podemos dar un carácter explicativo a nuestro modelo de redes neuronales, si bien es cierto que puede resultar más complicado encontrar interacciones entre las variables. Creo que tenéis un buen ejemplo de uso del nnet. Comentaros que no soy ningún experto en redes neuronales y si he cometido algún error o creéis interesante aportar algo estoy en rvaquerizo@analisisydecision.es

17 comentarios en “Monográfico. Paquete de R NNET para modelos de redes neuronales

  1. Hola,

    Estoy trasteando con este paquete y si no entiendo mal, size es el número de neuronas de una única capa oculta, y no se puede modificar el número de capas ¿Me equivoco?.

  2. Hola Fernando, nnet sirve para redes de una sóla neurona. Con size asignas el número de capas.

    Trabaja con neuralnet, elabora un monográfico, me lo mandas y lo publico.

  3. disculpa estoy intentado hacer una red neuronal y estoy siguiendo tus pasos, pero al intentar crear el modelo con nnet me sale un error que dice “missing values in ‘x'”, entiendo que es porq puede que tenga valores perdidos, esto es en mi matriz predictora o en el target? tamb vi que se puede corregir con na.action, pero no entiendo exactamente como, lo he intendo de muchas maneras; “na.action=TRUE, na.action=na.omit(pred)…” pero con todas me sigue saliendo el mismo error, agradeceria muchisimo su ayuda.

  4. Buenas Tardes

    ¿Hay algún método para representar gráficamente, la red neuronal que se genera no NNET?

    Según he podido comprobar para el comando neuralnet con “plot(x)” aparece la red. Pero no sé para el caso de nnet.

    Muchas gracias

  5. Buenas Tardes

    ¿Hay algún método para representar gráficamente, la red neuronal que se genera con NNET?

    Según he podido comprobar para el comando neuralnet con “plot(x)” aparece la red. Pero no sé para el caso de nnet.

    Muchas gracias

  6. Muchas gracias,

    Sin embargo, no me gráfica la red, es extraño,

    library(nnet)
    nnet.1 <- nnet(y.0~y.1+y.2+y.3,data=datos, size=n, entropy=T,abstol=0.001,decay=0.0001, maxit = 1000)
    plot(nnet.1)

    Bueno, igual muchas gracias.

    Y si es posible sabe como calcular el AIC de NNET .. el comando neuralnet tiene incorporada la opción pero no se la encuentro a nnet.

    Gracias

  7. Necesito ayuda como sea…. tengo que programar en R y no se… trabajo con SAS y SQL todo

    tengo un ejercicio que resolver con unos datos de 2 varibles (peso y edad) y me pide:

    No se puede interpolar exactamente una función analítica sencilla a estos datos, ya que no tenemos una función con valor único para cada argumento. En su lugar, tenemos un modelo por mínimos cuadrados no lineal de estos datos, y ello utilizando una exponencial negativa como se describe a continuación:

    y = 233.846(1-e exp -0.006042x)+ e

    con e el término de error.

    Utilizando el algoritmo de propagación hacia atrás, diseñar un perceptrón multicapa que proporcione una aproximación por mínimos cuadrados no lineal para este conjunto de datos. Comparar los resultados con los del modelo por mínimos cuadrados descrito.

    entiendo algo pakete neuralnet, aun asi no se calcular el perceptron y con el ajuste de minimo cuadrado me lio, me salen mucho fallos en el softwate de mi ordenador personal, maña quisiera poder sacar los resultados…

    por favor… gracias

  8. Hola sergio, creo que no es justo que aquí hagamos la práctica de Técnicas Avanzadas de Predicción.

    El problema está con los pesos iniciales y que por defecto los paquetes que empleamos tienen la opción LINOUT=F. Es la base principal LINOUT=T y los pesos iniciales.

  9. hola no quiero que me soluciones la practica es solo el programar el calculo en R DEL perceptron, o algo de orientacion.

    primero utilice el paquete “neural” pero nose si es antiguo o como pero no puedo cargarlo,
    porque trataba de utilizar el mlptrain() para dar soluciona mi cuestion

    despues de mucho indagar pense en utilizar el paquete “neuralnet” con su propia funcion neuralnet
    y en las opciones de procedimiento utilizar el “backprg” o algo asi vi que se llamaba en el asiatente de R

    No se si voi por buen camino, con respecto a lo de los pesos justo me salia un aviso de error sobre eso probare lo que dijiste.
    y seguire en ello.Aunque no me qeudo muy claro si dnetro del procedimiento podemos calcular estos peso o cargarlos previamente calculados…

    MUCHAS GRACIAS

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *