Cómo realizar el ajuste de hiperparámetros en la canalización (v2)

SE APLICA A:Extensión ML de la CLI de Azure v2 (actual)SDK de Python azure-ai-ml v2 (actual)

En este artículo, aprenderá a realizar el ajuste de hiperparámetros en la canalización de Azure Machine Learning.

Requisito previo

  1. Comprenda qué es el ajuste de hiperparámetros y cómo realizar el ajuste de hiperparámetros en Azure Machine Learning usando SweepJob.
  2. Descripción de lo que es una canalización de Azure Machine Learning
  3. Cree un componente de comando que tome hiperparámetros como entrada.

Cómo realizar el ajuste de hiperparámetros en la canalización de Azure Machine Learning

Esta sección explica cómo hacer el ajuste de hiperparámetros en la canalización de Azure Machine Learning mediante la CLI v2 y el SDK de Python. Ambos enfoques comparten el mismo requisito previo: ya tienen un componente de comando creado y el componente de comando toma hiperparámetros como entradas. Si aún no tiene un componente de comando. Siga los vínculos siguientes para crear primero un componente de comando.

CLI v2

El ejemplo que se usa en este artículo se puede encontrar en el repositorio azureml-example. Vaya a [azureml-examples/cli/jobs/pipelines-with-components/pipeline_with_hyperparameter_sweep para comprobar el ejemplo.

Supongamos que ya tiene un componente de comando definido en train.yaml. Un archivo YAML de trabajo de canalización de dos pasos (entrenamiento y predicción) es similar al siguiente.

$schema: https://azuremlschemas.azureedge.net/latest/pipelineJob.schema.json
type: pipeline
display_name: pipeline_with_hyperparameter_sweep
description: Tune hyperparameters using TF component
settings:
    default_compute: azureml:cpu-cluster
jobs:
  sweep_step:
    type: sweep
    inputs:
      data: 
        type: uri_file
        path: wasbs://datasets@azuremlexamples.blob.core.windows.net/iris.csv
      degree: 3
      gamma: "scale"
      shrinking: False
      probability: False
      tol: 0.001
      cache_size: 1024
      verbose: False
      max_iter: -1
      decision_function_shape: "ovr"
      break_ties: False
      random_state: 42
    outputs:
      model_output:
      test_data:
    sampling_algorithm: random
    trial: ./train.yml
    search_space:
      c_value:
        type: uniform
        min_value: 0.5
        max_value: 0.9
      kernel:
        type: choice
        values: ["rbf", "linear", "poly"]
      coef0:
        type: uniform
        min_value: 0.1
        max_value: 1
    objective:
      goal: minimize
      primary_metric: training_f1_score
    limits:
      max_total_trials: 5
      max_concurrent_trials: 3
      timeout: 7200

  predict_step:
    type: command
    inputs:
      model: ${{parent.jobs.sweep_step.outputs.model_output}}
      test_data: ${{parent.jobs.sweep_step.outputs.test_data}}
    outputs:
      predict_result:
    component: ./predict.yml

sweep_step es el paso para el ajuste de hiperparámetros. Su tipo debe ser sweep. Y trial hace referencia al componente de comando definido en train.yaml. En el campo search space podemos ver que se agregan tres hisparámetros (c_value, kernely coef) al espacio de búsqueda. Después de enviar este trabajo de canalización, Azure Machine Learning ejecutará el componente de prueba varias veces para barrir hiperparámetros en función del espacio de búsqueda y finalizar la directiva que definió en sweep_step. Compruebe el esquema YAML del trabajo de barrido para obtener el esquema completo del trabajo de barrido.

A continuación se muestra la definición del componente de prueba (archivo train.yml).

$schema: https://azuremlschemas.azureedge.net/latest/commandComponent.schema.json
type: command

name: train_model
display_name: train_model
version: 1

inputs: 
  data:
    type: uri_folder
  c_value:
    type: number
    default: 1.0
  kernel:
    type: string
    default: rbf
  degree:
    type: integer
    default: 3
  gamma:
    type: string
    default: scale
  coef0: 
    type: number
    default: 0
  shrinking:
    type: boolean
    default: false
  probability:
    type: boolean
    default: false
  tol:
    type: number
    default: 1e-3
  cache_size:
    type: number
    default: 1024
  verbose:
    type: boolean
    default: false
  max_iter:
    type: integer
    default: -1
  decision_function_shape:
    type: string
    default: ovr
  break_ties:
    type: boolean
    default: false
  random_state:
    type: integer
    default: 42

outputs:
  model_output:
    type: mlflow_model
  test_data:
    type: uri_folder
  
code: ./train-src

environment: azureml://registries/azureml/environments/sklearn-1.0/labels/latest

command: >-
  python train.py 
  --data ${{inputs.data}}
  --C ${{inputs.c_value}}
  --kernel ${{inputs.kernel}}
  --degree ${{inputs.degree}}
  --gamma ${{inputs.gamma}}
  --coef0 ${{inputs.coef0}}
  --shrinking ${{inputs.shrinking}}
  --probability ${{inputs.probability}}
  --tol ${{inputs.tol}}
  --cache_size ${{inputs.cache_size}}
  --verbose ${{inputs.verbose}}
  --max_iter ${{inputs.max_iter}}
  --decision_function_shape ${{inputs.decision_function_shape}}
  --break_ties ${{inputs.break_ties}}
  --random_state ${{inputs.random_state}}
  --model_output ${{outputs.model_output}}
  --test_data ${{outputs.test_data}}

Los hiperparámetros agregados al espacio de búsqueda en pipeline.yml deben ser entradas para el componente de prueba. El código fuente del componente de prueba se encuentra en la carpeta ./train-src. En este ejemplo, es un archivo único train.py. Este es el código que se ejecutará en cada prueba del trabajo de barrido. Asegúrese de que ha registrado las métricas en el código fuente del componente de prueba con exactamente el mismo nombre que el valor primary_metric en el archivo pipeline.yml. En este ejemplo, usamos mlflow.autolog(), que es la manera recomendada de realizar un seguimiento de los experimentos de ML. Obtenga más información sobre mlflow aquí

El fragmento de código que se puede ver a continuación es el código fuente del componente de prueba.

# imports
import os
import mlflow
import argparse

import pandas as pd
from pathlib import Path

from sklearn.svm import SVC
from sklearn.model_selection import train_test_split

# define functions
def main(args):
    # enable auto logging
    mlflow.autolog()

    # setup parameters
    params = {
        "C": args.C,
        "kernel": args.kernel,
        "degree": args.degree,
        "gamma": args.gamma,
        "coef0": args.coef0,
        "shrinking": args.shrinking,
        "probability": args.probability,
        "tol": args.tol,
        "cache_size": args.cache_size,
        "class_weight": args.class_weight,
        "verbose": args.verbose,
        "max_iter": args.max_iter,
        "decision_function_shape": args.decision_function_shape,
        "break_ties": args.break_ties,
        "random_state": args.random_state,
    }

    # read in data
    df = pd.read_csv(args.data)

    # process data
    X_train, X_test, y_train, y_test = process_data(df, args.random_state)

    # train model
    model = train_model(params, X_train, X_test, y_train, y_test)
    # Output the model and test data
    # write to local folder first, then copy to output folder

    mlflow.sklearn.save_model(model, "model")

    from distutils.dir_util import copy_tree

    # copy subdirectory example
    from_directory = "model"
    to_directory = args.model_output

    copy_tree(from_directory, to_directory)

    X_test.to_csv(Path(args.test_data) / "X_test.csv", index=False)
    y_test.to_csv(Path(args.test_data) / "y_test.csv", index=False)


def process_data(df, random_state):
    # split dataframe into X and y
    X = df.drop(["species"], axis=1)
    y = df["species"]

    # train/test split
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=random_state
    )

    # return split data
    return X_train, X_test, y_train, y_test


