Optimisation parallèle des hyperparamètres (HPO) sur Container Runtime pour ML

L’API Snowflake ML Hyperparameter Optimization (HPO) est un cadre agnostique de modèle qui permet un réglage efficace et parallélisé des hyperparamètres des modèles Vous pouvez utiliser n’importe quel cadre ou algorithme open-source. Vous pouvez également utiliser les APIs Snowflake ML.

Aujourd’hui, cette API est disponible pour une utilisation au sein d’un Notebook Snowflake configuré pour utiliser le Container Runtime sur Snowpark Container Services (SPCS). Après avoir créé un tel carnet, vous pouvez :

  • Entraîner un modèle à l’aide de n’importe quel paquet open source, et utiliser cette API pour distribuer le processus de réglage des hyperparamètres

  • Entraîner un modèle à l’aide des APIs de formation distribuée ML Snowflake, et mettre à l’échelle HPO tout en mettant à l’échelle chacun des cycles de formation

La charge de travail HPO, lancée à partir du carnet, s’exécute à l’intérieur de Snowpark Container Services sur les instances CPU ou GPU, et s’étend jusqu’aux cœurs (CPUs ou GPUs) disponibles sur un seul nœud du pool de calcul SPCS.

L’API HPO parallélisée offre les avantages suivants :

  • Une seule API qui gère automatiquement toutes les complexités de la distribution de la formation à travers de multiples ressources

  • La possibilité de s’entraîner virtuellement avec pratiquement n’importe quel cadre ou algorithme en utilisant les cadres ML open-source ou les APIs de modèle ML Snowflake

  • Une sélection d’options de réglage et d’échantillonnage, y compris des algorithmes de recherche bayésienne et aléatoire, ainsi que diverses fonctions d’échantillonnage continu et non continu

  • Intégration étroite avec le reste de Snowflake ; par exemple, ingestion efficace des données via les Datasets ou Dataframes de Snowflake et capture automatique du lignage sur ML

Note

Vous pouvez faire évoluer l’exécution de HPO pour utiliser plusieurs nœuds du pool de calcul SPCS. Pour plus d’informations, voir Exécution d’une charge de travail sur un cluster à plusieurs nœuds.

Optimiser les hyperparamètres d’un modèle

Utilisez l’API ML HPO Snowflake pour régler un modèle. Les étapes suivantes illustrent le processus :

  1. Ingérez les données.

  2. Utilisez l’algorithme de recherche pour définir la stratégie utilisée pour optimiser les hyperparamètres.

  3. Définissez la manière dont les hyperparamètres sont échantillonnés.

  4. Configurez le tuner.

  5. Obtenez les hyperparamètres et les paramètres de formation de chaque tâche d’entraînement.

  6. Lancez la tâche d’entraînement.

  7. Obtenez les résultats de la tâche d’entraînement.

Les sections suivantes décrivent les étapes précédentes. Pour un exemple, voir Container Runtime HPO.

Ingérer les données

Utilisez l’objet dataset_map pour ingérer les données dans l’API HPO. L’objet dataset_map est un dictionnaire qui associe l’ensemble de données d’entraînement ou de test à son objet DataConnector Snowflake correspondant. L’objet dataset_map est transmis à la fonction d’entraînement. Voici un exemple d’objet dataset_map :

dataset_map = {
  "x_train": DataConnector.from_dataframe(session.create_dataframe(X_train)),
  "y_train": DataConnector.from_dataframe(
      session.create_dataframe(y_train.to_frame())
  ),
  "x_test": DataConnector.from_dataframe(session.create_dataframe(X_test)),
  "y_test": DataConnector.from_dataframe(
      session.create_dataframe(y_test.to_frame())
  ),
}
Copy

Définir l’algorithme de recherche

Définissez l’algorithme de recherche utilisé pour explorer l’espace des hyperparamètres. L’algorithme utilise les résultats des essais précédents pour déterminer comment configurer les hyperparamètres. Vous pouvez utiliser les algorithmes de recherche suivants :

  • Recherche dans la grille

    Explore une grille pour les valeurs de l’hyperparamètre que vous définissez. L’API HPO évalue toutes les combinaisons possibles d’hyperparamètres. Voici un exemple de grille d’hyperparamètres :

    search_space = {
        "n_estimators": [50, 51],
        "max_depth": [4, 5]),
        "learning_rate": [0.01, 0.3],
    }
    
    Copy

    Dans l’exemple précédent, chaque paramètre a deux valeurs possibles. Il existe 8 (2 * 2 * 2) combinaisons possibles d’hyperparamètres.

  • Optimisation bayésienne

    Utilise un modèle probabiliste pour déterminer l’ensemble suivant d’hyperparamètres à évaluer. L’algorithme utilise les résultats des essais précédents pour déterminer comment configurer les hyperparamètres. Pour plus d’informations sur l’optimisation bayésienne, voir Optimisation bayésienne.

  • Recherche aléatoire

    Échantillonne de manière aléatoire l’espace des hyperparamètres. Il s’agit d’une approche simple et efficace qui fonctionne particulièrement bien avec des espaces de recherche larges ou mixtes (continus ou discrets).

