Bienvenido a Árboles de decisión usando Python. Código paso a paso

Esta es la tercera parte de una serie de post sobre árboles de decisiones

Se dividió así:

  1. Arboles de decisiones, todas las bases
  2. Árboles de decisiones. La matemática y estadística detrás de ellos
  3. Árboles de decisiones. Código en python paso a paso para construirlo

Todo el código de este post puedes encontrarlo aquí 

Los posts anteriores repasaron la teoría de los árboles de decisión. Una de las razones por las que es bueno aprender a tomar árboles de decisión en un lenguaje de programación es que trabajar con datos puede ayudar a comprender el algoritmo.


Carguemos el conjuntos de datos

Este sera el conjunto de datos que trabajaremos para hacer nuestro Árbol de decisión usando Python con código paso a paso


El conjunto de datos de Iris es uno de los conjuntos de datos que scikit-learn incluye y que no requieren la descarga de ningún archivo desde un sitio web externo. El siguiente código carga el conjunto de datos del iris.

import pandas as pd
from sklearn.datasets import load_irisdata = load_iris()
df = pd.DataFrame(data.data, columns=data.feature_names)
df['target'] = data.target

División de datos en conjuntos de entrenamiento y prueba


El siguiente código coloca el 75% de los datos en un conjunto de entrenamiento y el 25% de los datos en un conjunto de prueba.

import numpy as np
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(df[data.feature_names], df['target'], random_state=0)

Ten en cuenta que uno de los beneficios de los árboles de decisión es que no tiene que estandarizar sus datos a diferencia de la PCA y la regresión logística que son sensibles a los efectos de no estandarizar los datos.

Patrón de modelado de 4 pasos de Scikit-learn


Paso 1: importar el modelo a usar
En scikit-learn, todos los modelos de aprendizaje automático se implementan como clases de Python

from sklearn.tree import DecisionTreeClassifier

Paso 2: crea una instancia del modelo


En el código a continuación, configuramos el max_depth = 2 para pre podar el árbol y asegurarse de que no tenga una profundidad mayor que 2. Se debe tener en cuenta que la siguiente sección del tutorial explicará cómo elegir una máxima_depth óptima para el árbol .
También tenga en cuenta que en mi código a continuación, hice random_state = 0 para evitar la aleatoriedad

clf = DecisionTreeClassifier(max_depth = 2, 
random_state = 0)

Paso 3: capacitar al modelo en los datos


El modelo está aprendiendo la relación entre X (longitud del sépalo, ancho del sépalo, longitud del pétalo y ancho del pétalo) e Y (especie de iris)

clf.fit(X_train, Y_train)
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=2,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False,
random_state=0, splitter='best')

Paso 4: Predecir etiquetas de datos no vistos (prueba)

# Predict for 1 observation
clf.predict(X_test.iloc[0].values.reshape(1, -1))# Predict for multiple observations
clf.predict(X_test[0:10])
array([2, 1, 0, 2, 0, 2, 0, 1, 1, 1])

Recuerde, una predicción es solo la clase mayoritaria de las instancias en un nodo hoja.


Medición del rendimiento del modelo


Si bien hay otras formas de medir el rendimiento del modelo (precisión, recuperación, puntaje F1, curva ROC, etc.), vamos a mantener esto simple y utilizar la precisión como nuestra métrica.

La precisión se define como:
(fracción de predicciones correctas): predicciones correctas / número total de puntos de datos

# The score method returns the accuracy of the model
score = clf.score(X_test, Y_test)
print(score)
0.8947368421052632

Afinando la profundidad de un árbol


Encontrar el valor óptimo formax_depth es una forma de ajustar su modelo. El siguiente código muestra la precisión de los árboles de decisión con diferentes valores para max_depth.

# List of values to try for max_depth:
max_depth_range = list(range(1, 6))# List to store the average RMSE for each value of max_depth:
accuracy = []for depth in max_depth_range:

clf = DecisionTreeClassifier(max_depth = depth,
random_state = 0)
clf.fit(X_train, Y_train) score = clf.score(X_test, Y_test)
accuracy.append(score)

Dado que el siguiente gráfico muestra que la mejor precisión para el modelo es cuando el parámetro max_depth es mayor o igual a 3, podría ser mejor elegir el modelo menos complicado con max_depth = 3.

