Share via


Como fazer o ajuste de hiperparâmetro no pipeline (v2)

APLICA-SE A:Extensão de ML da CLI do Azure v2 (atual)SDK do Python azure-ai-ml v2 (atual)

Neste artigo, você aprenderá a fazer o ajuste de hiperparâmetro no pipeline do Azure Machine Learning.

Pré-requisito

  1. Entender o que é o ajuste de hiperparâmetro e como executá-lo no Azure Machine Learning usando SweepJob.
  2. Entender o que é um pipeline do Azure Machine Learning
  3. Criar um componente de comando que use um hiperparâmetro como entrada.

Como fazer o ajuste de hiperparâmetro no pipeline do Azure Machine Learning

Esta seção explica como fazer o ajuste de hiperparâmetro no pipeline do Azure Machine Learning usando a CLI v2 e o SDK do Python. As duas abordagens compartilham o mesmo pré-requisito: você já tem um componente de comando criado e ele usa hiperparâmetros como entradas. Se você ainda não tem um componente de comando, siga os links abaixo para criar um.

CLI v2

O exemplo usado neste artigo pode ser encontrado no repositório azureml-example. Navegue até [azureml-examples/cli/jobs/pipelines-with-components/pipeline_with_hyperparameter_sweep para verificar o exemplo.

Suponha que você já tem um componente de comando definido em train.yaml. Um arquivo YAML de trabalho de pipeline de duas etapas (treinar e prever) é semelhante ao mostrado abaixo.

$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

O sweep_step é a etapa de ajuste de hiperparâmetro. Seu tipo precisa ser sweep. E trial se refere ao componente de comando definido em train.yaml. No campo search space, podemos ver três hiparâmetros (c_value, kernel e coef) adicionados ao espaço de pesquisa. Após você enviar esse trabalho de pipeline, o Azure Machine Learning executará o componente de avaliação várias vezes para limpar hiperparâmetros com base no espaço de pesquisa na política de término definida em sweep_step. Verifique o esquema YAML do trabalho de limpeza para obter o esquema completo do trabalho de limpeza.

Abaixo está a definição do componente de avaliação (arquivo 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}}

Os hiperparâmetros adicionados ao espaço de pesquisa em pipeline.yml precisam ser entradas para o componente de avaliação. O código-fonte do componente de avaliação está na pasta ./train-src. Neste exemplo, é um arquivo train.py. Esse é o código que será executado em cada avaliação do trabalho de limpeza. Verifique se você registrou em log as métricas no código-fonte do componente de avaliação com exatamente o mesmo nome que o valor primary_metric no arquivo pipeline.yml. Neste exemplo, usamos mlflow.autolog(), que é a maneira recomendada de acompanhar experimentos de ML. Veja mais sobre o mlflow aqui

O snippet de código abaixo é o código-fonte do componente de avaliação.

# 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 do Python

O exemplo do SDK de Python pode ser encontrado no repositório azureml-example. Navegue até azureml-examples/sdk/jobs/pipelines/1c_pipeline_with_hyperparameter_sweep para verificar o exemplo.

No SDK de Python v2 do Azure Machine Learning, você pode habilitar o ajuste de hiperparâmetro para qualquer componente de comando chamando o método .sweep().

O snippet de código abaixo mostra como habilitar a limpeza 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"

Primeiro, carregamos train_component_func definido no arquivo train.yml. Ao criartrain_model, adicionamos c_value, kernel e coef0 ao espaço de pesquisa (linha 15-17). A linha 30-35 define a métrica primária, o algoritmo de amostragem etc.

Verificar o trabalho de pipeline com a etapa de limpeza no Estúdio

Após você enviar um trabalho de pipeline, o widget da CLI ou o SDK fornecerá um link de URL da Web para a interface do usuário do Estúdio. O link levará você ao modo de exibição de grafo de pipeline por padrão.

Para verificar os detalhes da etapa de limpeza, clique duas vezes na etapa de limpeza e navegue até a guia trabalho filho no painel à direita.

Captura de tela do pipeline com o trabalho filho e o nó train_model realçados.

Isso vinculará você à página do trabalho de limpeza, conforme visto na captura de tela abaixo. Navegue até a guia trabalho filho, onde você pode ver as métricas e a lista de todos os trabalhos filho.

Captura de tela da página do trabalho na guia trabalhos filho.

Se um trabalho filho falhar, selecione o nome desse trabalho para acessar a página de detalhes dele (confira a captura de tela abaixo). As informações úteis de depuração estão em Saídas + Logs.

Captura de tela da guia saída + logs de uma execução filho.

Notebooks de exemplo

Próximas etapas