Predicción del éxito o fracaso en Matemáticas utilizando Xgboost

Posted por @mirallesjm

a 30 de Junio, 2017

Matematicas

Podría resulta interesante tanto a educadores, padres y alumnos conocer en que media afectan características demográficos, sociales y relacionadas con la propia Escuela a la hora de explicar el éxito o fracaso escolar.  Esto es precisamente lo que me ha motivado a realizar este estudio analítico, para ello nos centraremos en la asignatura de Matemáticas.

¿En que medida afecta al suspenso en matemáticas factores como?

  • Nivel de estudio de los padres
  • Salir con los amigos
  • Tener novio/a
  • Zona de residencia Urbana/Rural
  • Tiempo libre 

A través de nuestro estudio daremos respuesta a estas preguntas. Para ello utilizaremos el algoritmo XGBoost, una técnica de Machine Learning muy popular en cualquier competición de Data Science. Nos apoyaremos en el conjunto de datos del consumo de alcohol en estudiantes de la UCI Machine Learning Repositorio

¿Qué es XGBoost?

Técnica supervisada que nos permite realizar tanto regresión como clasificación. Su capacidad de hacer computación en paralelo hace que sea 10 veces más rápido que el gradient boosting.

Su alto poder predictivo la convierte en una técnica ideal para competiciones. Cuenta con funciones adicionales para realizar validación cruzada y búsqueda de variables importante, además existen muchos parámetros que nos permiten optimizar el modelo.

Exploración analítica de los datos

Vamos a leer en los datos y exploraremos su estructura:

student <- read.table(“student-mat.txt”, header = TRUE, sep = “\t”, quote = “\””, dec = “.”, na.strings = “NA”, fill = TRUE, comment.char = “”,stringsAsFactors = T)

str(student)

‘data.frame’: 395 obs. of 33 variables:
$ school : Factor w/ 2 levels “GP”,”MS”: 1 1 1 1 1 1 1 1 1 1 …
$ sex : Factor w/ 2 levels “F”,”M”: 1 1 1 1 1 2 2 1 2 2 …
$ age : int 18 17 15 15 16 16 16 17 15 15 …
$ address : Factor w/ 2 levels “R”,”U”: 2 2 2 2 2 2 2 2 2 2 …
$ famsize : Factor w/ 2 levels “GT3″,”LE3”: 1 1 2 1 1 2 2 1 2 1 …
$ Pstatus : Factor w/ 2 levels “A”,”T”: 1 2 2 2 2 2 2 1 1 2 …
$ Medu : int 4 1 1 4 3 4 2 4 3 3 …
$ Fedu : int 4 1 1 2 3 3 2 4 2 4 …
$ Mjob : Factor w/ 5 levels “at_home”,”health”,..: 1 1 1 2 3 4 3 3 4 3 …
$ Fjob : Factor w/ 5 levels “at_home”,”health”,..: 5 3 3 4 3 3 3 5 3 3 …
$ reason : Factor w/ 4 levels “course”,”home”,..: 1 1 3 2 2 4 2 2 2 2 …
$ guardian : Factor w/ 3 levels “father”,”mother”,..: 2 1 2 2 1 2 2 2 2 2 …
$ traveltime: int 2 1 1 1 1 1 1 2 1 1 …
$ studytime : int 2 2 2 3 2 2 2 2 2 2 …
$ failures : int 0 0 3 0 0 0 0 0 0 0 …
$ schoolsup : Factor w/ 2 levels “no”,”yes”: 2 1 2 1 1 1 1 2 1 1 …
$ famsup : Factor w/ 2 levels “no”,”yes”: 1 2 1 2 2 2 1 2 2 2 …
$ paid : Factor w/ 2 levels “no”,”yes”: 1 1 2 2 2 2 1 1 2 2 …
$ activities: Factor w/ 2 levels “no”,”yes”: 1 1 1 2 1 2 1 1 1 2 …
$ nursery : Factor w/ 2 levels “no”,”yes”: 2 1 2 2 2 2 2 2 2 2 …
$ higher : Factor w/ 2 levels “no”,”yes”: 2 2 2 2 2 2 2 2 2 2 …
$ internet : Factor w/ 2 levels “no”,”yes”: 1 2 2 2 1 2 2 1 2 2 …
$ romantic : Factor w/ 2 levels “no”,”yes”: 1 1 1 2 1 1 1 1 1 1 …
$ famrel : int 4 5 4 3 4 5 4 4 4 5 …
$ freetime : int 3 3 3 2 3 4 4 1 2 5 …
$ goout : int 4 3 2 2 2 2 4 4 2 1 …
$ Dalc : int 1 1 2 1 1 1 1 1 1 1 …
$ Walc : int 1 1 3 1 2 2 1 1 1 1 …
$ health : int 3 3 3 5 5 5 3 1 1 5 …
$ absences : int 6 4 10 2 4 10 0 6 0 0 …
$ G1 : int 5 5 7 15 6 15 12 6 16 14 …
$ G2 : int 6 5 8 14 10 15 12 5 18 15 …
$ G3 : int 6 6 10 15 10 15 11 6 19 15 …

