Implementación de Árboles de Decisión y Bosques Aleatorios con Spark MLlib RDD

Los árboles de decisión y sus variantes de ensamble representan una de las metodologías más robustas para tareas de clasificación y regresión en el aprendizaje automático. Su popularidad radica en la facilidad de interpretación, la capacidad para procesar variables categóricas, su escalabilidad en entornos multiclase y el hecho de que no requieren una normalización estricta de las características para capturar interacciones no lineales.

A continuación, se detallan los mecanismos de control y la configuración de la API de Spark basada en RDD (mllib) para el entrenamiento de estos modelos.

Criterios de Finalización del Entrenamiento

En el proceso de construcción de un árbol, el algoritmo detiene la creación de nuevos nodos cuendo se cumple alguna de las siguientes condiciones:

  • La profundidad del nodo actual alcanza el valor definido en el parámetro maxDepth.
  • El incremento en la pureza (ganancia de información) resultante de una división es inferior al valor de minInfoGain.
  • No existen candidatos viables para la partición, por ejemplo, cuando todas las etiquetas en un subconjunto de datos son idénticas.

Configuración de Parámetros Fundamentales

Antes de ajustar el modelo, es necesario definir las propiedades estructurales del problema:

  • Algo: Define el propósito del modelo, ya sea Classification o Regression.
  • numClasses: Cantidad de categorías únicas para tareas de clasificación.
  • categoricalFeaturesInfo: Un mapa que asocia el índice de una columna con la cantidad de categorías que posee. Las columnas ausentes en este mapa se enterpretan como variables continuas.

Ejemplos de Implementación en Scala

1. Clasificación con Random Forest

El siguiente código ilustra cómo entrenar un bosque aleatorio para un problema de clasificación binaria utilizando el formato LIBSVM.

import org.apache.spark.mllib.tree.RandomForest
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}

object ClasificadorBosque {
  def main(args: Array[String]): Unit = {
    val configuracion = new SparkConf().setAppName("RF_Classification").setMaster("local[*]")
    val sc = new SparkContext(configuracion)

    // Carga de datos de ejemplo
    val ruta = "data/mllib/sample_libsvm_data.txt"
    val datasetCompleto = MLUtils.loadLibSVMFile(sc, ruta)
    
    // División en entrenamiento y prueba
    val particiones = datasetCompleto.randomSplit(Array(0.8, 0.2))
    val (setEntrenamiento, setEvaluacion) = (particiones(0), particiones(1))

    // Configuración del modelo
    val totalClases = 2
    val infoCategorias = Map[Int, Int]() // Todo continuo
    val cantidadArboles = 5 
    val estrategiaFeatures = "auto"
    val criterioPureza = "gini"
    val profundidadMax = 5
    val binsMax = 32

    val modeloRF = RandomForest.trainClassifier(
      setEntrenamiento, 
      totalClases, 
      infoCategorias, 
      cantidadArboles, 
      estrategiaFeatures, 
      criterioPureza, 
      profundidadMax, 
      binsMax
    )

    // Evaluación del modelo
    val prediccionesYEtiquetas = setEvaluacion.map { instancia =>
      val prediccion = modeloRF.predict(instancia.features)
      (instancia.label, prediccion)
    }
    
    val errorPrueba = prediccionesYEtiquetas.filter(r => r._1 != r._2).count().toDouble / setEvaluacion.count()
    println(f"Tasa de error en prueba: $errorPrueba%2.2f")
    
    sc.stop()
  }
}

2. Regresión con Random Forest

Para tareas de regresión, el proceso es aálogo, pero se utiliza la varianza como medida de impureza.

import org.apache.spark.mllib.tree.RandomForest
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}

object RegresionBosque {
  def main(args: Array[String]): Unit = {
    val sparkConf = new SparkConf().setAppName("RF_Regression").setMaster("local[*]")
    val sc = new SparkContext(sparkConf)

    val rddDatos = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
    val division = rddDatos.randomSplit(Array(0.7, 0.3))

    val modeloRegresor = RandomForest.trainRegressor(
      division(0), 
      Map[Int, Int](), 
      3, 
      "auto", 
      "variance", 
      4, 
      32
    )

    val errorCuadraticoMedio = division(1).map { p =>
      val diff = p.label - modeloRegresor.predict(p.features)
      diff * diff
    }.mean()

    println(s"MSE en Test: $errorCuadraticoMedio")
    sc.stop()
  }
}

3. Árboles con Aumento de Gradiente (GBT)

Los Gradient Boosted Trees (GBT) son potentes algoritmos de ensamble secuencial. A diferencia de Random Forest, los GBT entrenan un árbol a la vez para corregir los errores de los anteriores.

import org.apache.spark.mllib.tree.GradientBoostedTrees
import org.apache.spark.mllib.tree.configuration.BoostingStrategy
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.{SparkConf, SparkContext}

object ClasificadorGBT {
  def main(args: Array[String]): Unit = {
    val scConfig = new SparkConf().setAppName("GBT_Logic").setMaster("local[*]")
    val sc = new SparkContext(scConfig)

    val rawData = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
    val dataSplits = rawData.randomSplit(Array(0.7, 0.3))

    // Configuración mediante BoostingStrategy
    val estrategiaBoosting = BoostingStrategy.defaultParams("Classification")
    estrategiaBoosting.numIterations = 5 
    estrategiaBoosting.treeStrategy.maxDepth = 4
    estrategiaBoosting.treeStrategy.categoricalFeaturesInfo = Map[Int, Int]()

    val modeloGBT = GradientBoostedTrees.train(dataSplits(0), estrategiaBoosting)

    val precision = dataSplits(1).map { obs =>
      val p = modeloGBT.predict(obs.features)
      if (p == obs.label) 1.0 else 0.0
    }.mean()

    println(s"Precisión del modelo GBT: ${precision * 100}%")
    sc.stop()
  }
}

Es importante destacar que en los GBT, el ajuste de numIterations es crítico, ya que un valor demasiado alto puede conducir al sobreajuste (overfitting), mientras que un valor bajo puede resultar en un modelo con sesgo elevado.

Etiquetas: spark-mllib scala random-forest gradient-boosted-trees machine-learning

Publicado el 6-3 19:43