Machine Learning
sklearn-genetic-opt
Python
scikit-learn
feature selection
algoritmos genéticos
GAFeatureSelectionCV

Selección Evolutiva de Características para Machine Learning

Aplica selección evolutiva de características con GAFeatureSelectionCV de sklearn-genetic-opt y encuentra el subconjunto que maximiza el rendimiento del modelo.

16 de abril de 2026·8 min read

Por Qué Importa la Selección de Características

En machine learning, más datos no siempre es mejor. Más características, en cambio, a menudo empeoran el rendimiento. Esto es lo que se conoce como la maldición de la dimensionalidad: a medida que el número de características crece, el espacio de entrada se vuelve tan disperso que los modelos tienen dificultades para aprender patrones generalizables.

Los problemas concretos de tener demasiadas características son varios:

  • Sobreajuste: El modelo memoriza el ruido en lugar de aprender patrones reales.
  • Tiempo de entrenamiento: Más características = más cómputo.
  • Interpretabilidad: Un modelo con 200 variables es difícil de explicar.
  • Multicolinealidad: Características correlacionadas confunden a muchos algoritmos.
  • Ruido: Las características irrelevantes actúan como ruido y degradan las predicciones.

La selección de características resuelve todos estos problemas eligiendo el subconjunto de variables que maximiza el rendimiento del modelo. En este artículo usarás GAFeatureSelectionCV para evolucionar ese subconjunto automáticamente con un algoritmo genético, lo compararás contra SelectKBest y lo combinarás con el ajuste evolutivo de hiperparámetros.

Métodos Tradicionales y Sus Limitaciones

Existen tres familias de métodos clásicos de selección de características:

Métodos de filtro (como SelectKBest): Evalúan cada característica de forma independiente usando una estadística (chi-cuadrado, información mutua, correlación de Pearson). Son rápidos, pero ignoran las interacciones entre variables. Una característica irrelevante por sí sola puede ser muy informativa en combinación con otra.

Métodos wrapper (como RFE): Entrenan el modelo iterativamente, eliminando características en cada paso. Capturan interacciones, pero son costosos computacionalmente: requieren muchos entrenamientos completos del modelo.

Métodos embedded (como LASSO, feature_importances_): La selección ocurre dentro del propio entrenamiento del modelo. Son eficientes pero están ligados a un tipo específico de modelo.

Ninguno de estos métodos es una solución universal. Los de filtro son ingenuos, los wrapper son lentos, y los embedded son específicos del algoritmo.

Selección Evolutiva con GAFeatureSelectionCV

sklearn-genetic-opt ofrece un cuarto enfoque: selección evolutiva de características mediante GAFeatureSelectionCV. La idea es brillante en su simplicidad:

Cada característica del dataset es representada como un bit en un cromosoma binario. Un individuo con 20 características podría verse así: [1, 0, 1, 1, 0, 0, 1, ...], donde 1 significa "usar esta característica" y 0 significa "ignorarla".

El algoritmo genético evoluciona esta población de máscaras binarias, evaluando cada subconjunto de características mediante validación cruzada. Con el tiempo, converge hacia el subconjunto que maximiza el rendimiento del modelo.

Este enfoque tiene ventajas claras sobre los métodos clásicos:

  • Captura interacciones entre características (como los métodos wrapper).
  • Es agnóstico al modelo (como los métodos de filtro).
  • Es eficiente: No necesita probar todos los 2^n subconjuntos posibles.

Ejemplo Completo con el Dataset Breast Cancer

Usaremos el dataset clásico de diagnóstico de cáncer de mama de scikit-learn, que tiene 30 características y 569 muestras:

python
from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn_genetic import GAFeatureSelectionCV, EvolutionConfig

# Cargar datos
data = load_breast_cancer()
X, y = data.data, data.target
feature_names = data.feature_names

print(f"Dimensiones originales: {X.shape}")
# Dimensiones originales: (569, 30)

# Preprocesamiento
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Split
X_train, X_test, y_train, y_test = train_test_split(
    X_scaled, y, test_size=0.2, random_state=42, stratify=y
)

