Clasificación del árbol de decisión: explicado con código

Shraddha Pandey

Uno de los modelos más utilizados en Machine Learning son los árboles de decisión. Los árboles de decisión son tan populares entre la comunidad porque se pueden usar tanto para problemas de clasificación Y regresión — que cubre una gran parte de los problemas en Machine Learning.

En este artículo, veremos los árboles de decisión para la clasificación específicamente. Primero trataremos de entender la intuición detrás del algoritmo, luego pasaremos a la teoría que permite que los árboles de decisión hagan predicciones, y finalmente veremos un código completo que le permitirá hacer clasificaciones usando árboles de decisión con alta precisión.

Los árboles de decisión son un modelo de aprendizaje supervisado simple, lo que esencialmente significa que el modelo debe recibir datos de entrenamiento etiquetados con los que pueda hacer sus predicciones. Funciona de manera codiciosa, de arriba hacia abajo, comenzando desde el nodo raíz, que contiene un número n de ramas y termina con nodos de hoja.

Los nodos internos que están presentes dentro del árbol representan varios casos de prueba en forma de pregunta, los bordes que conectan los nodos corresponden al resultado de la pregunta y los nodos hoja predicen una clase para la instancia de prueba específica.

Esta es una buena representación de cómo se puede usar un árbol de decisión para un problema de clasificación. Hay dos clases de resultados en el diagrama anterior: «Caminata» y «Autobús» y, como puede ver, cualquiera que sea el camino que tome, aterrizará en cualquiera de las dos clases. Cada borde representa el resultado de la pregunta, por ejemplo, «¿Tiempo?» El nodo tiene tres bordes diferentes (a saber, Sol, Nube, Lluvia), cada uno de los cuales establece el valor del atributo Clima para esa instancia en particular.

Si comprende estos pasos muy básicos, no le quedará mucho por comprender en relación con los árboles de decisión teóricamente.

  1. Empezar con un árbol vacío
  2. Seleccione una característica para dividir los datos.
  3. Para cada división:
  • Si no hay más divisiones, haga predicciones (basadas en la clase mayoritaria).
  • De lo contrario, recursivamente vaya al paso 2.

Ahora bien, ¿cómo sabemos exactamente si no habrá más divisiones? Hay algunas condiciones para detener la partición, tales como:

  1. Todas las muestras para el nodo dado pertenecen a la misma clase
  2. No quedan atributos
  3. No quedan muestras
  4. Límite de profundidad del árbol alcanzado

Un término muy importante que acabamos de encontrar es terrible. A menos que su modelo de árbol de decisiones identifique correctamente en qué atributo dividir, es posible que no sea muy preciso. Entonces, ¿cómo sabemos qué atributos dividir?

Para dividir atributos en árboles de decisión, tenemos dos métricas principales:
1. Ganancia de información (ID3)
2. Índice de Gini (CART)

Ganancia de información

Inicialmente, debemos calcular la Entropía/Información que es un valor necesario para clasificar y observar y se calcula como:

Aquí, C es el número de clases de la salida y pi es la probabilidad de que una clase pertenezca a una clase Ci.

La ganancia de información se puede calcular aún más restando la Entropía después de dividir de la Entropía antes de dividir, y el objetivo del modelo es seleccionar el atributo con el valor de ganancia de información más alto para dividir.

Índice Gini

El índice de Gini se puede calcular como:

Aquí, C es el número de clases de la salida y pi es la probabilidad de que una clase pertenezca a una clase Ci.

Además, podemos calcular la reducción de impurezas utilizando los valores del índice de Gini restando el índice de Gini después de la división del índice de Gini antes de la división. El objetivo del modelo en este caso es elegir el atributo con el valor de índice de Gini más bajo o el valor más alto de reducción de Impureza.

Dado que inicialmente puede ser bastante abrumador realizar un seguimiento de todas las diferentes métricas, en este artículo solo utilizaremos el método Entropy.

Ahora que sabemos qué son las divisiones y cómo hacer buenas divisiones, no debemos exagerar con las divisiones, ya que también existen inconvenientes. A medida que aumenta el número de divisiones en un árbol de decisión, también aumenta el tiempo necesario para construir el árbol. Demasiadas ramas también pueden resultar en el sobreajuste del modelo ya que algunos puntos pueden reflejar anomalías debido al ruido o puntos atípicos. Es por esto que cuando limitamos el hiperparámetro máxima profundidad del modelo. También se pueden ajustar y utilizar otros hiperparámetros para controlar la división de un árbol de decisión, incluidos min_muestras_divididas, min_muestras_hojay max_features.

