tutoriales.com

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.

Intermedio18 min de lectura8 views
Reportar error

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.
🔥 **Importante:** La ingeniería de características es a menudo el factor diferenciador entre un modelo mediocre y uno excepcional. ¡No la subestimes!

🛠️ 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.).
💡 Consejo: Usa `PolynomialFeatures` de scikit-learn para generar estas características de forma sencilla.
# 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()
Distribución No Lineal 0 1 target feature_A Separación Lineal (A²) 0 1 target feature_A² UMBRAL Clase 0 Clase 1

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()
Comparación de Estrategias de Binning feature_A_binned_width (5 bins) Rangos de igual magnitud Frecuencia feature_B_binned_quantile (4 bins) Mismo número de observaciones Frecuencia Izquierda: Frecuencia variable | Derecha: Frecuencia equilibrada

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()
Distribución Original Valor Variable (X) Frecuencia Log-Transformada Log(X) Densidad

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()
Visualización PCA: PC1 vs PC2 Componente Principal 1 (PC1) Componente Principal 2 (PC2) Target: 0 Target: 1 -2.5 0.0 2.5 -2.0 0.0 2.0

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.

Paso 1: Preprocesar los datos originales y entrenar un modelo base.
Paso 2: Preprocesar los datos con características avanzadas y entrenar un segundo modelo.
Paso 3: Comparar métricas de rendimiento (ej. precisión, F1-score).
# 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()
55% 92% Modelo Base Modelo Avanzado Mejora de Precisión 0% 100%

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 Featuretools o tsfresh) que pueden automatizar parcialmente este proceso, generando muchas características candidatas que luego pueden ser seleccionadas.
TécnicaDescripciónCuándo UsarlaLibrería Python Común
------------
PolinomialesCrear potencias de características existentes.Cuando sospechas relaciones no lineales o curvas.sklearn.preprocessing.PolynomialFeatures
InteracciónMultiplicar dos o más características.Cuando la combinación de características tiene un efecto mayor que la suma individual.sklearn.preprocessing.PolynomialFeatures
------------
BinningDiscretizar características continuas en bins categóricos.Para manejar no linealidad, outliers, o simplificar el modelo.pandas.cut, pandas.qcut
Logarítmicas/MonotónicasAplicar funciones como log, sqrt, exp a características.Para normalizar distribuciones sesgadas o reducir rangos amplios.numpy.log, numpy.sqrt
------------
Ratios/LógicasCombinar 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 GrupoCalcular 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. DimensionalidadTransformar características en un espacio de menor dimensión.Para manejar multicolinealidad, reducir ruido o acelerar el entrenamiento.sklearn.decomposition.PCA
ClusteringUsar 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

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!