# Configurar la selección evolutiva de características
selector = GAFeatureSelectionCV(
    estimator=SVC(kernel="rbf", gamma="auto"),
    cv=5,
    scoring="accuracy",
    evolution_config=EvolutionConfig(
        population_size=20,
        generations=15,
        crossover_probability=0.8,
        mutation_probability=0.1,
        elitism=True,
        keep_top_k=2,
    ),
)

# Ajustar
selector.fit(X_train, y_train)

El proceso puede tardar unos minutos dependiendo de tu hardware, ya que está entrenando 20 SVMs × 15 generaciones × 5 folds = hasta 1.500 modelos (aunque el caché de evaluaciones reduce esto en la práctica).

Interpretando los Resultados

Una vez ajustado el selector, tienes acceso a varios atributos clave:

python
# Máscara booleana: True para las características seleccionadas
print("Máscara de soporte:", selector.support_)
# [True, False, True, True, False, True, ...]

# Índices de las características seleccionadas
selected_indices = selector.support_.nonzero()[0]
print("Índices seleccionados:", selected_indices)

# Nombres de las características seleccionadas
selected_features = feature_names[selector.support_]
print("Características seleccionadas:")
for feat in selected_features:
    print(f"  - {feat}")

# Número de características seleccionadas
n_selected = selector.support_.sum()
print(f"\nCaracterísticas seleccionadas: {n_selected} de {X.shape[1]}")

# Mejor puntuación de validación cruzada
print(f"Mejor CV score: {selector.best_score_:.4f}")

# El mejor estimador entrenado con las características seleccionadas
best_model = selector.best_estimator_

Un resultado típico podría mostrar que el algoritmo seleccionó 12-15 de las 30 características, manteniendo o mejorando la accuracy respecto al modelo completo.

Usando transform() para Aplicar la Selección

La interfaz de GAFeatureSelectionCV sigue el patrón transformer de scikit-learn. Puedes usar transform() para aplicar la misma selección a cualquier dataset:

python
# Transformar datos de entrenamiento
X_train_selected = selector.transform(X_train)
print(f"Shape antes: {X_train.shape}")  # (455, 30)
print(f"Shape después: {X_train_selected.shape}")  # (455, 14)

# Transformar datos de test (IMPORTANTE: misma transformación)
X_test_selected = selector.transform(X_test)

# Evaluar el mejor modelo sobre el test set
test_score = best_model.score(X_test_selected, y_test)
print(f"\nAccuracy en test (con selección): {test_score:.4f}")

# Comparar con el modelo sin selección
baseline = SVC(kernel="rbf", gamma="auto")
baseline.fit(X_train, y_train)
baseline_score = baseline.score(X_test, y_test)
print(f"Accuracy en test (sin selección): {baseline_score:.4f}")

Un punto crítico: nunca apliques fit_transform() sobre los datos de test. El selector aprendió qué características usar en el training set. El test set debe transformarse con exactamente las mismas características para una evaluación honesta.

También puedes usar selector.predict() directamente, ya que internamente aplica la transformación antes de predecir:

python
# Equivalente a selector.transform(X_test) + best_model.predict(...)
y_pred = selector.predict(X_test)

Comparación con SelectKBest

Para ver el valor de la selección evolutiva, comparemos con SelectKBest usando información mutua:

python
from sklearn.feature_selection import SelectKBest, mutual_info_classif
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score

# Probar distintos valores de k
results = {}
for k in [5, 10, 14, 20, 30]:
    pipeline = Pipeline([
        ("selector", SelectKBest(mutual_info_classif, k=k)),
        ("clf", SVC(kernel="rbf", gamma="auto")),
    ])
    scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring="accuracy")
    results[k] = scores.mean()
    print(f"SelectKBest (k={k:2d}): {scores.mean():.4f} ± {scores.std():.4f}")

# GAFeatureSelectionCV ya tiene su puntuación
print(f"\nGAFeatureSelectionCV ({n_selected} features): {selector.best_score_:.4f}")

La diferencia clave es que SelectKBest evalúa cada característica en aislamiento (su relación estadística con el target). GAFeatureSelectionCV evalúa subconjuntos completos de características, capturando el efecto de las interacciones. En datasets con características correlacionadas o con efectos de interacción, la selección evolutiva suele ganar.

Además, con SelectKBest tienes que elegir k a priori. Con GAFeatureSelectionCV, el número de características seleccionadas emerge del proceso evolutivo.

Consejos para Obtener Mejores Resultados

