Share via


Adaptar o script R para executá-lo em produção

Este artigo explica como usar um script R existente e fazer as alterações apropriadas para executá-lo como um trabalho no Azure Machine Learning.

Você precisará aproveitar ao máximo, se não todas, as alterações descritas em detalhes neste artigo.

Remover interação do usuário

O script R precisa ser projetado para ser executado de maneira autônoma e será executado por meio do comando Rscript no contêiner. Remova todas as entradas ou saídas interativas do script.

Adicionar análise

Se o script exigir qualquer tipo de parâmetro de entrada (a maioria dos scripts exige), transmita as entradas para o script por meio da chamada Rscript.

Rscript <name-of-r-script>.R
--data_file ${{inputs.<name-of-yaml-input-1>}} 
--brand ${{inputs.<name-of-yaml-input-2>}}

No script R, analise as entradas e faça as conversões de tipo adequadas. Recomendamos que você use o pacote optparse.

O seguinte snippet mostra como:

  • iniciar o analisador
  • adicionar todas as entradas como opções
  • analisar as entradas com os tipos de dados apropriados

Você também pode adicionar padrões, que são úteis para teste. Recomendamos que você adicione um parâmetro --output com um valor padrão igual a ./outputs para que qualquer saída do script seja armazenada.

library(optparse)

parser <- OptionParser()

parser <- add_option(
  parser,
  "--output",
  type = "character",
  action = "store",
  default = "./outputs"
)

parser <- add_option(
  parser,
  "--data_file",
  type = "character",
  action = "store",
  default = "data/myfile.csv"
)

parser <- add_option(
  parser,
  "--brand",
  type = "double",
  action = "store",
  default = 1
)
args <- parse_args(parser)

args é uma lista nomeada. Você pode usar um desses parâmetros posteriormente no script.

Gerar o script auxiliar azureml_utils.R

Você precisa gerar um script auxiliar chamado azureml_utils.R no mesmo diretório de trabalho do script R que será executado. O script auxiliar é necessário para que o script R em execução possa se comunicar com o servidor do MLflow. O script auxiliar fornece um método para recuperar continuamente o token de autenticação, pois o token é alterado rapidamente em um trabalho em execução. O script auxiliar também permite que você use as funções de log fornecidas na API do R para MLflow para registrar em log modelos, parâmetros, marcas e artefatos gerais.

  1. Crie o arquivo, azureml_utils.R, com este código:

    # Azure ML utility to enable usage of the MLFlow R API for tracking with Azure Machine Learning (Azure ML). This utility does the following::
    # 1. Understands Azure ML MLflow tracking url by extending OSS MLflow R client.
    # 2. Manages Azure ML Token refresh for remote runs (runs that execute in Azure Machine Learning). It uses tcktk2 R libraray to schedule token refresh.
    #    Token refresh interval can be controlled by setting the environment variable MLFLOW_AML_TOKEN_REFRESH_INTERVAL and defaults to 30 seconds.
    
    library(mlflow)
    library(httr)
    library(later)
    library(tcltk2)
    
    new_mlflow_client.mlflow_azureml <- function(tracking_uri) {
      host <- paste("https", tracking_uri$path, sep = "://")
      get_host_creds <- function () {
        mlflow:::new_mlflow_host_creds(
          host = host,
          token = Sys.getenv("MLFLOW_TRACKING_TOKEN"),
          username = Sys.getenv("MLFLOW_TRACKING_USERNAME", NA),
          password = Sys.getenv("MLFLOW_TRACKING_PASSWORD", NA),
          insecure = Sys.getenv("MLFLOW_TRACKING_INSECURE", NA)
        )
      }
      cli_env <- function() {
        creds <- get_host_creds()
        res <- list(
          MLFLOW_TRACKING_USERNAME = creds$username,
          MLFLOW_TRACKING_PASSWORD = creds$password,
          MLFLOW_TRACKING_TOKEN = creds$token,
          MLFLOW_TRACKING_INSECURE = creds$insecure
        )
        res[!is.na(res)]
      }
      mlflow:::new_mlflow_client_impl(get_host_creds, cli_env, class = "mlflow_azureml_client")
    }
    
    get_auth_header <- function() {
        headers <- list()
        auth_token <- Sys.getenv("MLFLOW_TRACKING_TOKEN")
        auth_header <- paste("Bearer", auth_token, sep = " ")
        headers$Authorization <- auth_header
        headers
    }
    
    get_token <- function(host, exp_id, run_id) {
        req_headers <- do.call(httr::add_headers, get_auth_header())
        token_host <- gsub("mlflow/v1.0","history/v1.0", host)
        token_host <- gsub("azureml://","https://", token_host)
        api_url <- paste0(token_host, "/experimentids/", exp_id, "/runs/", run_id, "/token")
        GET( api_url, timeout(getOption("mlflow.rest.timeout", 30)), req_headers)
    }
    
    
    fetch_token_from_aml <- function() {
        message("Refreshing token")
        tracking_uri <- Sys.getenv("MLFLOW_TRACKING_URI")
        exp_id <- Sys.getenv("MLFLOW_EXPERIMENT_ID")
        run_id <- Sys.getenv("MLFLOW_RUN_ID")
        sleep_for <- 1
        time_left <- 30
        response <- get_token(tracking_uri, exp_id, run_id)
        while (response$status_code == 429 && time_left > 0) {
            time_left <- time_left - sleep_for
            warning(paste("Request returned with status code 429 (Rate limit exceeded). Retrying after ",
                        sleep_for, " seconds. Will continue to retry 429s for up to ", time_left,
                        " second.", sep = ""))
            Sys.sleep(sleep_for)
            sleep_for <- min(time_left, sleep_for * 2)
            response <- get_token(tracking_uri, exp_id)
        }
    
        if (response$status_code != 200){
            error_response = paste("Error fetching token will try again after sometime: ", str(response), sep = " ")
            warning(error_response)
        }
    
        if (response$status_code == 200){
            text <- content(response, "text", encoding = "UTF-8")
            json_resp <-jsonlite::fromJSON(text, simplifyVector = FALSE)
            json_resp$token
            Sys.setenv(MLFLOW_TRACKING_TOKEN = json_resp$token)
            message("Refreshing token done")
        }
    }
    
    clean_tracking_uri <- function() {
        tracking_uri <- httr::parse_url(Sys.getenv("MLFLOW_TRACKING_URI"))
        tracking_uri$query = ""
        tracking_uri <-httr::build_url(tracking_uri)
        Sys.setenv(MLFLOW_TRACKING_URI = tracking_uri)
    }
    
    clean_tracking_uri()
    tcltk2::tclTaskSchedule(as.integer(Sys.getenv("MLFLOW_TOKEN_REFRESH_INTERVAL_SECONDS", 30))*1000, fetch_token_from_aml(), id = "fetch_token_from_aml", redo = TRUE)
    
    # Set MLFlow related env vars
    Sys.setenv(MLFLOW_BIN = system("which mlflow", intern = TRUE))
    Sys.setenv(MLFLOW_PYTHON_BIN = system("which python", intern = TRUE))
    
  2. Inicie o script R com a seguinte linha:

source("azureml_utils.R")

Ler arquivos de dados como arquivos locais

Quando você executa um script R como um trabalho, o Azure Machine Learning usa os dados especificados no envio do trabalho e os monta no contêiner em execução. Portanto, você poderá ler os arquivos de dados como se eles fossem arquivos locais no contêiner em execução.

  • Verifique se os dados de origem estão registrados como um ativo de dados
  • Transmita o ativo de dados pelo nome nos parâmetros de envio do trabalho
  • Leia os arquivos como você normalmente leria um arquivo local

Defina o parâmetro de entrada conforme mostrado na seção de parâmetros. Use o parâmetro data-file para especificar um caminho inteiro, de modo que você possa usar read_csv(args$data_file) para ler o ativo de dados.

Salvar artefatos de trabalho (imagens, dados etc.)

Importante

Esta seção não se aplica aos modelos. Confira as duas seções a seguir para obter instruções de salvamento e log específicas do modelo.

Você pode armazenar saídas de script arbitrárias, como arquivos de dados, imagens, objetos R serializados etc. que são gerados pelo script R no Azure Machine Learning. Crie um diretório ./outputs para armazenar os artefatos gerados (imagens, modelos, dados etc.). Todos os arquivos salvos em ./outputs serão incluídos automaticamente na execução e carregados no experimento no final da execução. Como você adicionou um valor padrão para o parâmetro --output na seção parâmetros de entrada, inclua o snippet de código a seguir no script R para criar o diretório output.

if (!dir.exists(args$output)) {
  dir.create(args$output)
}

Depois de criar o diretório, salve os artefatos nesse diretório. Por exemplo:

# create and save a plot
library(ggplot2)

myplot <- ggplot(...)

ggsave(myplot, 
       filename = file.path(args$output,"forecast-plot.png"))


# save an rds serialized object
saveRDS(myobject, file = file.path(args$output,"myobject.rds"))

Execute crate nos modelos com o pacote carrier

A documentação da API do R para MLflow especifica que os modelos de R precisam ser da cratevariante do modelo.

  • Se o script R treinar um modelo e você produzir um objeto de modelo, será necessário executar crate nele para que ele possa implantá-lo posteriormente com o Azure Machine Learning.
  • Ao usar a função crate, use namespaces explícitos ao chamar qualquer função de pacote necessária.

Digamos que você tenha um objeto de modelo de série temporal chamado my_ts_model, criado com o pacote fable. Para fazer com que esse modelo possa ser chamado quando for implantado, crie um crate em que você transmitirá o objeto de modelo e um horizonte de previsão em número de períodos:

library(carrier)
crated_model <- crate(function(x)
{
  fabletools::forecast(!!my_ts_model, h = x)
})

O objeto crated_model é aquele que você registrará em log.

Registrar em log modelos, parâmetros, marcas ou outros artefatos com a API do R para MLflow

Além de salvar os artefatos gerados, você também pode registrar em log modelos, marcas e parâmetros para cada execução. Use a API do R para MLflow para fazer isso.

Ao registrar um modelo em log, você registra o modelo em contêiner criado conforme descrito na seção anterior.

Observação

Quando você registra um modelo em log, o modelo também é salvo e adicionado aos artefatos de execução. Não é necessário salvar explicitamente um modelo, a menos que você não o tenha registrado em log.

Para registrar um modelo e/ou um parâmetro:

  1. Inicie a execução com mlflow_start_run()
  2. Registre os artefatos em log com mlflow_log_model, mlflow_log_param ou mlflow_log_batch
  3. Não termine a execução com mlflow_end_run(). Ignore essa chamada, pois, atualmente, ela causa um erro.

Por exemplo, para registrar em log o objeto crated_model conforme criado na seção anterior, inclua o seguinte código no script R:

Dica

Use models como valor para artifact_path ao registrar um modelo em log. Essa é uma melhor prática (mesmo que você possa dar outro nome a ele).

mlflow_start_run()

mlflow_log_model(
  model = crated_model, # the crate model object
  artifact_path = "models" # a path to save the model object to
  )

mlflow_log_param(<key-name>, <value>)

# mlflow_end_run() - causes an error, do not include mlflow_end_run()

Estrutura de script e exemplo

Use estes snippets de código como um guia para estruturar o script R, seguindo todas as alterações descritas neste artigo.

# BEGIN R SCRIPT

# source the azureml_utils.R script which is needed to use the MLflow back end
# with R
source("azureml_utils.R")

# load your packages here. Make sure that they are installed in the container.
library(...)

# parse the command line arguments.
library(optparse)

parser <- OptionParser()

parser <- add_option(
  parser,
  "--output",
  type = "character",
  action = "store",
  default = "./outputs"
)

parser <- add_option(
  parser,
  "--data_file",
  type = "character",
  action = "store",
  default = "data/myfile.csv"
)

parser <- add_option(
  parser,
  "--brand",
  type = "double",
  action = "store",
  default = 1
)
args <- parse_args(parser)

# your own R code goes here
# - model building/training
# - visualizations
# - etc.

# create the ./outputs directory
if (!dir.exists(args$output)) {
  dir.create(args$output)
}

# log models and parameters to MLflow
mlflow_start_run()

mlflow_log_model(
  model = crated_model, # the crate model object
  artifact_path = "models" # a path to save the model object to
  )

mlflow_log_param(<key-name>, <value>)

# mlflow_end_run() - causes an error, do not include mlflow_end_run()
## END OF R SCRIPT

Criar um ambiente

Para executar o script R, você usará a extensão ml para a CLI do Azure, também conhecida como CLI v2. O comando ml usa um arquivo de definições de trabalho YAML. Para obter mais informações sobre como enviar trabalhos com o az ml, confira Treinar modelos com a CLI do Azure Machine Learning.

O arquivo de trabalho YAML especifica um ambiente. Você precisará criar esse ambiente no seu workspace para executar o trabalho.

Você pode criar o ambiente no Estúdio do Azure Machine Learning ou com a CLI do Azure.

Seja qual for o método usado, você usará um Dockerfile. Todos os arquivos de contexto do Docker para ambientes do R precisam ter a seguinte especificação para funcionarem no Azure Machine Learning:

FROM rocker/tidyverse:latest

# Install python
RUN apt-get update -qq && \
 apt-get install -y python3-pip tcl tk libz-dev libpng-dev

RUN ln -f /usr/bin/python3 /usr/bin/python
RUN ln -f /usr/bin/pip3 /usr/bin/pip
RUN pip install -U pip

# Install azureml-MLflow
RUN pip install azureml-MLflow
RUN pip install MLflow

# Create link for python
RUN ln -f /usr/bin/python3 /usr/bin/python

# Install R packages required for logging with MLflow (these are necessary)
RUN R -e "install.packages('mlflow', dependencies = TRUE, repos = 'https://cloud.r-project.org/')"
RUN R -e "install.packages('carrier', dependencies = TRUE, repos = 'https://cloud.r-project.org/')"
RUN R -e "install.packages('optparse', dependencies = TRUE, repos = 'https://cloud.r-project.org/')"
RUN R -e "install.packages('tcltk2', dependencies = TRUE, repos = 'https://cloud.r-project.org/')"

A imagem base é rocker/tidyverse:latest, que tem vários pacotes R e as respectivas dependências já instaladas.

Importante

Você precisa instalar todos os pacotes do R que o script precisará executar com antecedência. Adicione mais linhas ao arquivo de contexto do Docker conforme necessário.

RUN R -e "install.packages('<package-to-install>', dependencies = TRUE, repos = 'https://cloud.r-project.org/')"

Outras sugestões

Algumas outras sugestões que talvez você queira considerar:

  • Usar a função tryCatch do R para tratamento de exceção e erro
  • Adicionar um log explícito para solução de problemas e depuração

Próximas etapas