Vous pouvez utiliser le code suivant pour définir l’algorithme de recherche :

from entities import search_algorithm
search_alg = search_algorithm.BayesOpt()
search_alg = search_algorithm.RandomSearch()
search_alg = search_algorithm.GridSearch()
Copy

Définir l’échantillonnage des hyperparamètres

Utilisez les fonctions de l’espace de recherche pour définir la méthode d’échantillonnage des hyperparamètres au cours de chaque essai. Utilisez-les pour décrire la plage et le type de valeurs que les hyperparamètres peuvent prendre.

Les fonctions d’échantillonnage disponibles sont les suivantes :

  • uniform(lower, upper) : Échantillonne une valeur continue uniformément entre la valeur inférieure et la valeur supérieure. Utile pour les paramètres tels que les taux d’abandon ou les forces de régularisation.

  • loguniform(lower, upper) : Échantillonne une valeur dans l’espace logarithmique, idéal pour les paramètres qui s’étendent sur plusieurs ordres de grandeur (par exemple, les taux d’entraînement).

  • randint(lower, upper) : Échantillonne un entier uniformément entre la valeur inférieure (inclusive) et la valeur supérieure (exclusive). Convient aux paramètres discrets tels que le nombre de couches.

  • choice(options) : Sélectionne au hasard une valeur dans une liste fournie. Souvent utilisé pour les paramètres de catégories.

Voici un exemple de la manière dont vous pouvez définir l’espace de recherche à l’aide de la fonction uniform :

search_space = {
    "n_estimators": tune.uniform(50, 200),
    "max_depth": tune.uniform(3, 10),
    "learning_rate": tune.uniform(0.01, 0.3),
}
Copy

Configurer le tuner

Utilisez l’objet TunerConfig pour configurer le tuner. Dans l’objet, vous spécifiez la métrique à optimiser, le mode d’optimisation et les autres paramètres d’exécution. Les options de configuration disponibles sont les suivantes :

  • Métrique : La métrique de performance, telle que la précision ou la perte, que vous optimisez.

  • Mode : Détermine si l’objet est de maximiser ou de minimiser la métrique ("max" ou "min").

  • Algorithme de recherche : Spécifie la stratégie d’exploration de l’espace des hyperparamètres.

  • Nombre d’essais : Définit le nombre total de configurations d’hyperparamètres à évaluer.

  • Concurrence : Définit le nombre d’essais qui peuvent s’exécuter simultanément.

L’exemple de code suivant utilise la bibliothèque d’optimisation bayésienne pour maximiser la précision d’un modèle sur cinq essais.

from snowflake.ml.modeling import tune
tuner_config = tune.TunerConfig(
  metric="accuracy",
  mode="max",
  search_alg=search_algorithm.BayesOpt(
      utility_kwargs={"kind": "ucb", "kappa": 2.5, "xi": 0.0}
  ),
  num_trials=5,
  max_concurrent_trials=1,
)
Copy

Obtenir les hyperparamètres et les métriques d’entraînement

L’API ML HPO Snowflake a besoin des métriques d’entraînement et des hyperparamètres de chaque cycle d’entraînement pour optimiser efficacement les hyperparamètres. Utilisez l’objet TunerContext pour obtenir les hyperparamètres et les métriques d’entraînement. L’exemple suivant crée une fonction d’entraînement pour obtenir les hyperparamètres et les métriques d’entraînement :

def train_func():
  tuner_context = get_tuner_context()
  config = tuner_context.get_hyper_params()
  dm = tuner_context.get_dataset_map()
  ...
  tuner_context.report(metrics={"accuracy": accuracy}, model=model)
Copy

Lancer la tâche d’entraînement

Utilisez l’objet Tuner pour lancer la tâche d’entraînement. L’objet Tuner prend comme arguments la fonction d’entraînement, l’espace de recherche et la configuration du tuner. Vous trouverez ci-dessous un exemple de la manière de lancer la tâche d’entraînement :

from snowflake.ml.modeling import tune
tuner = tune.Tuner(train_func, search_space, tuner_config)
tuner_results = tuner.run(dataset_map=dataset_map)
Copy

