Los árboles de decisión son una técnica de aprendizaje automático supervisado muy utilizada en muchos negocios. Como su nombre indica, esta técnica de machine learning toma una serie de decisiones en forma de árbol. Los nodos intermedios (las ramas) representan soluciones. Los nodos finales (las hojas) nos dan la predicción que vamos buscando.
Los árboles de decisión pueden usarse para resolver problemas tanto de clasificación como de regresión. Veamos cómo se usan en cada caso con ejemplos.
Índice
Árboles de Decisión para Clasificación
Datos para clasificación – Iris
Para explicar cómo funcionan los árboles de decisión con problemas de clasificación vamos a usar el conjunto de datos Iris. El problema consiste en clasificar correctamente la variedad de la flor iris a partir del ancho y largo de los pétalos y sépalos. Hay tres variedades de flor iris: setosa, versicolor y virginica.
Este conjunto de datos tiene 150 muestras:
- 50 iris setosa
- 50 iris versicolor
- 50 iris virginica
Teoría de los árboles de decisión para clasificación
Si le damos las 150 flores del conjunto de datos Iris a un árbol de decisión para que lo clasifique, nos quedaría un árbol como el que se muestra a continuación. Vamos a aprender a leerlo:
- cada color representa a una clase. El marrón para setosa, el verde para versicolor y el lila para virginica.
- el color es más intenso cuanto más seguros estamos que la clasificación es correcta
- los nodos blancos, por tanto, evidencia la falta de certeza
- Hay dos tipos de nodo:
- Nodos de decisión: tienen una condición al principio y tienen más nodos debajo de ellos
- Nodos de predicción: no tienen ninguna condición ni nodos debajo de ellos. También se denominan «nodos hijo»
La información de cada nodo es la siguiente:
- condición: si es un nodo donde se toma alguna decisión
- gini: es una medida de impureza. A continuación veremos cómo se calcula
- samples: número de muestras que satisfacen las condiciones necesarias para llegar a este nodo
- value: cuántas muestras de cada clase llegan a este nodo
- class: qué clase se le asigna a las muestras que llegan a este nodo
Interpretación
La interpretación del árbol de este árbol de decisión sería: si la longitud del pétalo es menos de 2.45 centímetros, entonces la flor iris pertenece a la variedad setosa. Si por el contrario, la longitud del pétalo es mayor que 2.45 centímetros, habría que mirar al ancho del pétalo. Cuando el ancho del pétalo es menor o igual a 1.75 centímetros, pertenece a la variedad versicolor con un 91% de probabilidad. Si no, parece que sería virginica con un 98% de probabilidad.
Gini: medida de impureza
Gini es una medida de impureza. Cuando Gini vale 0, significa que ese nodo es totalmente puro. La impureza se refiere a cómo de mezcladas están las clases en cada nodo. Para calcular la impureza gini, usamos la siguiente fórmula:
gini = 1\ - \sum_{k=1}^{n}{p_c^2}
pc se refiere a la probabilidad de cada clase. Podemos calcularla dividiendo el número de muestras de cada clase en cada nodo por el número de muestras totales por nodo.
Por ejemplo, para el caso del nodo donde la clasificación es versicolor, el cálculo sería el siguiente:
gini_{versicolor} = 1\ - \sum_{k=1}^{n}{p_c^2} = 1\ - \left(\frac{0}{54}\right)^2 - \left(\frac{49}{54}\right)^2 - \left(\frac{5}{54}\right)^2=0.168
¿Cómo se construyen los árboles de decisión para problemas de clasificación?
Los árboles de decisión se construyen usando un algoritmo voraz que optimiza la siguiente función de coste:
J(a,l_a)=\frac{m_{izquierdo}}{m}Gini_{izquierdo} + \frac{m_{derecho}}{m}Gini_{derecho}
- a es la abreviatura de atributo (también llamado característica o feature)
- la significa el límite del atributo
- m se refiere al número de muestras
Por ejemplo, al comprobar si la longitud del pétalo es menor o igual a 2.45 centímetros, tenemos:
- a = longitud del pétalo
- la = 2.45
- m = 150
El algoritmo voraz elige qué atributos y qué límites son los mejores para tomar las decisiones. Al usar un algoritmo voraz, no tenemos la garantía de que este sea el mejor árbol posible. Sin embargo, en la práctica, obtenemos un árbol bastante bueno mucho más rápidamente que si necesitáramos el «mejor árbol posible».
Código python para entrenar y predecir con árboles de decisión para clasificación
# importamos las librerías que necesitamos from sklearn.datasets import load_iris # datos de iris from sklearn.tree import DecisionTreeClassifier # árbol de decisión para clasificación iris = load_iris() print(iris.DESCR) # información sobre del conjunto de datos iris # lo más relevante es: # :Number of Instances: 150 (50 in each of three classes) # :Number of Attributes: 4 numeric, predictive attributes and the class # :Attribute Information: # - sepal length in cm # - sepal width in cm # - petal length in cm # - petal width in cm # - class: # - Iris-Setosa # - Iris-Versicolour # - Iris-Virginica # veamos 4 filas donde ocurre un cambio de clase print(iris.data[48:52,:]) # da el resultado # [[5.3 3.7 1.5 0.2] # [5. 3.3 1.4 0.2] # [7. 3.2 4.7 1.4] # [6.4 3.2 4.5 1.5]] # para la variable a predecir también hacemos lo mismo print(iris.target[48:52]) # La clase 0 es Iris-Setosa, la 1 es Iris-Versicolor y la 2 es Iris-Virginica # [0 0 1 1] # Vamos a crear y entrenar un árbol de decisión para clasificar los datos de Iris tree = DecisionTreeClassifier(max_depth=2, random_state=42) # vamos a usar un árbol de profundidad 2 tree.fit(iris.data, iris.target) # entrenamiento del árbol # podemos usar el método predict para obtener predicciones print( tree.predict(iris.data[47:53]) ) # resulta en # [0 0 0 1 1 1] # si queremos saber las probabilidades podemos usar el método predict_proba print( tree.predict_proba(iris.data[47:53]) ) # la primera clase (Setosa) es la primera columna, la segunda clase en la segunda, etc.. # este es el resultado: # [[1. 0. 0. ] # [1. 0. 0. ] # [1. 0. 0. ] # [0. 0.90740741 0.09259259] # [0. 0.90740741 0.09259259] # [0. 0.90740741 0.09259259]]
Árboles de Decisión para Regresión
Datos para regresión
Para explicar cómo funcionan los árboles de decisión para problemas de regresión vamos a usar los datos que se presentan en la siguiente gráfica. Para generarlos he usado la siguiente fórmula en el intervalo [-5, 5]:
y = 0.1x^2 + 0.2 (Ruido\ Gaussiano)
Teoría de los árboles de decisión para regresión
En el caso de regresión, en lugar de usar Gini como medida de impureza, usamos MSE, el error cuadrático medio. Para este problema, si usamos un árbol de decisión de profundidad 2, obtenemos el siguiente árbol.
Interpretación
La interpretación del árbol de este árbol de decisión sería: si el valor de x es menor que -4.25, predice 2.2777; si está en el intervalo (-4.25, -3.75] predict 1.5774); si está en el intervalo (-3.75, 3.05] predict 0.3566 y si es mayor que 3.05 predice 1.6273.
¿Cómo se construyen los árboles de decisión para problemas de regresión?
Los árboles de decisión para regresión también se construyen usando un algoritmo voraz. Para regresión, la función de coste es la siguiente:
J(a,l_a)=\frac{m_{izquierdo}}{m}MSE_{izquierdo} + \frac{m_{derecho}}{m}MSE_{derecho}
- a es la abreviatura de atributo (también llamado característica o feature)
- la significa el límite del atributo
- m se refiere al número de muestras
MSE es el error cuadrático medio por sus siglas en inglés (Mean Squared Error)
Código python para entrenar y predecir con árboles de decisión para regresión
# importamos las librerías que necesitamos import numpy as np # NumPy para manipulación numérica np.random.seed(42) # para hacer el código reproducible from sklearn.tree import DecisionTreeRegressor # árbol de decisión para regresión #función y = 0.1x^2 + 0.2(Ruido Gaussiano) def f(x): y = 0.1*np.square(x) + 0.2*np.random.randn(x.size) return y # Generamos los datos x, y con la función f(x) x = np.arange(-5,5,0.1) # x = [-5, -4.9, -4.8, ... 4.8, 4.9, 5] y = f(x) # Vamos a crear y entrenar un árbol de decisión para regresión tree = DecisionTreeRegressor(max_depth=2, random_state=42) # máxima profundidad 2 tree.fit(x.reshape(-1,1), y) # entrenamos el árbol de regresión # Ahora predecimos qué valores de y tendríamos para x2 = [-0.7, 0.5, 2.3] x2 = np.array([-0.7, 0.5, 2.3]).reshape(-1,1) print( tree.predict(x2) ) # obtenemos el siguiente resultado: # [0.35655791 0.35655791 0.35655791]
A tener en cuenta cuando usamos Árboles de Decisión
Regularización
Los árboles de decisión de scikit-learn no están regularizados por defecto. Esto significa que para obtener el menor error posible, pueden crear tantos nodos como necesiten. Esto puede resultar en un modelo muy complejo que funcione muy bien en el conjunto de entrenamiento pero muy mal con datos nuevos.
La regularización consiste en limitar de alguna forma las capacidades del modelo para obtener un modelo de aprendizaje automático que sea más simple y generalice mejor.
En scikit-learn podemos usar varios hiper-parámetros para configurar cómo regularizamos los árboles de decisión. Veamos los más usados:
- max_depth: la profundidad máxima del árbol. En los ejemplos anteriores hemos usado max_depth = 2
- min_samples_split: número mínimo de muestras necesarias antes de dividir este nodo. También se puede expresar en porcentaje.
- min_samples_leaf: número mínimo de muestras que debe haber en un nodo final (hoja). También se puede expresar en porcentaje.
- max_leaf_nodes: número máximo de nodos finales
El siguiente gráfico muestra los datos (en azul) y la predicción (en rojo) de dos árboles. El de la izquierda no está regularizado. El de la derecha está regularizado. La regularización, en este caso, consiste en que los nodos finales (las hojas del árbol) deben cubrir al menos un 5% de las muestras. Como puedes comprobar visualmente, el árbol regularizado va a generalizar mejor y no sufre de sobreajuste.
Generalización de los árboles de decisión
Los árboles de decisión son modelos no-paramétricos. Esto significa que no sabemos cuántos parámetros vamos a tener hasta que no veamos los datos y construyamos el árbol. La regresión lineal es un modelo paramétrico porque antes de ver los datos podemos decir con toda certeza cuántos parámetros vamos a necesitar.
Ventaja de los modelos no-paramétricos
La principal ventaja de los árboles de decisión, como modelo no-paramétrico, es que pueden aprender cualquier cosa. En el siguiente gráfico vemos cómo aprende sin problemas una parábola con ruido (a la izquierda) y una función sinusoidal con ruido (a la derecha).
Desventaja de los modelos no-paramétricos
Por otra parte, al ser un modelo no-paramétrico, no sabe qué es lo que se supone que debe pasar fuera del rango de entrenamiento. Al contrario que en el caso de lo modelos paramétricos, no «sabe» extrapolar. En el siguiente gráfico vemos que aunque parece que es capaz de modelar una función parabólica o una sinusoidal, en realidad, no «sabe» lo que está haciendo.
En realidad este problema no es tan malo como pudiera parecer en este gráfico. Normalmente, los problemas para los que usamos aprendizaje automático, tienen muchos atributos y habrá muchos de ellos para los que sí tendremos datos en el rango de entrenamiento. En cualquier caso, siempre conviene asegurarnos que la distribución de los datos que queremos predecir es similar a la distribución de datos que usamos para entrenar el modelo.
Importancia de los atributos
Podemos obtener una estimación de cómo de importante es cada atributo para realizar la predicción. Esto nos va a permitir entender mejor el problema que estemos resolviendo. Por ejemplo, en el caso de la clasificación de las distintas variedades de las flores Iris, tenemos esta importancia estimada:
- sepal length (cm): 0.0
- sepal width (cm): 0.0
- petal length (cm): 0.5619909502262443
- petal width (cm): 0.4380090497737556
Según estos resultados, no necesitamos los datos de los sépalos. El ancho y el largo de los pétalos son suficientes para clasificar las flores Iris en su variedad correcta.
Código python para obtener la importancia de los atributos en un árbol de decisión
# tree.feature_importances_ es un vector con la importancia estimada de cada atributo for name, importance in zip(iris.feature_names, tree.feature_importances_): print(name + ': ' + str(importance)) # resultado: # sepal length (cm): 0.0 # sepal width (cm): 0.0 # petal length (cm): 0.5619909502262443 # petal width (cm): 0.4380090497737556
Resumen
Los árboles de decisión son una técnica de aprendizaje automático muy utilizada. Su ventajas principales son:
- son fáciles de entender y explicar a personas que todavía no están familiarizadas con técnicas de Inteligencia Artificial
- se adaptan a cualquier tipo de datos
- descubren cuáles son los atributos relevantes
También tienen sus desventajas:
- no extrapolan bien fuera del rango de entrenamiento
- tienen la tendencia a sobre-ajustar, sobre todo si no se regularizan
Recursos
- En este [vídeo] explico cómo funcionan los árboles de decisión (en inglés)
- Árbol de Decisión para Clasificación en scikit-learn: DecisionTreeClassifier
- Árbol de Decisión para Regressión en scikit-learn: DecisionTreeRegressor
Mencionar que los árboles también se pueden usar sin usar el índice de Gini, sino el criterio de ganancia de información. Esta es usada por los algoritmos ID3 y C4.5 y siempre me ha parecido más elegante por estar basada en teoría de la información.
Hola Jose,
lo primero agradecerte y felicitarte por este blog. Para los que nos estamos iniciando en esto te puedo asegurar que nos ayuda mucho gracias a la claridad con que expones los temas.
Dicho esto no entiendo muy bien el apartado «desventajas de los modelos No-Paramétricos». Con rango de entrenamiento de refieres al conjunto de entrenamiento? y cuando dices que «no sabe extrapolar» te refieres a que no sabe generalizar con el conjunto de test? por extensión me pierdo cuando dices «Normalmente, los problemas para los que usamos aprendizaje automático, tienen muchos atributos y habrá muchos de ellos para los que sí tendremos datos en el rango de entrenamiento.»
Muchas gracias de antemano
Hola Sant, gracias por el feedback. Te intento aclarar tus preguntas:
1. con «rango de entrenamiento» NO me refiero al conjunto de entrenamiento. Con la expresión «rango de entrenamiento» quiero decir qué valores de entrada hemos usado para construir el modelo.
Me doy cuenta que esto, posiblemente, tampoco lo aclare mucho. Así que mejor te voy a poner un ejemplo. Si miras este gráfico: https://www.iartificial.net/wp-content/uploads/2019/05/arboles-no-parametricos-generalizacion.png verás que el árbol de decisión funciona muy bien en el «rango» de valores [-5, 5] para la x. La predicción y es bastante buena. Sin embargo, funciona muy mal para valores fuera del rango [-5, 5]. Funciona tan bien para el rango [-5, 5] porque este es el rango que yo, arbitrariamente, he elegido para entrenar el árbol de decisión en este ejemplo. Este es el rango de entrenamiento. Si los datos nuevos también están en este rango, las predicciones también serán buenas. Sin embargo, para datos nuevos que estén fuera de este rango (en el ejemplo [-5, 5]), la predicción será bastante mala.
2. cuando digo que «no sabe extrapolar» lo que quiero decir es que los árboles de decisión, al ser modelos no-paramétricos, no asumen que forma van a tener los datos. Te pongo un ejemplo de modelo paramétrico para que veas la diferencia más clara: una regresión lineal. Si te decides a usar una regresión lineal … incluso antes de mirar a los datos, ya sabes que el modelo será una línea. Sabes que será una línea independientemente del valor de x. Así que si quieres predecir el valor de la y para una x fuera del «rango de entrenamiento», lo harás extrapolando la línea. Si los datos siguen una relación lineal, la predicción será bastante buena. Si la relación es no lineal, seguramente el error será muy alto. Los modelos no-paramétricos no asumen cómo se van a comportar los datos. Esto hace que puedan aprender cualquier comportamiento. La desventaja es que «no saben» qué hacer fuera del rango de entrenamiento, precisamente porque no asumen cómo se van a comportar los datos.
3. «Normalmente, los problemas para los que usamos aprendizaje automático, tienen muchos atributos y habrá muchos de ellos para los que sí tendremos datos en el rango de entrenamiento.»
Creo que ahora debería estar más claro. Lo que vengo a decir es que si tu problema tiene, por ejemplo, 100 variables, y sólo una de ellas que se sale del rango de entrenamiento … no es tan malo como si tu problema sólo tiene una variable de entrada y se sale del rango de entrenamiento.
A lo mejor también te interesa el artículo sobre generalización: https://www.iartificial.net/generalizacion-en-machine-learning/
También puedes ver un vídeo donde explico cómo funcionan los árboles de decisión (en inglés): https://www.youtube.com/watch?v=Op4thnw-ig8&list=PLjai7zNYchWMJuV46s6XOIURPA3dDkooG
Muchas gracias por la respuesta, me queda muy claro.
Gracias por el aporte, queda claro la explicacion.
Buenas, hay alguna manera de guardar los datos entrenados en una base de datos para al momento de ingresar solomente necesite ingresar los datos a evaluar y no tener que entrenarlos desde 0 saludos
Hola Alex, sí, puedes guardar el modelo entrenado en un fichero y luego usarlo. Hay varias formas de hacerlo, una de ellas es usando pickle.
Excelente articulo. Mi pregunta es… Cual sería una posible solución para el problema de extrapolación? Que otros algoritmos aparte de la regresión lineal pueden generalizar para observaciones que tengan valores fuera del rango de los que se usaron para entrenar el modelo
Hola!
Excelente explicacion!
Sin embargo, lo estoy viendo desde mi celular. Y las formulas se ven en codigo latex :(
Saludos
Qué navegador usas? Con Chrome se ve bien también en el móvil