Skip to content

Development version

You are reading the latest (development) docs. For stable documentation, see stable.

Combine Feature Selection + Hyperparameter Tuning ​

Time: 8 min | Difficulty: Intermediate

What This Solves ​

Running hyperparameter search on all features wastes compute on noise. Running feature selection with default hyperparameters means the selection is biased. The two-stage approach: (1) select features with default params, (2) retune on the selected subset.

Avoid leaking test data

Apply selector.support_ to train data only, then apply the same mask to the test set. Never fit anything on the test set.

Recipe ​

python
import numpy as np
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, train_test_split

from sklearn_genetic import GAFeatureSelectionCV, GASearchCV, EvolutionConfig, RuntimeConfig
from sklearn_genetic.space import Categorical, Continuous, Integer

X, y = make_classification(
    n_samples=1000, n_features=30, n_informative=10, n_redundant=10, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, stratify=y, random_state=42
)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# Stage 1: Feature selection with default estimator params
selector = GAFeatureSelectionCV(
    estimator=RandomForestClassifier(n_estimators=100, random_state=42, n_jobs=-1),
    cv=cv,
    scoring="roc_auc",
    population_size=20,
    generations=15,
    elitism=True,
    verbose=False,
    n_jobs=-1,
    random_state=42,
)
selector.fit(X_train, y_train)
mask = selector.support_
print(f"Stage 1: {mask.sum()} features selected (from {X_train.shape[1]})")

# Apply mask
X_train_sel = X_train[:, mask]
X_test_sel  = X_test[:, mask]

# Stage 2: Hyperparameter tuning on the reduced feature set
param_grid = {
    "n_estimators":      Integer(50, 300),
    "max_depth":         Integer(3, 20),
    "min_samples_leaf":  Integer(1, 15),
    "max_features":      Continuous(0.1, 1.0),
    "class_weight":      Categorical([None, "balanced"]),
}

ga = GASearchCV(
    estimator=RandomForestClassifier(random_state=42, n_jobs=-1),
    param_grid=param_grid,
    scoring="roc_auc",
    cv=cv,
    evolution_config=EvolutionConfig(population_size=20, generations=15, elitism=True),
    runtime_config=RuntimeConfig(n_jobs=-1, verbose=True),
    random_state=42,
)
ga.fit(X_train_sel, y_train)

print(f"Stage 2 Best CV ROC AUC: {ga.best_score_:.4f}")
print(f"Test ROC AUC: {ga.score(X_test_sel, y_test):.4f}")

Key Points ​

  • Same cv object for both stages: Ensures the same fold splits are used.
  • Apply mask to train set, then test set: Never fit the selector or tuner on the test set.
  • Stage 2 can use a different estimator: Select features with a fast estimator (RF), then tune a more expensive one (XGBoost) on the reduced set.
  • Third stage (optional): Validate on a second estimator to confirm the selected features generalize — see the Feature Selection Tutorial.

See Also ​

Released under the MIT License.