Chapter 6: Practical Machine Learning Projects
6.1 Proyecto 1: Ingeniería de Características para Análisis Predictivo
Este proyecto se enfocará en aplicar técnicas de ingeniería de características a un conjunto de datos para mejorar el rendimiento de un modelo predictivo de machine learning. La ingeniería de características es crucial para hacer que los datos en bruto sean utilizables por los algoritmos de machine learning, al transformarlos en características significativas que mejoren el rendimiento del modelo.
Descripción General del Proyecto
En este proyecto, haremos lo siguiente:
- Explorar y preprocesar el conjunto de datos.
- Aplicar varias técnicas de ingeniería de características como el manejo de valores faltantes, la codificación de variables categóricas, la escalación de características y la creación de nuevas características.
- Construir un modelo predictivo utilizando los datos transformados para demostrar el impacto de la ingeniería de características en el rendimiento del modelo.
- Evaluar el rendimiento del modelo antes y después de la ingeniería de características.
Usaremos el conjunto de datos Titanic para este proyecto, ya que es adecuado para demostrar varias técnicas de ingeniería de características. La tarea es predecir si un pasajero sobrevivió al desastre del Titanic en función de características como la edad, el género, la clase del boleto y la tarifa.
6.1.1 Cargar y Explorar el Conjunto de Datos
Comenzaremos cargando el conjunto de datos Titanic y realizando una exploración inicial exhaustiva para obtener una comprensión profunda de su estructura y características. Este paso crucial implica examinar las dimensiones del conjunto de datos, los tipos de datos y las propiedades estadísticas básicas. También investigaremos la presencia de valores faltantes y visualizaremos las relaciones clave entre variables para sentar una base sólida para nuestros esfuerzos posteriores de ingeniería de características.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Load the Titanic dataset
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
titanic_df = pd.read_csv(url)
# Display the first few rows and basic information
print(titanic_df.head())
print(titanic_df.info())
print(titanic_df.describe())
# Visualize missing data
plt.figure(figsize=(10, 6))
sns.heatmap(titanic_df.isnull(), cbar=False, cmap='viridis')
plt.title("Missing Values Heatmap")
plt.show()
# Data Visualization
plt.figure(figsize=(12, 5))
plt.subplot(121)
sns.histplot(titanic_df['Age'].dropna(), kde=True)
plt.title('Age Distribution')
plt.subplot(122)
sns.boxplot(x='Pclass', y='Fare', data=titanic_df)
plt.title('Fare Distribution by Passenger Class')
plt.tight_layout()
plt.show()
# Correlation matrix
corr_matrix = titanic_df.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Matrix')
plt.show()
Aquí tienes un desglose de lo que hace este código:
- Importa las librerías necesarias:
pandas
para la manipulación de datos.matplotlib
yseaborn
para la visualización.
- Carga el conjunto de datos Titanic desde una URL utilizando
pandas
. - Muestra información básica sobre el conjunto de datos:
- Las primeras filas del conjunto de datos (
head()
). - Información del conjunto de datos (
info()
). - Resumen estadístico (
describe()
).
- Las primeras filas del conjunto de datos (
- Crea visualizaciones:
- Un heatmap para mostrar los valores faltantes en el conjunto de datos.
- Un histograma de la distribución de la edad.
- Un boxplot que muestra la distribución de la tarifa según la clase de pasajero.
- Un heatmap de la matriz de correlación para mostrar las relaciones entre las características numéricas.
Este código es parte del paso de exploración y preprocesamiento de datos, que es crucial para comprender el conjunto de datos antes de aplicar técnicas de ingeniería de características. Ayuda a identificar datos faltantes, visualizar distribuciones y entender las relaciones entre variables, estableciendo las bases para el análisis y la construcción del modelo.
6.1.2 Manejar Datos Faltantes
El conjunto de datos Titanic presenta varias características con valores faltantes, notablemente en Age (Edad) y Cabin (Camarote). Abordar estos valores faltantes es un paso crucial en nuestro proceso de ingeniería de características.
Para la característica Age, utilizaremos técnicas de imputación para llenar los vacíos con valores estadísticamente apropiados, como la mediana de la edad o predicciones basadas en otras características correlacionadas. En el caso de la característica Cabin, dado su alto porcentaje de entradas faltantes, evaluaremos cuidadosamente si es conveniente intentar la imputación o si es mejor excluirla de nuestro análisis.
Esta decisión se basará en el valor informativo potencial de la característica frente al riesgo de introducir sesgos a través de la imputación. Al manejar sistemáticamente estos valores faltantes, nuestro objetivo es maximizar la cantidad de información utilizable en el conjunto de datos, manteniendo al mismo tiempo la integridad de nuestros análisis posteriores.
# Fill missing values in the 'Age' column with the median age
titanic_df['Age'].fillna(titanic_df['Age'].median(), inplace=True)
# Fill missing values in the 'Embarked' column with the most frequent value
titanic_df['Embarked'].fillna(titanic_df['Embarked'].mode()[0], inplace=True)
# Drop the 'Cabin' column due to too many missing values
titanic_df.drop(columns=['Cabin'], inplace=True)
print(titanic_df.isnull().sum())
Aquí tienes un desglose de lo que hace este código:
- Rellena los valores faltantes en la columna 'Age' con la mediana del conjunto de datos. Este es un enfoque común para manejar datos numéricos faltantes.
- Para la columna 'Embarked', rellena los valores faltantes con el valor más frecuente (moda) en esa columna, un método común para datos categóricos con valores faltantes.
- Elimina la columna 'Cabin' por completo debido a la gran cantidad de valores faltantes. Esta decisión probablemente se tomó porque el alto porcentaje de datos faltantes en esta columna podría introducir más sesgo si se imputan.
- Finalmente, imprime la suma de los valores nulos en cada columna después de estas operaciones, lo que permite verificar que el manejo de valores faltantes se ha realizado con éxito.
Este enfoque para manejar los datos faltantes forma parte del proceso de ingeniería de características, con el objetivo de preparar el conjunto de datos para los algoritmos de machine learning, preservando la mayor cantidad de información útil posible.
6.1.3 Codificación de Características
El conjunto de datos Titanic incluye varias variables categóricas, como Sex y Embarked, que requieren una transformación a un formato numérico para que sean compatibles con los algoritmos de machine learning. Este proceso de conversión es crucial, ya que la mayoría de los modelos de machine learning están diseñados para trabajar con entradas numéricas. Para lograr esta transformación, emplearemos diversas técnicas de codificación, con un enfoque particular en la codificación one-hot.
La codificación one-hot es un método que crea columnas binarias para cada categoría dentro de una variable categórica. Por ejemplo, la variable 'Sex' se dividiría en dos columnas: 'Sex_male' y 'Sex_female', donde cada pasajero tendría un '1' en una columna y un '0' en la otra. Este enfoque nos permite representar datos categóricos de forma numérica sin implicar una relación ordinal entre las categorías.
Además, podríamos considerar otras técnicas de codificación, como label encoding para variables ordinales o target encoding para variables categóricas con alta cardinalidad, dependiendo de las características específicas de cada variable. La elección del método de codificación puede impactar significativamente en el rendimiento del modelo, lo que convierte este paso en un aspecto crítico del proceso de ingeniería de características.
# One-hot encode the 'Sex' and 'Embarked' columns
titanic_df = pd.get_dummies(titanic_df, columns=['Sex', 'Embarked'], drop_first=True)
print(titanic_df.head())
Aquí tienes un desglose de lo que hace este código:
- Utiliza la función
pd.get_dummies()
para codificar con one-hot las columnas 'Sex' y 'Embarked'. - El parámetro
columns=['Sex', 'Embarked']
especifica qué columnas se van a codificar. - El argumento
drop_first=True
se utiliza para evitar multicolinealidad, eliminando una de las columnas creadas para cada variable categórica original. - El resultado se guarda de nuevo en el DataFrame
titanic_df
, reemplazando efectivamente las columnas originales 'Sex' y 'Embarked' con sus versiones codificadas con one-hot. - Finalmente, imprime las primeras filas del DataFrame actualizado para mostrar los resultados de la codificación.
Este paso es crucial en el proceso de ingeniería de características ya que transforma los datos categóricos en un formato que puede ser fácilmente utilizado por los algoritmos de machine learning, que normalmente requieren entradas numéricas.
6.1.4 Escalado de Características
El escalado de características es un paso crucial en nuestro proceso de ingeniería de características, que aborda las diferencias significativas de escala entre ciertas características como Fare (Tarifa) y Age (Edad). Estas disparidades pueden tener efectos perjudiciales en el rendimiento del modelo, especialmente para algoritmos que son sensibles a las escalas de las características, como la regresión logística o K-nearest neighbors. Para mitigar estos problemas y asegurar un rendimiento óptimo del modelo, emplearemos el escalado estándar como nuestra técnica de normalización.
El escalado estándar, también conocido como normalización por z-score, transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esta transformación preserva la forma de la distribución original mientras lleva todas las características a una escala comparable. Al aplicar el escalado estándar a nuestro conjunto de datos, creamos un campo de juego uniforme para todas las características, lo que permite que los algoritmos las traten por igual y evita que las características con magnitudes mayores dominen el proceso de aprendizaje.
Los beneficios de este enfoque de escalado van más allá de mejorar el rendimiento del modelo. También mejora la interpretabilidad de los coeficientes del modelo, facilita una convergencia más rápida durante el proceso de entrenamiento y ayuda a comparar la importancia relativa de diferentes características. A medida que avanzamos en nuestro análisis, este paso de escalado será fundamental para extraer insights significativos y construir modelos predictivos robustos.
from sklearn.preprocessing import StandardScaler
scaling_features = ['Age', 'Fare']
scaler = StandardScaler()
titanic_df[scaling_features] = scaler.fit_transform(titanic_df[scaling_features])
print(titanic_df[scaling_features].head())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
StandardScaler
desklearn.preprocessing
. - Define una lista llamada
scaling_features
que contiene las columnas 'Age' y 'Fare', que son las características a escalar. - Crea una instancia de
StandardScaler
llamadascaler
. - Aplica el método
fit_transform
del escalador a las características especificadas en el DataFrametitanic_df
. Este paso ajusta el escalador a los datos y los transforma. - Finalmente, imprime el head de las características escaladas para mostrar el resultado.
El StandardScaler
transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esto es importante para muchos algoritmos de machine learning que son sensibles a la escala de las características de entrada, ya que ayuda a evitar que las características con magnitudes mayores dominen el proceso de entrenamiento del modelo.
6.1.5 Creación de Características
La creación de nuevas características es una técnica poderosa que puede mejorar significativamente la capacidad de un modelo para capturar y aprovechar relaciones complejas dentro de los datos. Este proceso, conocido como ingeniería de características, implica derivar nuevas variables a partir de las existentes para proporcionar información adicional o representar los datos de una manera más significativa. En este paso crucial de nuestro análisis, nos centraremos en la creación de una nueva característica llamada FamilySize.
La característica FamilySize se creará combinando dos variables existentes: SibSp (número de hermanos y cónyuges a bordo) y Parch (número de padres e hijos a bordo). Al agregar estas características relacionadas, buscamos crear una representación más completa del tamaño de la unidad familiar de un pasajero. Esta nueva característica tiene el potencial de capturar dinámicas sociales importantes y patrones de supervivencia que podrían no ser evidentes al considerar por separado a los hermanos/cónyuges y padres/hijos.
La lógica detrás de esta decisión de ingeniería de características está basada en la hipótesis de que el tamaño de la familia pudo haber desempeñado un papel significativo en los resultados de supervivencia durante el desastre del Titanic. Por ejemplo, las familias más grandes podrían haber enfrentado diferentes desafíos o haber recibido un trato distinto en comparación con los individuos que viajaban solos o en grupos más pequeños. Al crear la característica FamilySize, proporcionamos a nuestro modelo una comprensión más matizada del contexto familiar de cada pasajero, lo que potencialmente mejorará sus capacidades predictivas.
# Create a new feature 'FamilySize'
titanic_df['FamilySize'] = titanic_df['SibSp'] + titanic_df['Parch'] + 1
# Create a new feature 'IsAlone'
titanic_df['IsAlone'] = (titanic_df['FamilySize'] == 1).astype(int)
print(titanic_df[['SibSp', 'Parch', 'FamilySize', 'IsAlone']].head())
Este fragmento de código demuestra la creación de dos nuevas características en el conjunto de datos Titanic mediante la ingeniería de características:
- FamilySize: Esta característica se crea sumando los valores de 'SibSp' (número de hermanos y cónyuges a bordo), 'Parch' (número de padres e hijos a bordo) y añadiendo 1 (para incluir al propio pasajero). Esto proporciona una medida completa del tamaño total de la familia para cada pasajero.
- IsAlone: Esta es una característica binaria que indica si un pasajero viaja solo o no. Se deriva de la característica 'FamilySize', donde un valor de 1 indica que el pasajero está solo, y un valor de 0 indica que está acompañado por familiares.
El código luego imprime las primeras filas del DataFrame, mostrando estas nuevas características junto con las columnas originales 'SibSp' y 'Parch' para compararlas.
Estas nuevas características tienen como objetivo capturar información más matizada sobre el contexto familiar de cada pasajero, lo que podría mejorar el poder predictivo del modelo de machine learning para la predicción de supervivencia.
6.1.6 Selección de Características
La selección de características es un paso crucial en el pipeline de machine learning que consiste en identificar y seleccionar las características más relevantes del conjunto de datos. Este proceso ayuda a reducir la dimensionalidad, mejorar el rendimiento del modelo y aumentar la interpretabilidad. En nuestro proyecto de predicción de supervivencia en el Titanic, utilizaremos técnicas de selección de características para identificar las más informativas para nuestro modelo predictivo.
Existen varios métodos para la selección de características, incluidos métodos de filtro (por ejemplo, selección basada en correlación), métodos wrapper (por ejemplo, eliminación recursiva de características) y métodos integrados (por ejemplo, regularización L1). Para este proyecto, usaremos un método de filtro llamado SelectKBest, que selecciona las características en función de su relación estadística con la variable objetivo.
Al aplicar la selección de características, nuestro objetivo es:
- Reducir el sobreajuste eliminando características irrelevantes o redundantes.
- Mejorar la precisión del modelo al enfocarnos en las características más predictivas.
- Disminuir el tiempo de entrenamiento al reducir la dimensionalidad del conjunto de datos.
- Mejorar la interpretabilidad del modelo al identificar las características más importantes.
Vamos a proceder con la implementación del método SelectKBest para elegir las mejores características para nuestro modelo de predicción de supervivencia en el Titanic.
from sklearn.feature_selection import SelectKBest, f_classif
X = titanic_df.drop(columns=['Survived', 'PassengerId', 'Name', 'Ticket'])
y = titanic_df['Survived']
# Select top 10 features
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X, y)
# Get selected feature names
selected_features = X.columns[selector.get_support()].tolist()
print("Selected features:", selected_features)
Aquí tienes un desglose de lo que hace este código:
- Importa las funciones necesarias del módulo
feature_selection
de scikit-learn. - Prepara la matriz de características
X
eliminando columnas que no son necesarias para la predicción ('Survived', 'PassengerId', 'Name', 'Ticket') del DataFrametitanic_df
. - Define la variable objetivo
y
como la columna 'Survived'. - Crea un objeto
SelectKBest
conf_classif
como función de puntuación yk=10
, lo que significa que seleccionará las 10 mejores características. - Aplica la selección de características a los datos utilizando
fit_transform()
, que ajusta el selector a los datos y transforma el conjunto de datos para incluir solo las características seleccionadas. - Finalmente, recupera los nombres de las características seleccionadas y los imprime.
Este paso de selección de características es crucial en el pipeline de machine learning, ya que ayuda a identificar las características más relevantes para predecir la supervivencia en el Titanic. Al reducir el número de características a las más informativas, puede mejorar el rendimiento del modelo, reducir el sobreajuste y aumentar la interpretabilidad.
6.1.7 Manejar Datos Desbalanceados
En muchos conjuntos de datos del mundo real, incluido el conjunto de datos Titanic, el desbalanceo de clases es un problema común. Esto ocurre cuando una clase (en nuestro caso, sobrevivientes o no sobrevivientes) supera significativamente a la otra. Este desbalance puede llevar a modelos sesgados que rinden mal en la clase minoritaria.
Para abordar este problema, utilizaremos una técnica llamada Técnica de Sobremuestreo de Minorías Sintéticas (SMOTE). SMOTE funciona creando ejemplos sintéticos de la clase minoritaria, equilibrando efectivamente el conjunto de datos. Este enfoque puede mejorar la capacidad del modelo para predecir ambas clases de manera precisa.
from imblearn.over_sampling import SMOTE
# Check class distribution
print("Original class distribution:", y.value_counts())
# Apply SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_selected, y)
print("Resampled class distribution:", pd.Series(y_resampled).value_counts())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
SMOTE
del móduloimblearn.over_sampling
. - Imprime la distribución de clases original utilizando
y.value_counts()
para mostrar el desbalance en el conjunto de datos. - Crea un objeto SMOTE con
random_state=42
para garantizar la reproducibilidad. - Aplica SMOTE a las características seleccionadas (
X_selected
) y la variable objetivo (y
) utilizando el métodofit_resample()
. Esto crea ejemplos sintéticos de la clase minoritaria para equilibrar el conjunto de datos. - Finalmente, imprime la distribución de clases re-muestreadas para mostrar cómo SMOTE ha equilibrado las clases.
Este paso es crucial para abordar el problema del desbalanceo de clases, que puede conducir a modelos sesgados. Al crear ejemplos sintéticos de la clase minoritaria, SMOTE ayuda a mejorar la capacidad del modelo para predecir ambas clases con precisión.
6.1.8 Construcción y Evaluación de Modelos
En esta fase crucial de nuestro proyecto, construiremos y evaluaremos varios modelos de machine learning utilizando las características que hemos desarrollado. Este paso es esencial para determinar la efectividad de nuestros esfuerzos de ingeniería de características y para identificar el modelo más adecuado para predecir la supervivencia en el Titanic.
Emplearemos múltiples algoritmos, incluidos Regresión Logística, Random Forest y Máquinas de Soporte Vectorial (SVM). Al comparar su rendimiento, obtendremos información sobre qué modelo captura mejor los patrones en nuestro conjunto de datos con características ingenierizadas. Utilizaremos validación cruzada para garantizar una evaluación robusta, y métricas como precisión, matriz de confusión y reporte de clasificación para evaluar de manera integral el rendimiento de cada modelo.
Esta sección demostrará cómo nuestro trabajo de ingeniería de características se traduce en poder predictivo, destacando la importancia de todo el proceso en el desarrollo de soluciones efectivas de machine learning.
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.2, random_state=42)
# Initialize models
models = {
'Logistic Regression': LogisticRegression(),
'Random Forest': RandomForestClassifier(),
'SVM': SVC()
}
# Train and evaluate models
for name, model in models.items():
# Cross-validation
cv_scores = cross_val_score(model, X_train, y_train, cv=5)
print(f"{name} CV Score: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")
# Train the model
model.fit(X_train, y_train)
# Make predictions
y_pred = model.predict(X_test)
# Evaluate the model
print(f"{name} Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"{name} Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print(f"{name} Classification Report:\n", classification_report(y_test, y_pred))
print("\n")
Aquí tienes un desglose de lo que hace el código:
- Importa las bibliotecas y funciones necesarias para el entrenamiento del modelo, la evaluación y la validación cruzada.
- Los datos se dividen en conjuntos de entrenamiento y prueba utilizando
train_test_split
. - Se inicializan tres modelos diferentes: Regresión Logística, Bosque Aleatorio y Máquina de Vectores de Soporte (SVM).
- Para cada modelo, el código realiza los siguientes pasos:
- Realiza la validación cruzada utilizando
cross_val_score
para evaluar el rendimiento del modelo en diferentes subconjuntos de los datos de entrenamiento. - Entrena el modelo con el conjunto completo de entrenamiento.
- Realiza predicciones sobre el conjunto de prueba.
- Evalúa el rendimiento del modelo utilizando varias métricas:
- Puntuación de precisión
- Matriz de confusión
- Informe de clasificación (que incluye precisión, recall y puntuación F1)
- Realiza la validación cruzada utilizando
Esta evaluación exhaustiva permite comparar el rendimiento de diferentes modelos en las características generadas, ayudando a identificar qué modelo capta mejor los patrones en el conjunto de datos. El uso de la validación cruzada garantiza una evaluación robusta al probar los modelos en diferentes subconjuntos de los datos.
6.1.9 Ajuste de Hiperparámetros
El ajuste de hiperparámetros es un paso crucial en la optimización de modelos de machine learning. Implica encontrar la mejor combinación de hiperparámetros que ofrezca el mejor rendimiento del modelo. En esta sección, utilizaremos GridSearchCV
para buscar sistemáticamente un conjunto predefinido de hiperparámetros para nuestro modelo de Bosque Aleatorio.
Los hiperparámetros son parámetros que no se aprenden de los datos, sino que se establecen antes del entrenamiento. Para un Bosque Aleatorio, estos pueden incluir el número de árboles (n_estimators
), la profundidad máxima de los árboles (max_depth
) y el número mínimo de muestras requeridas para dividir un nodo interno (min_samples_split
).
Al ajustar estos hiperparámetros, podemos mejorar potencialmente el rendimiento y la capacidad de generalización de nuestro modelo. Este proceso nos ayuda a encontrar el equilibrio óptimo entre la complejidad del modelo y su rendimiento, reduciendo el riesgo de sobreajuste o subajuste.
from sklearn.model_selection import GridSearchCV
# Example for Random Forest
param_grid = {
'n_estimators': [100, 200, 300],
'max_depth': [5, 10, None],
'min_samples_split': [2, 5, 10]
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5)
grid_search.fit(X_train, y_train)
print("Best parameters:", grid_search.best_params_)
print("Best cross-validation score:", grid_search.best_score_)
# Evaluate the best model
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
print("Best Model Accuracy:", accuracy_score(y_test, y_pred))
Este es el desglose de lo que hace el código:
- Importa
GridSearchCV
de scikit-learn, que se utiliza para buscar los mejores parámetros para un modelo. - Se define una cuadrícula de parámetros (
param_grid
) con diferentes valores paran_estimators
,max_depth
ymin_samples_split
. Estos son los hiperparámetros que queremos optimizar. - Se inicializa un
RandomForestClassifier
con un estado aleatorio fijo para garantizar la reproducibilidad. GridSearchCV
se configura con el modelo de Bosque Aleatorio, la cuadrícula de parámetros y una validación cruzada de 5 pliegues.- Se realiza la búsqueda en la cuadrícula usando
fit()
sobre los datos de entrenamiento. - Se imprimen los mejores parámetros y la mejor puntuación de validación cruzada.
- Finalmente, se utiliza el mejor modelo (con los parámetros optimizados) para hacer predicciones sobre el conjunto de prueba y se imprime su precisión.
Este proceso ayuda a encontrar la combinación óptima de hiperparámetros que ofrece el mejor rendimiento del modelo, lo que potencialmente mejora la precisión y la capacidad de generalización del modelo.
6.1.10 Análisis de Importancia de Características
El análisis de importancia de características es un paso crucial para entender qué características contribuyen más significativamente a las predicciones de nuestro modelo. Este análisis nos ayuda a identificar los factores más influyentes en la determinación de la supervivencia de los pasajeros del Titanic, proporcionando valiosas ideas sobre el conjunto de datos y el proceso de toma de decisiones de nuestro modelo.
Al examinar la importancia de las características, podemos:
- Obtener una comprensión más profunda de los factores que más afectaron las tasas de supervivencia.
- Validar nuestros esfuerzos de ingeniería de características al ver qué características generadas son más impactantes.
- Potencialmente simplificar nuestro modelo al enfocarnos en las características más importantes.
- Informar los futuros esfuerzos de recopilación de datos al destacar la información más crítica.
En el siguiente código, utilizaremos nuestro mejor modelo de Bosque Aleatorio para calcular y visualizar la importancia de las características, proporcionando una imagen clara de qué características están impulsando nuestras predicciones.
# Using the best Random Forest model
feature_importance = best_model.feature_importances_
feature_names = X.columns[selector.get_support()].tolist()
# Sort features by importance
feature_importance_sorted = sorted(zip(feature_importance, feature_names), reverse=True)
# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar([x[1] for x in feature_importance_sorted], [x[0] for x in feature_importance_sorted])
plt.title('Feature Importance')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
Este es el desglose de lo que hace el código:
- Extrae las puntuaciones de importancia de las características del mejor modelo de Bosque Aleatorio utilizando
best_model.feature_importances_
. - Recupera los nombres de las características seleccionadas usando
X.columns[selector.get_support()].tolist()
. - Se combinan y ordenan las importancias de las características junto con sus nombres en orden descendente de importancia.
- Se crea un gráfico de barras para visualizar las importancias de las características:
- El gráfico se establece con un tamaño de 10x6 pulgadas.
- Los nombres de las características se colocan en el eje x y sus puntuaciones de importancia en el eje y.
- Al gráfico se le asigna un título, una etiqueta para el eje x y una para el eje y.
- Las etiquetas del eje x se rotan 45 grados para mejorar la legibilidad.
Esta visualización ayuda a identificar qué características tienen el mayor impacto en las predicciones del modelo, proporcionando información sobre los factores que más influyen en las predicciones de supervivencia en el conjunto de datos del Titanic.
6.1.11 Análisis de Errores
El análisis de errores es un paso crucial para entender dónde y por qué nuestro modelo está cometiendo errores. Este proceso implica examinar los casos donde las predicciones del modelo difieren de los resultados reales. Al analizar estas clasificaciones erróneas, podemos obtener información valiosa sobre las debilidades de nuestro modelo e identificar posibles áreas de mejora.
En esta sección, examinaremos las características de las muestras mal clasificadas, comparando sus atributos con los de las instancias correctamente clasificadas. Este análisis puede revelar patrones o subgrupos específicos donde el modelo tiene dificultades, potencialmente señalando la necesidad de ingeniería de características adicional, recopilación de datos o ajustes al modelo.
import pandas as pd
# Convert X_test to DataFrame with column names
X_test_df = pd.DataFrame(X_test, columns=selected_features)
# Identify misclassified samples
misclassified = X_test_df[y_test != y_pred].copy()
misclassified['true_label'] = y_test[y_test != y_pred]
misclassified['predicted_label'] = y_pred[y_test != y_pred]
# Display sample misclassified instances
print("Sample of misclassified instances:")
print(misclassified.head())
# Analyze misclassifications
print("\nMisclassification analysis:")
for feature in selected_features:
print(f"\nFeature: {feature}")
print(misclassified.groupby(['true_label', 'predicted_label'])[feature].mean())
Aquí se explica lo que hace el código:
- Identifica las muestras mal clasificadas comparando las etiquetas reales (
y_test
) con las etiquetas predichas (y_pred
). - Crea un nuevo DataFrame llamado
misclassified
, que contiene solo las instancias clasificadas incorrectamente del conjunto de prueba. - Agrega dos nuevas columnas a este DataFrame:
'true_label'
: la etiqueta real dey_test
'predicted_label'
: la etiqueta predicha por el modelo (y_pred
)
- Imprime una muestra de estas instancias mal clasificadas usando la función
head()
. - Luego, realiza un análisis detallado de las clasificaciones erróneas:
- Itera a través de cada característica en el DataFrame
misclassified
. - Para cada característica, calcula e imprime el valor medio agrupado por
true_label
ypredicted_label
. - Esto ayuda a comprender los patrones en los errores del modelo.
- Itera a través de cada característica en el DataFrame
¿Por qué es esto útil?
- Nos permite identificar características específicas donde ocurre la clasificación errónea.
- Ayuda a identificar sesgos potenciales en el modelo.
- Puede guiar las mejoras en la ingeniería de características o el ajuste de hiperparámetros para mejorar el rendimiento del modelo.
6.1.12 Conclusión
En este proyecto, aplicamos diversas técnicas de ingeniería de características al conjunto de datos del Titanic y construimos múltiples modelos predictivos. Ampliamos el proyecto original al incluir visualización de datos, selección de características, manejo de datos desbalanceados, prueba de múltiples modelos, implementación de validación cruzada, ajuste de hiperparámetros, análisis de importancia de características y análisis de errores. Estos pasos adicionales proporcionan una comprensión más completa del conjunto de datos y del impacto de la ingeniería de características en el rendimiento del modelo.
Los resultados demuestran la importancia de la ingeniería de características para mejorar la precisión y la interpretabilidad del modelo. Al seleccionar, transformar y crear características cuidadosamente, pudimos construir modelos predictivos más robustos. El análisis de la importancia de las características y de errores proporciona información sobre qué factores son más cruciales para predecir la supervivencia y dónde el modelo podría estar fallando.
Este proyecto sirve como un ejemplo completo del proceso de ingeniería de características y su importancia en el pipeline de machine learning. Demuestra cómo se pueden combinar diversas técnicas para extraer información valiosa de datos crudos y mejorar el rendimiento del modelo.
6.1 Proyecto 1: Ingeniería de Características para Análisis Predictivo
Este proyecto se enfocará en aplicar técnicas de ingeniería de características a un conjunto de datos para mejorar el rendimiento de un modelo predictivo de machine learning. La ingeniería de características es crucial para hacer que los datos en bruto sean utilizables por los algoritmos de machine learning, al transformarlos en características significativas que mejoren el rendimiento del modelo.
Descripción General del Proyecto
En este proyecto, haremos lo siguiente:
- Explorar y preprocesar el conjunto de datos.
- Aplicar varias técnicas de ingeniería de características como el manejo de valores faltantes, la codificación de variables categóricas, la escalación de características y la creación de nuevas características.
- Construir un modelo predictivo utilizando los datos transformados para demostrar el impacto de la ingeniería de características en el rendimiento del modelo.
- Evaluar el rendimiento del modelo antes y después de la ingeniería de características.
Usaremos el conjunto de datos Titanic para este proyecto, ya que es adecuado para demostrar varias técnicas de ingeniería de características. La tarea es predecir si un pasajero sobrevivió al desastre del Titanic en función de características como la edad, el género, la clase del boleto y la tarifa.
6.1.1 Cargar y Explorar el Conjunto de Datos
Comenzaremos cargando el conjunto de datos Titanic y realizando una exploración inicial exhaustiva para obtener una comprensión profunda de su estructura y características. Este paso crucial implica examinar las dimensiones del conjunto de datos, los tipos de datos y las propiedades estadísticas básicas. También investigaremos la presencia de valores faltantes y visualizaremos las relaciones clave entre variables para sentar una base sólida para nuestros esfuerzos posteriores de ingeniería de características.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Load the Titanic dataset
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
titanic_df = pd.read_csv(url)
# Display the first few rows and basic information
print(titanic_df.head())
print(titanic_df.info())
print(titanic_df.describe())
# Visualize missing data
plt.figure(figsize=(10, 6))
sns.heatmap(titanic_df.isnull(), cbar=False, cmap='viridis')
plt.title("Missing Values Heatmap")
plt.show()
# Data Visualization
plt.figure(figsize=(12, 5))
plt.subplot(121)
sns.histplot(titanic_df['Age'].dropna(), kde=True)
plt.title('Age Distribution')
plt.subplot(122)
sns.boxplot(x='Pclass', y='Fare', data=titanic_df)
plt.title('Fare Distribution by Passenger Class')
plt.tight_layout()
plt.show()
# Correlation matrix
corr_matrix = titanic_df.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Matrix')
plt.show()
Aquí tienes un desglose de lo que hace este código:
- Importa las librerías necesarias:
pandas
para la manipulación de datos.matplotlib
yseaborn
para la visualización.
- Carga el conjunto de datos Titanic desde una URL utilizando
pandas
. - Muestra información básica sobre el conjunto de datos:
- Las primeras filas del conjunto de datos (
head()
). - Información del conjunto de datos (
info()
). - Resumen estadístico (
describe()
).
- Las primeras filas del conjunto de datos (
- Crea visualizaciones:
- Un heatmap para mostrar los valores faltantes en el conjunto de datos.
- Un histograma de la distribución de la edad.
- Un boxplot que muestra la distribución de la tarifa según la clase de pasajero.
- Un heatmap de la matriz de correlación para mostrar las relaciones entre las características numéricas.
Este código es parte del paso de exploración y preprocesamiento de datos, que es crucial para comprender el conjunto de datos antes de aplicar técnicas de ingeniería de características. Ayuda a identificar datos faltantes, visualizar distribuciones y entender las relaciones entre variables, estableciendo las bases para el análisis y la construcción del modelo.
6.1.2 Manejar Datos Faltantes
El conjunto de datos Titanic presenta varias características con valores faltantes, notablemente en Age (Edad) y Cabin (Camarote). Abordar estos valores faltantes es un paso crucial en nuestro proceso de ingeniería de características.
Para la característica Age, utilizaremos técnicas de imputación para llenar los vacíos con valores estadísticamente apropiados, como la mediana de la edad o predicciones basadas en otras características correlacionadas. En el caso de la característica Cabin, dado su alto porcentaje de entradas faltantes, evaluaremos cuidadosamente si es conveniente intentar la imputación o si es mejor excluirla de nuestro análisis.
Esta decisión se basará en el valor informativo potencial de la característica frente al riesgo de introducir sesgos a través de la imputación. Al manejar sistemáticamente estos valores faltantes, nuestro objetivo es maximizar la cantidad de información utilizable en el conjunto de datos, manteniendo al mismo tiempo la integridad de nuestros análisis posteriores.
# Fill missing values in the 'Age' column with the median age
titanic_df['Age'].fillna(titanic_df['Age'].median(), inplace=True)
# Fill missing values in the 'Embarked' column with the most frequent value
titanic_df['Embarked'].fillna(titanic_df['Embarked'].mode()[0], inplace=True)
# Drop the 'Cabin' column due to too many missing values
titanic_df.drop(columns=['Cabin'], inplace=True)
print(titanic_df.isnull().sum())
Aquí tienes un desglose de lo que hace este código:
- Rellena los valores faltantes en la columna 'Age' con la mediana del conjunto de datos. Este es un enfoque común para manejar datos numéricos faltantes.
- Para la columna 'Embarked', rellena los valores faltantes con el valor más frecuente (moda) en esa columna, un método común para datos categóricos con valores faltantes.
- Elimina la columna 'Cabin' por completo debido a la gran cantidad de valores faltantes. Esta decisión probablemente se tomó porque el alto porcentaje de datos faltantes en esta columna podría introducir más sesgo si se imputan.
- Finalmente, imprime la suma de los valores nulos en cada columna después de estas operaciones, lo que permite verificar que el manejo de valores faltantes se ha realizado con éxito.
Este enfoque para manejar los datos faltantes forma parte del proceso de ingeniería de características, con el objetivo de preparar el conjunto de datos para los algoritmos de machine learning, preservando la mayor cantidad de información útil posible.
6.1.3 Codificación de Características
El conjunto de datos Titanic incluye varias variables categóricas, como Sex y Embarked, que requieren una transformación a un formato numérico para que sean compatibles con los algoritmos de machine learning. Este proceso de conversión es crucial, ya que la mayoría de los modelos de machine learning están diseñados para trabajar con entradas numéricas. Para lograr esta transformación, emplearemos diversas técnicas de codificación, con un enfoque particular en la codificación one-hot.
La codificación one-hot es un método que crea columnas binarias para cada categoría dentro de una variable categórica. Por ejemplo, la variable 'Sex' se dividiría en dos columnas: 'Sex_male' y 'Sex_female', donde cada pasajero tendría un '1' en una columna y un '0' en la otra. Este enfoque nos permite representar datos categóricos de forma numérica sin implicar una relación ordinal entre las categorías.
Además, podríamos considerar otras técnicas de codificación, como label encoding para variables ordinales o target encoding para variables categóricas con alta cardinalidad, dependiendo de las características específicas de cada variable. La elección del método de codificación puede impactar significativamente en el rendimiento del modelo, lo que convierte este paso en un aspecto crítico del proceso de ingeniería de características.
# One-hot encode the 'Sex' and 'Embarked' columns
titanic_df = pd.get_dummies(titanic_df, columns=['Sex', 'Embarked'], drop_first=True)
print(titanic_df.head())
Aquí tienes un desglose de lo que hace este código:
- Utiliza la función
pd.get_dummies()
para codificar con one-hot las columnas 'Sex' y 'Embarked'. - El parámetro
columns=['Sex', 'Embarked']
especifica qué columnas se van a codificar. - El argumento
drop_first=True
se utiliza para evitar multicolinealidad, eliminando una de las columnas creadas para cada variable categórica original. - El resultado se guarda de nuevo en el DataFrame
titanic_df
, reemplazando efectivamente las columnas originales 'Sex' y 'Embarked' con sus versiones codificadas con one-hot. - Finalmente, imprime las primeras filas del DataFrame actualizado para mostrar los resultados de la codificación.
Este paso es crucial en el proceso de ingeniería de características ya que transforma los datos categóricos en un formato que puede ser fácilmente utilizado por los algoritmos de machine learning, que normalmente requieren entradas numéricas.
6.1.4 Escalado de Características
El escalado de características es un paso crucial en nuestro proceso de ingeniería de características, que aborda las diferencias significativas de escala entre ciertas características como Fare (Tarifa) y Age (Edad). Estas disparidades pueden tener efectos perjudiciales en el rendimiento del modelo, especialmente para algoritmos que son sensibles a las escalas de las características, como la regresión logística o K-nearest neighbors. Para mitigar estos problemas y asegurar un rendimiento óptimo del modelo, emplearemos el escalado estándar como nuestra técnica de normalización.
El escalado estándar, también conocido como normalización por z-score, transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esta transformación preserva la forma de la distribución original mientras lleva todas las características a una escala comparable. Al aplicar el escalado estándar a nuestro conjunto de datos, creamos un campo de juego uniforme para todas las características, lo que permite que los algoritmos las traten por igual y evita que las características con magnitudes mayores dominen el proceso de aprendizaje.
Los beneficios de este enfoque de escalado van más allá de mejorar el rendimiento del modelo. También mejora la interpretabilidad de los coeficientes del modelo, facilita una convergencia más rápida durante el proceso de entrenamiento y ayuda a comparar la importancia relativa de diferentes características. A medida que avanzamos en nuestro análisis, este paso de escalado será fundamental para extraer insights significativos y construir modelos predictivos robustos.
from sklearn.preprocessing import StandardScaler
scaling_features = ['Age', 'Fare']
scaler = StandardScaler()
titanic_df[scaling_features] = scaler.fit_transform(titanic_df[scaling_features])
print(titanic_df[scaling_features].head())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
StandardScaler
desklearn.preprocessing
. - Define una lista llamada
scaling_features
que contiene las columnas 'Age' y 'Fare', que son las características a escalar. - Crea una instancia de
StandardScaler
llamadascaler
. - Aplica el método
fit_transform
del escalador a las características especificadas en el DataFrametitanic_df
. Este paso ajusta el escalador a los datos y los transforma. - Finalmente, imprime el head de las características escaladas para mostrar el resultado.
El StandardScaler
transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esto es importante para muchos algoritmos de machine learning que son sensibles a la escala de las características de entrada, ya que ayuda a evitar que las características con magnitudes mayores dominen el proceso de entrenamiento del modelo.
6.1.5 Creación de Características
La creación de nuevas características es una técnica poderosa que puede mejorar significativamente la capacidad de un modelo para capturar y aprovechar relaciones complejas dentro de los datos. Este proceso, conocido como ingeniería de características, implica derivar nuevas variables a partir de las existentes para proporcionar información adicional o representar los datos de una manera más significativa. En este paso crucial de nuestro análisis, nos centraremos en la creación de una nueva característica llamada FamilySize.
La característica FamilySize se creará combinando dos variables existentes: SibSp (número de hermanos y cónyuges a bordo) y Parch (número de padres e hijos a bordo). Al agregar estas características relacionadas, buscamos crear una representación más completa del tamaño de la unidad familiar de un pasajero. Esta nueva característica tiene el potencial de capturar dinámicas sociales importantes y patrones de supervivencia que podrían no ser evidentes al considerar por separado a los hermanos/cónyuges y padres/hijos.
La lógica detrás de esta decisión de ingeniería de características está basada en la hipótesis de que el tamaño de la familia pudo haber desempeñado un papel significativo en los resultados de supervivencia durante el desastre del Titanic. Por ejemplo, las familias más grandes podrían haber enfrentado diferentes desafíos o haber recibido un trato distinto en comparación con los individuos que viajaban solos o en grupos más pequeños. Al crear la característica FamilySize, proporcionamos a nuestro modelo una comprensión más matizada del contexto familiar de cada pasajero, lo que potencialmente mejorará sus capacidades predictivas.
# Create a new feature 'FamilySize'
titanic_df['FamilySize'] = titanic_df['SibSp'] + titanic_df['Parch'] + 1
# Create a new feature 'IsAlone'
titanic_df['IsAlone'] = (titanic_df['FamilySize'] == 1).astype(int)
print(titanic_df[['SibSp', 'Parch', 'FamilySize', 'IsAlone']].head())
Este fragmento de código demuestra la creación de dos nuevas características en el conjunto de datos Titanic mediante la ingeniería de características:
- FamilySize: Esta característica se crea sumando los valores de 'SibSp' (número de hermanos y cónyuges a bordo), 'Parch' (número de padres e hijos a bordo) y añadiendo 1 (para incluir al propio pasajero). Esto proporciona una medida completa del tamaño total de la familia para cada pasajero.
- IsAlone: Esta es una característica binaria que indica si un pasajero viaja solo o no. Se deriva de la característica 'FamilySize', donde un valor de 1 indica que el pasajero está solo, y un valor de 0 indica que está acompañado por familiares.
El código luego imprime las primeras filas del DataFrame, mostrando estas nuevas características junto con las columnas originales 'SibSp' y 'Parch' para compararlas.
Estas nuevas características tienen como objetivo capturar información más matizada sobre el contexto familiar de cada pasajero, lo que podría mejorar el poder predictivo del modelo de machine learning para la predicción de supervivencia.
6.1.6 Selección de Características
La selección de características es un paso crucial en el pipeline de machine learning que consiste en identificar y seleccionar las características más relevantes del conjunto de datos. Este proceso ayuda a reducir la dimensionalidad, mejorar el rendimiento del modelo y aumentar la interpretabilidad. En nuestro proyecto de predicción de supervivencia en el Titanic, utilizaremos técnicas de selección de características para identificar las más informativas para nuestro modelo predictivo.
Existen varios métodos para la selección de características, incluidos métodos de filtro (por ejemplo, selección basada en correlación), métodos wrapper (por ejemplo, eliminación recursiva de características) y métodos integrados (por ejemplo, regularización L1). Para este proyecto, usaremos un método de filtro llamado SelectKBest, que selecciona las características en función de su relación estadística con la variable objetivo.
Al aplicar la selección de características, nuestro objetivo es:
- Reducir el sobreajuste eliminando características irrelevantes o redundantes.
- Mejorar la precisión del modelo al enfocarnos en las características más predictivas.
- Disminuir el tiempo de entrenamiento al reducir la dimensionalidad del conjunto de datos.
- Mejorar la interpretabilidad del modelo al identificar las características más importantes.
Vamos a proceder con la implementación del método SelectKBest para elegir las mejores características para nuestro modelo de predicción de supervivencia en el Titanic.
from sklearn.feature_selection import SelectKBest, f_classif
X = titanic_df.drop(columns=['Survived', 'PassengerId', 'Name', 'Ticket'])
y = titanic_df['Survived']
# Select top 10 features
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X, y)
# Get selected feature names
selected_features = X.columns[selector.get_support()].tolist()
print("Selected features:", selected_features)
Aquí tienes un desglose de lo que hace este código:
- Importa las funciones necesarias del módulo
feature_selection
de scikit-learn. - Prepara la matriz de características
X
eliminando columnas que no son necesarias para la predicción ('Survived', 'PassengerId', 'Name', 'Ticket') del DataFrametitanic_df
. - Define la variable objetivo
y
como la columna 'Survived'. - Crea un objeto
SelectKBest
conf_classif
como función de puntuación yk=10
, lo que significa que seleccionará las 10 mejores características. - Aplica la selección de características a los datos utilizando
fit_transform()
, que ajusta el selector a los datos y transforma el conjunto de datos para incluir solo las características seleccionadas. - Finalmente, recupera los nombres de las características seleccionadas y los imprime.
Este paso de selección de características es crucial en el pipeline de machine learning, ya que ayuda a identificar las características más relevantes para predecir la supervivencia en el Titanic. Al reducir el número de características a las más informativas, puede mejorar el rendimiento del modelo, reducir el sobreajuste y aumentar la interpretabilidad.
6.1.7 Manejar Datos Desbalanceados
En muchos conjuntos de datos del mundo real, incluido el conjunto de datos Titanic, el desbalanceo de clases es un problema común. Esto ocurre cuando una clase (en nuestro caso, sobrevivientes o no sobrevivientes) supera significativamente a la otra. Este desbalance puede llevar a modelos sesgados que rinden mal en la clase minoritaria.
Para abordar este problema, utilizaremos una técnica llamada Técnica de Sobremuestreo de Minorías Sintéticas (SMOTE). SMOTE funciona creando ejemplos sintéticos de la clase minoritaria, equilibrando efectivamente el conjunto de datos. Este enfoque puede mejorar la capacidad del modelo para predecir ambas clases de manera precisa.
from imblearn.over_sampling import SMOTE
# Check class distribution
print("Original class distribution:", y.value_counts())
# Apply SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_selected, y)
print("Resampled class distribution:", pd.Series(y_resampled).value_counts())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
SMOTE
del móduloimblearn.over_sampling
. - Imprime la distribución de clases original utilizando
y.value_counts()
para mostrar el desbalance en el conjunto de datos. - Crea un objeto SMOTE con
random_state=42
para garantizar la reproducibilidad. - Aplica SMOTE a las características seleccionadas (
X_selected
) y la variable objetivo (y
) utilizando el métodofit_resample()
. Esto crea ejemplos sintéticos de la clase minoritaria para equilibrar el conjunto de datos. - Finalmente, imprime la distribución de clases re-muestreadas para mostrar cómo SMOTE ha equilibrado las clases.
Este paso es crucial para abordar el problema del desbalanceo de clases, que puede conducir a modelos sesgados. Al crear ejemplos sintéticos de la clase minoritaria, SMOTE ayuda a mejorar la capacidad del modelo para predecir ambas clases con precisión.
6.1.8 Construcción y Evaluación de Modelos
En esta fase crucial de nuestro proyecto, construiremos y evaluaremos varios modelos de machine learning utilizando las características que hemos desarrollado. Este paso es esencial para determinar la efectividad de nuestros esfuerzos de ingeniería de características y para identificar el modelo más adecuado para predecir la supervivencia en el Titanic.
Emplearemos múltiples algoritmos, incluidos Regresión Logística, Random Forest y Máquinas de Soporte Vectorial (SVM). Al comparar su rendimiento, obtendremos información sobre qué modelo captura mejor los patrones en nuestro conjunto de datos con características ingenierizadas. Utilizaremos validación cruzada para garantizar una evaluación robusta, y métricas como precisión, matriz de confusión y reporte de clasificación para evaluar de manera integral el rendimiento de cada modelo.
Esta sección demostrará cómo nuestro trabajo de ingeniería de características se traduce en poder predictivo, destacando la importancia de todo el proceso en el desarrollo de soluciones efectivas de machine learning.
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.2, random_state=42)
# Initialize models
models = {
'Logistic Regression': LogisticRegression(),
'Random Forest': RandomForestClassifier(),
'SVM': SVC()
}
# Train and evaluate models
for name, model in models.items():
# Cross-validation
cv_scores = cross_val_score(model, X_train, y_train, cv=5)
print(f"{name} CV Score: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")
# Train the model
model.fit(X_train, y_train)
# Make predictions
y_pred = model.predict(X_test)
# Evaluate the model
print(f"{name} Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"{name} Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print(f"{name} Classification Report:\n", classification_report(y_test, y_pred))
print("\n")
Aquí tienes un desglose de lo que hace el código:
- Importa las bibliotecas y funciones necesarias para el entrenamiento del modelo, la evaluación y la validación cruzada.
- Los datos se dividen en conjuntos de entrenamiento y prueba utilizando
train_test_split
. - Se inicializan tres modelos diferentes: Regresión Logística, Bosque Aleatorio y Máquina de Vectores de Soporte (SVM).
- Para cada modelo, el código realiza los siguientes pasos:
- Realiza la validación cruzada utilizando
cross_val_score
para evaluar el rendimiento del modelo en diferentes subconjuntos de los datos de entrenamiento. - Entrena el modelo con el conjunto completo de entrenamiento.
- Realiza predicciones sobre el conjunto de prueba.
- Evalúa el rendimiento del modelo utilizando varias métricas:
- Puntuación de precisión
- Matriz de confusión
- Informe de clasificación (que incluye precisión, recall y puntuación F1)
- Realiza la validación cruzada utilizando
Esta evaluación exhaustiva permite comparar el rendimiento de diferentes modelos en las características generadas, ayudando a identificar qué modelo capta mejor los patrones en el conjunto de datos. El uso de la validación cruzada garantiza una evaluación robusta al probar los modelos en diferentes subconjuntos de los datos.
6.1.9 Ajuste de Hiperparámetros
El ajuste de hiperparámetros es un paso crucial en la optimización de modelos de machine learning. Implica encontrar la mejor combinación de hiperparámetros que ofrezca el mejor rendimiento del modelo. En esta sección, utilizaremos GridSearchCV
para buscar sistemáticamente un conjunto predefinido de hiperparámetros para nuestro modelo de Bosque Aleatorio.
Los hiperparámetros son parámetros que no se aprenden de los datos, sino que se establecen antes del entrenamiento. Para un Bosque Aleatorio, estos pueden incluir el número de árboles (n_estimators
), la profundidad máxima de los árboles (max_depth
) y el número mínimo de muestras requeridas para dividir un nodo interno (min_samples_split
).
Al ajustar estos hiperparámetros, podemos mejorar potencialmente el rendimiento y la capacidad de generalización de nuestro modelo. Este proceso nos ayuda a encontrar el equilibrio óptimo entre la complejidad del modelo y su rendimiento, reduciendo el riesgo de sobreajuste o subajuste.
from sklearn.model_selection import GridSearchCV
# Example for Random Forest
param_grid = {
'n_estimators': [100, 200, 300],
'max_depth': [5, 10, None],
'min_samples_split': [2, 5, 10]
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5)
grid_search.fit(X_train, y_train)
print("Best parameters:", grid_search.best_params_)
print("Best cross-validation score:", grid_search.best_score_)
# Evaluate the best model
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
print("Best Model Accuracy:", accuracy_score(y_test, y_pred))
Este es el desglose de lo que hace el código:
- Importa
GridSearchCV
de scikit-learn, que se utiliza para buscar los mejores parámetros para un modelo. - Se define una cuadrícula de parámetros (
param_grid
) con diferentes valores paran_estimators
,max_depth
ymin_samples_split
. Estos son los hiperparámetros que queremos optimizar. - Se inicializa un
RandomForestClassifier
con un estado aleatorio fijo para garantizar la reproducibilidad. GridSearchCV
se configura con el modelo de Bosque Aleatorio, la cuadrícula de parámetros y una validación cruzada de 5 pliegues.- Se realiza la búsqueda en la cuadrícula usando
fit()
sobre los datos de entrenamiento. - Se imprimen los mejores parámetros y la mejor puntuación de validación cruzada.
- Finalmente, se utiliza el mejor modelo (con los parámetros optimizados) para hacer predicciones sobre el conjunto de prueba y se imprime su precisión.
Este proceso ayuda a encontrar la combinación óptima de hiperparámetros que ofrece el mejor rendimiento del modelo, lo que potencialmente mejora la precisión y la capacidad de generalización del modelo.
6.1.10 Análisis de Importancia de Características
El análisis de importancia de características es un paso crucial para entender qué características contribuyen más significativamente a las predicciones de nuestro modelo. Este análisis nos ayuda a identificar los factores más influyentes en la determinación de la supervivencia de los pasajeros del Titanic, proporcionando valiosas ideas sobre el conjunto de datos y el proceso de toma de decisiones de nuestro modelo.
Al examinar la importancia de las características, podemos:
- Obtener una comprensión más profunda de los factores que más afectaron las tasas de supervivencia.
- Validar nuestros esfuerzos de ingeniería de características al ver qué características generadas son más impactantes.
- Potencialmente simplificar nuestro modelo al enfocarnos en las características más importantes.
- Informar los futuros esfuerzos de recopilación de datos al destacar la información más crítica.
En el siguiente código, utilizaremos nuestro mejor modelo de Bosque Aleatorio para calcular y visualizar la importancia de las características, proporcionando una imagen clara de qué características están impulsando nuestras predicciones.
# Using the best Random Forest model
feature_importance = best_model.feature_importances_
feature_names = X.columns[selector.get_support()].tolist()
# Sort features by importance
feature_importance_sorted = sorted(zip(feature_importance, feature_names), reverse=True)
# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar([x[1] for x in feature_importance_sorted], [x[0] for x in feature_importance_sorted])
plt.title('Feature Importance')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
Este es el desglose de lo que hace el código:
- Extrae las puntuaciones de importancia de las características del mejor modelo de Bosque Aleatorio utilizando
best_model.feature_importances_
. - Recupera los nombres de las características seleccionadas usando
X.columns[selector.get_support()].tolist()
. - Se combinan y ordenan las importancias de las características junto con sus nombres en orden descendente de importancia.
- Se crea un gráfico de barras para visualizar las importancias de las características:
- El gráfico se establece con un tamaño de 10x6 pulgadas.
- Los nombres de las características se colocan en el eje x y sus puntuaciones de importancia en el eje y.
- Al gráfico se le asigna un título, una etiqueta para el eje x y una para el eje y.
- Las etiquetas del eje x se rotan 45 grados para mejorar la legibilidad.
Esta visualización ayuda a identificar qué características tienen el mayor impacto en las predicciones del modelo, proporcionando información sobre los factores que más influyen en las predicciones de supervivencia en el conjunto de datos del Titanic.
6.1.11 Análisis de Errores
El análisis de errores es un paso crucial para entender dónde y por qué nuestro modelo está cometiendo errores. Este proceso implica examinar los casos donde las predicciones del modelo difieren de los resultados reales. Al analizar estas clasificaciones erróneas, podemos obtener información valiosa sobre las debilidades de nuestro modelo e identificar posibles áreas de mejora.
En esta sección, examinaremos las características de las muestras mal clasificadas, comparando sus atributos con los de las instancias correctamente clasificadas. Este análisis puede revelar patrones o subgrupos específicos donde el modelo tiene dificultades, potencialmente señalando la necesidad de ingeniería de características adicional, recopilación de datos o ajustes al modelo.
import pandas as pd
# Convert X_test to DataFrame with column names
X_test_df = pd.DataFrame(X_test, columns=selected_features)
# Identify misclassified samples
misclassified = X_test_df[y_test != y_pred].copy()
misclassified['true_label'] = y_test[y_test != y_pred]
misclassified['predicted_label'] = y_pred[y_test != y_pred]
# Display sample misclassified instances
print("Sample of misclassified instances:")
print(misclassified.head())
# Analyze misclassifications
print("\nMisclassification analysis:")
for feature in selected_features:
print(f"\nFeature: {feature}")
print(misclassified.groupby(['true_label', 'predicted_label'])[feature].mean())
Aquí se explica lo que hace el código:
- Identifica las muestras mal clasificadas comparando las etiquetas reales (
y_test
) con las etiquetas predichas (y_pred
). - Crea un nuevo DataFrame llamado
misclassified
, que contiene solo las instancias clasificadas incorrectamente del conjunto de prueba. - Agrega dos nuevas columnas a este DataFrame:
'true_label'
: la etiqueta real dey_test
'predicted_label'
: la etiqueta predicha por el modelo (y_pred
)
- Imprime una muestra de estas instancias mal clasificadas usando la función
head()
. - Luego, realiza un análisis detallado de las clasificaciones erróneas:
- Itera a través de cada característica en el DataFrame
misclassified
. - Para cada característica, calcula e imprime el valor medio agrupado por
true_label
ypredicted_label
. - Esto ayuda a comprender los patrones en los errores del modelo.
- Itera a través de cada característica en el DataFrame
¿Por qué es esto útil?
- Nos permite identificar características específicas donde ocurre la clasificación errónea.
- Ayuda a identificar sesgos potenciales en el modelo.
- Puede guiar las mejoras en la ingeniería de características o el ajuste de hiperparámetros para mejorar el rendimiento del modelo.
6.1.12 Conclusión
En este proyecto, aplicamos diversas técnicas de ingeniería de características al conjunto de datos del Titanic y construimos múltiples modelos predictivos. Ampliamos el proyecto original al incluir visualización de datos, selección de características, manejo de datos desbalanceados, prueba de múltiples modelos, implementación de validación cruzada, ajuste de hiperparámetros, análisis de importancia de características y análisis de errores. Estos pasos adicionales proporcionan una comprensión más completa del conjunto de datos y del impacto de la ingeniería de características en el rendimiento del modelo.
Los resultados demuestran la importancia de la ingeniería de características para mejorar la precisión y la interpretabilidad del modelo. Al seleccionar, transformar y crear características cuidadosamente, pudimos construir modelos predictivos más robustos. El análisis de la importancia de las características y de errores proporciona información sobre qué factores son más cruciales para predecir la supervivencia y dónde el modelo podría estar fallando.
Este proyecto sirve como un ejemplo completo del proceso de ingeniería de características y su importancia en el pipeline de machine learning. Demuestra cómo se pueden combinar diversas técnicas para extraer información valiosa de datos crudos y mejorar el rendimiento del modelo.
6.1 Proyecto 1: Ingeniería de Características para Análisis Predictivo
Este proyecto se enfocará en aplicar técnicas de ingeniería de características a un conjunto de datos para mejorar el rendimiento de un modelo predictivo de machine learning. La ingeniería de características es crucial para hacer que los datos en bruto sean utilizables por los algoritmos de machine learning, al transformarlos en características significativas que mejoren el rendimiento del modelo.
Descripción General del Proyecto
En este proyecto, haremos lo siguiente:
- Explorar y preprocesar el conjunto de datos.
- Aplicar varias técnicas de ingeniería de características como el manejo de valores faltantes, la codificación de variables categóricas, la escalación de características y la creación de nuevas características.
- Construir un modelo predictivo utilizando los datos transformados para demostrar el impacto de la ingeniería de características en el rendimiento del modelo.
- Evaluar el rendimiento del modelo antes y después de la ingeniería de características.
Usaremos el conjunto de datos Titanic para este proyecto, ya que es adecuado para demostrar varias técnicas de ingeniería de características. La tarea es predecir si un pasajero sobrevivió al desastre del Titanic en función de características como la edad, el género, la clase del boleto y la tarifa.
6.1.1 Cargar y Explorar el Conjunto de Datos
Comenzaremos cargando el conjunto de datos Titanic y realizando una exploración inicial exhaustiva para obtener una comprensión profunda de su estructura y características. Este paso crucial implica examinar las dimensiones del conjunto de datos, los tipos de datos y las propiedades estadísticas básicas. También investigaremos la presencia de valores faltantes y visualizaremos las relaciones clave entre variables para sentar una base sólida para nuestros esfuerzos posteriores de ingeniería de características.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Load the Titanic dataset
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
titanic_df = pd.read_csv(url)
# Display the first few rows and basic information
print(titanic_df.head())
print(titanic_df.info())
print(titanic_df.describe())
# Visualize missing data
plt.figure(figsize=(10, 6))
sns.heatmap(titanic_df.isnull(), cbar=False, cmap='viridis')
plt.title("Missing Values Heatmap")
plt.show()
# Data Visualization
plt.figure(figsize=(12, 5))
plt.subplot(121)
sns.histplot(titanic_df['Age'].dropna(), kde=True)
plt.title('Age Distribution')
plt.subplot(122)
sns.boxplot(x='Pclass', y='Fare', data=titanic_df)
plt.title('Fare Distribution by Passenger Class')
plt.tight_layout()
plt.show()
# Correlation matrix
corr_matrix = titanic_df.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Matrix')
plt.show()
Aquí tienes un desglose de lo que hace este código:
- Importa las librerías necesarias:
pandas
para la manipulación de datos.matplotlib
yseaborn
para la visualización.
- Carga el conjunto de datos Titanic desde una URL utilizando
pandas
. - Muestra información básica sobre el conjunto de datos:
- Las primeras filas del conjunto de datos (
head()
). - Información del conjunto de datos (
info()
). - Resumen estadístico (
describe()
).
- Las primeras filas del conjunto de datos (
- Crea visualizaciones:
- Un heatmap para mostrar los valores faltantes en el conjunto de datos.
- Un histograma de la distribución de la edad.
- Un boxplot que muestra la distribución de la tarifa según la clase de pasajero.
- Un heatmap de la matriz de correlación para mostrar las relaciones entre las características numéricas.
Este código es parte del paso de exploración y preprocesamiento de datos, que es crucial para comprender el conjunto de datos antes de aplicar técnicas de ingeniería de características. Ayuda a identificar datos faltantes, visualizar distribuciones y entender las relaciones entre variables, estableciendo las bases para el análisis y la construcción del modelo.
6.1.2 Manejar Datos Faltantes
El conjunto de datos Titanic presenta varias características con valores faltantes, notablemente en Age (Edad) y Cabin (Camarote). Abordar estos valores faltantes es un paso crucial en nuestro proceso de ingeniería de características.
Para la característica Age, utilizaremos técnicas de imputación para llenar los vacíos con valores estadísticamente apropiados, como la mediana de la edad o predicciones basadas en otras características correlacionadas. En el caso de la característica Cabin, dado su alto porcentaje de entradas faltantes, evaluaremos cuidadosamente si es conveniente intentar la imputación o si es mejor excluirla de nuestro análisis.
Esta decisión se basará en el valor informativo potencial de la característica frente al riesgo de introducir sesgos a través de la imputación. Al manejar sistemáticamente estos valores faltantes, nuestro objetivo es maximizar la cantidad de información utilizable en el conjunto de datos, manteniendo al mismo tiempo la integridad de nuestros análisis posteriores.
# Fill missing values in the 'Age' column with the median age
titanic_df['Age'].fillna(titanic_df['Age'].median(), inplace=True)
# Fill missing values in the 'Embarked' column with the most frequent value
titanic_df['Embarked'].fillna(titanic_df['Embarked'].mode()[0], inplace=True)
# Drop the 'Cabin' column due to too many missing values
titanic_df.drop(columns=['Cabin'], inplace=True)
print(titanic_df.isnull().sum())
Aquí tienes un desglose de lo que hace este código:
- Rellena los valores faltantes en la columna 'Age' con la mediana del conjunto de datos. Este es un enfoque común para manejar datos numéricos faltantes.
- Para la columna 'Embarked', rellena los valores faltantes con el valor más frecuente (moda) en esa columna, un método común para datos categóricos con valores faltantes.
- Elimina la columna 'Cabin' por completo debido a la gran cantidad de valores faltantes. Esta decisión probablemente se tomó porque el alto porcentaje de datos faltantes en esta columna podría introducir más sesgo si se imputan.
- Finalmente, imprime la suma de los valores nulos en cada columna después de estas operaciones, lo que permite verificar que el manejo de valores faltantes se ha realizado con éxito.
Este enfoque para manejar los datos faltantes forma parte del proceso de ingeniería de características, con el objetivo de preparar el conjunto de datos para los algoritmos de machine learning, preservando la mayor cantidad de información útil posible.
6.1.3 Codificación de Características
El conjunto de datos Titanic incluye varias variables categóricas, como Sex y Embarked, que requieren una transformación a un formato numérico para que sean compatibles con los algoritmos de machine learning. Este proceso de conversión es crucial, ya que la mayoría de los modelos de machine learning están diseñados para trabajar con entradas numéricas. Para lograr esta transformación, emplearemos diversas técnicas de codificación, con un enfoque particular en la codificación one-hot.
La codificación one-hot es un método que crea columnas binarias para cada categoría dentro de una variable categórica. Por ejemplo, la variable 'Sex' se dividiría en dos columnas: 'Sex_male' y 'Sex_female', donde cada pasajero tendría un '1' en una columna y un '0' en la otra. Este enfoque nos permite representar datos categóricos de forma numérica sin implicar una relación ordinal entre las categorías.
Además, podríamos considerar otras técnicas de codificación, como label encoding para variables ordinales o target encoding para variables categóricas con alta cardinalidad, dependiendo de las características específicas de cada variable. La elección del método de codificación puede impactar significativamente en el rendimiento del modelo, lo que convierte este paso en un aspecto crítico del proceso de ingeniería de características.
# One-hot encode the 'Sex' and 'Embarked' columns
titanic_df = pd.get_dummies(titanic_df, columns=['Sex', 'Embarked'], drop_first=True)
print(titanic_df.head())
Aquí tienes un desglose de lo que hace este código:
- Utiliza la función
pd.get_dummies()
para codificar con one-hot las columnas 'Sex' y 'Embarked'. - El parámetro
columns=['Sex', 'Embarked']
especifica qué columnas se van a codificar. - El argumento
drop_first=True
se utiliza para evitar multicolinealidad, eliminando una de las columnas creadas para cada variable categórica original. - El resultado se guarda de nuevo en el DataFrame
titanic_df
, reemplazando efectivamente las columnas originales 'Sex' y 'Embarked' con sus versiones codificadas con one-hot. - Finalmente, imprime las primeras filas del DataFrame actualizado para mostrar los resultados de la codificación.
Este paso es crucial en el proceso de ingeniería de características ya que transforma los datos categóricos en un formato que puede ser fácilmente utilizado por los algoritmos de machine learning, que normalmente requieren entradas numéricas.
6.1.4 Escalado de Características
El escalado de características es un paso crucial en nuestro proceso de ingeniería de características, que aborda las diferencias significativas de escala entre ciertas características como Fare (Tarifa) y Age (Edad). Estas disparidades pueden tener efectos perjudiciales en el rendimiento del modelo, especialmente para algoritmos que son sensibles a las escalas de las características, como la regresión logística o K-nearest neighbors. Para mitigar estos problemas y asegurar un rendimiento óptimo del modelo, emplearemos el escalado estándar como nuestra técnica de normalización.
El escalado estándar, también conocido como normalización por z-score, transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esta transformación preserva la forma de la distribución original mientras lleva todas las características a una escala comparable. Al aplicar el escalado estándar a nuestro conjunto de datos, creamos un campo de juego uniforme para todas las características, lo que permite que los algoritmos las traten por igual y evita que las características con magnitudes mayores dominen el proceso de aprendizaje.
Los beneficios de este enfoque de escalado van más allá de mejorar el rendimiento del modelo. También mejora la interpretabilidad de los coeficientes del modelo, facilita una convergencia más rápida durante el proceso de entrenamiento y ayuda a comparar la importancia relativa de diferentes características. A medida que avanzamos en nuestro análisis, este paso de escalado será fundamental para extraer insights significativos y construir modelos predictivos robustos.
from sklearn.preprocessing import StandardScaler
scaling_features = ['Age', 'Fare']
scaler = StandardScaler()
titanic_df[scaling_features] = scaler.fit_transform(titanic_df[scaling_features])
print(titanic_df[scaling_features].head())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
StandardScaler
desklearn.preprocessing
. - Define una lista llamada
scaling_features
que contiene las columnas 'Age' y 'Fare', que son las características a escalar. - Crea una instancia de
StandardScaler
llamadascaler
. - Aplica el método
fit_transform
del escalador a las características especificadas en el DataFrametitanic_df
. Este paso ajusta el escalador a los datos y los transforma. - Finalmente, imprime el head de las características escaladas para mostrar el resultado.
El StandardScaler
transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esto es importante para muchos algoritmos de machine learning que son sensibles a la escala de las características de entrada, ya que ayuda a evitar que las características con magnitudes mayores dominen el proceso de entrenamiento del modelo.
6.1.5 Creación de Características
La creación de nuevas características es una técnica poderosa que puede mejorar significativamente la capacidad de un modelo para capturar y aprovechar relaciones complejas dentro de los datos. Este proceso, conocido como ingeniería de características, implica derivar nuevas variables a partir de las existentes para proporcionar información adicional o representar los datos de una manera más significativa. En este paso crucial de nuestro análisis, nos centraremos en la creación de una nueva característica llamada FamilySize.
La característica FamilySize se creará combinando dos variables existentes: SibSp (número de hermanos y cónyuges a bordo) y Parch (número de padres e hijos a bordo). Al agregar estas características relacionadas, buscamos crear una representación más completa del tamaño de la unidad familiar de un pasajero. Esta nueva característica tiene el potencial de capturar dinámicas sociales importantes y patrones de supervivencia que podrían no ser evidentes al considerar por separado a los hermanos/cónyuges y padres/hijos.
La lógica detrás de esta decisión de ingeniería de características está basada en la hipótesis de que el tamaño de la familia pudo haber desempeñado un papel significativo en los resultados de supervivencia durante el desastre del Titanic. Por ejemplo, las familias más grandes podrían haber enfrentado diferentes desafíos o haber recibido un trato distinto en comparación con los individuos que viajaban solos o en grupos más pequeños. Al crear la característica FamilySize, proporcionamos a nuestro modelo una comprensión más matizada del contexto familiar de cada pasajero, lo que potencialmente mejorará sus capacidades predictivas.
# Create a new feature 'FamilySize'
titanic_df['FamilySize'] = titanic_df['SibSp'] + titanic_df['Parch'] + 1
# Create a new feature 'IsAlone'
titanic_df['IsAlone'] = (titanic_df['FamilySize'] == 1).astype(int)
print(titanic_df[['SibSp', 'Parch', 'FamilySize', 'IsAlone']].head())
Este fragmento de código demuestra la creación de dos nuevas características en el conjunto de datos Titanic mediante la ingeniería de características:
- FamilySize: Esta característica se crea sumando los valores de 'SibSp' (número de hermanos y cónyuges a bordo), 'Parch' (número de padres e hijos a bordo) y añadiendo 1 (para incluir al propio pasajero). Esto proporciona una medida completa del tamaño total de la familia para cada pasajero.
- IsAlone: Esta es una característica binaria que indica si un pasajero viaja solo o no. Se deriva de la característica 'FamilySize', donde un valor de 1 indica que el pasajero está solo, y un valor de 0 indica que está acompañado por familiares.
El código luego imprime las primeras filas del DataFrame, mostrando estas nuevas características junto con las columnas originales 'SibSp' y 'Parch' para compararlas.
Estas nuevas características tienen como objetivo capturar información más matizada sobre el contexto familiar de cada pasajero, lo que podría mejorar el poder predictivo del modelo de machine learning para la predicción de supervivencia.
6.1.6 Selección de Características
La selección de características es un paso crucial en el pipeline de machine learning que consiste en identificar y seleccionar las características más relevantes del conjunto de datos. Este proceso ayuda a reducir la dimensionalidad, mejorar el rendimiento del modelo y aumentar la interpretabilidad. En nuestro proyecto de predicción de supervivencia en el Titanic, utilizaremos técnicas de selección de características para identificar las más informativas para nuestro modelo predictivo.
Existen varios métodos para la selección de características, incluidos métodos de filtro (por ejemplo, selección basada en correlación), métodos wrapper (por ejemplo, eliminación recursiva de características) y métodos integrados (por ejemplo, regularización L1). Para este proyecto, usaremos un método de filtro llamado SelectKBest, que selecciona las características en función de su relación estadística con la variable objetivo.
Al aplicar la selección de características, nuestro objetivo es:
- Reducir el sobreajuste eliminando características irrelevantes o redundantes.
- Mejorar la precisión del modelo al enfocarnos en las características más predictivas.
- Disminuir el tiempo de entrenamiento al reducir la dimensionalidad del conjunto de datos.
- Mejorar la interpretabilidad del modelo al identificar las características más importantes.
Vamos a proceder con la implementación del método SelectKBest para elegir las mejores características para nuestro modelo de predicción de supervivencia en el Titanic.
from sklearn.feature_selection import SelectKBest, f_classif
X = titanic_df.drop(columns=['Survived', 'PassengerId', 'Name', 'Ticket'])
y = titanic_df['Survived']
# Select top 10 features
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X, y)
# Get selected feature names
selected_features = X.columns[selector.get_support()].tolist()
print("Selected features:", selected_features)
Aquí tienes un desglose de lo que hace este código:
- Importa las funciones necesarias del módulo
feature_selection
de scikit-learn. - Prepara la matriz de características
X
eliminando columnas que no son necesarias para la predicción ('Survived', 'PassengerId', 'Name', 'Ticket') del DataFrametitanic_df
. - Define la variable objetivo
y
como la columna 'Survived'. - Crea un objeto
SelectKBest
conf_classif
como función de puntuación yk=10
, lo que significa que seleccionará las 10 mejores características. - Aplica la selección de características a los datos utilizando
fit_transform()
, que ajusta el selector a los datos y transforma el conjunto de datos para incluir solo las características seleccionadas. - Finalmente, recupera los nombres de las características seleccionadas y los imprime.
Este paso de selección de características es crucial en el pipeline de machine learning, ya que ayuda a identificar las características más relevantes para predecir la supervivencia en el Titanic. Al reducir el número de características a las más informativas, puede mejorar el rendimiento del modelo, reducir el sobreajuste y aumentar la interpretabilidad.
6.1.7 Manejar Datos Desbalanceados
En muchos conjuntos de datos del mundo real, incluido el conjunto de datos Titanic, el desbalanceo de clases es un problema común. Esto ocurre cuando una clase (en nuestro caso, sobrevivientes o no sobrevivientes) supera significativamente a la otra. Este desbalance puede llevar a modelos sesgados que rinden mal en la clase minoritaria.
Para abordar este problema, utilizaremos una técnica llamada Técnica de Sobremuestreo de Minorías Sintéticas (SMOTE). SMOTE funciona creando ejemplos sintéticos de la clase minoritaria, equilibrando efectivamente el conjunto de datos. Este enfoque puede mejorar la capacidad del modelo para predecir ambas clases de manera precisa.
from imblearn.over_sampling import SMOTE
# Check class distribution
print("Original class distribution:", y.value_counts())
# Apply SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_selected, y)
print("Resampled class distribution:", pd.Series(y_resampled).value_counts())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
SMOTE
del móduloimblearn.over_sampling
. - Imprime la distribución de clases original utilizando
y.value_counts()
para mostrar el desbalance en el conjunto de datos. - Crea un objeto SMOTE con
random_state=42
para garantizar la reproducibilidad. - Aplica SMOTE a las características seleccionadas (
X_selected
) y la variable objetivo (y
) utilizando el métodofit_resample()
. Esto crea ejemplos sintéticos de la clase minoritaria para equilibrar el conjunto de datos. - Finalmente, imprime la distribución de clases re-muestreadas para mostrar cómo SMOTE ha equilibrado las clases.
Este paso es crucial para abordar el problema del desbalanceo de clases, que puede conducir a modelos sesgados. Al crear ejemplos sintéticos de la clase minoritaria, SMOTE ayuda a mejorar la capacidad del modelo para predecir ambas clases con precisión.
6.1.8 Construcción y Evaluación de Modelos
En esta fase crucial de nuestro proyecto, construiremos y evaluaremos varios modelos de machine learning utilizando las características que hemos desarrollado. Este paso es esencial para determinar la efectividad de nuestros esfuerzos de ingeniería de características y para identificar el modelo más adecuado para predecir la supervivencia en el Titanic.
Emplearemos múltiples algoritmos, incluidos Regresión Logística, Random Forest y Máquinas de Soporte Vectorial (SVM). Al comparar su rendimiento, obtendremos información sobre qué modelo captura mejor los patrones en nuestro conjunto de datos con características ingenierizadas. Utilizaremos validación cruzada para garantizar una evaluación robusta, y métricas como precisión, matriz de confusión y reporte de clasificación para evaluar de manera integral el rendimiento de cada modelo.
Esta sección demostrará cómo nuestro trabajo de ingeniería de características se traduce en poder predictivo, destacando la importancia de todo el proceso en el desarrollo de soluciones efectivas de machine learning.
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.2, random_state=42)
# Initialize models
models = {
'Logistic Regression': LogisticRegression(),
'Random Forest': RandomForestClassifier(),
'SVM': SVC()
}
# Train and evaluate models
for name, model in models.items():
# Cross-validation
cv_scores = cross_val_score(model, X_train, y_train, cv=5)
print(f"{name} CV Score: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")
# Train the model
model.fit(X_train, y_train)
# Make predictions
y_pred = model.predict(X_test)
# Evaluate the model
print(f"{name} Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"{name} Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print(f"{name} Classification Report:\n", classification_report(y_test, y_pred))
print("\n")
Aquí tienes un desglose de lo que hace el código:
- Importa las bibliotecas y funciones necesarias para el entrenamiento del modelo, la evaluación y la validación cruzada.
- Los datos se dividen en conjuntos de entrenamiento y prueba utilizando
train_test_split
. - Se inicializan tres modelos diferentes: Regresión Logística, Bosque Aleatorio y Máquina de Vectores de Soporte (SVM).
- Para cada modelo, el código realiza los siguientes pasos:
- Realiza la validación cruzada utilizando
cross_val_score
para evaluar el rendimiento del modelo en diferentes subconjuntos de los datos de entrenamiento. - Entrena el modelo con el conjunto completo de entrenamiento.
- Realiza predicciones sobre el conjunto de prueba.
- Evalúa el rendimiento del modelo utilizando varias métricas:
- Puntuación de precisión
- Matriz de confusión
- Informe de clasificación (que incluye precisión, recall y puntuación F1)
- Realiza la validación cruzada utilizando
Esta evaluación exhaustiva permite comparar el rendimiento de diferentes modelos en las características generadas, ayudando a identificar qué modelo capta mejor los patrones en el conjunto de datos. El uso de la validación cruzada garantiza una evaluación robusta al probar los modelos en diferentes subconjuntos de los datos.
6.1.9 Ajuste de Hiperparámetros
El ajuste de hiperparámetros es un paso crucial en la optimización de modelos de machine learning. Implica encontrar la mejor combinación de hiperparámetros que ofrezca el mejor rendimiento del modelo. En esta sección, utilizaremos GridSearchCV
para buscar sistemáticamente un conjunto predefinido de hiperparámetros para nuestro modelo de Bosque Aleatorio.
Los hiperparámetros son parámetros que no se aprenden de los datos, sino que se establecen antes del entrenamiento. Para un Bosque Aleatorio, estos pueden incluir el número de árboles (n_estimators
), la profundidad máxima de los árboles (max_depth
) y el número mínimo de muestras requeridas para dividir un nodo interno (min_samples_split
).
Al ajustar estos hiperparámetros, podemos mejorar potencialmente el rendimiento y la capacidad de generalización de nuestro modelo. Este proceso nos ayuda a encontrar el equilibrio óptimo entre la complejidad del modelo y su rendimiento, reduciendo el riesgo de sobreajuste o subajuste.
from sklearn.model_selection import GridSearchCV
# Example for Random Forest
param_grid = {
'n_estimators': [100, 200, 300],
'max_depth': [5, 10, None],
'min_samples_split': [2, 5, 10]
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5)
grid_search.fit(X_train, y_train)
print("Best parameters:", grid_search.best_params_)
print("Best cross-validation score:", grid_search.best_score_)
# Evaluate the best model
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
print("Best Model Accuracy:", accuracy_score(y_test, y_pred))
Este es el desglose de lo que hace el código:
- Importa
GridSearchCV
de scikit-learn, que se utiliza para buscar los mejores parámetros para un modelo. - Se define una cuadrícula de parámetros (
param_grid
) con diferentes valores paran_estimators
,max_depth
ymin_samples_split
. Estos son los hiperparámetros que queremos optimizar. - Se inicializa un
RandomForestClassifier
con un estado aleatorio fijo para garantizar la reproducibilidad. GridSearchCV
se configura con el modelo de Bosque Aleatorio, la cuadrícula de parámetros y una validación cruzada de 5 pliegues.- Se realiza la búsqueda en la cuadrícula usando
fit()
sobre los datos de entrenamiento. - Se imprimen los mejores parámetros y la mejor puntuación de validación cruzada.
- Finalmente, se utiliza el mejor modelo (con los parámetros optimizados) para hacer predicciones sobre el conjunto de prueba y se imprime su precisión.
Este proceso ayuda a encontrar la combinación óptima de hiperparámetros que ofrece el mejor rendimiento del modelo, lo que potencialmente mejora la precisión y la capacidad de generalización del modelo.
6.1.10 Análisis de Importancia de Características
El análisis de importancia de características es un paso crucial para entender qué características contribuyen más significativamente a las predicciones de nuestro modelo. Este análisis nos ayuda a identificar los factores más influyentes en la determinación de la supervivencia de los pasajeros del Titanic, proporcionando valiosas ideas sobre el conjunto de datos y el proceso de toma de decisiones de nuestro modelo.
Al examinar la importancia de las características, podemos:
- Obtener una comprensión más profunda de los factores que más afectaron las tasas de supervivencia.
- Validar nuestros esfuerzos de ingeniería de características al ver qué características generadas son más impactantes.
- Potencialmente simplificar nuestro modelo al enfocarnos en las características más importantes.
- Informar los futuros esfuerzos de recopilación de datos al destacar la información más crítica.
En el siguiente código, utilizaremos nuestro mejor modelo de Bosque Aleatorio para calcular y visualizar la importancia de las características, proporcionando una imagen clara de qué características están impulsando nuestras predicciones.
# Using the best Random Forest model
feature_importance = best_model.feature_importances_
feature_names = X.columns[selector.get_support()].tolist()
# Sort features by importance
feature_importance_sorted = sorted(zip(feature_importance, feature_names), reverse=True)
# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar([x[1] for x in feature_importance_sorted], [x[0] for x in feature_importance_sorted])
plt.title('Feature Importance')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
Este es el desglose de lo que hace el código:
- Extrae las puntuaciones de importancia de las características del mejor modelo de Bosque Aleatorio utilizando
best_model.feature_importances_
. - Recupera los nombres de las características seleccionadas usando
X.columns[selector.get_support()].tolist()
. - Se combinan y ordenan las importancias de las características junto con sus nombres en orden descendente de importancia.
- Se crea un gráfico de barras para visualizar las importancias de las características:
- El gráfico se establece con un tamaño de 10x6 pulgadas.
- Los nombres de las características se colocan en el eje x y sus puntuaciones de importancia en el eje y.
- Al gráfico se le asigna un título, una etiqueta para el eje x y una para el eje y.
- Las etiquetas del eje x se rotan 45 grados para mejorar la legibilidad.
Esta visualización ayuda a identificar qué características tienen el mayor impacto en las predicciones del modelo, proporcionando información sobre los factores que más influyen en las predicciones de supervivencia en el conjunto de datos del Titanic.
6.1.11 Análisis de Errores
El análisis de errores es un paso crucial para entender dónde y por qué nuestro modelo está cometiendo errores. Este proceso implica examinar los casos donde las predicciones del modelo difieren de los resultados reales. Al analizar estas clasificaciones erróneas, podemos obtener información valiosa sobre las debilidades de nuestro modelo e identificar posibles áreas de mejora.
En esta sección, examinaremos las características de las muestras mal clasificadas, comparando sus atributos con los de las instancias correctamente clasificadas. Este análisis puede revelar patrones o subgrupos específicos donde el modelo tiene dificultades, potencialmente señalando la necesidad de ingeniería de características adicional, recopilación de datos o ajustes al modelo.
import pandas as pd
# Convert X_test to DataFrame with column names
X_test_df = pd.DataFrame(X_test, columns=selected_features)
# Identify misclassified samples
misclassified = X_test_df[y_test != y_pred].copy()
misclassified['true_label'] = y_test[y_test != y_pred]
misclassified['predicted_label'] = y_pred[y_test != y_pred]
# Display sample misclassified instances
print("Sample of misclassified instances:")
print(misclassified.head())
# Analyze misclassifications
print("\nMisclassification analysis:")
for feature in selected_features:
print(f"\nFeature: {feature}")
print(misclassified.groupby(['true_label', 'predicted_label'])[feature].mean())
Aquí se explica lo que hace el código:
- Identifica las muestras mal clasificadas comparando las etiquetas reales (
y_test
) con las etiquetas predichas (y_pred
). - Crea un nuevo DataFrame llamado
misclassified
, que contiene solo las instancias clasificadas incorrectamente del conjunto de prueba. - Agrega dos nuevas columnas a este DataFrame:
'true_label'
: la etiqueta real dey_test
'predicted_label'
: la etiqueta predicha por el modelo (y_pred
)
- Imprime una muestra de estas instancias mal clasificadas usando la función
head()
. - Luego, realiza un análisis detallado de las clasificaciones erróneas:
- Itera a través de cada característica en el DataFrame
misclassified
. - Para cada característica, calcula e imprime el valor medio agrupado por
true_label
ypredicted_label
. - Esto ayuda a comprender los patrones en los errores del modelo.
- Itera a través de cada característica en el DataFrame
¿Por qué es esto útil?
- Nos permite identificar características específicas donde ocurre la clasificación errónea.
- Ayuda a identificar sesgos potenciales en el modelo.
- Puede guiar las mejoras en la ingeniería de características o el ajuste de hiperparámetros para mejorar el rendimiento del modelo.
6.1.12 Conclusión
En este proyecto, aplicamos diversas técnicas de ingeniería de características al conjunto de datos del Titanic y construimos múltiples modelos predictivos. Ampliamos el proyecto original al incluir visualización de datos, selección de características, manejo de datos desbalanceados, prueba de múltiples modelos, implementación de validación cruzada, ajuste de hiperparámetros, análisis de importancia de características y análisis de errores. Estos pasos adicionales proporcionan una comprensión más completa del conjunto de datos y del impacto de la ingeniería de características en el rendimiento del modelo.
Los resultados demuestran la importancia de la ingeniería de características para mejorar la precisión y la interpretabilidad del modelo. Al seleccionar, transformar y crear características cuidadosamente, pudimos construir modelos predictivos más robustos. El análisis de la importancia de las características y de errores proporciona información sobre qué factores son más cruciales para predecir la supervivencia y dónde el modelo podría estar fallando.
Este proyecto sirve como un ejemplo completo del proceso de ingeniería de características y su importancia en el pipeline de machine learning. Demuestra cómo se pueden combinar diversas técnicas para extraer información valiosa de datos crudos y mejorar el rendimiento del modelo.
6.1 Proyecto 1: Ingeniería de Características para Análisis Predictivo
Este proyecto se enfocará en aplicar técnicas de ingeniería de características a un conjunto de datos para mejorar el rendimiento de un modelo predictivo de machine learning. La ingeniería de características es crucial para hacer que los datos en bruto sean utilizables por los algoritmos de machine learning, al transformarlos en características significativas que mejoren el rendimiento del modelo.
Descripción General del Proyecto
En este proyecto, haremos lo siguiente:
- Explorar y preprocesar el conjunto de datos.
- Aplicar varias técnicas de ingeniería de características como el manejo de valores faltantes, la codificación de variables categóricas, la escalación de características y la creación de nuevas características.
- Construir un modelo predictivo utilizando los datos transformados para demostrar el impacto de la ingeniería de características en el rendimiento del modelo.
- Evaluar el rendimiento del modelo antes y después de la ingeniería de características.
Usaremos el conjunto de datos Titanic para este proyecto, ya que es adecuado para demostrar varias técnicas de ingeniería de características. La tarea es predecir si un pasajero sobrevivió al desastre del Titanic en función de características como la edad, el género, la clase del boleto y la tarifa.
6.1.1 Cargar y Explorar el Conjunto de Datos
Comenzaremos cargando el conjunto de datos Titanic y realizando una exploración inicial exhaustiva para obtener una comprensión profunda de su estructura y características. Este paso crucial implica examinar las dimensiones del conjunto de datos, los tipos de datos y las propiedades estadísticas básicas. También investigaremos la presencia de valores faltantes y visualizaremos las relaciones clave entre variables para sentar una base sólida para nuestros esfuerzos posteriores de ingeniería de características.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Load the Titanic dataset
url = 'https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv'
titanic_df = pd.read_csv(url)
# Display the first few rows and basic information
print(titanic_df.head())
print(titanic_df.info())
print(titanic_df.describe())
# Visualize missing data
plt.figure(figsize=(10, 6))
sns.heatmap(titanic_df.isnull(), cbar=False, cmap='viridis')
plt.title("Missing Values Heatmap")
plt.show()
# Data Visualization
plt.figure(figsize=(12, 5))
plt.subplot(121)
sns.histplot(titanic_df['Age'].dropna(), kde=True)
plt.title('Age Distribution')
plt.subplot(122)
sns.boxplot(x='Pclass', y='Fare', data=titanic_df)
plt.title('Fare Distribution by Passenger Class')
plt.tight_layout()
plt.show()
# Correlation matrix
corr_matrix = titanic_df.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Matrix')
plt.show()
Aquí tienes un desglose de lo que hace este código:
- Importa las librerías necesarias:
pandas
para la manipulación de datos.matplotlib
yseaborn
para la visualización.
- Carga el conjunto de datos Titanic desde una URL utilizando
pandas
. - Muestra información básica sobre el conjunto de datos:
- Las primeras filas del conjunto de datos (
head()
). - Información del conjunto de datos (
info()
). - Resumen estadístico (
describe()
).
- Las primeras filas del conjunto de datos (
- Crea visualizaciones:
- Un heatmap para mostrar los valores faltantes en el conjunto de datos.
- Un histograma de la distribución de la edad.
- Un boxplot que muestra la distribución de la tarifa según la clase de pasajero.
- Un heatmap de la matriz de correlación para mostrar las relaciones entre las características numéricas.
Este código es parte del paso de exploración y preprocesamiento de datos, que es crucial para comprender el conjunto de datos antes de aplicar técnicas de ingeniería de características. Ayuda a identificar datos faltantes, visualizar distribuciones y entender las relaciones entre variables, estableciendo las bases para el análisis y la construcción del modelo.
6.1.2 Manejar Datos Faltantes
El conjunto de datos Titanic presenta varias características con valores faltantes, notablemente en Age (Edad) y Cabin (Camarote). Abordar estos valores faltantes es un paso crucial en nuestro proceso de ingeniería de características.
Para la característica Age, utilizaremos técnicas de imputación para llenar los vacíos con valores estadísticamente apropiados, como la mediana de la edad o predicciones basadas en otras características correlacionadas. En el caso de la característica Cabin, dado su alto porcentaje de entradas faltantes, evaluaremos cuidadosamente si es conveniente intentar la imputación o si es mejor excluirla de nuestro análisis.
Esta decisión se basará en el valor informativo potencial de la característica frente al riesgo de introducir sesgos a través de la imputación. Al manejar sistemáticamente estos valores faltantes, nuestro objetivo es maximizar la cantidad de información utilizable en el conjunto de datos, manteniendo al mismo tiempo la integridad de nuestros análisis posteriores.
# Fill missing values in the 'Age' column with the median age
titanic_df['Age'].fillna(titanic_df['Age'].median(), inplace=True)
# Fill missing values in the 'Embarked' column with the most frequent value
titanic_df['Embarked'].fillna(titanic_df['Embarked'].mode()[0], inplace=True)
# Drop the 'Cabin' column due to too many missing values
titanic_df.drop(columns=['Cabin'], inplace=True)
print(titanic_df.isnull().sum())
Aquí tienes un desglose de lo que hace este código:
- Rellena los valores faltantes en la columna 'Age' con la mediana del conjunto de datos. Este es un enfoque común para manejar datos numéricos faltantes.
- Para la columna 'Embarked', rellena los valores faltantes con el valor más frecuente (moda) en esa columna, un método común para datos categóricos con valores faltantes.
- Elimina la columna 'Cabin' por completo debido a la gran cantidad de valores faltantes. Esta decisión probablemente se tomó porque el alto porcentaje de datos faltantes en esta columna podría introducir más sesgo si se imputan.
- Finalmente, imprime la suma de los valores nulos en cada columna después de estas operaciones, lo que permite verificar que el manejo de valores faltantes se ha realizado con éxito.
Este enfoque para manejar los datos faltantes forma parte del proceso de ingeniería de características, con el objetivo de preparar el conjunto de datos para los algoritmos de machine learning, preservando la mayor cantidad de información útil posible.
6.1.3 Codificación de Características
El conjunto de datos Titanic incluye varias variables categóricas, como Sex y Embarked, que requieren una transformación a un formato numérico para que sean compatibles con los algoritmos de machine learning. Este proceso de conversión es crucial, ya que la mayoría de los modelos de machine learning están diseñados para trabajar con entradas numéricas. Para lograr esta transformación, emplearemos diversas técnicas de codificación, con un enfoque particular en la codificación one-hot.
La codificación one-hot es un método que crea columnas binarias para cada categoría dentro de una variable categórica. Por ejemplo, la variable 'Sex' se dividiría en dos columnas: 'Sex_male' y 'Sex_female', donde cada pasajero tendría un '1' en una columna y un '0' en la otra. Este enfoque nos permite representar datos categóricos de forma numérica sin implicar una relación ordinal entre las categorías.
Además, podríamos considerar otras técnicas de codificación, como label encoding para variables ordinales o target encoding para variables categóricas con alta cardinalidad, dependiendo de las características específicas de cada variable. La elección del método de codificación puede impactar significativamente en el rendimiento del modelo, lo que convierte este paso en un aspecto crítico del proceso de ingeniería de características.
# One-hot encode the 'Sex' and 'Embarked' columns
titanic_df = pd.get_dummies(titanic_df, columns=['Sex', 'Embarked'], drop_first=True)
print(titanic_df.head())
Aquí tienes un desglose de lo que hace este código:
- Utiliza la función
pd.get_dummies()
para codificar con one-hot las columnas 'Sex' y 'Embarked'. - El parámetro
columns=['Sex', 'Embarked']
especifica qué columnas se van a codificar. - El argumento
drop_first=True
se utiliza para evitar multicolinealidad, eliminando una de las columnas creadas para cada variable categórica original. - El resultado se guarda de nuevo en el DataFrame
titanic_df
, reemplazando efectivamente las columnas originales 'Sex' y 'Embarked' con sus versiones codificadas con one-hot. - Finalmente, imprime las primeras filas del DataFrame actualizado para mostrar los resultados de la codificación.
Este paso es crucial en el proceso de ingeniería de características ya que transforma los datos categóricos en un formato que puede ser fácilmente utilizado por los algoritmos de machine learning, que normalmente requieren entradas numéricas.
6.1.4 Escalado de Características
El escalado de características es un paso crucial en nuestro proceso de ingeniería de características, que aborda las diferencias significativas de escala entre ciertas características como Fare (Tarifa) y Age (Edad). Estas disparidades pueden tener efectos perjudiciales en el rendimiento del modelo, especialmente para algoritmos que son sensibles a las escalas de las características, como la regresión logística o K-nearest neighbors. Para mitigar estos problemas y asegurar un rendimiento óptimo del modelo, emplearemos el escalado estándar como nuestra técnica de normalización.
El escalado estándar, también conocido como normalización por z-score, transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esta transformación preserva la forma de la distribución original mientras lleva todas las características a una escala comparable. Al aplicar el escalado estándar a nuestro conjunto de datos, creamos un campo de juego uniforme para todas las características, lo que permite que los algoritmos las traten por igual y evita que las características con magnitudes mayores dominen el proceso de aprendizaje.
Los beneficios de este enfoque de escalado van más allá de mejorar el rendimiento del modelo. También mejora la interpretabilidad de los coeficientes del modelo, facilita una convergencia más rápida durante el proceso de entrenamiento y ayuda a comparar la importancia relativa de diferentes características. A medida que avanzamos en nuestro análisis, este paso de escalado será fundamental para extraer insights significativos y construir modelos predictivos robustos.
from sklearn.preprocessing import StandardScaler
scaling_features = ['Age', 'Fare']
scaler = StandardScaler()
titanic_df[scaling_features] = scaler.fit_transform(titanic_df[scaling_features])
print(titanic_df[scaling_features].head())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
StandardScaler
desklearn.preprocessing
. - Define una lista llamada
scaling_features
que contiene las columnas 'Age' y 'Fare', que son las características a escalar. - Crea una instancia de
StandardScaler
llamadascaler
. - Aplica el método
fit_transform
del escalador a las características especificadas en el DataFrametitanic_df
. Este paso ajusta el escalador a los datos y los transforma. - Finalmente, imprime el head de las características escaladas para mostrar el resultado.
El StandardScaler
transforma las características para que tengan una media de 0 y una desviación estándar de 1. Esto es importante para muchos algoritmos de machine learning que son sensibles a la escala de las características de entrada, ya que ayuda a evitar que las características con magnitudes mayores dominen el proceso de entrenamiento del modelo.
6.1.5 Creación de Características
La creación de nuevas características es una técnica poderosa que puede mejorar significativamente la capacidad de un modelo para capturar y aprovechar relaciones complejas dentro de los datos. Este proceso, conocido como ingeniería de características, implica derivar nuevas variables a partir de las existentes para proporcionar información adicional o representar los datos de una manera más significativa. En este paso crucial de nuestro análisis, nos centraremos en la creación de una nueva característica llamada FamilySize.
La característica FamilySize se creará combinando dos variables existentes: SibSp (número de hermanos y cónyuges a bordo) y Parch (número de padres e hijos a bordo). Al agregar estas características relacionadas, buscamos crear una representación más completa del tamaño de la unidad familiar de un pasajero. Esta nueva característica tiene el potencial de capturar dinámicas sociales importantes y patrones de supervivencia que podrían no ser evidentes al considerar por separado a los hermanos/cónyuges y padres/hijos.
La lógica detrás de esta decisión de ingeniería de características está basada en la hipótesis de que el tamaño de la familia pudo haber desempeñado un papel significativo en los resultados de supervivencia durante el desastre del Titanic. Por ejemplo, las familias más grandes podrían haber enfrentado diferentes desafíos o haber recibido un trato distinto en comparación con los individuos que viajaban solos o en grupos más pequeños. Al crear la característica FamilySize, proporcionamos a nuestro modelo una comprensión más matizada del contexto familiar de cada pasajero, lo que potencialmente mejorará sus capacidades predictivas.
# Create a new feature 'FamilySize'
titanic_df['FamilySize'] = titanic_df['SibSp'] + titanic_df['Parch'] + 1
# Create a new feature 'IsAlone'
titanic_df['IsAlone'] = (titanic_df['FamilySize'] == 1).astype(int)
print(titanic_df[['SibSp', 'Parch', 'FamilySize', 'IsAlone']].head())
Este fragmento de código demuestra la creación de dos nuevas características en el conjunto de datos Titanic mediante la ingeniería de características:
- FamilySize: Esta característica se crea sumando los valores de 'SibSp' (número de hermanos y cónyuges a bordo), 'Parch' (número de padres e hijos a bordo) y añadiendo 1 (para incluir al propio pasajero). Esto proporciona una medida completa del tamaño total de la familia para cada pasajero.
- IsAlone: Esta es una característica binaria que indica si un pasajero viaja solo o no. Se deriva de la característica 'FamilySize', donde un valor de 1 indica que el pasajero está solo, y un valor de 0 indica que está acompañado por familiares.
El código luego imprime las primeras filas del DataFrame, mostrando estas nuevas características junto con las columnas originales 'SibSp' y 'Parch' para compararlas.
Estas nuevas características tienen como objetivo capturar información más matizada sobre el contexto familiar de cada pasajero, lo que podría mejorar el poder predictivo del modelo de machine learning para la predicción de supervivencia.
6.1.6 Selección de Características
La selección de características es un paso crucial en el pipeline de machine learning que consiste en identificar y seleccionar las características más relevantes del conjunto de datos. Este proceso ayuda a reducir la dimensionalidad, mejorar el rendimiento del modelo y aumentar la interpretabilidad. En nuestro proyecto de predicción de supervivencia en el Titanic, utilizaremos técnicas de selección de características para identificar las más informativas para nuestro modelo predictivo.
Existen varios métodos para la selección de características, incluidos métodos de filtro (por ejemplo, selección basada en correlación), métodos wrapper (por ejemplo, eliminación recursiva de características) y métodos integrados (por ejemplo, regularización L1). Para este proyecto, usaremos un método de filtro llamado SelectKBest, que selecciona las características en función de su relación estadística con la variable objetivo.
Al aplicar la selección de características, nuestro objetivo es:
- Reducir el sobreajuste eliminando características irrelevantes o redundantes.
- Mejorar la precisión del modelo al enfocarnos en las características más predictivas.
- Disminuir el tiempo de entrenamiento al reducir la dimensionalidad del conjunto de datos.
- Mejorar la interpretabilidad del modelo al identificar las características más importantes.
Vamos a proceder con la implementación del método SelectKBest para elegir las mejores características para nuestro modelo de predicción de supervivencia en el Titanic.
from sklearn.feature_selection import SelectKBest, f_classif
X = titanic_df.drop(columns=['Survived', 'PassengerId', 'Name', 'Ticket'])
y = titanic_df['Survived']
# Select top 10 features
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X, y)
# Get selected feature names
selected_features = X.columns[selector.get_support()].tolist()
print("Selected features:", selected_features)
Aquí tienes un desglose de lo que hace este código:
- Importa las funciones necesarias del módulo
feature_selection
de scikit-learn. - Prepara la matriz de características
X
eliminando columnas que no son necesarias para la predicción ('Survived', 'PassengerId', 'Name', 'Ticket') del DataFrametitanic_df
. - Define la variable objetivo
y
como la columna 'Survived'. - Crea un objeto
SelectKBest
conf_classif
como función de puntuación yk=10
, lo que significa que seleccionará las 10 mejores características. - Aplica la selección de características a los datos utilizando
fit_transform()
, que ajusta el selector a los datos y transforma el conjunto de datos para incluir solo las características seleccionadas. - Finalmente, recupera los nombres de las características seleccionadas y los imprime.
Este paso de selección de características es crucial en el pipeline de machine learning, ya que ayuda a identificar las características más relevantes para predecir la supervivencia en el Titanic. Al reducir el número de características a las más informativas, puede mejorar el rendimiento del modelo, reducir el sobreajuste y aumentar la interpretabilidad.
6.1.7 Manejar Datos Desbalanceados
En muchos conjuntos de datos del mundo real, incluido el conjunto de datos Titanic, el desbalanceo de clases es un problema común. Esto ocurre cuando una clase (en nuestro caso, sobrevivientes o no sobrevivientes) supera significativamente a la otra. Este desbalance puede llevar a modelos sesgados que rinden mal en la clase minoritaria.
Para abordar este problema, utilizaremos una técnica llamada Técnica de Sobremuestreo de Minorías Sintéticas (SMOTE). SMOTE funciona creando ejemplos sintéticos de la clase minoritaria, equilibrando efectivamente el conjunto de datos. Este enfoque puede mejorar la capacidad del modelo para predecir ambas clases de manera precisa.
from imblearn.over_sampling import SMOTE
# Check class distribution
print("Original class distribution:", y.value_counts())
# Apply SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_selected, y)
print("Resampled class distribution:", pd.Series(y_resampled).value_counts())
Aquí tienes un desglose de lo que hace este código:
- Importa la clase
SMOTE
del móduloimblearn.over_sampling
. - Imprime la distribución de clases original utilizando
y.value_counts()
para mostrar el desbalance en el conjunto de datos. - Crea un objeto SMOTE con
random_state=42
para garantizar la reproducibilidad. - Aplica SMOTE a las características seleccionadas (
X_selected
) y la variable objetivo (y
) utilizando el métodofit_resample()
. Esto crea ejemplos sintéticos de la clase minoritaria para equilibrar el conjunto de datos. - Finalmente, imprime la distribución de clases re-muestreadas para mostrar cómo SMOTE ha equilibrado las clases.
Este paso es crucial para abordar el problema del desbalanceo de clases, que puede conducir a modelos sesgados. Al crear ejemplos sintéticos de la clase minoritaria, SMOTE ayuda a mejorar la capacidad del modelo para predecir ambas clases con precisión.
6.1.8 Construcción y Evaluación de Modelos
En esta fase crucial de nuestro proyecto, construiremos y evaluaremos varios modelos de machine learning utilizando las características que hemos desarrollado. Este paso es esencial para determinar la efectividad de nuestros esfuerzos de ingeniería de características y para identificar el modelo más adecuado para predecir la supervivencia en el Titanic.
Emplearemos múltiples algoritmos, incluidos Regresión Logística, Random Forest y Máquinas de Soporte Vectorial (SVM). Al comparar su rendimiento, obtendremos información sobre qué modelo captura mejor los patrones en nuestro conjunto de datos con características ingenierizadas. Utilizaremos validación cruzada para garantizar una evaluación robusta, y métricas como precisión, matriz de confusión y reporte de clasificación para evaluar de manera integral el rendimiento de cada modelo.
Esta sección demostrará cómo nuestro trabajo de ingeniería de características se traduce en poder predictivo, destacando la importancia de todo el proceso en el desarrollo de soluciones efectivas de machine learning.
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X_resampled, y_resampled, test_size=0.2, random_state=42)
# Initialize models
models = {
'Logistic Regression': LogisticRegression(),
'Random Forest': RandomForestClassifier(),
'SVM': SVC()
}
# Train and evaluate models
for name, model in models.items():
# Cross-validation
cv_scores = cross_val_score(model, X_train, y_train, cv=5)
print(f"{name} CV Score: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")
# Train the model
model.fit(X_train, y_train)
# Make predictions
y_pred = model.predict(X_test)
# Evaluate the model
print(f"{name} Accuracy: {accuracy_score(y_test, y_pred):.4f}")
print(f"{name} Confusion Matrix:\n", confusion_matrix(y_test, y_pred))
print(f"{name} Classification Report:\n", classification_report(y_test, y_pred))
print("\n")
Aquí tienes un desglose de lo que hace el código:
- Importa las bibliotecas y funciones necesarias para el entrenamiento del modelo, la evaluación y la validación cruzada.
- Los datos se dividen en conjuntos de entrenamiento y prueba utilizando
train_test_split
. - Se inicializan tres modelos diferentes: Regresión Logística, Bosque Aleatorio y Máquina de Vectores de Soporte (SVM).
- Para cada modelo, el código realiza los siguientes pasos:
- Realiza la validación cruzada utilizando
cross_val_score
para evaluar el rendimiento del modelo en diferentes subconjuntos de los datos de entrenamiento. - Entrena el modelo con el conjunto completo de entrenamiento.
- Realiza predicciones sobre el conjunto de prueba.
- Evalúa el rendimiento del modelo utilizando varias métricas:
- Puntuación de precisión
- Matriz de confusión
- Informe de clasificación (que incluye precisión, recall y puntuación F1)
- Realiza la validación cruzada utilizando
Esta evaluación exhaustiva permite comparar el rendimiento de diferentes modelos en las características generadas, ayudando a identificar qué modelo capta mejor los patrones en el conjunto de datos. El uso de la validación cruzada garantiza una evaluación robusta al probar los modelos en diferentes subconjuntos de los datos.
6.1.9 Ajuste de Hiperparámetros
El ajuste de hiperparámetros es un paso crucial en la optimización de modelos de machine learning. Implica encontrar la mejor combinación de hiperparámetros que ofrezca el mejor rendimiento del modelo. En esta sección, utilizaremos GridSearchCV
para buscar sistemáticamente un conjunto predefinido de hiperparámetros para nuestro modelo de Bosque Aleatorio.
Los hiperparámetros son parámetros que no se aprenden de los datos, sino que se establecen antes del entrenamiento. Para un Bosque Aleatorio, estos pueden incluir el número de árboles (n_estimators
), la profundidad máxima de los árboles (max_depth
) y el número mínimo de muestras requeridas para dividir un nodo interno (min_samples_split
).
Al ajustar estos hiperparámetros, podemos mejorar potencialmente el rendimiento y la capacidad de generalización de nuestro modelo. Este proceso nos ayuda a encontrar el equilibrio óptimo entre la complejidad del modelo y su rendimiento, reduciendo el riesgo de sobreajuste o subajuste.
from sklearn.model_selection import GridSearchCV
# Example for Random Forest
param_grid = {
'n_estimators': [100, 200, 300],
'max_depth': [5, 10, None],
'min_samples_split': [2, 5, 10]
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5)
grid_search.fit(X_train, y_train)
print("Best parameters:", grid_search.best_params_)
print("Best cross-validation score:", grid_search.best_score_)
# Evaluate the best model
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
print("Best Model Accuracy:", accuracy_score(y_test, y_pred))
Este es el desglose de lo que hace el código:
- Importa
GridSearchCV
de scikit-learn, que se utiliza para buscar los mejores parámetros para un modelo. - Se define una cuadrícula de parámetros (
param_grid
) con diferentes valores paran_estimators
,max_depth
ymin_samples_split
. Estos son los hiperparámetros que queremos optimizar. - Se inicializa un
RandomForestClassifier
con un estado aleatorio fijo para garantizar la reproducibilidad. GridSearchCV
se configura con el modelo de Bosque Aleatorio, la cuadrícula de parámetros y una validación cruzada de 5 pliegues.- Se realiza la búsqueda en la cuadrícula usando
fit()
sobre los datos de entrenamiento. - Se imprimen los mejores parámetros y la mejor puntuación de validación cruzada.
- Finalmente, se utiliza el mejor modelo (con los parámetros optimizados) para hacer predicciones sobre el conjunto de prueba y se imprime su precisión.
Este proceso ayuda a encontrar la combinación óptima de hiperparámetros que ofrece el mejor rendimiento del modelo, lo que potencialmente mejora la precisión y la capacidad de generalización del modelo.
6.1.10 Análisis de Importancia de Características
El análisis de importancia de características es un paso crucial para entender qué características contribuyen más significativamente a las predicciones de nuestro modelo. Este análisis nos ayuda a identificar los factores más influyentes en la determinación de la supervivencia de los pasajeros del Titanic, proporcionando valiosas ideas sobre el conjunto de datos y el proceso de toma de decisiones de nuestro modelo.
Al examinar la importancia de las características, podemos:
- Obtener una comprensión más profunda de los factores que más afectaron las tasas de supervivencia.
- Validar nuestros esfuerzos de ingeniería de características al ver qué características generadas son más impactantes.
- Potencialmente simplificar nuestro modelo al enfocarnos en las características más importantes.
- Informar los futuros esfuerzos de recopilación de datos al destacar la información más crítica.
En el siguiente código, utilizaremos nuestro mejor modelo de Bosque Aleatorio para calcular y visualizar la importancia de las características, proporcionando una imagen clara de qué características están impulsando nuestras predicciones.
# Using the best Random Forest model
feature_importance = best_model.feature_importances_
feature_names = X.columns[selector.get_support()].tolist()
# Sort features by importance
feature_importance_sorted = sorted(zip(feature_importance, feature_names), reverse=True)
# Plot feature importance
plt.figure(figsize=(10, 6))
plt.bar([x[1] for x in feature_importance_sorted], [x[0] for x in feature_importance_sorted])
plt.title('Feature Importance')
plt.xlabel('Features')
plt.ylabel('Importance')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
Este es el desglose de lo que hace el código:
- Extrae las puntuaciones de importancia de las características del mejor modelo de Bosque Aleatorio utilizando
best_model.feature_importances_
. - Recupera los nombres de las características seleccionadas usando
X.columns[selector.get_support()].tolist()
. - Se combinan y ordenan las importancias de las características junto con sus nombres en orden descendente de importancia.
- Se crea un gráfico de barras para visualizar las importancias de las características:
- El gráfico se establece con un tamaño de 10x6 pulgadas.
- Los nombres de las características se colocan en el eje x y sus puntuaciones de importancia en el eje y.
- Al gráfico se le asigna un título, una etiqueta para el eje x y una para el eje y.
- Las etiquetas del eje x se rotan 45 grados para mejorar la legibilidad.
Esta visualización ayuda a identificar qué características tienen el mayor impacto en las predicciones del modelo, proporcionando información sobre los factores que más influyen en las predicciones de supervivencia en el conjunto de datos del Titanic.
6.1.11 Análisis de Errores
El análisis de errores es un paso crucial para entender dónde y por qué nuestro modelo está cometiendo errores. Este proceso implica examinar los casos donde las predicciones del modelo difieren de los resultados reales. Al analizar estas clasificaciones erróneas, podemos obtener información valiosa sobre las debilidades de nuestro modelo e identificar posibles áreas de mejora.
En esta sección, examinaremos las características de las muestras mal clasificadas, comparando sus atributos con los de las instancias correctamente clasificadas. Este análisis puede revelar patrones o subgrupos específicos donde el modelo tiene dificultades, potencialmente señalando la necesidad de ingeniería de características adicional, recopilación de datos o ajustes al modelo.
import pandas as pd
# Convert X_test to DataFrame with column names
X_test_df = pd.DataFrame(X_test, columns=selected_features)
# Identify misclassified samples
misclassified = X_test_df[y_test != y_pred].copy()
misclassified['true_label'] = y_test[y_test != y_pred]
misclassified['predicted_label'] = y_pred[y_test != y_pred]
# Display sample misclassified instances
print("Sample of misclassified instances:")
print(misclassified.head())
# Analyze misclassifications
print("\nMisclassification analysis:")
for feature in selected_features:
print(f"\nFeature: {feature}")
print(misclassified.groupby(['true_label', 'predicted_label'])[feature].mean())
Aquí se explica lo que hace el código:
- Identifica las muestras mal clasificadas comparando las etiquetas reales (
y_test
) con las etiquetas predichas (y_pred
). - Crea un nuevo DataFrame llamado
misclassified
, que contiene solo las instancias clasificadas incorrectamente del conjunto de prueba. - Agrega dos nuevas columnas a este DataFrame:
'true_label'
: la etiqueta real dey_test
'predicted_label'
: la etiqueta predicha por el modelo (y_pred
)
- Imprime una muestra de estas instancias mal clasificadas usando la función
head()
. - Luego, realiza un análisis detallado de las clasificaciones erróneas:
- Itera a través de cada característica en el DataFrame
misclassified
. - Para cada característica, calcula e imprime el valor medio agrupado por
true_label
ypredicted_label
. - Esto ayuda a comprender los patrones en los errores del modelo.
- Itera a través de cada característica en el DataFrame
¿Por qué es esto útil?
- Nos permite identificar características específicas donde ocurre la clasificación errónea.
- Ayuda a identificar sesgos potenciales en el modelo.
- Puede guiar las mejoras en la ingeniería de características o el ajuste de hiperparámetros para mejorar el rendimiento del modelo.
6.1.12 Conclusión
En este proyecto, aplicamos diversas técnicas de ingeniería de características al conjunto de datos del Titanic y construimos múltiples modelos predictivos. Ampliamos el proyecto original al incluir visualización de datos, selección de características, manejo de datos desbalanceados, prueba de múltiples modelos, implementación de validación cruzada, ajuste de hiperparámetros, análisis de importancia de características y análisis de errores. Estos pasos adicionales proporcionan una comprensión más completa del conjunto de datos y del impacto de la ingeniería de características en el rendimiento del modelo.
Los resultados demuestran la importancia de la ingeniería de características para mejorar la precisión y la interpretabilidad del modelo. Al seleccionar, transformar y crear características cuidadosamente, pudimos construir modelos predictivos más robustos. El análisis de la importancia de las características y de errores proporciona información sobre qué factores son más cruciales para predecir la supervivencia y dónde el modelo podría estar fallando.
Este proyecto sirve como un ejemplo completo del proceso de ingeniería de características y su importancia en el pipeline de machine learning. Demuestra cómo se pueden combinar diversas técnicas para extraer información valiosa de datos crudos y mejorar el rendimiento del modelo.