@@ -17,6 +17,8 @@ def test_anomaly_detection_rejects_unsupported_builtin_estimator():
1717 estimator_list = ["lgbm" ],
1818 time_budget = 1 ,
1919 )
20+
21+
2022def test_isolation_forest_rejects_non_anomaly_task ():
2123 automl = AutoML ()
2224
@@ -32,6 +34,7 @@ def test_isolation_forest_rejects_non_anomaly_task():
3234 time_budget = 1 ,
3335 )
3436
37+
3538def test_automl_anomaly_detection_e2e ():
3639 X_normal , _ = make_blobs (
3740 n_samples = 100 ,
@@ -181,3 +184,119 @@ def test_normalize_anomaly_labels_single_class():
181184 normalize_anomaly_labels (np .array ([- 1 , 1 , 1 ])),
182185 np .array ([1 , 0 , 0 ]),
183186 )
187+
188+
189+ def test_anomaly_detection_hpo_rejects_single_class_evaluation_labels ():
190+ rng = np .random .RandomState (42 )
191+ X_train = rng .randn (80 , 2 )
192+ X_val = rng .randn (20 , 2 )
193+
194+ with pytest .raises (
195+ ValueError ,
196+ match = "requires evaluation labels containing both normal and anomaly classes" ,
197+ ):
198+ AutoML ().fit (
199+ X_train = X_train ,
200+ y_train = np .zeros (len (X_train )),
201+ X_val = X_val ,
202+ y_val = np .zeros (len (X_val )),
203+ task = "anomaly_detection" ,
204+ estimator_list = ["isolation_forest" ],
205+ metric = "roc_auc" ,
206+ max_iter = 2 ,
207+ )
208+
209+
210+ def test_anomaly_detection_x_only_hpo_with_training_metric_logging ():
211+ X_normal , _ = make_blobs (
212+ n_samples = 100 ,
213+ centers = 1 ,
214+ cluster_std = 0.5 ,
215+ random_state = 42 ,
216+ )
217+ rng = np .random .RandomState (42 )
218+ X_anomaly = rng .uniform (low = 6 , high = 8 , size = (20 , 2 ))
219+
220+ X_val = np .vstack ([X_normal [:20 ], X_anomaly ])
221+ y_val = np .array ([0 ] * 20 + [1 ] * 20 )
222+
223+ automl = AutoML ()
224+ automl .fit (
225+ X_train = X_normal ,
226+ X_val = X_val ,
227+ y_val = y_val ,
228+ task = "anomaly_detection" ,
229+ estimator_list = ["isolation_forest" ],
230+ metric = "roc_auc" ,
231+ max_iter = 2 ,
232+ log_training_metric = True ,
233+ )
234+
235+ assert automl .best_estimator == "isolation_forest"
236+ assert automl .score (X_val , y_val ) > 0.9
237+
238+
239+ @pytest .mark .parametrize (
240+ "label_location" ,
241+ ["train" , "validation" ],
242+ )
243+ def test_anomaly_detection_rejects_nonnumeric_labels (label_location ):
244+ rng = np .random .RandomState (42 )
245+ X_train = rng .randn (40 , 2 )
246+
247+ if label_location == "train" :
248+ with pytest .raises (
249+ ValueError ,
250+ match = "y_train for anomaly_detection must be numeric" ,
251+ ):
252+ AutoML ().fit (
253+ X_train = X_train ,
254+ y_train = np .array (["normal" ] * 40 ),
255+ task = "anomaly_detection" ,
256+ estimator_list = ["isolation_forest" ],
257+ max_iter = 1 ,
258+ )
259+ else :
260+ X_val = rng .randn (20 , 2 )
261+ y_val = np .array (["normal" ] * 10 + ["anomaly" ] * 10 )
262+
263+ with pytest .raises (
264+ ValueError ,
265+ match = "y_val for anomaly_detection must be numeric" ,
266+ ):
267+ AutoML ().fit (
268+ X_train = X_train ,
269+ X_val = X_val ,
270+ y_val = y_val ,
271+ task = "anomaly_detection" ,
272+ estimator_list = ["isolation_forest" ],
273+ max_iter = 2 ,
274+ )
275+
276+
277+ def test_anomaly_detection_rejects_callable_metric ():
278+ X = np .random .RandomState (42 ).randn (50 , 2 )
279+
280+ def custom_metric (
281+ X_val ,
282+ y_val ,
283+ estimator ,
284+ labels ,
285+ X_train ,
286+ y_train ,
287+ * args ,
288+ ** kwargs ,
289+ ):
290+ return 0.0 , {}
291+
292+ with pytest .raises (
293+ ValueError ,
294+ match = "Callable metrics are not currently supported for anomaly_detection" ,
295+ ):
296+ AutoML ().fit (
297+ X_train = X ,
298+ task = "anomaly_detection" ,
299+ estimator_list = ["isolation_forest" ],
300+ metric = custom_metric ,
301+ max_iter = 1 ,
302+ )
0 commit comments