Le code précédent répartit la fonction d’entraînement entre les ressources disponibles. Il recueille et résume les résultats des essais et identifie la configuration la plus performante.

Obtenir les résultats de la tâche d’entraînement

Une fois tous les essais terminés, l’objet TunerResults consolide les résultats de chaque essai. Il fournit un accès structuré aux métriques de performance, à la meilleure configuration et au meilleur modèle.

Les attributs disponibles sont les suivants :

  • results : Un DataFrame Pandas contenant les métriques et les configurations pour chaque essai.

  • best_result : Une ligne DataFrame résumant l’essai avec la meilleure performance.

  • best_model : L’instance de modèle associée au meilleur essai, le cas échéant.

Le code suivant permet d’obtenir les résultats, le meilleur modèle et le meilleur résultat :

print(tuner_results.results)
print(tuner_results.best_model)
print(tuner_results.best_result)
Copy

Référence API

Tuner

Voici l’instruction d’importation pour le module Tuner :

from snowflake.ml.modeling.tune import Tuner
Copy

La classe Tuner est la principale interface d’interaction avec l’API container runtime HPO. Pour exécuter une tâche HPO, utilisez le code suivant pour initialiser un objet Tuner et appeler la méthode d’exécution avec les ensembles de données Snowflake.

class Tuner:
  def __init__(
      self,
      train_func: Callable,
      search_space: SearchSpace,
      tuner_config: TunerConfig,
  )

  def run(
      self, dataset_map: Optional[Dict[str, DataConnector]] = None
  ) -> TunerResults
Copy

SearchSpace

Voici l’instruction d’importation pour l’espace de recherche :

from entities.search_space import uniform, choice, loguniform, randint
Copy

Le code suivant définit les fonctions de l’espace de recherche :

def uniform(lower: float, upper: float)
    """
    Sample a float value uniformly between lower and upper.

    Use for parameters where all values in range are equally likely to be optimal.
    Examples: dropout rates (0.1 to 0.5), batch normalization momentum (0.1 to 0.9).
    """


def loguniform(lower: float, upper: float) -> float:
    """
    Sample a float value uniformly in log space between lower and upper.

    Use for parameters spanning several orders of magnitude.
    Examples: learning rates (1e-5 to 1e-1), regularization strengths (1e-4 to 1e-1).
    """


def randint(lower: int, upper: int) -> int:
    """
    Sample an integer value uniformly between lower(inclusive) and upper(exclusive).

    Use for discrete parameters with a range of values.
    Examples: number of layers, number of epochs, number of estimators.
    """



def choice(options: List[Union[float, int, str]]) -> Union[float, int, str]:
    """
    Sample a value uniformly from the given options.

    Use for categorical parameters or discrete options.
    Examples: activation functions ['relu', 'tanh', 'sigmoid']
    """
Copy

TunerConfig

Voici l’instruction d’importation du module TunerConfig :

from snowflake.ml.modeling.tune import TunerConfig
Copy

Utilisez le code suivant pour définir la classe de configuration du tuner :

class TunerConfig:
  """
  Configuration class for the tuning process.

  Attributes:
    metric (str): The name of the metric to optimize. This should correspond
        to a key in the metrics dictionary reported by the training function.

    mode (str): The optimization mode for the metric. Must be either "min"
        for minimization or "max" for maximization.

    search_alg (SearchAlgorithm): The search algorithm to use for
        exploring the hyperparameter space. Defaults to random search.

    num_trials (int): The maximum number of parameter configurations to
        try. Defaults to 5

    max_concurrent_trials (Optional[int]): The maximum number of concurrently running trials per node. If   not specified, it defaults to the total number of nodes in the cluster. This value must be a positive
    integer if provided.


  Example:
      >>> from entities import search_algorithm        >>> from snowflake.ml.modeling.tune import  TunerConfig
      >>> config = TunerConfig(
      ...     metric="accuracy",
      ...     mode="max",
      ...     num_trials=5,
      ...     max_concurrent_trials=1
      ... )
  """
Copy

SearchAlgorithm

Voici l’instruction d’importation de l’algorithme de recherche :

from entities.search_algorithm import BayesOpt, RandomSearch, GridSearch
Copy

Le code suivant crée un objet algorithme de recherche d’optimisation bayésienne :