def train_model(params, X_train, X_test, y_train, y_test):
    # train model
    model = SVC(**params)
    model = model.fit(X_train, y_train)

    # return model
    return model


def parse_args():
    # setup arg parser
    parser = argparse.ArgumentParser()

    # add arguments
    parser.add_argument("--data", type=str)
    parser.add_argument("--C", type=float, default=1.0)
    parser.add_argument("--kernel", type=str, default="rbf")
    parser.add_argument("--degree", type=int, default=3)
    parser.add_argument("--gamma", type=str, default="scale")
    parser.add_argument("--coef0", type=float, default=0)
    parser.add_argument("--shrinking", type=bool, default=False)
    parser.add_argument("--probability", type=bool, default=False)
    parser.add_argument("--tol", type=float, default=1e-3)
    parser.add_argument("--cache_size", type=float, default=1024)
    parser.add_argument("--class_weight", type=dict, default=None)
    parser.add_argument("--verbose", type=bool, default=False)
    parser.add_argument("--max_iter", type=int, default=-1)
    parser.add_argument("--decision_function_shape", type=str, default="ovr")
    parser.add_argument("--break_ties", type=bool, default=False)
    parser.add_argument("--random_state", type=int, default=42)
    parser.add_argument("--model_output", type=str, help="Path of output model")
    parser.add_argument("--test_data", type=str, help="Path of output model")

    # parse args
    args = parser.parse_args()

    # return args
    return args