En teoría, tenemos otro método para el mismo, llamado poda. La poda es el proceso de eliminar las ramas del árbol que tienen poca o ninguna importancia en el proceso de toma de decisiones. Hay dos tipos diferentes de poda: prepoda y pospoda.

La poda previa se realiza mientras el árbol está creciendo y la idea básica detrás de esto es que no debemos dividir un nodo si la bondad del árbol cae por debajo de cierto umbral. Este método no es muy óptimo ya que podría resultar difícil elegir un umbral apropiado. La pospoda se realiza eliminando las ramas de un árbol completamente desarrollado. Estas ramas son las que no afectan el proceso de toma de decisiones del clasificador.

Ahora que hemos entendido la teoría detrás de los árboles de decisión y su funcionamiento, intentemos aplicar nuestro conocimiento construyendo nuestro propio Clasificador de árboles de decisión.

Paso 1: Importación de las bibliotecas y los conjuntos de datos necesarios

Aunque usaremos un par de bibliotecas diferentes, especialmente Sci-kit, por ahora solo importaremos las bibliotecas populares como Pandas y Numpy.

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)

El conjunto de datos que estoy usando para este ejemplo se tomó de Kaggle y se puede acceder aquí. Es un conjunto de datos que tiene información médica sobre pacientes como datos de entrenamiento, y el objetivo es predecir si dicho paciente tiene o no diabetes.

Si no sabe qué es Kaggle, le sugiero que lo investigue, ya que es una herramienta extensa de código abierto que es perfecta para ingenieros de aprendizaje automático o científicos de datos. Puedes encontrar mi cuaderno Kaggle con este código completo aquí.

Ahora, importemos el conjunto de datos en un marco de datos de pandas para facilitar el acceso.

train_data = pd.read_csv("/kaggle/input/diabetes-prediction-dataset/diabetes_prediction_dataset.csv")
train_data.head()

Podemos usar el método .head() para ver las primeras 5 instancias del conjunto de datos, y se parece a lo siguiente:

Como podemos ver en el conjunto de datos, algunas columnas como «género» y «historial_de_fumar» también contienen datos categóricos. La mayoría de los modelos de aprendizaje automático, incluidos los árboles de decisión, no pueden funcionar con valores categóricos, por lo que mientras procesamos previamente los datos, debemos asegurarnos de codificar los valores categóricos y convertirlos en continuos.

Ya que hablamos sobre la codificación antes, permítanme explicar rápidamente los dos tipos básicos de codificación, los cuales usaremos para este ejemplo.

  1. Codificación ordinal: la codificación ordinal se usa cuando los diferentes valores de la columna también deben recibir diferentes pesos/prioridades. Por ejemplo, en este conjunto de datos, en el historia_de_fumar característica, no podemos considerar igual a un paciente que ‘nunca’ ha fumado que a un paciente que ‘actualmente’ fuma. Obviamente, ser fumador actual tendrá un impacto negativo en la salud del paciente y puede afectar la variable objetivo. Entonces, en este caso, cada valor debe codificarse de tal manera que se mantenga su prioridad.
  2. Codificación nominal: la codificación nominal se usa cuando el orden en que se numeran los diferentes valores de la columna no importa. Por ejemplo, en este conjunto de datos, mientras codificamos el género característica, no importará si le damos a hembra un valor de 0 y macho un valor de 1 o viceversa.
#Ordinal encoding for smoking history (manually using a python dictionary)
#no info = NULL, never = 0, former = 1, current = 2

smoking_history_dict = {'never':0, 'former':1, 'current':2}

train_data['smoking_history'] = train_data.smoking_history.map(smoking_history_dict)

#Nominal encoding for gender (manually using a python dictionary)
#female = 0, male = 1

gender_dict = {'Female':0, 'Male':1}

train_data['gender'] = train_data.gender.map(gender_dict)

