Unidad 3(Random Forest): Determinación de Sistemas de Aprendizaje automático

Random Forest

Code
library(kknn)

# library(rpart)
# library(rpart.plot)
# library(bayesQR)
library(broom)
library(corrplot)
library(yardstick)
library(pROC)
# library(ggrepel)
# library(purrr)
library(plotly)
library(randomForest)
library(skimr)
library(MASS)
library(tidyverse)

Algoritmos de Machine Learning

  • Regresión Lineal

  • Regresión Logística

  • Árboles de Decisión

  • Random Forest

  • k Nearest Neighbor (KNN)

  • SVM

  • Naive Bayes

  • Clustering jerárquico

  • K-Means

  • PCA

  • Redes Neuronales

  • Aprendizaje profundo

Introducción al Algoritmo KNN

KNN es un algoritmo de aprendizaje supervisado que puede usarse tanto para clasificación como para regresión. La idea principal detrás de KNN es simple: para predecir la salida para un nuevo dato, el algoritmo busca en los datos de entrenamiento los K vecinos más cercanos a este nuevo dato.

La predicción se hace entonces por mayoría de votos (clasificación) o promedio (regresión) de los K vecinos más cercanos, donde K es un hiperparámetro a optimizar.

La “cercanía” se mide comúnmente mediante la distancia euclídea, aunque se pueden usar otras métricas de distancia. La fórmula de la distancia euclídea entre dos puntos, p y q, en un espacio n-dimensional es:

\[ d(p, q) = \sqrt{(p_1 - q_1)^2 + (p_2 - q_2)^2 + ... + (p_n - q_n)^2} \] ### Ventajas de KNN

  • Simplicidad: Fácil de entender e implementar.
  • Flexibilidad: No hace suposiciones sobre la forma de los datos.
  • Eficacia en datasets pequeños: Muy útil en casos con menos datos.

Desventajas de KNN

  • Escalabilidad: No es eficiente con datasets grandes.
  • Necesidad de preprocesamiento: Sensible a la escala de las variables y requiere codificación de variables categóricas.
Code
# Gráfico de las variables originales vs. escalado estándar
load("data/corruption.RData")
ggplot(corruption) +
  geom_point(aes(x=age/10, y=hours/10), color="blue", alpha=0.5) +
  geom_point(aes(x=scale(age), y=scale(hours)), color="red", alpha=0.5) +
  labs(title="Comparación de Escalado vs variables originales (entre 10)",
       x="Edad (azul: original, rojo: estándar)",
       y="Horas (azul: original, rojo: estándar)") +
  theme_minimal()

Code
ggplot(corruption) +
  geom_point(aes(x=age/10, y=hours/10), color="blue", alpha=0.5) +
  geom_point(aes(x=scales::rescale(age), y=scales::rescale(hours)), color="green", alpha=0.5) +
  labs(title="Comparación de Escalado Min-Max",
       x="Edad (azul: original, verde: Min-Max)",
       y="Horas (azul: original, verde: Min-Max)") +
  theme_minimal()

  • Determinación de k: Elegir el número correcto de vecinos es crucial y puede ser difícil. Un valor muy bajo puede hacer que el modelo sea muy sensible al ruido en los datos, mientras que un valor muy alto puede hacer que sea demasiado general. No hay una regla fija para elegir k. Es un hiperparámetro a configurar.

Comparación con Otros Modelos

  • Frente a CART y RandomForest: Estos modelos pueden manejar mejor las variables categóricas y son más interpretables. RandomForest, además, es menos sensible a las variables irrelevantes.

  • Frente a modelos de regresión lineal: KNN puede capturar relaciones no lineales sin necesidad de especificar la forma de esta relación, mientras que los modelos lineales se limitan a relaciones lineales pero son más fáciles de interpretar y más rápidos computacionalmente.

KNN se adapta bien a muchos problemas de regresión, especialmente cuando se entiende y se maneja adecuadamente el preprocesamiento de los datos.

Paquetes y opciones en R

Existe una gran diversidad de paquetes donde se puede encontrar alguna función que implemente el algoritmo. En estos ejemplos trataremos con el paquete kknn, y en posteriores temas trabajaremos con librerías genéricas de ML que permiten aplicar diversas técnicas, como caret o tidymodels

Code
# install.packages("kknn")

Ejemplo práctico de regresión con KNN

Code
load("data/corruption.RData")
glimpse(corruption)
Rows: 52
Columns: 5
$ country          <fct> Argentina, Australia, Austria, Belgium, Brazil, Canad…
$ cpi              <dbl> 2.9, 8.7, 7.9, 7.1, 3.7, 8.9, 7.2, 3.5, 3.5, 6.3, 4.6…
$ age              <dbl> 72, 64, 72, 67, 59, 61, 70, 49, 79, 58, 42, 76, 59, 7…
$ hours            <dbl> 35.0, 32.0, 32.0, 30.1, 35.0, 33.4, 34.0, 34.0, 33.0,…
$ emergent_country <fct> yes, no, no, no, yes, no, yes, yes, yes, yes, yes, no…
Code
# Preprocesamiento
corruption <- corruption %>%
  dplyr::select(-country) %>%
  # escalado
  mutate(age = scale(age), hours = scale(hours)) %>%
  # one hot encoding
  mutate(emergent_country = ifelse(corruption$emergent_country == "yes", 1, 0)) 
Code
knnModel <- kknn(cpi ~ ., corruption, corruption, k=9)
Code
rmse <- sqrt(mean((fitted(knnModel) - corruption$cpi)^2))
print(paste("RMSE:", rmse))
[1] "RMSE: 1.20034257905687"
Code
# Calculamos R^2
SST <- sum((corruption$cpi - mean(corruption$cpi))^2)
SSE <- sum((corruption$cpi - fitted(knnModel))^2)
r_squared <- 1 - (SSE/SST)
print(paste("R^2:", r_squared))
[1] "R^2: 0.734354679187239"

Ejemplo práctico de clasificación con KNN

Code
# Preprocesamiento
load("data/corruption.RData")
corruption <- corruption %>%
  dplyr::select(-country) %>%
  # escalado
  mutate_if(is.numeric, scale) 
Code
kknnResult <- kknn(emergent_country ~ ., train = corruption, test = corruption, k = 9, distance = 1, kernel = "optimal")
Code
predictions <- fitted(kknnResult)
predictions
 [1] yes no  no  no  yes no  no  yes yes yes yes no  yes no  no  yes no  yes yes
[20] no  no  yes no  yes yes yes yes yes no  no  no  yes yes yes yes yes yes yes
[39] yes no  yes yes no  no  yes yes yes yes yes no  no  yes
Levels: no yes
Code
response <- corruption$emergent_country
Code
probabilidades <- predict(kknnResult, corruption, corruption, type = "prob")
probabilidades <- probabilidades[,2]
Code
ROC <- roc((response == "yes"), probabilidades)
Setting levels: control = FALSE, case = TRUE
Setting direction: controls < cases
Code
plot(ROC, col = "blue")

Code
auc(ROC)
Area under the curve: 0.977
# Matriz de confusión manual:
outcomes <- table(1*(response == "yes"), round(probabilidades))
outcomes
   
     0  1
  0 18  3
  1  2 29
Code
confusion <- conf_mat(outcomes)
summary(confusion, event_level = "second")
# A tibble: 13 × 3
   .metric              .estimator .estimate
   <chr>                <chr>          <dbl>
 1 accuracy             binary         0.904
 2 kap                  binary         0.799
 3 sens                 binary         0.906
 4 spec                 binary         0.9  
 5 ppv                  binary         0.935
 6 npv                  binary         0.857
 7 mcc                  binary         0.799
 8 j_index              binary         0.806
 9 bal_accuracy         binary         0.903
10 detection_prevalence binary         0.596
11 precision            binary         0.935
12 recall               binary         0.906
13 f_meas               binary         0.921

Ejercicio

Trata de encontrar el mejor valor de k entre 5 y 18. Ten en cuenta que es un ejercicio didáctico, ya que los datasets son pequeños y además los modelos se se entrenan con todo el dataset, por lo que están sujetos a sobreajuste.