Es importante tener en cuenta que max_depth no es lo mismo que la profundidad de un árbol de decisión. max_depth es una forma de podar previamente un árbol de decisión. En otras palabras, si un árbol ya es lo más puro posible a una profundidad, no continuará dividiéndose. La siguiente imagen muestra árboles de decisión con valores de profundidad máxima de 3, 4 y 5. Observa que los árboles con una profundidad máxima de 4 y 5 son idénticos. Ambos tienen una profundidad de 4.

Si alguna vez te preguntas cuál es la profundidad de el árbol de decisión entrenado, puedes usar el método get_depth. Además, se puede obtener el número de nodos hoja para un árbol de decisión entrenado utilizando el método get_n_leaves.
Si bien en los posts se ha cubierto los criterios de selección (índice de Gini, entropía, etc.) y la profundidad máxima de un árbol, ten en cuenta que también puede ajustar las muestras mínimas para que un nodo se divida (min_samples_leaf), el número máximo de nodos hoja (max_leaf_nodes), y más.


Importancia de la característica


Una ventaja de los árboles de clasificación es que son relativamente fáciles de interpretar. Los árboles de clasificación en scikit-learn le permiten calcular la importancia de la característica, que es la cantidad total que disminuye el índice de Gini o la entropía debido a divisiones en una característica determinada. Scikit-learn genera un número entre 0 y 1 para cada característica. Todas las características importantes se normalizan para sumar 1. El siguiente código muestra las características importantes para cada característica en un modelo de árbol de decisión.

importances = pd.DataFrame({'feature':X_train.columns,'importance':np.round(clf.feature_importances_,3)})
importances = importances.sort_values('importance',ascending=False)

En el ejemplo anterior , el ancho del pétalo tiene el peso de importancia de característica más alto. Podemos confirmar mirando el árbol de decisión correspondiente.

Ten en cuenta que si una característica tiene un valor de importancia de característica bajo, no significa necesariamente que la característica no sea importante para la predicción, solo significa que la característica particular no se eligió en un nivel particularmente temprano del árbol. También podría ser que la característica podría ser idéntica o altamente correlacionada con otra característica informativa. Los valores de importancia de las características tampoco le dicen para qué clase son muy predictivos o las relaciones entre las características que pueden influir en la predicción. 

Visualizar un árbol de decisión 

Importamos la función export_graphviz y un par de clases adicionales de sklearn, IPython y pydotplus:

from sklearn.externals.six import StringIO  
from IPython.display import Image
from sklearn.tree import export_graphviz
import pydotplus
dot_data = StringIO()
export_graphviz(model, out_file = dot_data,
filled = True, rounded = True,
special_characters = True)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
Image(graph.create_png())

También se puede visualizar así

from sklearn.tree import export_graphviz
from pydotplus import graph_from_dot_data
dot_data = export_graphviz(tree,
feature_names=feature_names)
graph = graph_from_dot_data(dot_data)
graph.write_png('tree.png')

Ahora vamos a hacer otro árbol de decisión con el dataset Titanic

#from sklearn.model_selection import COMPLETAR
#X_train, X_test, y_train, y_test = COMPLETAR
df = pd.read_csv("DS_Encuentro_05_titanic-Copy1.csv")
df.head(3)
#COMPLETAR.COMPLETAR(X_train, y_train)
df.drop(['Cabin','PassengerId','Name','Ticket','Embarked'], axis=1,inplace=True)
df.dropna(inplace=True)
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
df['Sex'] = le.fit_transform(df['Sex'])
from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier()

Primer modelo

X = df[['Pclass','Sex']]
y = df['Survived']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
tree.fit(X_train, y_train)
Resultado
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False,
random_state=None, splitter='best')

Hallamos la precisión

from sklearn.metrics import accuracy_score
# Predecimos sobre nuestro set de entrenamieto
y_train_pred = tree.predict(X_train)
# Predecimos sobre nuestro set de test
y_test_pred = tree.predict(X_test)
# Comaparamos con las etiquetas reales
print('Accuracy sobre conjunto de Train:', accuracy_score(y_train_pred,y_train))
print('Accuracy sobre conjunto de Test:', accuracy_score(y_test_pred,y_test))
Resultado
Accuracy sobre conjunto de Train: 0.799163179916318
Accuracy sobre conjunto de Test: 0.7754237288135594

