Anpassen Ihres R-Skripts für die Ausführung in der Produktion

In diesem Artikel erfahren Sie, wie Sie ein vorhandenes R-Skript entsprechend ändern, um es als Auftrag in Azure Machine Learning auszuführen.

Dazu müssen Sie die meisten (oder sogar alle) Änderungen vornehmen, die in diesem Artikel ausführlich beschrieben sind.

Entfernen von Benutzerinteraktion

Ihr R-Skript muss für die unbeaufsichtigte Ausführung konzipiert sein und wird über den Rscript Befehl innerhalb des Containers ausgeführt. Entfernen Sie alle interaktiven Ein- oder Ausgaben aus dem Skript.

Hinzufügen von Analyse

Wenn Ihr Skript Eingabeparameter erfordert (was bei den meisten Skripts der Fall ist), übergeben Sie die Eingaben über den Aufruf Rscript an das Skript.

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

Analysieren Sie die Eingaben in Ihrem R-Skript, und führen Sie die richtigen Typkonvertierungen durch. Wir empfehlen die Verwendung des Pakets optparse.

Der im Anschluss bereitgestellte Codeschnipsel zeigt Folgendes:

  • Initiieren des Parsers
  • Hinzufügen aller Eingaben als Optionen
  • Analysieren der Eingaben mit den entsprechenden Datentypen

Sie können auch Standardwerte hinzufügen. Dies ist beim Testen hilfreich. Es empfiehlt sich, einen Parameter vom Typ --output mit dem Standardwert ./outputs hinzuzufügen, damit alle Ausgaben des Skripts gespeichert werden.

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 ist eine benannte Liste. Sie können jeden dieser Parameter später in Ihrem Skript verwenden.

Einbinden des Hilfsskripts azureml_utils.R

Sie müssen ein Hilfsskript namens azureml_utils.R einbinden. Dieses Skript muss sich im gleichen Arbeitsverzeichnis befinden wie das ausgeführte R-Skript. Das Hilfsskript wird benötigt, damit das ausgeführte R-Skript mit dem MLflow-Server kommunizieren kann. Es bietet eine Möglichkeit zum kontinuierlichen Abrufen des Authentifizierungstokens, da sich das Token in einem ausgeführten Auftrag schnell ändert. Mit dem Hilfsskript können Sie auch die in der R MLflow-API bereitgestellten Protokollierungsfunktionen verwenden, um Modelle, Parameter, Tags und allgemeine Artefakte zu protokollieren.

  1. Erstellen Sie Ihre Datei (azureml_utils.R) mit folgendem 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))
    
  2. Beginnen Sie Ihr R-Skript mit der folgenden Zeile:

source("azureml_utils.R")

Lesen von Datendateien als lokale Dateien

Wenn Sie ein R-Skript als Auftrag ausführen, bindet Azure Machine Learning die bei der Auftragsübermittlung angegebenen Daten im ausgeführten Container ein. Daher können die Datendateien wie lokale Dateien im ausgeführten Container gelesen werden.

  • Stellen Sie sicher, dass Ihre Quelldaten als Datenressource registriert sind.
  • Übergeben Sie in den Auftragsübermittlungsparametern den Namen der Datenressource.
  • Lesen Sie die Dateien wie eine lokale Datei.

Definieren Sie den Eingabeparameter wie im Parameterabschnitt gezeigt. Verwenden Sie den Parameter data-file, um einen vollständigen Pfad anzugeben und read_csv(args$data_file) zum Lesen der Datenressource nutzen zu können.

Speichern von Auftragsartefakten (Bilder, Daten usw.)

Wichtig

Dieser Abschnitt gilt nicht für Modelle. Eine spezifische Anleitung zum Speichern und Protokollieren von Modellen finden Sie in den beiden nachfolgenden Abschnitten.

Sie können beliebige Skriptausgaben wie Datendateien, Bilder, serialisierte R-Objekte usw. speichern, die durch das R-Skript in Azure Machine Learning generiert werden. Erstellen Sie ein Verzeichnis namens ./outputs als Speicherort für alle generierten Artefakte (Bilder, Modelle, Daten usw.). Alle in ./outputs gespeicherten Dateien werden automatisch in die Ausführung einbezogen und am Ende der Ausführung in das Experiment hochgeladen. Da Sie im Abschnitt für die Eingabeparameter einen Standardwert für den Parameter --output hinzugefügt haben, müssen Sie den folgenden Codeschnipsel in Ihr R-Skript einschließen, um das Verzeichnis output zu erstellen:

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

Speichern Sie nach der Erstellung des Verzeichnisses Ihre Artefakte in diesem Verzeichnis. Beispiel:

