Adapter votre script R pour qu’il s’exécute en production
Cet article explique comment prendre un script R existant et apporter les modifications appropriées pour l’exécuter en tant que travail dans Azure Machine Learning.
Vous devrez effectuer la plupart, si ce n’est la totalité, des modifications décrites en détail dans cet article.
Supprimer l’interaction utilisateur
Votre script R doit être conçu pour fonctionner sans assistance et être exécuté au moyen de la commande Rscript
dans le conteneur. Veillez à supprimer du script les entrées ou sorties interactives.
Ajouter l’analyse
Si votre script nécessite un type de paramètre d’entrée (c’est le cas de la plupart des scripts), passez les entrées dans le script via l’appel Rscript
.
Rscript <name-of-r-script>.R
--data_file ${{inputs.<name-of-yaml-input-1>}}
--brand ${{inputs.<name-of-yaml-input-2>}}
Dans votre script R, analysez les entrées et effectuez les conversions de type appropriées. Nous vous recommandons d’utiliser le package optparse
.
L’extrait de code suivant vous montre comment :
- lancer l’analyseur ;
- ajouter toutes vos entrées sous forme d’options ;
- analyser les entrées avec les types de données appropriés.
Vous pouvez également ajouter des valeurs par défaut, qui sont pratiques pour les tests. Nous vous recommandons d’ajouter un paramètre --output
avec ./outputs
comme valeur par défaut, afin que toute sortie du script soit stockée.
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
est une liste nommée. Vous pouvez utiliser tous ces paramètres ultérieurement dans votre script.
Utiliser le script d’assistance azureml_utils.R
comme source
Vous devez utiliser un script d’assistance nommé script azureml_utils.R
comme source dans le même répertoire de travail que le script R qui sera exécuté. Le script d’assistance est nécessaire pour que le script R en cours d’exécution puisse communiquer avec le serveur MLflow. Le script d’assistance fournit une méthode permettant de récupérer en continu le jeton d’authentification, car il change rapidement dans un travail en cours d’exécution. Le script d’assistance vous permet également d’utiliser les fonctions de journalisation fournies dans l’API R MLflow pour consigner les modèles, les paramètres, les étiquettes et les artefacts généraux.
Créez votre fichier,
azureml_utils.R
, au moyen de ce code :# 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))
Démarrez votre script R avec la ligne suivante :
source("azureml_utils.R")
Lire les fichiers de données en tant que fichiers locaux
Lorsque vous exécutez un script R en tant que travail, Azure Machine Learning prend les données que vous spécifiez dans l’envoi du travail, puis les monte sur le conteneur en cours d’exécution. Vous pourrez ainsi lire le ou les fichiers de données, comme s’il s’agissait de fichiers locaux sur le conteneur en cours d’exécution.
- Vérifiez que vos données sources sont inscrites comme ressource de données.
- Passez la ressource de données par nom dans les paramètres d’envoi du travail.
- Lisez les fichiers comme vous le feriez normalement avec un fichier local.
Définissez le paramètre d’entrée comme indiqué dans la section des paramètres. Utilisez le paramètre data-file
pour spécifier un chemin complet, afin que vous puissiez utiliser read_csv(args$data_file)
pour lire la ressource de données.
Enregistrer les artefacts de travail (images, données, etc.)
Important
Cette section ne s’applique pas aux modèles. Consultez les deux sections suivantes pour obtenir des instructions d’enregistrement et de journalisation spécifiques au modèle.
Vous pouvez stocker des sorties de script arbitraires, telles que des fichiers de données, des images, des objets R sérialisés, etc. ; elles sont générées par le script R dans Azure Machine Learning. Créez un répertoire ./outputs
pour stocker tous les artefacts générés (images, modèles, données, etc.). Tous les fichiers enregistrés dans ./outputs
seront automatiquement inclus dans l’exécution, et chargés dans l’expérience à la fin de l’exécution. Étant donné que vous avez ajouté une valeur par défaut pour le paramètre --output
à la section paramètres d’entrée, incluez l’extrait de code suivant dans votre script R pour créer le répertoire output
.
if (!dir.exists(args$output)) {
dir.create(args$output)
}
Après avoir créé le répertoire, enregistrez vos artefacts dans ce répertoire. Par exemple :
# 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
vos modèles avec le package carrier
La documentation de l’API R MLflow précise que vos modèles R doivent être du type saveur de modèle crate
.
- Si votre script R entraîne un modèle et que vous produisez un objet de modèle, vous devez le
crate
pour pouvoir le déployer ultérieurement avec Azure Machine Learning. - Lorsque vous utilisez la fonction
crate
, utilisez des espaces de noms explicites lors de l’appel d’une fonction de package dont vous avez besoin.
Supposons que vous ayez un objet de modèle de série chronologique nommé my_ts_model
, créé avec le package fable
. Pour rendre ce modèle joignable lors de son déploiement, créez un élément crate
dans lequel vous passerez l’objet de modèle et un horizon de prévision en nombre de périodes :
library(carrier)
crated_model <- crate(function(x)
{
fabletools::forecast(!!my_ts_model, h = x)
})
L’objet crated_model
est celui que vous allez consigner.
Journaliser modèles, paramètres, étiquettes ou d’autres artefacts avec l’API R MLflow
En plus d’enregistrer tous les artefacts générés, vous pouvez également enregistrer des modèles, des étiquettes et des paramètres pour chaque exécution. Utilisez l’API R MLflow pour ce faire.
Lorsque vous journalisez un modèle, vous consignez dans le journal l’objet modèle emballé que vous avez créé, comme décrit à la section précédente.
Notes
Lorsque vous journalisez un modèle, le modèle est également enregistré et ajouté aux artefacts d’exécution. Il n’est pas nécessaire d’enregistrer explicitement un modèle, sauf si vous ne l’avez pas journalisé.
Pour enregistrer un modèle et/ou un paramètre :
- Démarrez l’exécution avec
mlflow_start_run()
. - Journalisez les artefacts avec
mlflow_log_model
,mlflow_log_param
oumlflow_log_batch
. - Ne terminez pas l’exécution avec
mlflow_end_run()
. Ignorez cet appel, car il provoque actuellement une erreur.
Par exemple, pour consigner l’objet crated_model
tel qu’il a été créé à la section précédente, vous devez inclure le code suivant dans votre script R :
Conseil
Utilisez models
comme valeur pour artifact_path
lors de la journalisation d’un modèle ; il s’agit d’une bonne pratique (même si vous pouvez le nommer autrement.)
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()
Structure et exemple de script
Utilisez ces extraits de code comme guide pour structurer votre script R, tout en suivant l’ensemble des modifications décrites dans cet article.
# 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
Créer un environnement
Pour exécuter votre script R, vous allez utiliser l’extension ml
pour Azure CLI, également appelée CLI v2. La commande ml
utilise un fichier de définition de travail YAML. Pour plus d’informations sur l’envoi de travaux avec az ml
, consultez Entraîner des modèles avec l’interface CLI Azure Machine Learning.
Le fichier de travail YAML précise un environnement. Vous devez créer cet environnement dans votre espace de travail avant de pouvoir exécuter le travail.
Vous pouvez créer l’environnement dans Azure Machine Learning studio ou avec l’interface Azure CLI.
Quelle que soit la méthode dont vous vous servez, vous utiliserez un fichier Dockerfile. Tous les fichiers de contexte Docker destinés aux environnements R doivent comporter la spécification suivante pour fonctionner sur 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/')"
L’image de base est rocker/tidyverse:latest
, qui comprend de nombreux packages R avec leurs dépendances déjà installées.
Important
Vous devez installer tous les packages R dont votre script a besoin pour s’exécuter à l’avance. Ajoutez d’autres lignes au fichier de contexte Docker en fonction des besoins.
RUN R -e "install.packages('<package-to-install>', dependencies = TRUE, repos = 'https://cloud.r-project.org/')"
Suggestions supplémentaires
Voici quelques suggestions supplémentaires que vous pouvez envisager :
- Utiliser la fonction
tryCatch
de R pour la gestion des exceptions et des erreurs - Ajouter une journalisation explicite pour la résolution des problèmes et le débogage