@@ -400,6 +400,23 @@ def __init__(
400400 if self .global_parameters .verbose >= 3 :
401401 self .logger .debug ("Running hyperparameter search" )
402402
403+ # Define default scores early to handle timeouts in search phase
404+ default_scores = {
405+ "test_accuracy" : np .array ([0.5 ]),
406+ "test_f1" : np .array ([0.5 ]),
407+ "test_auc" : np .array ([0.5 ]),
408+ "fit_time" : np .array ([0 ]),
409+ "score_time" : np .array ([0 ]),
410+ "train_score" : np .array ([0.5 ]),
411+ "test_recall" : np .array ([0.5 ]),
412+ }
413+
414+ failed = False
415+ scores = None
416+
417+ # Initialize start_time early
418+ start_time = time .time ()
419+
403420 try :
404421 # Verify initial index alignment
405422 try :
@@ -435,6 +452,11 @@ def __init__(
435452 # Pass reset data to search
436453 current_algorithm = search .run_search (X_train_reset , y_train_reset )
437454
455+ except TimeoutError :
456+ self .logger .warning ("Timeout occurred during hyperparameter search." )
457+ failed = "Timeout"
458+ scores = default_scores
459+
438460 except Exception as e :
439461 if "dual coefficients or intercepts are not finite" in str (e ):
440462 self .logger .warning (
@@ -454,7 +476,7 @@ def __init__(
454476 # --- PERFORMANCE FIX for testing ---
455477 # If in test_mode, we have already verified that the search runs without crashing.
456478 # We can skip the final, slow cross-validation and return a dummy score.
457- if getattr (self .global_parameters , "test_mode" , False ):
479+ if not failed and getattr (self .global_parameters , "test_mode" , False ):
458480 self .logger .info (
459481 "Test mode enabled. Skipping final cross-validation for speed."
460482 )
@@ -463,7 +485,7 @@ def __init__(
463485 self ._shutdown_h2o_if_needed (current_algorithm )
464486 return
465487
466- if self .global_parameters .verbose >= 3 :
488+ if not failed and self .global_parameters .verbose >= 3 :
467489 self .logger .debug ("Fitting final model" )
468490
469491 # In production, we re-fit the best estimator on the full training data before CV.
@@ -472,15 +494,14 @@ def __init__(
472494
473495 metric_list = self .metric_list
474496
475- # Catch only one class present AUC not defined:
476-
477- if len (np .unique (self .y_train )) < 2 :
497+ # Catch only one class present AUC not defined (check only if not already failed)
498+ if not failed and len (np .unique (self .y_train )) < 2 :
478499 raise ValueError (
479500 "Only one class present in y_train. ROC AUC score is not defined "
480501 "in that case. grid_search_cross_validate>>>cross_validate"
481502 )
482503
483- if self .global_parameters .verbose >= 1 :
504+ if not failed and self .global_parameters .verbose >= 1 :
484505 self .logger .info ("Getting cross validation scores" )
485506 self .logger .debug (
486507 f"X_train shape: { self .X_train .shape } , y_train shape: { self .y_train .shape } "
@@ -490,27 +511,6 @@ def __init__(
490511 # Set a time threshold in seconds
491512 time_threshold = 60 # For example, 60 seconds
492513
493- start_time = time .time ()
494-
495- # Define default scores (e.g., mean score of 0.5 for binary classification)
496- # Default scores if cross-validation fails
497- default_scores = {
498- "test_accuracy" : np .array (
499- [0.5 ]
500- ), # Default to random classifier performance
501- "test_f1" : np .array (
502- [0.5 ]
503- ), # Default F1 score (again, 0.5 for random classification)
504- "test_auc" : np .array (
505- [0.5 ]
506- ), # Default ROC AUC score (0.5 for random classifier)
507- "fit_time" : np .array ([0 ]), # No fitting time if the model fails
508- "score_time" : np .array ([0 ]), # No scoring time if the model fails
509- "train_score" : np .array ([0.5 ]), # Default train score
510- "test_recall" : np .array ([0.5 ]),
511- #'test_auc': [0.5] # ?
512- }
513-
514514 # --- CRITICAL FIX for H2O multiprocessing error ---
515515 # H2O models cannot be pickled and sent to other processes for parallel
516516 # execution with joblib. We must detect if the current algorithm is an
@@ -541,9 +541,10 @@ def __init__(
541541 "H2O or Keras model detected. Forcing n_jobs=1 for final cross-validation."
542542 )
543543
544- failed = False
545-
546544 try :
545+ if failed :
546+ raise TimeoutError
547+
547548 # H2O models require pandas DataFrames with column names, while other
548549 # sklearn models can benefit from using NumPy arrays.
549550 if isinstance (current_algorithm , h2o_model_types ):
@@ -737,17 +738,19 @@ def __init__(
737738 )
738739
739740 # Set default scores if the AdaBoostClassifier fails
741+ failed = True
740742 scores = default_scores # Use default scores
741743
742744 else :
743745 self .logger .error (
744746 f"An unexpected ValueError occurred during cross-validation: { e } " ,
745747 exc_info = True ,
746748 )
749+ failed = True
747750 scores = default_scores # Use default scores for other errors
748751
749752 except RuntimeError as e :
750- raise e # raise h2o errors to aid development
753+ # raise e # raise h2o errors to aid development
751754 # --- FIX for UnboundLocalError with H2OStackedEnsemble ---
752755 # Catch any RuntimeError, which can be raised by H2O models during fit
753756 # (e.g., base model training failure) or predict.
@@ -759,12 +762,18 @@ def __init__(
759762 failed = True
760763 scores = default_scores
761764
765+ except TimeoutError :
766+ self .logger .warning ("Timeout occurred during cross-validation." )
767+ failed = "Timeout"
768+ scores = default_scores
769+
762770 except Exception as e :
763771 # Catch any other general exceptions and log them
764772 self .logger .error (
765773 f"An unexpected error occurred during cross-validation: { e } " ,
766774 exc_info = True ,
767775 )
776+ failed = True
768777 scores = default_scores # Use default scores if an error occurs
769778
770779 # End the timer
@@ -801,7 +810,10 @@ def __init__(
801810 # plot_auc_results(grid.best_estimator_, X_test_orig, self.y_test_orig, cv)
802811
803812 # this should be x_test...?
804- best_pred_orig = current_algorithm .predict (self .X_test ) # exp
813+ try :
814+ best_pred_orig = current_algorithm .predict (self .X_test ) # exp
815+ except Exception :
816+ best_pred_orig = np .zeros (len (self .X_test ))
805817
806818 # Call the update_score_log method on the provided instance
807819 if self .project_score_save_class_instance :
@@ -822,7 +834,10 @@ def __init__(
822834 )
823835
824836 # calculate metric for optimisation
825- auc = metrics .roc_auc_score (self .y_test , best_pred_orig )
837+ try :
838+ auc = metrics .roc_auc_score (self .y_test , best_pred_orig )
839+ except Exception :
840+ auc = 0.5
826841
827842 self .grid_search_cross_validate_score_result = auc
828843
0 commit comments