# 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"))

Verwenden der Funktion crate für Ihre Modelle mit dem Paket carrier

In der Dokumentation zur R MLflow-API ist angegeben, dass Ihre R-Modelle die Modellvariante crate aufweisen müssen.

  • Wenn Ihr R-Skript ein Modell trainiert und Sie ein Modellobjekt erstellen, müssen Sie dafür die Funktion crate verwenden, um es zu einem späteren Zeitpunkt mit Azure Machine Learning bereitstellen können.
  • Geben Sie bei Verwendung der Funktion crate explizite Namespaces an, wenn Sie eine benötigte Paketfunktion aufrufen.

Angenommen, Sie verfügen über ein Zeitreihenmodell-Objekt namens my_ts_model, das mit dem Paket fable erstellt wurde. Damit dieses Modell nach der Bereitstellung aufgerufen werden kann, erstellen Sie ein Element vom Typ crate, an das Sie das Modellobjekt und einen Vorhersagehorizont als Anzahl von Zeiträumen übergeben:

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

Anschließend protokollieren Sie das Objekt crated_model.

Protokollieren von Modellen, Parametern, Tags oder anderen Artefakten mit der R MLflow-API

Sie können nicht nur alle generierten Artefakte speichern, sondern auch Modelle, Tags und Parameter für jede Ausführung protokollieren. Verwenden Sie hierzu die R MLflow-API.

Wenn Sie ein Modell protokollieren, protokollieren Sie das Crate-Modell, das Sie wie im vorherigen Abschnitt beschrieben erstellt haben.

Hinweis

Wenn Sie ein Modell protokollieren, wird das Modell auch gespeichert und den Ausführungsartefakten hinzugefügt. Modelle müssen nicht explizit gespeichert werden (es sei denn, Sie haben sie nicht protokolliert).

So protokollieren Sie ein Modell und/oder einen Parameter:

  1. Starten Sie die Ausführung mit mlflow_start_run().
  2. Protokollieren Sie Artefakte mit mlflow_log_model, mlflow_log_param oder mlflow_log_batch.
  3. Beenden Sie die Ausführung nicht mit mlflow_end_run(). Überspringen Sie diesen Aufruf, da er derzeit einen Fehler verursacht.

Fügen Sie also beispielsweise den folgenden Code in Ihr R-Skript ein, um das im vorherigen Abschnitt erstellte Objekt crated_model zu protokollieren:

Tipp

Verwenden Sie models als Wert für artifact_path, wenn Sie ein Modell protokollieren. Dies ist eine bewährte Methode. (Sie können aber einen anderen Namen verwenden.)

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()

Skriptstruktur und Beispiel

Orientieren Sie sich bei der Strukturierung Ihres R-Skripts an den folgenden Codeschnipseln, um alle in diesem Artikel beschriebenen Änderungen vorzunehmen.

# 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

Erstellen einer Umgebung

Verwenden Sie zum Ausführen Ihres R-Skripts die Erweiterung ml für die Azure CLI (auch „CLI v2“ genannt). Im Befehl ml wird eine YAML-Auftragsdefinitionsdatei verwendet. Weitere Informationen zum Übermitteln von Aufträgen mit az ml finden Sie im Artikel „Trainieren von Modellen mit der CLI, dem SDK und der REST-API von Azure Machine Learning“ unter 4. Übermitteln des Trainingsauftrags.

Die YAML-Auftragsdatei gibt eine Umgebung an. Sie müssen diese Umgebung in Ihrem Arbeitsbereich erstellen, bevor Sie den Auftrag ausführen können.

Sie können die Umgebung in Azure Machine Learning Studio oder über die Azure CLI erstellen.

Bei beiden Methoden wird ein Dockerfile verwendet. Alle Docker-Kontextdateien für R-Umgebungen müssen die folgende Spezifikation aufweisen, damit sie in Azure Machine Learning verwendet werden können:

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/')"

Das Basisimage ist rocker/tidyverse:latest. Darin sind bereits viele R-Pakete und deren Abhängigkeiten installiert.

Wichtig

Alle R-Pakete, die Ihr Skript benötigt, müssen vorab installiert werden. Fügen Sie der Docker-Kontextdatei bei Bedarf weitere Zeilen hinzu.

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

Weitere Empfehlungen

Folgende Punkte sind ggf. ebenfalls empfehlenswert:

  • Verwenden Sie die Funktion tryCatch von R für die Behandlung von Ausnahmen und Fehlern.
  • Fügen Sie eine explizite Protokollierung für die Problembehandlung und das Debuggen hinzu.

Nächste Schritte