# run script
if __name__ == "__main__":
    # parse args
    args = parse_args()

    # run main function
    main(args)

SDK de Python

El ejemplo del SDK de Python se puede encontrar en el repositorio azureml-example. Vaya a azureml-examples/sdk/jobs/pipelines/1c_pipeline_with_hyperparameter_sweep para comprobar el ejemplo.

En Azure Machine Learning SDK de Python v2, puede habilitar el ajuste de hiperparámetros para cualquier componente de comando mediante una llamada al método .sweep().

En el fragmento de código siguiente se muestra cómo habilitar el barrido para train_model.

train_component_func = load_component(source="./train.yml")
score_component_func = load_component(source="./predict.yml")

# define a pipeline
@pipeline()
def pipeline_with_hyperparameter_sweep():
    """Tune hyperparameters using sample components."""
    train_model = train_component_func(
        data=Input(
            type="uri_file",
            path="wasbs://datasets@azuremlexamples.blob.core.windows.net/iris.csv",
        ),
        c_value=Uniform(min_value=0.5, max_value=0.9),
        kernel=Choice(["rbf", "linear", "poly"]),
        coef0=Uniform(min_value=0.1, max_value=1),
        degree=3,
        gamma="scale",
        shrinking=False,
        probability=False,
        tol=0.001,
        cache_size=1024,
        verbose=False,
        max_iter=-1,
        decision_function_shape="ovr",
        break_ties=False,
        random_state=42,
    )
    sweep_step = train_model.sweep(
        primary_metric="training_f1_score",
        goal="minimize",
        sampling_algorithm="random",
        compute="cpu-cluster",
    )
    sweep_step.set_limits(max_total_trials=20, max_concurrent_trials=10, timeout=7200)

    score_data = score_component_func(
        model=sweep_step.outputs.model_output, test_data=sweep_step.outputs.test_data
    )


pipeline_job = pipeline_with_hyperparameter_sweep()

# set pipeline level compute
pipeline_job.settings.default_compute = "cpu-cluster"

En primer lugar, cargamos train_component_func definidas en el archivo train.yml. Al crear train_model, agregamos c_valuekernel y coef0 al espacio de búsqueda (línea 15-17). La línea 30-35 define la métrica principal, el algoritmo de muestra, etc.

Comprobación del trabajo de canalización con el paso de barrido en Studio

Después de enviar un trabajo de canalización, el SDK o el widget de la CLI le proporcionarán un vínculo de dirección URL web a la interfaz de usuario de Studio. El vínculo le guiará a la vista de gráfico de canalización de forma predeterminada.

Para comprobar los detalles del paso de barrido, haga doble clic en el paso de barrido y vaya a la pestaña de trabajo secundario en el panel de la derecha.

Captura de pantalla de la canalización con el trabajo secundario y el nodo train_model resaltado.

Esto le vinculará a la página de trabajo de barrido, como se muestra en la captura de pantalla siguiente. Vaya a la pestaña del trabajo secundario, aquí puede ver las métricas de todos los trabajos secundarios y la lista de todos los trabajos secundarios.

Captura de pantalla de la página de trabajo en la pestaña de trabajos secundarios.

Si se ha producido un error en los trabajos secundarios, seleccione el nombre de ese trabajo secundario para especificar la página de detalles de ese trabajo secundario específico (consulte la siguiente captura de pantalla). La información de depuración útil se encuentra en Salidas y registros.

Captura de pantalla de la pestaña de salida + registros de una ejecución secundaria.

Cuadernos de ejemplo

Pasos siguientes