Skip to content
Merged
46 changes: 45 additions & 1 deletion flaml/automl/automl.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ def score(

def predict(
self,
X: np.array | DataFrame | list[str] | list[list[str]] | psDataFrame,
X: np.ndarray | DataFrame | list[str] | list[list[str]] | psDataFrame,
**pred_kwargs,
):
"""Predict label from features.
Expand Down Expand Up @@ -848,6 +848,50 @@ def predict_proba(self, X, **pred_kwargs):
proba = self._trained_estimator.predict_proba(X, **pred_kwargs)
return proba

def preprocess(
self,
X: np.ndarray | DataFrame | list[str] | list[list[str]] | psDataFrame,
):
"""Preprocess data using task-level preprocessing.

This method applies task-level preprocessing transformations to the input data,
including handling of data types, sparse matrices, and feature transformations
that were learned during the fit phase. This should be called before any
estimator-level preprocessing.

Args:
X: A numpy array or pandas dataframe or pyspark.pandas dataframe
of featurized instances, shape n * m,
or for time series forecast tasks:
a pandas dataframe with the first column containing
timestamp values (datetime type) or an integer n for
the predict steps (only valid when the estimator is
arima or sarimax). Other columns in the dataframe
are assumed to be exogenous variables (categorical
or numeric).

Returns:
Preprocessed data in the same format as input (numpy array, DataFrame, etc.).

Raises:
AttributeError: If the model has not been fitted yet.

Example:
```python
automl = AutoML()
automl.fit(X_train, y_train, task="classification")

# Apply task-level preprocessing to new data
X_test_preprocessed = automl.preprocess(X_test)
```
"""
if not hasattr(self, "_state") or self._state is None:
raise AttributeError("AutoML instance has not been fitted yet. Please call fit() first.")
if not hasattr(self, "_transformer"):
raise AttributeError("Transformer not initialized. Please call fit() first.")

return self._state.task.preprocess(X, self._transformer)

def add_learner(self, learner_name, learner_class):
"""Add a customized learner.

Expand Down
29 changes: 29 additions & 0 deletions flaml/automl/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,35 @@ def fit(self, X_train, y_train, budget=None, free_mem_ratio=0, **kwargs):
train_time = self._fit(X_train, y_train, **kwargs)
return train_time

def preprocess(self, X):
"""Preprocess data using estimator-level preprocessing.

This method applies estimator-specific preprocessing transformations to the input data.
This is the second level of preprocessing that should be applied after task-level
preprocessing (automl.preprocess()). Different estimator types may apply different
preprocessing steps (e.g., sparse matrix conversion, dataframe handling).

Args:
X: A numpy array or a dataframe of featurized instances, shape n*m.

Returns:
Preprocessed data ready for the estimator's predict/fit methods.

Example:
```python
automl = AutoML()
automl.fit(X_train, y_train, task="classification")

# First apply task-level preprocessing
X_test_task = automl.preprocess(X_test)

# Then apply estimator-level preprocessing
estimator = automl.model
X_test_estimator = estimator.preprocess(X_test_task)
```
"""
return self._preprocess(X)

def predict(self, X, **kwargs):
"""Predict label from features.

Expand Down
97 changes: 97 additions & 0 deletions notebook/preprocess_api_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Example demonstrating the use of FLAML's preprocess() API.

This script shows how to use both task-level and estimator-level preprocessing
APIs exposed by FLAML AutoML.
"""

import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

from flaml import AutoML

# Load and split data
print("Loading breast cancer dataset...")
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

print(f"Training data shape: {X_train.shape}")
print(f"Test data shape: {X_test.shape}")

# Train AutoML model
print("\nTraining AutoML model...")
automl = AutoML()
automl_settings = {
"time_budget": 10, # 10 seconds
"task": "classification",
"metric": "accuracy",
"estimator_list": ["lgbm", "xgboost"],
"verbose": 0,
}
automl.fit(X_train, y_train, **automl_settings)

print(f"Best estimator: {automl.best_estimator}")
print(f"Best accuracy: {1 - automl.best_loss:.4f}")

# Example 1: Using task-level preprocessing
print("\n" + "=" * 60)
print("Example 1: Task-level preprocessing")
print("=" * 60)
X_test_task = automl.preprocess(X_test)
print(f"Original test data shape: {X_test.shape}")
print(f"After task preprocessing: {X_test_task.shape}")

# Example 2: Using estimator-level preprocessing
print("\n" + "=" * 60)
print("Example 2: Estimator-level preprocessing")
print("=" * 60)
estimator = automl.model
X_test_estimator = estimator.preprocess(X_test_task)
print(f"After estimator preprocessing: {X_test_estimator.shape}")

# Example 3: Complete preprocessing pipeline
print("\n" + "=" * 60)
print("Example 3: Complete preprocessing pipeline")
print("=" * 60)
# Apply both levels of preprocessing
X_preprocessed = automl.preprocess(X_test)
X_final = automl.model.preprocess(X_preprocessed)

# Manual prediction using fully preprocessed data
y_pred_manual = automl.model._model.predict(X_final)

# Compare with AutoML's predict method (which does preprocessing internally)
y_pred_auto = automl.predict(X_test)

print(f"Predictions match: {np.array_equal(y_pred_manual, y_pred_auto)}")
print(f"Manual prediction sample: {y_pred_manual[:5]}")
print(f"Auto prediction sample: {y_pred_auto[:5]}")

# Example 4: Using preprocessing for custom inference
print("\n" + "=" * 60)
print("Example 4: Custom inference with preprocessing")
print("=" * 60)
# You might want to apply preprocessing separately for:
# - Debugging
# - Custom inference pipelines
# - Integration with other tools

# Get preprocessed features
X_features = automl.preprocess(X_test)
X_features = automl.model.preprocess(X_features)

# Now you can use these features with the underlying model or for analysis
print(f"Preprocessed features ready for custom use: {X_features.shape}")
print(f"Feature statistics - Mean: {np.mean(X_features):.4f}, Std: {np.std(X_features):.4f}")

print("\n" + "=" * 60)
print("Summary")
print("=" * 60)
print("The preprocess() API allows you to:")
print("1. Apply task-level preprocessing with automl.preprocess()")
print("2. Apply estimator-level preprocessing with estimator.preprocess()")
print("3. Chain both for complete preprocessing pipeline")
print("4. Use preprocessed data for custom inference or analysis")
print("\nNote: Task-level preprocessing should always be applied before")
print(" estimator-level preprocessing.")
Loading
Loading