dim(student)
[1] 395 33

En nuestro caso contamos con un dataset que contiene 395 alumnos con 33 características medibles. Nuestro objetivo es clasificar a los alumnos en función de la variable respuesta G3 – nota final (numérica: de 0 a 20, target).

Veamos la distribución de la nota final, en este caso utilizaremos la libraria ggplot2.

# Distribucion Target
library(ggplot2)
ggplot(student, aes(x=G3)) +
geom_histogram(binwidth = 1, color=”white”, fill=rgb(0.2,0.7,0.1,0.4))

Target

# discretizaremos la variable objetivo
student$Result <- as.factor(ifelse(student$G3 <= 9.9, ‘0’,
ifelse(student$G3 > 9.9 & student$G3 <=13.9, ‘1’,
ifelse(student$G3 > 13.9 & student$G3 <=16.9, ‘2’,
ifelse(student$G3 > 16.9, ‘3’, ‘0’)))))

table(student$Result)

0 1 2 3
130 165 76 24

El siguiente paso es realizar un estudio bivariante de nuestra target (Result) frente a algunos de las principales variables de nuestro dataset. Con ello podremos observar como se comportan las distribuciones y hacernos una idea de aquellos posibles predictores.

library(GGally)
library(ggplot2)

levels(student$Result) <- c(“Suspenso”,”Aprobado”,”Notable”,”Sobresaliente”)
ggpairs(student[,c(“absences”,”failures”,”health”,”goout”,”Medu”,”Result”)],
aes(col = Result))

ggpairs

 

library(scales)
library(gridExtra)

plot1 <- ggplot(student, aes(x = Result, y = age, fill= Result)) +
scale_x_discrete(“Target”) +
scale_y_continuous(“Edad”, breaks = seq(0,22, 2)) +
geom_boxplot()

plot2 <- ggplot(student, aes(x = Result, y = studytime , fill= Result)) +
scale_x_discrete(“Target”) +
scale_y_continuous(“Tiempo Estudio”, breaks = seq(0,4, 1)) +
geom_boxplot()

plot3 <- ggplot(student, aes(x= sex, group = Result )) +
geom_bar(aes(y = ..prop.., fill = factor(..x..)), stat = “count”) +
geom_text(aes(label = scales::percent(..prop..), y = ..prop..), stat = “count”, vjust = -.5) +
labs(y = “Percent”, fill = “Sexo”) +
facet_grid(~Result) +
scale_y_continuous(labels = percent)

plot4 <- ggplot(student, aes(x= school, group = Result )) +
geom_bar(aes(y = ..prop.., fill = factor(..x..)), stat = “count”) +
geom_text(aes(label = scales::percent(..prop..), y = ..prop..), stat = “count”, vjust = -.5) +
labs(y = “Percent”, fill = “School”) +
facet_grid(~Result) +
scale_y_continuous(labels = percent)

grid.arrange(plot1,plot2,plot3,plot4,ncol=2,nrow=2)

grid

Este análisis descriptivo podemos observar como las mujeres presentan mejores notas que los hombre, como afecta el absentismo, salud, salir fuera con los amigos, nivel de estudio de la madre al éxito/fracaso en el examen de matemáticas.

Construcción del Modelo Xgboost

Ya estamos preparados para construir nuestro modelo de clasificación supervisado. Necesitaremos cargar la librería xgboost.

library(xgboost)

# creamos nuestro train y test

set.seed(1976)
trainIndex <- createDataPartition(student$Result, p= .7, list = F, times = 1)

train <- student[trainIndex,]
test <- student[trainIndex,]

# utilizaremos la data.table para ser mas eficientes en memoria

library(data.table)

train = data.table(train)
test = data.table(test)

# preparando los datos usuando XGBoost
library(Matrix)

# necesitamos convertir los datos en una matriz dumificada

sparse.matrix.train <- sparse.model.matrix(Result ~ .-1, train)
sparse.matrix.test <- sparse.model.matrix(Result ~ .-1, test)

# xgboost necesita que la variable objetivo sea numerica

target_train = as.numeric(train[,Result])
target_test = as.numeric(test[,Result])

# convertimos al formato xgb.DMatrix que nos permite utilizar funciones avanzadas
dtrain <- xgb.DMatrix(data = sparse.matrix.train, label=target_train)
dtest <- xgb.DMatrix(data = sparse.matrix.test, label=target_test)
watchlist <- list(train = dtrain, test =dtest)

# construimos nuestro machine learning model

# primero definimos los parámetros xgboost
param <- list(“objective” = “multi:softprob”, # multiclass classification
“num_class” = 4, # numero de clases
“eval_metric” = “merror”, # evaluacion de la metrica
“nthread” = 8, # numero de hilos a utilizar
“max_depth” = 16, # profundidad maxima del arbol
“eta” = 0.3, # tamaño de cada paso
“gamma” = 0, # reduccion minima de perdidas
“subsample” = 1, # Parte de las instancias de datos para crecer el arbol
“colsample_bytree” = 1, # Sub-muestra de columnas al construir cada árbol
“min_child_weight” = 12 # Cantidad mínima de peso de la instancia necesaria en un child
)

Una forma de medir el progreso en el aprendizaje de un modelo es proporcionar a XGBoost un segundo conjunto de datos ya clasificada. Por lo tanto, se puede aprender en el primer conjunto de datos y poner a prueba su modelo en la segunda. Algunas métricas se miden después de cada ronda durante el aprendizaje, para evitar este sobreaprendizaje utilizaremos el parámetro watchlist que nos permitirá observar el error de calculo en cada ronda tanto para el train como para el test.

# entrenamiento del modelo real con datos completos
xgb <- xgb.train(param=param,
data=dtrain,
nrounds=10,
verbose=1,
watchlist = watchlist)

[1] train-merror:0.464029 test-merror:0.464029
[2] train-merror:0.413669 test-merror:0.413669
[3] train-merror:0.402878 test-merror:0.402878
[4] train-merror:0.381295 test-merror:0.381295
[5] train-merror:0.377698 test-merror:0.377698
[6] train-merror:0.352518 test-merror:0.352518
[7] train-merror:0.323741 test-merror:0.323741
[8] train-merror:0.323741 test-merror:0.323741
[9] train-merror:0.320144 test-merror:0.320144
[10] train-merror:0.316547 test-merror:0.316547

Hemos calculado para cada ronda la misma métrica de error promedio (nrounds a 10). En este caso, train-merror esta asociado a la muestra entrenamiento y test-merror al conjunto de prueba.

Ambos indicadores relacionados con los errores de entrenamiento y prueba son muy similares. Por lo tanto, el aprendizaje realizado en el train coincide con las observaciones del test.

Por último, observamos el gráfico que nos muestra de manera ordenada aquellas variables mas importantes y que finalmente contribuyen a explicar las notas finales en matemáticas.

# obtenemos el modelo
modelo= xgb.dump(xgb, with_stats=TRUE)
# obtenemos los nombres de las variables
names = dimnames(dtrain)[[2]]
# generamos la matriz de variables importantes
importance_matrix = xgb.importance(names, model=xgb)

# generamos el grafico de importancia
xgb.plot.importance(importance_matrix)
Importancia

Atributos como el absentismo, fracasos en cursos anteriores, salida con los amigos y nivel de estudio de la madre, entre otras, hacen bastante explicativo el modelo que nos permite determinar la calificación el la asignatura de matemáticas.

Hasta aquí el final del post, espero que os haya parecido interesante.

 

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s