RECUERDEN: Cuando el error de entrenamiento es mayor al del test es porque hay overfitting 

Segundo modelo

X = df[['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare']]
y = df['Survived']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
tree.fit(X_train, y_train)
Resultado
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=None,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False,
random_state=None, splitter='best')

Precisión

from sklearn.metrics import accuracy_score
# Predecimos sobre nuestro set de entrenamieto
y_train_pred = tree.predict(X_train)
# Predecimos sobre nuestro set de test
y_test_pred = tree.predict(X_test)
# Comaparamos con las etiquetas reales
print('Accuracy sobre conjunto de Train:', accuracy_score(y_train_pred,y_train))
print('Accuracy sobre conjunto de Test:', accuracy_score(y_test_pred,y_test))
RESULTADO
Accuracy sobre conjunto de Train: 0.9937238493723849
Accuracy sobre conjunto de Test: 0.7584745762711864

Por último quiero hacer dataset IRIS de otra manera

import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
sns.set()

Importamos los datos

iris = pd.read_csv("IRIS-Copy1.csv")
iris.head(5)

Vamos a verlo gráficamente 

sns.pairplot(iris, hue="species")
plt.show()

Separaramos del dataframe los features y las etiquetas. Llamar X a los features e y a las etiquetas. Elegir qué features usar (pueden ser todos). Fijarse si es necesario transformar las etiquetas o si Scikit-Learn puede trabajar con ellas

X = iris.drop("species", axis=1)
y = iris.species
X.head()
y.head()

Separar en conjuntos de Train y Test

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

Importar un DecisionTreeClassifier de Scikit-Learn

from sklearn.tree import DecisionTreeClassifier

Crear un DecisionTreeClassifier con max_depth = 2.

tree = DecisionTreeClassifier(max_depth = 2)

Entrenar el DecisionTreeClassifier que se creo.

tree.fit(X,y)
RESULTADO
DecisionTreeClassifier(class_weight=None, criterion='gini', max_depth=2,
max_features=None, max_leaf_nodes=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, presort=False,
random_state=None, splitter='best')

Explorar algunas características del modelo entrenado

importances = tree.feature_importances_
columns = X.columns
sns.barplot(columns, importances)
plt.title('Importancia de cada Feature')
plt.show()

Predecir con el modelo las etiquetas en el conjunto de Train y de Test. ¿Cómo son las etiquetamos?

# Predecimos sobre nuestro set de entrenamieto
y_train_pred = tree.predict(X_train)
# Predecimos sobre nuestro set de test
y_test_pred = tree.predict(X_test)

Evaluar la performance del modelo.

# Comparamos con las etiquetas reales
from sklearn.metrics import accuracy_score
print('Accuracy sobre conjunto de Train:', accuracy_score(y_train_pred,y_train))
print('Accuracy sobre conjunto de Test:', accuracy_score(y_test_pred,y_test))
RESULTADO
Accuracy sobre conjunto de Train: 0.95
Accuracy sobre conjunto de Test: 0.98

Por último les comparto el código para visualizar fronteras con max_deep

## Creamos el clasificador
clf = DecisionTreeClassifier(max_depth = 3)
# # Modificamos un poco los datos para poder graficar
y_train = y.map({'Iris-setosa': 0,'Iris-versicolor': 1, 'Iris-virginica': 2})
# Entrenamos
clf.fit(X[['petal_width', 'petal_length']], y_train)
# Graficamos los datos y las fronteras creadas
plt.figure()
ax = sns.scatterplot(X.petal_width, X.petal_length, hue=y, palette='Set2')
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xx, yy = np.meshgrid(np.linspace(*xlim, num=200),
np.linspace(*ylim, num=200))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]).reshape(xx.shape)
contours = ax.contourf(xx, yy, Z, alpha=0.3, cmap = 'Set2')
plt.show()

Y eso es todo! 

Gracias por leerme 

Recursos

Visualización de un árbol de decisión
La librería sklearn incluye la función sklearn.tree.export_graphviz que permite la visualización de un árbol de…www.interactivechaos.com

Suscribete en mi canal de Youtube

Apoyame para seguir creando contenido y accede a mentorías personalizadas aquí

Gracias por leerme 💜

1 comment on “Árboles de decisión usando Python. Código paso a paso

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *