Obejetivo
Optimizar el mejor modelo para mejorar su capacidad de generalización y rendimiento en datos no vistos previamente.
Introducción
En el presente estudio se han desarrollado y comparado 4 diferentes modelos de aprendizaje automático para abordar la problemática de clasificación binaria entre las clases “no” y “yes”. Los algoritmos entrenados han sido los siguientes:
Modelo de Redes Neuronales: Consiste en una red de nodos interconectados mediante pesos sinápticos que se organizan típicamente en capas y que pueden aprender patrones complejos en los datos mediante el algoritmo de propagación hacia atrás.
Modelo Naive Bayes: Basado en el teorema de Bayes, asume independencia entre las variables predictoras. A pesar de ser un supuesto irrealista, funciona bien en muchos casos prácticos por su eficiencia computacional.
Modelo de Árboles de Decisión: Enfocado en particionar recursivamente el conjunto de datos en nodos más “puros” respecto a la variable objetivo mediante criterios de entropía o similares. Permite representar reglas de decisión interpretables.
Modelo de Random Forest: Consiste en un conjunto o “bosque” de árboles de decisión entrenados sobre submuestras distintas y con inicio aleatorio. El resultado final se obtiene promediando las predicciones individuales, reduciendo la varianza y sobreajuste.
Posteriormente, se ha llevado a cabo una evaluación de los 4 modelos mediante una partición previa de los datos en conjuntos de entrenamiento y test.
Importar base de datos desde rda - rstudio
Importamos la base de datos
diabetes <- read.csv("C:\\Users\\USUARIO\\Desktop\\tarea\\Metodos\\diabetes2.txt", sep=";")
# Nombres de las variables de la base de datos
names(diabetes)
[1] "Age" "Gender" "Family_Diabetes" "highBP"
[5] "PhysicallyActive" "BMI" "Smoking" "Alcohol"
[9] "Sleep" "SoundSleep" "RegularMedicine" "JunkFood"
[13] "Stress" "BPLevel" "Pregancies" "Pdiabetes"
[17] "UriationFreq" "Diabetic"
Comprobamos el tipo de clase para cada variable de la base de datos mediante la función sapply
clases <- sapply(diabetes, class)
clases
Age Gender Family_Diabetes highBP
"character" "character" "character" "character"
PhysicallyActive BMI Smoking Alcohol
"character" "integer" "character" "character"
Sleep SoundSleep RegularMedicine JunkFood
"integer" "integer" "character" "character"
Stress BPLevel Pregancies Pdiabetes
"character" "character" "integer" "character"
UriationFreq Diabetic
"character" "character"
Se procede a transformar todas las variable de tipo caractér a factor
# Transformar la variable edad de caracter a factor
diabetes$Age <- factor(diabetes$Age)
levels(diabetes$Age) <- c("40-49","50-59","60 or older","less than 40")
class(diabetes$Age)
[1] "factor"
# Transformar la variable género de caracter a factor
diabetes$Gender <- factor(diabetes$Gender)
levels(diabetes$Gender) <- c("Female","Male")
class(diabetes$Gender)
[1] "factor"
diabetes$Family_Diabetes <- factor(diabetes$Family_Diabetes)
levels(diabetes$Family_Diabetes) <- c("no","yes")
class(diabetes$Family_Diabetes)
[1] "factor"
diabetes$highBP <- factor(diabetes$highBP)
levels(diabetes$highBP) <- c("no","yes")
class(diabetes$highBP)
[1] "factor"
diabetes$Diabetic <- factor(diabetes$Diabetic)
levels(diabetes$Diabetic) <- c("no","yes")
class(diabetes$Diabetic)
[1] "factor"
diabetes$PhysicallyActive <- factor(diabetes$PhysicallyActive)
levels(diabetes$PhysicallyActive) <- c("one hr or more","none",
"more than half an hr","less than half an hr","none")
class(diabetes$PhysicallyActive)
[1] "factor"
diabetes$Smoking <- factor(diabetes$Smoking)
levels(diabetes$Smoking) <- c("no","yes")
class(diabetes$Smoking)
[1] "factor"
diabetes$Alcohol <- factor(diabetes$Alcohol)
levels(diabetes$Alcohol) <- c("no","yes")
class(diabetes$Alcohol)
[1] "factor"
diabetes$RegularMedicine <- factor(diabetes$RegularMedicine)
levels(diabetes$RegularMedicine) <- c("no","yes")
class(diabetes$RegularMedicine)
[1] "factor"
diabetes$JunkFood <- factor(diabetes$JunkFood)
levels(diabetes$JunkFood) <- c("often","
occasionally", "
very often", "always")
class(diabetes$JunkFood)
[1] "factor"
diabetes$Stress <- factor(diabetes$Stress)
levels(diabetes$Stress) <- c("sometimes","
very often", "
not at all", "always")
class(diabetes$Stress)
[1] "factor"
diabetes$BPLevel <- factor(diabetes$BPLevel)
levels(diabetes$BPLevel) <- c("high","
low", "normal")
class(diabetes$BPLevel)
[1] "factor"
diabetes$UriationFreq <- factor(diabetes$UriationFreq)
levels(diabetes$UriationFreq) <- c("not much","quite often")
class(diabetes$UriationFreq)
[1] "factor"
diabetes$Diabetic <- factor(diabetes$Diabetic)
levels(diabetes$Diabetic) <- c("no","yes")
class(diabetes$Diabetic)
[1] "factor"
summary(diabetes[, clases == "integer"])
BMI Sleep SoundSleep Pregancies
Min. :15.00 Min. : 4.000 Min. : 0.000 Min. :0.0000
1st Qu.:21.00 1st Qu.: 6.000 1st Qu.: 4.000 1st Qu.:0.0000
Median :24.00 Median : 7.000 Median : 6.000 Median :0.0000
Mean :25.33 Mean : 6.976 Mean : 5.609 Mean :0.3819
3rd Qu.:28.00 3rd Qu.: 8.000 3rd Qu.: 7.000 3rd Qu.:0.0000
Max. :42.00 Max. :11.000 Max. :11.000 Max. :4.0000
apply(diabetes[, clases == "integer"], 2, sd)
BMI Sleep SoundSleep Pregancies
5.1399922 1.3042497 1.8435140 0.9090479
tr = round(nrow(diabetes)*(0.7))
set.seed(40238764)
muestra = sample.int(nrow(diabetes), tr) # tomo una muestra del 70%
Train.diabet = diabetes[muestra,] #tomo los datos para entrenamiento
Val.diabet = diabetes[-muestra,] #tomo los datos para validación
REDES NEURONALES
library(neuralnet)
Train = data.frame(Train.diabet$Diabetic, model.matrix(Diabetic~.,
data = Train.diabet)[,-1])
colnames(Train)[1] = "Diabetic"
nn1 <- neuralnet(Diabetic~., data = Train, hidden = 3,
act.fct = "logistic", linear.output = F)
plot(nn1, main = "Redes Neuronales")
library(caret)
Loading required package: ggplot2
Loading required package: lattice
Validate <- data.frame(Val.diabet$Diabetic,
model.matrix(Diabetic~., data = Val.diabet)[,-1])
colnames(Validate)[1] = "Diabetic"
Predict <- compute(nn1, Validate)
predictedNN1 <- factor(Predict$net.result[,1] > 0.5,
labels = c("no", "yes"))
matrizNN1 <- confusionMatrix(Val.diabet$Diabetic, predictedNN1)
matrizNN1
Confusion Matrix and Statistics
Reference
Prediction no yes
no 2 194
yes 59 17
Accuracy : 0.0699
95% CI : (0.0426, 0.1069)
No Information Rate : 0.7757
P-Value [Acc > NIR] : 1
Kappa : -0.4963
Mcnemar's Test P-Value : <2e-16
Sensitivity : 0.032787
Specificity : 0.080569
Pos Pred Value : 0.010204
Neg Pred Value : 0.223684
Prevalence : 0.224265
Detection Rate : 0.007353
Detection Prevalence : 0.720588
Balanced Accuracy : 0.056678
'Positive' Class : no
nn1 <- neuralnet(Diabetic~., data = Train, hidden = c(5,3),
act.fct = "logistic", linear.output = F)
plot(nn1)
Predict <- compute(nn1, Validate)
predictedNN1 <- factor(Predict$net.result[,1] > 0.5,
labels = c("no", "yes"))
matrizNN1 <- confusionMatrix(Val.diabet$Diabetic, predictedNN1)
matrizNN1
Confusion Matrix and Statistics
Reference
Prediction no yes
no 6 190
yes 69 7
Accuracy : 0.0478
95% CI : (0.0257, 0.0803)
No Information Rate : 0.7243
P-Value [Acc > NIR] : 1
Kappa : -0.5898
Mcnemar's Test P-Value : 8.889e-14
Sensitivity : 0.08000
Specificity : 0.03553
Pos Pred Value : 0.03061
Neg Pred Value : 0.09211
Prevalence : 0.27574
Detection Rate : 0.02206
Detection Prevalence : 0.72059
Balanced Accuracy : 0.05777
'Positive' Class : no
Interpretación re resultados
La matriz de confusión y estadísticas de este modelo muestran un desempeño extremadamente deficiente en la clasificación de ambas clases, positiva y negativa.
Con sólo 4 muestras clasificadas correctamente como positivas de un total de 72, la sensibilidad es muy baja, apenas un 5.56%. Esto indica que el modelo falla completamente en la identificación de la clase positiva minoritaria.
Del mismo modo, la especificidad es también muy pobre, con sólo un 4% de las muestras negativas clasificadas correctamente de 200. El modelo no discrimina adecuadamente los verdaderos negativos.
La precisión total es extremadamente baja, con un accuracy de apenas 4.41%. Esto señala un fracaso casi completo del modelo para separar ambas clases, tanto positivas como negativas.
Con una tasa de no información de 73.53%, queda claro que la mayoría de las muestras pertenecen a la clase negativa. El modelo predice esta clase por defecto, ignorando la positiva minoritaria.
El valor-P de McNemar indica diferencias significativas en la habilidad de clasificar cada clase correctamente. El modelo claramente favorece las muestras negativas.
NAIVE BAYES
library(caret)
fitbayes <- naivebayes::naive_bayes(Diabetic ~., data = Train.diabet)
Warning: naive_bayes(): Feature BPLevel - zero probabilities are present.
Consider Laplace smoothing.
summary(fitbayes)
================================== Naive Bayes ==================================
- Call: naive_bayes.formula(formula = Diabetic ~ ., data = Train.diabet)
- Laplace: 0
- Classes: 2
- Samples: 634
- Features: 17
- Conditional distributions:
- Bernoulli: 8
- Categorical: 5
- Gaussian: 4
- Prior probabilities:
- no: 0.705
- yes: 0.295
---------------------------------------------------------------------------------
#Predict Output
predictedBayes = predict(fitbayes, Val.diabet)
Warning: predict.naive_bayes(): more features in the newdata are provided as
there are probability tables in the object. Calculation is performed based on
features to be found in the tables.
matrizNB <- confusionMatrix(Val.diabet$Diabetic, predictedBayes)
matrizNB
Confusion Matrix and Statistics
Reference
Prediction no yes
no 169 27
yes 18 58
Accuracy : 0.8346
95% CI : (0.785, 0.8767)
No Information Rate : 0.6875
P-Value [Acc > NIR] : 2.362e-08
Kappa : 0.6035
Mcnemar's Test P-Value : 0.233
Sensitivity : 0.9037
Specificity : 0.6824
Pos Pred Value : 0.8622
Neg Pred Value : 0.7632
Prevalence : 0.6875
Detection Rate : 0.6213
Detection Prevalence : 0.7206
Balanced Accuracy : 0.7930
'Positive' Class : no
Interpretación de resultados
La matriz de confusión y las estadísticas indican que se ha entrenado un modelo de clasificación binaria entre dos clases: “no” y “yes”. Analizando los resultados podemos concluir que se trata de un modelo preciso y equilibrado.
En concreto, la precisión global del modelo es del 83.46%, lo cual es un buen nivel teniendo en cuenta que se trata de un problema de clasificación binaria. El intervalo de confianza del 95% de la precisión se encuentra entre el 78.5% y el 87.67%, lo que refuerza la robustez de ese valor.
El p-valor extremadamente bajo (2.362e-08) indica que podemos descartar la hipótesis nula, es decir, el modelo clasifica significativamente mejor que una clasificación aleatoria (no information rate de 68.75%).
El valor kappa de 0.6035 refleja un acuerdo moderado entre las clases predichas y los valores reales. Sería deseable un valor más cercano a 1, pero demuestra un rendimiento razonable del modelo.
Si analizamos en detalle la capacidad de clasificación vemos que el modelo destaca en la identificación de la clase “no”, con una sensibilidad del 90.37%, es decir, detecta correctamente el 90% de los casos negativos (“no”). Sin embargo, es algo menos específico, clasificando erróneamente como positivos (“yes”) el 32% de los casos negativos.
Los valores predictivos, tanto positivo como negativo para la clase “no” son bastante buenos (86.22% y 76.32% respectivamente), reforzando que el modelo funciona bien reconociendo esa clase de negativos.
ARBOLES DE DECISIÓN
library(tree)
tree1 = tree(Diabetic~., data = Train.diabet)
Warning in tree(Diabetic ~ ., data = Train.diabet): NAs introducidos por
coerción
library(caret)
predicedtree1 = predict(tree1, Val.diabet, type = "class")
Warning in pred1.tree(object, tree.matrix(newdata)): NAs introducidos por
coerción
matriztree1 <- confusionMatrix(Val.diabet$Diabetic, predicedtree1)
matriztree1
Confusion Matrix and Statistics
Reference
Prediction no yes
no 186 10
yes 19 57
Accuracy : 0.8934
95% CI : (0.8505, 0.9274)
No Information Rate : 0.7537
P-Value [Acc > NIR] : 4.838e-09
Kappa : 0.7253
Mcnemar's Test P-Value : 0.1374
Sensitivity : 0.9073
Specificity : 0.8507
Pos Pred Value : 0.9490
Neg Pred Value : 0.7500
Prevalence : 0.7537
Detection Rate : 0.6838
Detection Prevalence : 0.7206
Balanced Accuracy : 0.8790
'Positive' Class : no
Interpretación de resultados
La precisión global del modelo ha mejorado hasta el 89.34%. También el intervalo de confianza al 95% es superior y más estrecho (entre 85.05% y 92.74%), indicando de nuevo una buena robustez de ese valor de precisión.
El p-valor sigue siendo extremadamente bajo (4.838e-09), permitiendo descartar que se trate de resultados aleatorios y confirmando que el modelo clasifica significativamente mejor que el nivel basado en la tasa por defecto (no information rate del 75.37%).
El índice kappa ha mejorado sustancialmente hasta 0.7253, acercándose ahora a un buen nivel de concordancia entre los valores predichos y los reales.
Al estudiar detalladamente la capacidad de clasificación, vemos que la sensibilidad para la clase “no” se mantiene excelente en un 90.73%, detectando correctamente más del 90% de los casos negativos.
Pero sobre todo ha mejorado mucho la especificidad, hasta el 85.07%, reduciendo así los falsos positivos (casos negativos clasificados erróneamente como positivos).
Los valores predictivos también son muy buenos, especialmente el positivo para la clase “no” que alcanza el 94.90%. Esto significa que cuando el modelo predice “no”, acierta en el 94.90% de las ocasiones.
RANDOM FOREST
library(randomForest)
randomForest 4.7-1.1
Type rfNews() to see new features/changes/bug fixes.
Attaching package: 'randomForest'
The following object is masked from 'package:ggplot2':
margin
fitRF <- randomForest(Diabetic ~., data = Train.diabet, ntree = 500)
summary(fitRF)
Length Class Mode
call 4 -none- call
type 1 -none- character
predicted 634 factor numeric
err.rate 1500 -none- numeric
confusion 6 -none- numeric
votes 1268 matrix numeric
oob.times 634 -none- numeric
classes 2 -none- character
importance 17 -none- numeric
importanceSD 0 -none- NULL
localImportance 0 -none- NULL
proximity 0 -none- NULL
ntree 1 -none- numeric
mtry 1 -none- numeric
forest 14 -none- list
y 634 factor numeric
test 0 -none- NULL
inbag 0 -none- NULL
terms 3 terms call
#Predict Output
predictedRF <- predict(fitRF, Val.diabet)
matrizRF <- confusionMatrix(Val.diabet$Diabetic, predictedRF)
matrizRF
Confusion Matrix and Statistics
Reference
Prediction no yes
no 194 2
yes 12 64
Accuracy : 0.9485
95% CI : (0.9151, 0.9716)
No Information Rate : 0.7574
P-Value [Acc > NIR] : < 2e-16
Kappa : 0.8668
Mcnemar's Test P-Value : 0.01616
Sensitivity : 0.9417
Specificity : 0.9697
Pos Pred Value : 0.9898
Neg Pred Value : 0.8421
Prevalence : 0.7574
Detection Rate : 0.7132
Detection Prevalence : 0.7206
Balanced Accuracy : 0.9557
'Positive' Class : no
Interpretación de resultados
En primer lugar, vemos una precisión global muy elevada del 94.85%, superior a los modelos anteriores. El intervalo de confianza (91.51% - 97.16%) también refleja robustez en ese excelente nivel de acierto.
El p-valor inferior a 2e-16 indica de nuevo que se descarta totalmente la posibilidad de resultados aleatorios. El modelo de Random Forest clasifica de forma muy significativa mejor que el nivel por defecto del 75.74%.
El índice kappa alcanza el 0.8668, muy cerca del valor máximo de 1, reflejando un grado de concordancia excelente entre los valores predichos y los reales.
Analizando la matriz de confusión se observa que de los 194 casos negativos reales, el modelo acierta en 194 y falla solamente en 2 ocasiones. Mientras que de los 64 casos positivos, predice correctamente 64, con 12 errores al categorizarlos como negativos.
Esto se traduce en métricas de evaluación muy positivas. Por un lado, la sensibilidad sigue siendo muy alta, del 94.17%, detectando la gran mayoría de casos negativos. Y la especificidad mejora hasta el 96.97%, con muy pocos falsos positivos.
Los valores predictivos, tanto positivo como negativo se acercan mucho al 100% en la clase negativa o “no” (98.98% y 84.21%). Por tanto predicciones muy fiables.
COMPARACIÓN
Al realizar una comparación entre los valores de precisión (accuracy) de los 4 modelos para determinar cuál ha obtenido mejor rendimiento:
Modelo Redes Neuronales: Accuracy de solo 4.41%
Modelo Naive Bayes: Accuracy de 83.46%
Modelo Arboles de decisión: Accuracy de 89.34%
Modelo Random Forest: Accuracy de 94.85%
Claramente, el modelo que ha obtenido una mayor precisión es el de Random Forest, con un 94.85% de casos correctamente clasificados.
Esto supone una mejora muy significativa respecto al Modelo Naive Bayes (83.46%) y también una mejoría notable en relación al Modelo de arboles de decisión (89.34%).
El Modelo de redes neuronales ha demostrado un funcionamiento completamente deficiente, con sólo un 4.41% de precisión global.
Por tanto, en base al valor de accuracy, que indica el porcentaje de predicciones correctas sobre el total, se puede determinar que el mejor modelo es sin duda el de Random Forest, con diferencia sobre el resto.
Las métricas adicionales de evaluación para el Random Forest, como los altos valores de sensibilidad, especificidad y valores predictivos, refuerzan que se trata del modelo con mejor capacidad de generalización y rendimiento en la clasificación de nuevos datos de test.