Add initial anomaly_detection task with IsolationForest - #1567
Muhammad Rashid (PhD) (rashidrao-pk) wants to merge 18 commits into
Conversation
|
@microsoft-github-policy-service agree |
|
cc Kevin Chen (@int-chaos) for review, as discussed in #413. |
There was a problem hiding this comment.
Pull request overview
This PR introduces an initial anomaly detection task type to FLAML, centered around a first estimator implementation based on scikit-learn’s IsolationForest, plus a basic unit test to validate anomaly scoring behavior.
Changes:
- Adds a new task identifier
anomaly_detectionand a correspondingis_anomaly_detection()helper onTask. - Registers a new estimator name
isolation_forestand adds anIsolationForestEstimator(subclassingSKLearnEstimator). - Adds a unit test using synthetic point anomalies to validate prediction output shape and anomaly ranking quality.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| test/automl/test_anomaly_detection.py | Adds a unit test validating IsolationForest-based anomaly scoring on synthetic data. |
| flaml/automl/task/task.py | Introduces the ANOMALY_DETECTION task constant and Task.is_anomaly_detection(). |
| flaml/automl/task/generic_task.py | Registers the isolation_forest estimator and wires anomaly detection defaults (estimator + metric). |
| flaml/automl/model.py | Adds IsolationForestEstimator implementation and exposes score_samples / decision_function. |
|
Thanks for the review. I will fix the search-space init value, formatting, and Spark-dataframe error path. For the |
|
Hi Muhammad Rashid (PhD) (@rashidrao-pk) , could you add an e2e test for anomaly_detection? Thanks a lot! |
|
Thanks for the suggestion. I've added an end-to-end test for The test now exercises the In addition, I reran the test suite and the project's pre-commit checks locally:
Please let me know if you'd like the e2e test to cover any additional scenarios or edge cases. |
| from flaml import AutoML | ||
| from flaml.automl.model import IsolationForestEstimator |
| elif self.is_anomaly_detection(): | ||
| assert split_type in ["auto", "uniform", "time", "group"] | ||
| return split_type if split_type != "auto" else "uniform" |
There was a problem hiding this comment.
Hi Muhammad Rashid (PhD) (@rashidrao-pk) , could you address this comment? Thanks.
| elif self.is_anomaly_detection(): | ||
| return "ap" |
There was a problem hiding this comment.
Hi Muhammad Rashid (PhD) (@rashidrao-pk) , could you address this comment? Thanks.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Suppressed comments (5)
flaml/automl/task/generic_task.py:1096
- The new split type is accepted, but holdout preparation only creates a validation split for classification or regression (
prepare_data()lines 969–1016). For anomaly detection without an explicitX_val,state.X_valremainsNone, and evaluation subsequently callspredict(None). Extend the uniform holdout branch to split anomaly-detection data as well.
elif self.is_anomaly_detection():
assert split_type in ["auto", "uniform", "time", "group"]
return split_type if split_type != "auto" else "uniform"
flaml/automl/task/generic_task.py:1378
apdoes not currently evaluate anomaly scores for this task.get_y_pred()only uses probability scores for binary tasks, so anomaly detection falls through topredict()and supplies hard labels where1means normal and-1means anomalous. With the 0/1 anomaly labels used by the added test, average precision therefore ranks normals as positives. Add anomaly-specific prediction/label polarity handling (for example, using negatedscore_samples) before makingapthe default.
elif self.is_anomaly_detection():
return "ap"
test/automl/test_anomaly_detection.py:6
- This import is unused and will be flagged by the repository's Ruff F401 check. Remove it unless the test directly instantiates the estimator.
from flaml.automl.model import IsolationForestEstimator
flaml/automl/model.py:1531
IsolationForestsupportsn_jobs, so removing it forces every forest fit to run serially and ignores FLAML's configured worker count. Preserve this parameter as the other sklearn forest estimators do.
params.pop("n_jobs", None)
test/automl/test_anomaly_detection.py:64
- The PR adds
decision_function()as part of the public anomaly-detection API, but this end-to-end test only exercisespredict()andscore_samples(). Add a call and shape assertion so regressions in the third exposed method are covered.
preds = automl.predict(X_val)
scores = automl.model.score_samples(X_val)
assert preds.shape == y_val.shape
assert scores.shape == y_val.shape
|
Hi Muhammad Rashid (PhD) (@rashidrao-pk) , could you help addressing the latest comments? Thanks. |
|
Thanks for the follow-up. I've addressed the latest review comments and pushed the updates. The changes now include:
I also verified locally that:
Please let me know if any further adjustments are needed. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Suppressed comments (1)
flaml/automl/model.py:1552
AutoML.score()delegates to this wrapper's inheritedBaseEstimator.score(). With no metric, that method callsIsolationForest.score(), which does not exist; withmetric="ap", it evaluates hardpredict()outputs instead of the continuous-score_samples()used during tuning. Add anomaly-aware score handling so public scoring does not fail or disagree with the optimization metric.
def score_samples(self, X):
X = self._preprocess(X)
return self._model.score_samples(X)
| if self.is_anomaly_detection(): | ||
| if is_spark_dataframe: | ||
| raise ValueError("anomaly_detection does not support Spark dataframes yet. Use numpy/pandas data.") | ||
| estimator_list = ["isolation_forest"] |
| if task.is_anomaly_detection() and eval_metric in ["ap", "roc_auc"]: | ||
| y_pred = -estimator.score_samples(X) |
| scores = automl.model.score_samples(X_val) | ||
| decision_scores = automl.model.decision_function(X_val) |
| def size(cls, config): | ||
| return config.get("n_estimators", 100) |
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
The anomaly-detection integration is not ready to merge yet:
- The advertised label-free flow is unreachable:
AutoML.fit(X_train=X, task="anomaly_detection")is rejected by generic validation beforeIsolationForestEstimator.fit()can ignorey_train. Define and implement X-only fitting semantics, including how model selection works without labeled validation data. - Explicit estimator lists are not task-validated, so classification accepts
isolation_forestand anomaly detection accepts learners such asrf; both fail later through missingpredict_proba()/score_samples()APIs. - Public
AutoML.score()either calls nonexistentIsolationForest.score()or evaluates AP on hard{-1, 1}predictions, disagreeing with the continuous anomaly scores used during tuning. score_samples()anddecision_function()bypass the fittedDataTransformer; add AutoML-level forwarding APIs so they accept the same raw inputs aspredict().- Restrict or correctly implement explicit anomaly metrics. Several currently accepted metrics use inverted hard labels or unsupported probability APIs.
- Strengthen the E2E test to verify anomaly ranking quality and cover X-only fitting/public scoring, not only output shape and label domain.
|
Thanks for the detailed review. I’ve pushed an update addressing the requested anomaly-detection issues. The changes now:
The targeted regression suite currently passes: Commit: 5b892aa Thanks again for the review — happy to make any further adjustments. |
There was a problem hiding this comment.
🟡 Changes recommended
Label-free training-metric logging can fail, and single-class labels currently permit meaningless hyperparameter optimization.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 8/8 changed files
- Comments generated: 2
- Review effort level: Balanced
| unlabeled_anomaly = ( | ||
| task.is_anomaly_detection() | ||
| and self._y_train_all is None | ||
| and self._state.y_val is None | ||
| ) |
| y_train_for_metric = ( | ||
| normalize_anomaly_labels(y_train) | ||
| if task.is_anomaly_detection() and eval_metric in ["ap", "roc_auc"] | ||
| else y_train |
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
I reviewed the commits since my last review. The earlier integration issues are substantially improved, but three blockers remain:
flaml/automl/automl.py:2431: single-class anomaly labels are still accepted for HPO, producing meaningless optimization. Validate the effective holdout/CV labels and require both normalized classes before search.flaml/automl/ml.py:653: supported X-only training with labeled validation crashes whenlog_training_metric=Truebecause training-label normalization receivesNone. Skip training-loss calculation wheny_train is None.flaml/automl/ml.py:307: string anomaly labels are generically label-encoded before anomaly normalization, which can invert their meaning and the resulting metric. Normalize/validate anomaly labels before generic encoding, or explicitly reject nonnumeric labels.
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
The merge from main is conflict-free but does not resolve the outstanding blockers:
flaml/automl/automl.py:2516: single-class anomaly labels still permit meaningless HPO instead of rejecting evaluation data without both classes.flaml/automl/ml.py:658: X-only training with labeled validation andlog_training_metric=Truestill attempts to normalizey_train=Noneand raises.flaml/automl/data.py:397: nonnumeric anomaly labels are still generically label-encoded before anomaly normalization, which can reverse normal/anomaly semantics.
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
The merge from main is conflict-free but the outstanding blockers remain:
flaml/automl/automl.py:2516-2520andflaml/automl/ml.py:303-308: labeled anomaly HPO still accepts single-class labels, producing meaningless AP/ROC-AUC optimization.flaml/automl/ml.py:658-669: X-only training with labeled validation andlog_training_metric=Truestill normalizesy_train=Noneand raises.flaml/automl/data.py:397-406: nonnumeric anomaly labels are still generically label-encoded before anomaly normalization, which can reverse normal/anomaly semantics.
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
Overall review of the complete current PR: changes are still required.
flaml/automl/ml.py:303-308: labeled anomaly HPO accepts single-class{0}or{1}evaluation labels, producing meaningless AP/ROC-AUC optimization. Require the normalized evaluation-label set to be exactly{0, 1}.flaml/automl/ml.py:658-669: supported X-only training with labeled validation andlog_training_metric=Truestill normalizesy_train=Noneand raises. Skip training-loss computation when training labels are absent.flaml/automl/data.py:397-406: genericLabelEncoderprocessing can reverse nonnumeric anomaly-label semantics before normalization. Use an explicit anomaly mapping first or reject nonnumeric labels.
Posted by thinkall-agent-auto-reviewer
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
Overall review of the complete current PR: changes are still required.
flaml/automl/automl.py:2516-2520andflaml/automl/ml.py:303-308: single-class normalized anomaly labels still permit meaningless AP/ROC-AUC HPO. Require the effective evaluation-label set to be exactly{0, 1}.flaml/automl/ml.py:658-669: X-only HPO with labeled validation andlog_training_metric=Truestill calls anomaly-label normalization withy_train=None. Skip training-loss computation when labels are absent.flaml/automl/data.py:397-406andflaml/automl/automl.py:892-893: generic label encoding reverses nonnumeric anomaly semantics and can make prediction fail on-1. Explicitly map anomaly labels before generic encoding, or reject nonnumeric labels.flaml/automl/automl.py:843-846andflaml/automl/model.py:1574-1577: callable anomaly metrics can drive fitting but are ignored or rejected by publicAutoML.score(). Either reject them during validation or support them consistently.
The current formatting workflow also fails because Black rewrites five changed files.
Posted by thinkall-agent-auto-reviewer
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
Overall review of the complete current PR: changes are still required.
flaml/automl/ml.py:295-309: single-class anomaly evaluation labels still permit meaningless AP/ROC-AUC HPO. Require the normalized evaluation-label set to be exactly{0, 1}.flaml/automl/ml.py:658-669: X-only HPO withlog_training_metric=Truestill normalizesy_train=Noneand raises. Skip training-loss calculation when training labels are absent.flaml/automl/data.py:397-406andflaml/automl/automl.py:892-893: generic encoding reverses nonnumeric anomaly semantics and prediction can fail while inverse-transforming-1.flaml/automl/automl.py:843-846,2608-2613: callable anomaly metrics can drive fitting but are not honored by publicAutoML.score().- Current formatting CI fails because Black rewrites five changed files.
Posted by thinkall-agent-auto-reviewer
|
Thanks for the detailed review. I’ve pushed commit e5db672 addressing the remaining anomaly-detection issues:
|
Li Jiang (thinkall)
left a comment
There was a problem hiding this comment.
Overall review of the complete current PR: changes are still required.
flaml/automl/task/generic_task.py:1120,1147-1149andflaml/automl/ml.py:638-644: labeled anomaly holdout/CV uses uniform/KFold splitting, so imbalanced but feasible datasets can produce single-class evaluation folds and fail. Stratify on normalized anomaly labels and validate minority count againstn_splits.flaml/automl/automl.py:2683: label-free mode unconditionally changesmax_iter=0to1, violating the documented explicit no-fit/search-space-only contract. Preserve explicit zero and default only unspecified one-shot requests.flaml/automl/model.py:1500-1585: current formatting CI still fails because Black rewrites mixed line endings. Commit the Black/pre-commit output.
Posted by thinkall-agent-auto-reviewer
Closes #413
This draft PR adds initial support for anomaly detection in FLAML using IsolationForest.
Scope:
anomaly_detectiontask typeis_anomaly_detection()isolation_forestIsolationForestEstimatorsubclassingSKLearnEstimatorpredict,score_samples, anddecision_function1= normal,-1= anomalyNotes:
AutoML.fit(X_train=..., task="anomaly_detection")integration can be completed after maintainer feedback.cc Li Jiang (@thinkall)