Aunque hice esto manualmente usando el mapeo de diccionarios ya que solo había dos funciones para codificar, esto también se puede hacer muy fácilmente usando métodos en la biblioteca Sci-Kit como OneHotEncoder.

Ahora que todos nuestros datos están en forma continua, debemos tratar con los valores NULL. Como habrá notado en la vista anterior del conjunto de datos, existe una instancia para la cual el historia_de_fumar columnas dice ‘sin información’. Durante el mapeo, esto cambia a un valor NULL y la mayoría de los modelos, incluidos los árboles de decisión, no saben cómo manejar los valores NULL en los datos de entrenamiento.

Para que podamos tratar con valores NULL, existen dos métodos principales:

  1. Eliminar las filas que contienen valores NULL

2. Impute los datos (reemplazando los valores NULL con la media, la mediana, la moda de los datos o incluso un valor constante de su elección)

#checking for missing values
train_data.isnull().sum()

Desde historia_de_fumar tiene más del 46% de valores NULL, no podemos usar el primer método para eliminar filas con valores NULL, ya que eso significaría que sufrimos una gran pérdida de datos (se perdería casi el 50% de nuestros datos originales) y, por lo tanto, nuestro modelo no ser exacto

Por lo tanto, debemos buscar métodos de imputación en este escenario.

from sklearn.impute import SimpleImputer

imputer = SimpleImputer(strategy='mean')

train_data = imputer.fit_transform(train_data)

Mientras se imputa, el DataFrame se convierte en una matriz numpy, por lo que debemos volver a convertirlo en un DataFrame.

train_data = pd.DataFrame(train_data, columns=['gender', 'age', 'hypertension', 'heart_disease', 'smoking_history', 'bmi', 'HbA1c_level', 'blood_glucose_level', 'diabetes'])

Lo último que debe hacer para preprocesar los datos es escalarlos. Para ello utilizaremos el MinMaxScaler de la librería Sci-Kit.

#scaling the data for higher accuracy 
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
train_data = scaler.fit_transform(train_data)

Y finalmente, podemos ver nuestros datos transformados usando el método head().

Ahora que el conjunto de datos ha sido preprocesado, finalmente podemos dividirlo en datos de entrenamiento y prueba. Dado que deseamos favorecer la precisión del modelo, debemos proporcionar al modelo datos de entrenamiento más grandes y, por lo tanto, podemos dividirlo en una proporción de 80-20.

#spliiting the data into training and test data
from sklearn.model_selection import train_test_split

X = train_data[train_data.columns[0:8]]
y = train_data['diabetes']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)

Ahora tenemos nuestros datos de entrenamiento y prueba, por lo que podemos continuar y usar los datos de entrenamiento para entrenar el modelo y hacer que haga predicciones sobre los datos de prueba (que luego usaremos para calcular la precisión del modelo)

#training the data using Decision Trees
from sklearn.tree import DecisionTreeClassifier

clf = DecisionTreeClassifier(criterion = 'entropy', max_depth = 3, random_state = 0)

clf.fit(X_train, y_train)

El hiperparámetro ‘criterio’ aquí son los métodos de selección de división de características que discutimos anteriormente. Podemos calcular la división utilizando la ganancia de información estableciendo el hiperparámetro igual a ‘entropía’ o usando el método del índice de Gini estableciendo el hiperparámetro igual a ‘gini’.

#predicting the values on test data
y_preds = clf.predict(X_test)

Ahora que el modelo ha hecho sus predicciones, podemos comparar esto con los valores reales que tenemos y calcular la precisión del modelo. La precisión de cualquier modelo de aprendizaje automático se puede calcular mediante:

Número de predicciones correctas / Número total de predicciones

#checking the accuracy of our model
from sklearn.metrics import accuracy_score

accuracy_score(y_test, y_preds)

Nuestro modelo ofrece una precisión de 0,97225, ¡lo cual es asombroso! Con eso, hemos creado con éxito un clasificador de árboles de decisión con alta precisión y también llegamos al final de este artículo. Este artículo fue escrito principalmente como una revisión para repasar mis conceptos, pero si termina ayudando a una sola persona, ¡estaré extasiado! Si tiene alguna pregunta o si cree que he hecho alguna pregunta, no dude en ponerse en contacto conmigo a través de LinkedIn.


Comentarios

Deja una respuesta

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