Árboles de decisión en aprendizaje automático: conceptos, algoritmos y técnicas de poda

Introducción

Los árboles de decisión son algoritmos de aprendizaje automático ampliamente utilizados para tareas de clasificación y regresión. Su estructura ramificada permite tomar decisiones secuenciales basadas en características de los datos, llevando a conclusiones en nodos hoja. Este artículo explora los fundamentos, implementaciones y mejoras de los árboles de decisión, con ejemplos en Python.

Fundamentos de los árboles de decisión

Definición y estrucutra

Un árbol de decisión es un modelo predictivo que utiliza una jerarquía de nodos de decisión y hojas. Cada nodo interno representa una prueba sobre una característica, cada rama un resultado de la prueba, y cada nodo hoja una etiqueta de clase o valor de predicción. La construcción implica seleccionar características que maximicen la pureza de los nodos, reduciendo la incertidumbre en las predicciones.

Flujo de construcción

El proceso general para construir un árbol de decisión incluye:

  1. Recopilar y preparar datos, discretizando si es necesario.
  2. Analizar datos para identificar características relevantes.
  3. Entrenar el árbol mediante división recursiva basada en métricas como la ganancia de información.
  4. Evaluar el modelo con datos de prueba.
  5. Desplegar el árbol para predicciones interpretables.

Entropía y ganancia de información

La entropía mide la impureza o desorden en un conjunto de datos. Se calcula como:

H(S) = - Σ p_i log₂(p_i)

donde p_i es la proporción de muestras de la clase i. La ganancia de información evalúa cuánto reduce la entropía al dividir los datos por una característica. Se prefiere la característica con mayor ganancia.

Ejemplo de cálculo de entropía en Python

import math
def calcular_entropia(datos):
    n = len(datos)
    conteo_etiquetas = {}
    for punto in datos:
        etiqueta = punto[-1]
        conteo_etiquetas[etiqueta] = conteo_etiquetas.get(etiqueta, 0) + 1
    entropia = 0.0
    for etiqueta, frecuencia in conteo_etiquetas.items():
        prob = frecuencia / n
        entropia -= prob * math.log2(prob)
    return entropia

División de datos y selección de características

Para dividir datos, se evalúan subconjuntos basados en valores de características. La función de división extrae muestras donde la característica coincide con un valor específico, eliminando esa característica del conjunto resultante.

def dividir_datos(datos, indice_caracteristica, valor):
    subconjunto = []
    for punto in datos:
        if punto[indice_caracteristica] == valor:
            punto_reducido = punto[:indice_caracteristica] + punto[indice_caracteristica+1:]
            subconjunto.append(punto_reducido)
    return subconjunto

Construcción recursiva del árbol

El árbol se construye recursivamente seleccionando la mejor característica en cada paso. Si todas las muestras en un nodo pertenecen a la misma clase, se crea una hoja. Si se agotan las características, se usa votación mayoritaria para la etiqueta.

def votar_mayoria(etiquetas):
    conteo = {}
    for etiqueta in etiquetas:
        conteo[etiqueta] = conteo.get(etiqueta, 0) + 1
    return max(conteo, key=conteo.get)

def construir_arbol(datos, nombres_caracteristicas):
    etiquetas = [punto[-1] for punto in datos]
    if len(set(etiquetas)) == 1:
        return etiquetas[0]
    if not nombres_caracteristicas:
        return votar_mayoria(etiquetas)
    mejor_caract = seleccionar_mejor_caracteristica(datos)
    nombre_caract = nombres_caracteristicas[mejor_caract]
    arbol = {nombre_caract: {}}
    nombres_rest = nombres_caracteristicas.copy()
    del nombres_rest[mejor_caract]
    valores_unicos = set([punto[mejor_caract] for punto in datos])
    for valor in valores_unicos:
        sub_datos = dividir_datos(datos, mejor_caract, valor)
        arbol[nombre_caract][valor] = construir_arbol(sub_datos, nombres_rest)
    return arbol

Visualización del árbol

Se puede usar Matplotlib para dibujar el árbol, calculando el número de hojas y la profundidad para posicionar nodos. Anotaciones y estilos diefrencian nodos de decisión de hojas.

import matplotlib.pyplot as plt

def dibujar_nodo(texto, centro, padre, estilo):
    plt.annotate(texto, xy=padre, xytext=centro, va="center", ha="center", bbox=estilo, arrowprops=dict(arrowstyle="<-"))

def visualizar_arbol(arbol):
    # Implementación simplificada para propósitos ilustrativos
    # Se omite código detallado por brevedad
    pass

Mejoras y algoritmos avanzados

Algoritmo ID3 y sus limitaciones

ID3 utiliza ganancia de información para dividir, pero puede sesgarse hacia características con muchos valores. Por ejemplo, un identificador único tendría ganancia máxima pero no es útil predictivamente.

C4.5: Tasa de ganancia de información

C4.5 aborda el sesgo de ID3 usando la tasa de ganancia de información, que normaliza la ganancia por la entropía intrínseca de la característica. Esto penaliza características con valores numerosos.

CART: Coeficiente Gini

CART usa el índice de Gini como métrica de impureza, definido como Gini(S) = 1 - Σ p_i². Es más rápido de calcular y produce árboles binarios.

Técnicas de poda para evitar sobreajuste

Poda previa

La poda previa detiene el crecimiento del árbol durante el entrenamiento si la división no mejora la precisión en un conjunto de validación. Reduce complejidad y tiempo de entrenamiento, pero puede causar subajuste.

Poda posterior

La poda posterior recorta ramas después de construir el árbol completo, evaluando la precisión en datos de validación. Suele producir modelos más precisos pero es más costosa computacionalmente.

Implementación de poda previa

def construir_arbol_con_poda_previa(datos_entrenamiento, etiquetas_entrenamiento, datos_validacion, etiquetas_validacion, nombres):
    # Lógica para construir el árbol, verificando en cada paso si la división mejora la precisión en validación
    # Si no mejora, se devuelve la etiqueta mayoritaria como hoja
    # Implementación omitida por brevedad
    pass

Implementación de poda posterior

def podar_arbol(arbol, datos_prueba, etiquetas_prueba, nombres):
    # Recorre el árbol recursivamente, reemplazando subárboles por hojas si reducen el error
    # Utiliza una métrica de pérdida que combina impureza y número de hojas
    # Implementación omitida por brevedad
    pass

Etiquetas: Árbol de Decisión Aprendizaje Automático Entropía Ganancia de Información ID3

Publicado el 6-7 03:12