Adapte seu script R para ser executado 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 Aprendizado de Máquina do Azure.

Você terá que fazer a maioria, se não todas, as alterações descritas em detalhes neste artigo.

Remover a interação do usuário

Seu script R deve ser projetado para ser executado sem supervisão e será executado através do Rscript comando dentro do contêiner. Certifique-se de remover todas as entradas ou saídas interativas do script.

Adicionar análise

Se o script requer qualquer tipo de parâmetro de entrada (a maioria dos scripts faz), passe as entradas para o script através da Rscript chamada.

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 optparse pacote.

O trecho a seguir mostra como:

  • Iniciar o analisador
  • adicionar todas as suas entradas como opções
  • Analise as entradas com os tipos de dados apropriados

Você também pode adicionar padrões, que são úteis para testes. Recomendamos que você adicione um --output parâmetro com um valor padrão de ./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 qualquer um desses parâmetros posteriormente em seu script.

Originar o azureml_utils.R script auxiliar

Você deve originar um script auxiliar chamado azureml_utils.R script 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 MLflow. O script auxiliar fornece um método para recuperar continuamente o token de autenticação, uma vez que o token muda rapidamente em um trabalho em execução. O script auxiliar também permite que você use as funções de log fornecidas na API R MLflow para registrar modelos, parâmetros, tags e artefatos gerais.

  1. Crie o seu ficheiro, 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 Aprendizado de Máquina do Azure pega os dados especificados no envio do trabalho e os monta no contêiner em execução. Portanto, você poderá ler o(s) arquivo(s) de dados como se fossem arquivos locais no contêiner em execução.

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

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

Salvar artefatos de trabalho (imagens, dados, etc.)

Importante

Esta secção não se aplica aos modelos. Consulte as duas seções a seguir para obter instruções de salvamento e registro 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 geradas pelo script R no Aprendizado de Máquina do Azure. Crie um ./outputs diretório para armazenar todos os artefatos gerados (imagens, modelos, dados, etc.) Todos os arquivos salvos serão ./outputs automaticamente incluídos na execução e carregados para o experimento no final da execução. Como você adicionou um valor padrão para o --output parâmetro na seção de parâmetros de entrada, inclua o seguinte trecho de código no script R para criar o output diretório.

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

Depois de criar o diretório, salve seus 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"))

crate os seus modelos com a carrier embalagem

A documentação da API R MLflow especifica que seus modelos R precisam ter o crate tipo de modelo.

  • Se o script R treinar um modelo e você produzir um objeto de crate modelo, precisará dele para poder implantá-lo posteriormente com o Aprendizado de Máquina do Azure.
  • Ao usar a crate função, 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 fable pacote. Para tornar esse modelo chamável quando for implantado, crie um crate local onde você passará no objeto do 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 crated_model objeto é aquele que você registrará.

Registrar modelos, parâmetros, tags ou outros artefatos com a API R MLflow

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

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

Nota

Quando você registra um modelo, o modelo também é salvo e adicionado aos artefatos de execução. Não há necessidade de salvar explicitamente um modelo, a menos que você não o registre.

Para registrar um modelo e/ou parâmetro:

  1. Inicie a execução com mlflow_start_run()
  2. Registrar artefatos com mlflow_log_model, mlflow_log_paramou mlflow_log_batch
  3. Não termine a corrida com mlflow_end_run(). Ignore esta chamada, pois ela atualmente causa um erro.

Por exemplo, para registrar o crated_model objeto conforme criado na seção anterior, você deve incluir o seguinte código no script R:

Gorjeta

Use models como valor para artifact_path ao registrar um modelo, essa é uma prática recomendada (embora você possa nomeá-lo de outra forma).

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 e exemplo de script

Use esses trechos de código como um guia para estruturar seu 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 seu script R, você usará a extensão para a CLI ml do Azure, também conhecida como CLI v2. O ml comando usa um arquivo de definições de trabalho YAML. Para obter mais informações sobre como enviar trabalhos com az mlo , consulte Treinar modelos com a CLI do Azure Machine Learning.

O arquivo de trabalho YAML especifica um ambiente. Você precisará criar esse ambiente em seu espaço de trabalho antes de 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 R devem ter a seguinte especificação para funcionar 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 muitos pacotes R e suas dependências já instaladas.

Importante

Você deve instalar todos os pacotes R que seu 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/')"

Sugestões adicionais

Algumas sugestões adicionais que você pode querer considerar:

  • Use a função de R para tratamento de tryCatch exceções e erros
  • Adicionar log explícito para solução de problemas e depuração

Próximos passos