Escala los datos primero. Muchos algoritmos (SVM, KNN, regresión logística) son sensibles a la escala de las características. Aplica StandardScaler o MinMaxScaler antes de ajustar el selector.

Empieza con una población pequeña. Para datasets con muchas características, empieza con population_size=20 y generations=15 y aumenta si ves que el algoritmo no ha convergido.

Usa un modelo rápido como estimador. Para la fase de selección de características, elige un estimador rápido (como LogisticRegression o RandomForestClassifier con pocos árboles). Una vez identificadas las mejores características, puedes entrenar el modelo definitivo (incluso uno más lento como SVM o XGBoost) con esas características.

python
from sklearn.linear_model import LogisticRegression

# Selección rápida con LogisticRegression
selector_fast = GAFeatureSelectionCV(
    estimator=LogisticRegression(max_iter=1000),
    cv=5,
    scoring="accuracy",
    evolution_config=EvolutionConfig(population_size=20, generations=10),
)
selector_fast.fit(X_train, y_train)

# Entrenamiento final con SVM sobre las características seleccionadas
X_train_sel = selector_fast.transform(X_train)
X_test_sel = selector_fast.transform(X_test)
final_model = SVC(kernel="rbf", C=10)
final_model.fit(X_train_sel, y_train)
print(f"Accuracy final: {final_model.score(X_test_sel, y_test):.4f}")

Combina con GASearchCV. El patrón más potente es correr primero GAFeatureSelectionCV para identificar el mejor subconjunto de características, y luego correr GASearchCV sobre ese dataset reducido para ajustar los hiperparámetros. Cada paso se vuelve más rápido porque la dimensionalidad de entrada es menor.

python
from sklearn_genetic import GASearchCV
from sklearn_genetic.space import Continuous

# Paso 1: Seleccionar características
selector.fit(X_train, y_train)
X_train_sel = selector.transform(X_train)
X_test_sel = selector.transform(X_test)

# Paso 2: Ajustar hiperparámetros sobre el dataset reducido
hp_search = GASearchCV(
    estimator=SVC(kernel="rbf"),
    param_grid={
        "C": Continuous(0.1, 100),
        "gamma": Continuous(0.001, 1.0),
    },
    cv=5,
    scoring="accuracy",
    evolution_config=EvolutionConfig(population_size=20, generations=15),
)
hp_search.fit(X_train_sel, y_train)
print(f"Accuracy en test optimizada: {hp_search.best_estimator_.score(X_test_sel, y_test):.4f}")

Conclusión

GAFeatureSelectionCV lleva la selección de características a un nivel superior:

  • Automático: No necesitas decidir cuántas características usar ni cuáles.
  • Inteligente: Explora interacciones entre características, no solo importancias individuales.
  • Compatible: Sigue la interfaz de scikit-learn — fit, transform, predict.
  • Combinable: Puede usarse junto con GASearchCV para optimizar características e hiperparámetros.

La selección de características es a menudo el paso más ignorado en los proyectos de machine learning, pero puede marcar la diferencia entre un modelo que sobreajusta y uno que generaliza bien. La evolución natural tardó millones de años en optimizar los genomas. Tú puedes optimizar tu conjunto de características en minutos.


GAFeatureSelectionCV es parte de sklearn-genetic-opt. Para optimizar también los hiperparámetros en el subconjunto seleccionado, consulta Ajusta Tu Modelo de Scikit-learn Usando Algoritmos Evolutivos.


Sobre el autor

Rodrigo Arenas es arquitecto de software e ingeniero de machine learning en Medellín, Colombia. Construye productos, plataformas y software open source para IA, Data Engineering y Machine Learning — es el creador de Ciaren, sklearn-genetic-opt y PyWorkforce.



Rodrigo Arenas — ML Engineer & Open Source Maintainer

¿Estás trabajando en algo similar? Puedo ayudarte a llevarlo a producción.

¿Vas a construir una plataforma de IA o de datos?

Productos, plataformas y software open source para IA, Data Engineering y Machine Learning.

Ver proyectos
Visión

Construyendo un ecosistema de productos y software open source para IA, Data Engineering y Machine Learning.

Proyectos
sklearn-genetic-optPyWorkforceCiaren

Iterixs (En construcción)

© 2026 Rodrigo Arenas