Ingeniería de Características Avanzada para Modelos de Machine Learning: ¡Potencia tus Datos!
La ingeniería de características es un paso crucial en Machine Learning que impacta directamente el rendimiento del modelo. Este tutorial explora técnicas avanzadas para transformar y crear nuevas características, permitiéndote extraer el máximo valor de tus datos. Descubre cómo aplicar estas estrategias para construir modelos más robustos y precisos.
La ingeniería de características (Feature Engineering) es, sin duda, una de las fases más creativas y decisivas en el ciclo de vida de un proyecto de Machine Learning. No importa cuán sofisticado sea tu algoritmo, si las características de entrada no son representativas o informativas, el modelo tendrá dificultades para aprender patrones significativos. Este tutorial te guiará a través de un conjunto de técnicas avanzadas para ir más allá de la transformación básica de datos y desbloquear el verdadero potencial de tus conjuntos de datos.
🎯 ¿Por qué es Crucial la Ingeniería de Características Avanzada?
Aunque los algoritmos modernos, especialmente las redes neuronales profundas, tienen cierta capacidad para aprender características por sí mismos, la ingeniería manual o asistida sigue siendo indispensable por varias razones:
- Mejora el Rendimiento del Modelo: Las características bien diseñadas pueden simplificar la tarea de aprendizaje para el algoritmo, llevando a una mayor precisión y generalización.
- Reduce el Sobreajuste (Overfitting): Al crear características más significativas, podemos reducir la complejidad necesaria del modelo y, a su vez, el riesgo de sobreajuste.
- Acelera el Entrenamiento: Menos características irrelevantes o redundantes significan menos trabajo para el algoritmo.
- Facilita la Interpretación: Las características significativas a menudo conducen a modelos más interpretables, lo cual es crucial en muchos dominios.
- Maneja Datos Complejos: Ayuda a transformar datos crudos y heterogéneos en un formato que los algoritmos puedan procesar eficazmente.
🛠️ Preparando Nuestro Entorno: Herramientas Necesarias
Para este tutorial práctico, utilizaremos Python y algunas de sus librerías más populares para ciencia de datos. Asegúrate de tenerlas instaladas. Si no, puedes hacerlo con pip:
pip install pandas numpy scikit-learn matplotlib seaborn
Una vez instaladas, importaremos las librerías necesarias al inicio de nuestro script o notebook:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import PolynomialFeatures, StandardScaler, QuantileTransformer
from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score
# Configuración para visualización
sns.set_style('whitegrid')
plt.rcParams['figure.figsize'] = (10, 6)
📖 Creando Datos Sintéticos para Ejemplificar
Para ilustrar las técnicas, trabajaremos con un conjunto de datos sintético. Esto nos permite controlar las relaciones subyacentes y observar el impacto de nuestras transformaciones.
# Generación de datos sintéticos
np.random.seed(42)
data_size = 1000
X = np.random.rand(data_size, 3) * 10 # 3 características originales
# Crear una relación no lineal y una interacción
y = (X[:, 0]**2 + 2 * X[:, 1] * X[:, 2] - 5 * X[:, 0] + np.random.randn(data_size) * 5 > 50).astype(int)
df = pd.DataFrame(X, columns=['feature_A', 'feature_B', 'feature_C'])
df['target'] = y
print("Primeras 5 filas del dataset original:")
print(df.head())
print("\nEstadísticas descriptivas:")
print(df.describe())
🚀 Técnicas de Ingeniería de Características Avanzada
Exploraremos diversas estrategias para enriquecer nuestro conjunto de datos.
1. 📈 Características Polinomiales y de Interacción
Los modelos lineales asumen una relación lineal entre las características y la variable objetivo. Sin embargo, muchas relaciones en el mundo real son no lineales o involucran interacciones entre características. Las características polinomiales y de interacción permiten capturar estas relaciones.
- Características Polinomiales: Crean nuevas características elevando las existentes a una potencia (x^2, x^3, etc.).
- Características de Interacción: Crean nuevas características multiplicando dos o más características existentes (x1 * x2, x1 * x2 * x3, etc.).
# Crear características polinomiales y de interacción
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(df[['feature_A', 'feature_B', 'feature_C']])
# Nombres de las nuevas características
poly_feature_names = poly.get_feature_names_out(['feature_A', 'feature_B', 'feature_C'])
df_poly = pd.DataFrame(X_poly, columns=poly_feature_names)
df_poly['target'] = y
print("\nPrimeras 5 filas con características polinomiales y de interacción:")
print(df_poly.head())
# Visualización de una característica original vs. polinomial
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
sns.scatterplot(x='feature_A', y='target', data=df, alpha=0.6)
plt.title('Feature A vs. Target (Original)')
plt.subplot(1, 2, 2)
sns.scatterplot(x='feature_A^2', y='target', data=df_poly, alpha=0.6)
plt.title('Feature A^2 vs. Target (Polinomial)')
plt.tight_layout()
plt.show()
2. ✂️ Binning (Discretización)
El binning transforma una característica numérica continua en una característica categórica (discreta) agrupando valores en "bins" o intervalos. Esto puede ser útil para:
- Manejar la no linealidad.
- Reducir el efecto de outliers.
- Simplificar el modelo.
- Evitar el sobreajuste para modelos sensibles a valores exactos.
Existen varios métodos de binning:
- Ancho Fijo (Equal-width binning): Los bins tienen el mismo tamaño de intervalo.
- Frecuencia Fija (Equal-depth binning / Quantile binning): Cada bin tiene aproximadamente el mismo número de observaciones.
# Binning de una característica
df_binned = df.copy()
df_binned['feature_A_binned_width'] = pd.cut(df_binned['feature_A'], bins=5, labels=False, include_lowest=True)
df_binned['feature_B_binned_quantile'] = pd.qcut(df_binned['feature_B'], q=4, labels=False, duplicates='drop')
print("\nPrimeras 5 filas con características binned:")
print(df_binned[['feature_A', 'feature_A_binned_width', 'feature_B', 'feature_B_binned_quantile']].head())
# Visualización del binning
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
sns.histplot(df_binned['feature_A_binned_width'], kde=False, bins=5)
plt.title('Distribución de Feature A (Ancho Fijo)')
plt.subplot(1, 2, 2)
sns.histplot(df_binned['feature_B_binned_quantile'], kde=False, bins=4)
plt.title('Distribución de Feature B (Frecuencia Fija)')
plt.tight_layout()
plt.show()
3. 🏷️ Transformaciones Logarítmicas y Otras Monotónicas
Las transformaciones logarítmicas, de raíz cuadrada o de potencia pueden ser muy útiles para:
- Reducir la asimetría (skewness): Normalizar distribuciones sesgadas.
- Estabilizar la varianza: Hacer que la varianza sea más constante.
- Manejar datos con una amplia gama de valores: Comprimir el rango de valores para evitar que características con valores grandes dominen el modelo.
# Aplicar transformación logarítmica (asegurar valores positivos)
df_transformed = df.copy()
df_transformed['feature_A_log'] = np.log1p(df_transformed['feature_A']) # log1p = log(1+x) para manejar ceros
print("\nPrimeras 5 filas con característica log-transformada:")
print(df_transformed[['feature_A', 'feature_A_log']].head())
# Visualización de la transformación logarítmica
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
sns.histplot(df_transformed['feature_A'], kde=True)
plt.title('Distribución de Feature A (Original)')
plt.subplot(1, 2, 2)
sns.histplot(df_transformed['feature_A_log'], kde=True)
plt.title('Distribución de Feature A (Log-transformada)')
plt.tight_layout()
plt.show()
4. 🔗 Creación de Ratios y Combinaciones Lógicas
Combinar características existentes de manera significativa puede crear nuevas características con un alto poder predictivo. Esto es especialmente útil en dominios donde ciertas relaciones son inherentemente importantes (ej., relación deuda/ingresos en finanzas).
# Crear una característica de ratio
df_ratios = df.copy()
df_ratios['feature_A_div_B'] = df_ratios['feature_A'] / (df_ratios['feature_B'] + 1e-6) # Evitar división por cero
# Crear una característica basada en una condición lógica
df_ratios['is_high_A_and_low_C'] = ((df_ratios['feature_A'] > df_ratios['feature_A'].mean()) & \
(df_ratios['feature_C'] < df_ratios['feature_C'].mean())).astype(int)
print("\nPrimeras 5 filas con características de ratio y lógicas:")
print(df_ratios[['feature_A', 'feature_B', 'feature_A_div_B', 'is_high_A_and_low_C', 'target']].head())
# Visualización de la relación entre la nueva característica y el target
plt.figure(figsize=(6, 5))
sns.boxplot(x='is_high_A_and_low_C', y='feature_A_div_B', hue='target', data=df_ratios)
plt.title('Relación de is_high_A_and_low_C y feature_A_div_B con el Target')
plt.show()
5. 🧑🤝🧑 Características Basadas en Agregación de Grupos (Group-by Features)
Cuando tienes datos que pueden agruparse por una característica categórica (ej., 'tipo_producto', 'país'), puedes crear nuevas características calculando estadísticas agregadas (media, suma, desviación estándar, etc.) para cada grupo. Esto añade contexto global al nivel de cada observación.
# Añadir una característica categórica al dataset para el ejemplo
df_grouped = df.copy()
df_grouped['category'] = np.random.choice(['Group1', 'Group2', 'Group3'], size=data_size)
# Calcular la media de 'feature_A' por 'category'
df_grouped['mean_feature_A_by_category'] = df_grouped.groupby('category')['feature_A'].transform('mean')
# Calcular la desviación estándar de 'feature_B' por 'category'
df_grouped['std_feature_B_by_category'] = df_grouped.groupby('category')['feature_B'].transform('std')
print("\nPrimeras 5 filas con características de agregación de grupo:")
print(df_grouped[['feature_A', 'category', 'mean_feature_A_by_category', 'std_feature_B_by_category', 'target']].head())
# Visualización de una característica agregada
plt.figure(figsize=(8, 6))
sns.barplot(x='category', y='mean_feature_A_by_category', hue='target', data=df_grouped)
plt.title('Media de Feature A por Categoría y Target')
plt.show()
6. 📉 Reducción de Dimensionalidad y Extracción de Características
Cuando tienes muchas características, algunas pueden ser redundantes o aportar poco valor. Las técnicas de reducción de dimensionalidad como el Análisis de Componentes Principales (PCA) o la Factorización de Matrices No Negativas (NMF) pueden crear un conjunto más pequeño de nuevas características que capturan la mayor parte de la varianza de los datos originales. Esto no es estrictamente ingeniería de características, sino más bien extracción de características, pero a menudo se solapa en la práctica.
# PCA para reducir la dimensionalidad
pca = PCA(n_components=2)
X_pca = pca.fit_transform(df[['feature_A', 'feature_B', 'feature_C']])
df_pca = pd.DataFrame(X_pca, columns=['PC1', 'PC2'])
df_pca['target'] = y
print("\nPrimeras 5 filas con características PCA:")
print(df_pca.head())
# Visualización de las componentes principales
plt.figure(figsize=(8, 6))
sns.scatterplot(x='PC1', y='PC2', hue='target', data=df_pca, alpha=0.7)
plt.title('PCA: Componentes Principales con Target')
plt.xlabel(f'Componente Principal 1 ({pca.explained_variance_ratio_[0]*100:.2f}%)')
plt.ylabel(f'Componente Principal 2 ({pca.explained_variance_ratio_[1]*100:.2f}%)')
plt.show()
7. 🔗 Características Basadas en Clustering
El clustering se puede usar para crear nuevas características. Por ejemplo, la distancia de cada punto a los centroides de los clusters, o la asignación de cluster como una característica categórica.
# Aplicar K-Means para crear características de cluster
kmeans = KMeans(n_clusters=3, random_state=42, n_init=10) # n_init para evitar warnings
df_clustered = df.copy()
df_clustered['cluster'] = kmeans.fit_predict(df[['feature_A', 'feature_B', 'feature_C']])
# Distancia al centroide más cercano como característica
# (Alternativamente, podrías calcular la distancia a cada centroide)
df_clustered['distance_to_centroid'] = kmeans.transform(df[['feature_A', 'feature_B', 'feature_C']]).min(axis=1)
print("\nPrimeras 5 filas con características de cluster:")
print(df_clustered[['feature_A', 'feature_B', 'feature_C', 'cluster', 'distance_to_centroid', 'target']].head())
# Visualización de los clusters y el target
plt.figure(figsize=(8, 6))
sns.scatterplot(x='feature_A', y='feature_B', hue='cluster', style='target', data=df_clustered, palette='viridis', alpha=0.7)
plt.title('Clusters y Target en Feature A vs. Feature B')
plt.show()
🔍 Evaluación del Impacto de la Ingeniería de Características
Una vez que hemos creado nuestras nuevas características, es crucial evaluar si realmente aportan valor al modelo. Usaremos un modelo simple de Regresión Logística para comparar el rendimiento antes y después de la ingeniería de características.
# Preparar datos para el modelo base
X_base = df[['feature_A', 'feature_B', 'feature_C']]
y_base = df['target']
scaler = StandardScaler()
X_base_scaled = scaler.fit_transform(X_base)
X_train_base, X_test_base, y_train_base, y_test_base = train_test_split(X_base_scaled, y_base, test_size=0.3, random_state=42)
# Modelo base
model_base = LogisticRegression(random_state=42)
model_base.fit(X_train_base, y_train_base)
y_pred_base = model_base.predict(X_test_base)
accuracy_base = accuracy_score(y_test_base, y_pred_base)
print(f"\nPrecisión del modelo base: {accuracy_base:.4f}")
# Preparar datos con ingeniería de características avanzada (ejemplo: polinomiales + binning + ratio)
# Combinamos algunas de las técnicas que hemos visto
X_advanced = df.copy()
# Polinomiales e Interacción
poly_advanced = PolynomialFeatures(degree=2, include_bias=False)
X_poly_advanced = poly_advanced.fit_transform(X_advanced[['feature_A', 'feature_B', 'feature_C']])
poly_feature_names_advanced = poly_advanced.get_feature_names_out(['feature_A', 'feature_B', 'feature_C'])
df_poly_advanced = pd.DataFrame(X_poly_advanced, columns=poly_feature_names_advanced)
X_advanced = pd.concat([X_advanced, df_poly_advanced], axis=1)
# Binning
X_advanced['feature_A_binned_quantile'] = pd.qcut(X_advanced['feature_A'], q=5, labels=False, duplicates='drop')
# Ratio
X_advanced['feature_A_div_C'] = X_advanced['feature_A'] / (X_advanced['feature_C'] + 1e-6)
# Eliminamos las características originales para evitar multicolinealidad con las polinomiales, pero no siempre es necesario
X_advanced = X_advanced.drop(columns=['feature_A', 'feature_B', 'feature_C', 'target'])
# Escalar las características avanzadas
scaler_advanced = StandardScaler()
X_advanced_scaled = scaler_advanced.fit_transform(X_advanced)
X_train_advanced, X_test_advanced, y_train_advanced, y_test_advanced = train_test_split(X_advanced_scaled, y_base, test_size=0.3, random_state=42)
# Modelo con características avanzadas
model_advanced = LogisticRegression(random_state=42)
model_advanced.fit(X_train_advanced, y_train_advanced)
y_pred_advanced = model_advanced.predict(X_test_advanced)
accuracy_advanced = accuracy_score(y_test_advanced, y_pred_advanced)
print(f"Precisión del modelo con ingeniería de características avanzada: {accuracy_advanced:.4f}")
# Comparación visual
results = pd.DataFrame({
'Modelo': ['Base', 'Avanzado'],
'Precisión': [accuracy_base, accuracy_advanced]
})
plt.figure(figsize=(7, 5))
sns.barplot(x='Modelo', y='Precisión', data=results, palette='viridis')
plt.title('Comparación de Precisión de Modelos')
plt.ylim(0.5, 1.0)
plt.show()
Como puedes observar en el ejemplo, la aplicación de técnicas de ingeniería de características avanzadas ha resultado en una mejora significativa en la precisión del modelo. Esto demuestra el poder de transformar tus datos de manera inteligente.
✅ Consideraciones y Mejores Prácticas
- Conocimiento del Dominio (Domain Knowledge): Siempre es la herramienta más poderosa para la ingeniería de características. Un experto en el campo puede sugerir relaciones y transformaciones que serían difíciles de descubrir automáticamente.
- Evitar la Fuga de Datos (Data Leakage): Asegúrate de que las características que creas no incluyan información de la variable objetivo que no estaría disponible en un escenario de producción. Por ejemplo, si calculas la media de una columna por grupo, asegúrate de hacerlo solo en el conjunto de entrenamiento si el agrupamiento se basa en el target.
- Escalado de Características: Después de crear nuevas características, especialmente las polinomiales o de ratios, a menudo es crucial escalarlas (ej.,
StandardScaler,MinMaxScaler) antes de alimentar el modelo, para evitar que algunas características dominen debido a sus rangos de valores más amplios. - Selección de Características: Con la ingeniería de características, puedes terminar con una gran cantidad de características. La selección de características es el siguiente paso lógico para identificar las más importantes y eliminar las redundantes o irrelevantes, lo que puede mejorar el rendimiento y la interpretabilidad del modelo.
- Iteración Constante: La ingeniería de características es un proceso iterativo. Experimenta con diferentes transformaciones, evalúa el impacto y repite.
- Automatización: Para conjuntos de datos muy grandes o con muchas características, la ingeniería de características manual puede ser tediosa. Existen herramientas y librerías (como
Featuretoolsotsfresh) que pueden automatizar parcialmente este proceso, generando muchas características candidatas que luego pueden ser seleccionadas.
| Técnica | Descripción | Cuándo Usarla | Librería Python Común |
|---|---|---|---|
| --- | --- | --- | --- |
| Polinomiales | Crear potencias de características existentes. | Cuando sospechas relaciones no lineales o curvas. | sklearn.preprocessing.PolynomialFeatures |
| Interacción | Multiplicar dos o más características. | Cuando la combinación de características tiene un efecto mayor que la suma individual. | sklearn.preprocessing.PolynomialFeatures |
| --- | --- | --- | --- |
| Binning | Discretizar características continuas en bins categóricos. | Para manejar no linealidad, outliers, o simplificar el modelo. | pandas.cut, pandas.qcut |
| Logarítmicas/Monotónicas | Aplicar funciones como log, sqrt, exp a características. | Para normalizar distribuciones sesgadas o reducir rangos amplios. | numpy.log, numpy.sqrt |
| --- | --- | --- | --- |
| Ratios/Lógicas | Combinar características con operaciones aritméticas o booleanas. | Cuando el conocimiento del dominio sugiere relaciones de proporción o condiciones. | pandas (operaciones directas) |
| Agregación por Grupo | Calcular estadísticas (media, std) dentro de grupos de datos. | Cuando una característica categórica define grupos con comportamientos distintivos. | pandas.groupby().transform() |
| --- | --- | --- | --- |
| PCA/Red. Dimensionalidad | Transformar características en un espacio de menor dimensión. | Para manejar multicolinealidad, reducir ruido o acelerar el entrenamiento. | sklearn.decomposition.PCA |
| Clustering | Usar resultados de algoritmos de clustering como nuevas características. | Cuando la estructura intrínseca de los datos (grupos) es importante para la predicción. | sklearn.cluster.KMeans |
¿Cuál es la diferencia entre Selección de Características y Ingeniería de Características?
La **Ingeniería de Características** es el proceso de crear nuevas características o transformar las existentes a partir de los datos crudos, para mejorar el rendimiento del modelo. Es un proceso creativo y a menudo manual.La Selección de Características es el proceso de elegir un subconjunto de características existentes (originales o previamente ingeniadas) que sean más relevantes para el modelo. Su objetivo es reducir la dimensionalidad, eliminar redundancias y mejorar la generalización del modelo. Mientras que la ingeniería crea, la selección elige.
Conclusión ✨
La ingeniería de características avanzada es un arte y una ciencia que te permite transformar datos crudos en información de alto valor predictivo. Dominar estas técnicas no solo mejorará la precisión de tus modelos de Machine Learning, sino que también te dará una comprensión más profunda de tus datos y del problema que intentas resolver.
Recuerda que no existe una solución única para todos los problemas; la clave está en experimentar, validar y usar tu conocimiento del dominio para guiar tus decisiones. ¡Ahora estás equipado para llevar tus habilidades de Machine Learning al siguiente nivel!
Tutoriales relacionados
- Optimización de Hiperparámetros con Grid Search y Random Search en Pythonintermediate18 min
- Detección de Anomalías con Isolation Forest en Python: Guía Completaintermediate15 min
- Introducción al Reconocimiento de Imágenes con Redes Neuronales Convolucionales (CNN) en Kerasbeginner20 min
- Optimización de Algoritmos de Machine Learning con Algoritmos Genéticos en Pythonintermediate25 min
- Clasificación de Texto con Embeddings y Redes Neuronales en Python: ¡De cero a experto!intermediate18 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!