@dataclass
class BayesOpt():
    """
    Bayesian Optimization class that encapsulates parameters for the acquisition function.

    This class is designed to facilitate Bayesian optimization by configuring
    the acquisition function through a dictionary of keyword arguments.

    Attributes:
        utility_kwargs (Optional[Dict[str, Any]]):
            A dictionary specifying parameters for the utility (acquisition) function.
            If not provided, it defaults to:
                {
                    'kind': 'ucb',   # Upper Confidence Bound acquisition strategy
                    'kappa': 2.576,  # Exploration parameter for UCB
                    'xi': 0.0      # Exploitation parameter
                }
    """
    utility_kwargs: Optional[Dict[str, Any]] = None
Copy

Le code suivant crée un objet algorithme de recherche aléatoire :

@dataclass
class RandomSearch():
    The default and most basic way to do hyperparameter search is via random search.

    Attributes:
Seed or NumPy random generator for reproducible results. If set to None (default), the global generator (np.random) is used.
    random_state: Optional[int] = None
Copy

TunerResults

Voici l’instruction d’importation du module TunerResults :

from entities.tuner_results import TunerResults
Copy

Le code suivant crée un objet TunerResults :

@dataclass
class TunerResults:
    results: pd.DataFrame
    best_result: pd.DataFrame
    best_model: Optional[Any]
Copy

get_tuner_context

Voici l’instruction d’importation du module get_tuner_context :

from snowflake.ml.modeling.tune import get_tuner_context
Copy

Cette méthode d’aide est conçue pour être appelée dans la fonction d’entraînement. Elle renvoie un objet TunerContext qui encapsule plusieurs champs utiles pour l’exécution de l’essai, notamment ce qui suit :

  • Hyperparamètres sélectionnés par le cadre HPO pour l’essai en cours.

  • L’ensemble de données requis pour l’entraînement.

  • Une fonction d’aide pour signaler des métriques, orientant le cadre HPO à suggérer l’ensemble suivant d’hyperparamètres

Le code suivant crée un objet de contexte de tuner :

class TunerContext:
    """
    A centralized context class for managing trial configuration, reporting, and dataset information.
    """

    def get_hyper_params(self) -> Dict[str, Any]:
        """
        Retrieve the configuration dictionary.

        Returns:
            Dict[str, Any]: The configuration dictionary for the trial.
        """
        return self._hyper_params

    def report(self, metrics: Dict[str, Any], model: Optional[Any] = None) -> None:
    """
    Report metrics and optionally the model if provided.

    This method is used to report the performance metrics of a model and, if provided, the model itself.
    The reported metrics will be used to guide the next set of hyperparameters selection in the
    optimization process.

    Args:
        metrics (Dict[str, Any]): A dictionary containing the performance metrics of the model.
            The keys are metric names, and the values are the corresponding metric values.
        model (Optional[Any], optional): The trained model to be reported. Defaults to None.

    Returns:
        None: This method doesn't return anything.
    """

    def get_dataset_map(self) -> Optional[Dict[str, Type[DataConnector]]]:
        """
        Retrieve the dataset mapping.

        Returns:
            Optional[Dict[str, Type[DataConnector]]]: A mapping of dataset names to DataConnector types, if available.
        """
        return self._dataset_map
Copy

Limitations

L’optimisation bayésienne nécessite des espaces de recherche continus et ne fonctionne qu’avec la fonction d’échantillonnage uniforme. Elle est incompatible avec les paramètres discrets échantillonnés à l’aide des méthodes tune.randint ou tune.choice. Pour contourner cette limite, vous pouvez soit utiliser tune.uniform et placer le paramètre à l’intérieur de la fonction d’apprentissage, soit utiliser un algorithme d’échantillonnage qui traite les espaces discrets et continus, tel que tune.RandomSearch.

Résolution des problèmes

Message d’erreur

Causes possibles

Solutions possibles

Configuration non valide de l’espace de recherche : BayesOpt exige que toutes les fonctions d’échantillonnage soient de type « uniforme ».

L’optimisation bayésienne ne fonctionne qu’avec un échantillonnage uniforme, pas avec des échantillons discrets. (Voir Limites ci-dessus)

  • Utilisez tune.uniform et inscrivez le résultat dans votre fonction de formation.

  • Passez à l’algorithme RandomSearch, qui accepte les échantillons discrets et non discrets.

Insuffisance des ressources CPU. Exigence : 16, Disponible : 8. Le nombre de ressources requises et disponibles peut varier.

max_concurrent_trials est fixé à une valeur supérieure à celle des cœurs disponibles.

Suivez les indications fournies par le message d’erreur.

Insuffisance des ressources GPU. Obligatoire : 4, Disponible : 2. Peut faire référence à CPU ou GPU. Le nombre de ressources requises et disponibles peut varier.

max_concurrent_trials est fixé à une valeur supérieure à celle des cœurs disponibles.

Suivez les indications fournies par le message d’erreur.