ALGORITMO DE REGRESIÓN
Los árboles de decisión no sirven sólo para categorizar datos: ¡también son eficaces para predecir valores numéricos! Los árboles de clasificación a menudo se roban el show, pero los regresores de árboles de decisión (o árboles de regresión) son herramientas poderosas y versátiles en el mundo de la predicción de variables continuas.
Si bien discutiremos la mecánica de construcción del árbol de regresión (que en su mayoría es similar al árbol de clasificación), también iremos más allá de la pre-Métodos de poda como «muestra mínima de hojas» y «profundidad máxima del árbol» introducidos en el artículo clasificador. Exploraremos los más comunes. trabajo-método de poda que es tamaño de la complejidad del costoque introduce un parámetro de complejidad en la función de costos del árbol de decisión.
Un árbol de decisión para regresión es un modelo que predice valores numéricos utilizando una estructura de árbol. Divide los datos en función de características clave, comenzando con una pregunta raíz y diversificándose. Cada nodo pregunta sobre una característica, dividiendo aún más los datos hasta llegar a los nodos hoja con las predicciones finales. Para obtener un resultado, sigues el camino correspondiente a las características de tus datos desde la raíz hasta la hoja.
Para demostrar nuestros conceptos, trabajaremos con nuestro conjunto de datos estándar. Este conjunto de datos se utiliza para predecir la cantidad de golfistas que visitarán en un día determinado e incluye variables como las perspectivas climáticas, la temperatura, la humedad y las condiciones del viento.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split# Create dataset
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rain', 'rain', 'rain', 'overcast', 'sunny', 'sunny', 'rain', 'sunny', 'overcast', 'overcast', 'rain', 'sunny', 'overcast', 'rain', 'sunny', 'sunny', 'rain', 'overcast', 'rain', 'sunny', 'overcast', 'sunny', 'overcast', 'rain', 'overcast'],
'Temp.': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humid.': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Num_Players': [52, 39, 43, 37, 28, 19, 43, 47, 56, 33, 49, 23, 42, 13, 33, 29, 25, 51, 41, 14, 34, 29, 49, 36, 57, 21, 23, 41]
}
df = pd.DataFrame(dataset_dict)
# One-hot encode 'Outlook' column
df = pd.get_dummies(df, columns=['Outlook'],prefix='',prefix_sep='')
# Convert 'Wind' column to binary
df['Wind'] = df['Wind'].astype(int)
# Split data into features and target, then into training and test sets
X, y = df.drop(columns='Num_Players'), df['Num_Players']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)
El árbol de decisión para la regresión funciona dividiendo recursivamente los datos en función de las características que mejor reducen el error de predicción. Aquí está el proceso general:
- Comience con todo el conjunto de datos en el nodo raíz.
- Elija la característica que minimice una medida de error específica (como el error cuadrático medio o la varianza) para dividir los datos.
- Cree nodos secundarios basados en divisiones, donde cada hijo represente un subconjunto de datos alineados con los valores de características correspondientes.
- Repita los pasos 2 y 3 para cada nodo secundario y continúe dividiendo los datos hasta que se alcance una condición de detención.
- Asigne un valor previsto final a cada nodo de hoja, normalmente el promedio de los valores objetivo en este nodo.
Exploraremos la parte de regresión en el algoritmo de árbol de decisión CART (árboles de clasificación y regresión). Construye árboles binarios y generalmente sigue estos pasos:
1.Comience con todas las muestras de entrenamiento en el nodo raíz.
2.Para cada entidad del conjunto de datos:
A. Ordene los valores de las características en orden ascendente.
b. Considere todos los puntos medios entre valores adyacentes como posibles puntos de división.
3. Para cada posible punto de intercambio:
A. Calcule el error cuadrático medio (MSE) del nodo actual.
b. Calcule el promedio ponderado de los errores de la distribución resultante.
4. Después de evaluar todas las características y compartir puntos, seleccione la que tenga el promedio ponderado de MSE más bajo.
5. Cree dos nodos secundarios según la funcionalidad elegida y el punto compartido:
– Hijo izquierdo: muestras con valor característico <= punto de división
– Hijo derecho: muestras con valor de característica> punto de división
6. Repita recursivamente los pasos del 2 al 5 para cada nodo secundario. (Continúe hasta que se cumpla un criterio de parada).
7. En cada nodo hoja, asigne el valor objetivo promedio de las muestras en ese nodo como predicción.
from sklearn.tree import DecisionTreeRegressor, plot_tree
import matplotlib.pyplot as plt# Train the model
regr = DecisionTreeRegressor(random_state=42)
regr.fit(X_train, y_train)
# Visualize the decision tree
plt.figure(figsize=(26,8))
plot_tree(regr, feature_names=X.columns, filled=True, rounded=True, impurity=False, fontsize=16, precision=2)
plt.tight_layout()
plt.show()
Así es como un árbol de regresión hace predicciones para datos nuevos:
1. Comience en la parte superior (raíz) del árbol.
2. En cada punto de decisión (nodo):
– Observe la funcionalidad y el valor dividido.
– Si el valor característico del punto de datos es menor o igual, vaya a la izquierda.
– Si es más grande, ve hacia la derecha.
3. Continúa bajando por el árbol hasta llegar al final (una hoja).
4. La predicción es el valor promedio almacenado en esta hoja.
Etapa de evaluación
Después de construir el árbol, lo único de lo que debemos preocuparnos es de cómo reducir el tamaño del árbol para evitar un ajuste excesivo. De forma general, el método de poda se puede clasificar de la siguiente manera:
Pre-tamaño
La poda previa, también llamada parada temprana, implica detener el crecimiento de un árbol de decisión durante el proceso de entrenamiento en función de algunos criterios predefinidos. Este enfoque tiene como objetivo evitar que el árbol se vuelva demasiado complejo y sobreajuste los datos de entrenamiento. Las técnicas comunes de prepoda incluyen:
- Profundidad máxima: Limite la profundidad a la que puede crecer el árbol.
- Muestras mínimas para dividir.: Requerir un número mínimo de muestras para justificar la división de un nodo.
- Muestras mínimas por hoja: Asegúrese de que cada nodo de hoja contenga al menos una cierta cantidad de muestras.
- Número máximo de nodos de hoja: Restringe el número total de nodos de hoja en el árbol.
- Reducción mínima de impurezas.: Sólo permita divisiones que reduzcan las impurezas en una cantidad específica.
Estos métodos detienen el crecimiento del árbol cuando se cumplen las condiciones específicas, «podando» efectivamente el árbol durante su fase de construcción.
(Ya hemos discutido estos métodos, que es exactamente lo mismo en el caso de la regresión).
postpoda
La pospoda, por otro lado, permite que el árbol de decisión crezca en toda su extensión y luego lo poda para reducir la complejidad. Este enfoque primero crea un árbol completo y luego elimina o colapsa las ramas que no contribuyen significativamente al rendimiento del modelo. Una técnica común después de la poda se llama Poda de complejidad de costos.
Paso 1: Calcular la impureza para cada nodo.
Para cada nodo intermedio, calcule la impureza (MSE para el caso de regresión). Luego ordenamos este valor de menor a mayor.
# Visualize the decision tree
plt.figure(figsize=(26,8))
plot_tree(regr, feature_names=X.columns, filled=True, rounded=True, impurity=True, fontsize=16, precision=2)
plt.tight_layout()
plt.show()
Paso 2: cree subárboles eliminando el enlace más débil
El objetivo es transformar gradualmente los nudos intermedios en hojas del nodo con el MSE más bajo (= eslabón más débil). Podemos crear un camino de poda basado en esto.
Paso 3: Calcule el total de impurezas de las hojas para cada subárbol
Para cada subárbol timpurezas totales de la hoja (A.(t)) se puede calcular de la siguiente manera:
A.(t) = (1/norte) Σ I(l) * norte_l
O:
· l rangos en todos los nodos de las hojas
· n_L es el número de muestras en la hoja l
· norte es el número total de muestras en el árbol
· I(l) es la impureza (MSE) hoja l
Paso 4: Calcular la función de costo
Para controlar cuándo dejar de convertir los nodos intermedios en hojas, primero verificamos la complejidad del costo de cada subárbol. t usando la siguiente fórmula:
Costo(t) = A.(t) + α * |t|
O:
· A.(t) son las impurezas totales de la hoja
· |t| es el número de nodos hoja en el subárbol
· α es el parámetro de complejidad
Paso 5: seleccione Alfa
El valor de alfa controla con qué subárbol terminaremos. EL el subárbol con el menor costo será el árbol final.
Aunque podemos definir libremente el αen scikit-learn también puedes obtener el valor más pequeño de α para obtener un subárbol particular. esto se llama eficaz α.
# Compute the cost-complexity pruning path
tree = DecisionTreeRegressor(random_state=42)
effective_alphas = tree.cost_complexity_pruning_path(X_train, y_train).ccp_alphas
impurities = tree.cost_complexity_pruning_path(X_train, y_train).impurities# Function to count leaf nodes
count_leaves = lambda tree: sum(tree.tree_.children_left[i] == tree.tree_.children_right[i] == -1 for i in range(tree.tree_.node_count))
# Train trees and count leaves for each complexity parameter
leaf_counts = [count_leaves(DecisionTreeRegressor(random_state=0, ccp_alpha=alpha).fit(X_train_scaled, y_train)) for alpha in effective_alphas]
# Create DataFrame with analysis results
pruning_analysis = pd.DataFrame({
'total_leaf_impurities': impurities,
'leaf_count': leaf_counts,
'cost_function': [f"{imp:.3f} + {leaves}α" for imp, leaves in zip(impurities, leaf_counts)],
'effective_α': effective_alphas
})
print(pruning_analysis)
Comentarios finales
Los métodos de poda previa son generalmente más rápidos y más eficientes en términos de memoria porque, en primer lugar, evitan que el árbol crezca demasiado.
La pospoda puede potencialmente crear árboles más óptimos porque considera toda la estructura del árbol antes de tomar decisiones de poda. Sin embargo, esto puede resultar más costoso desde el punto de vista computacional.
Ambos enfoques apuntan a lograr un equilibrio entre la complejidad y el rendimiento del modelo, con el objetivo de crear un modelo que se generalice bien a datos invisibles. La elección entre poda previa y pospoda (o una combinación de ambas) a menudo depende del conjunto de datos específico, el problema en cuestión y, por supuesto, los recursos informáticos disponibles.
En la práctica, es común utilizar una combinación de estos métodos, como aplicar ciertos criterios previos a la poda para evitar árboles demasiado grandes y luego utilizar la pospoda para ajustar la complejidad del modelo.
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import root_mean_squared_error
from sklearn.tree import DecisionTreeRegressor
from sklearn.preprocessing import StandardScaler# Create dataset
dataset_dict = {
'Outlook': ['sunny', 'sunny', 'overcast', 'rain', 'rain', 'rain', 'overcast', 'sunny', 'sunny', 'rain', 'sunny', 'overcast', 'overcast', 'rain', 'sunny', 'overcast', 'rain', 'sunny', 'sunny', 'rain', 'overcast', 'rain', 'sunny', 'overcast', 'sunny', 'overcast', 'rain', 'overcast'],
'Temperature': [85.0, 80.0, 83.0, 70.0, 68.0, 65.0, 64.0, 72.0, 69.0, 75.0, 75.0, 72.0, 81.0, 71.0, 81.0, 74.0, 76.0, 78.0, 82.0, 67.0, 85.0, 73.0, 88.0, 77.0, 79.0, 80.0, 66.0, 84.0],
'Humidity': [85.0, 90.0, 78.0, 96.0, 80.0, 70.0, 65.0, 95.0, 70.0, 80.0, 70.0, 90.0, 75.0, 80.0, 88.0, 92.0, 85.0, 75.0, 92.0, 90.0, 85.0, 88.0, 65.0, 70.0, 60.0, 95.0, 70.0, 78.0],
'Wind': [False, True, False, False, False, True, True, False, False, False, True, True, False, True, True, False, False, True, False, True, True, False, True, False, False, True, False, False],
'Num_Players': [52,39,43,37,28,19,43,47,56,33,49,23,42,13,33,29,25,51,41,14,34,29,49,36,57,21,23,41]
}
df = pd.DataFrame(dataset_dict)
# One-hot encode 'Outlook' column
df = pd.get_dummies(df, columns=['Outlook'], prefix='', prefix_sep='', dtype=int)
# Convert 'Wind' column to binary
df['Wind'] = df['Wind'].astype(int)
# Split data into features and target, then into training and test sets
X, y = df.drop(columns='Num_Players'), df['Num_Players']
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, shuffle=False)
# Initialize Decision Tree Regressor
tree = DecisionTreeRegressor(random_state=42)
# Get the cost complexity path, impurities, and effective alpha
path = tree.cost_complexity_pruning_path(X_train, y_train)
ccp_alphas, impurities = path.ccp_alphas, path.impurities
print(ccp_alphas)
print(impurities)
# Train the final tree with the chosen alpha
final_tree = DecisionTreeRegressor(random_state=42, ccp_alpha=0.1)
final_tree.fit(X_train_scaled, y_train)
# Make predictions
y_pred = final_tree.predict(X_test)
# Calculate and print RMSE
rmse = root_mean_squared_error(y_test, y_pred)
print(f"RMSE: {rmse:.4f}")