Freigeben über


Konvertieren von ML-Experimenten in Python-Produktionscode

GILT FÜR: Python SDK azureml v1

In diesem Tutorial erfahren Sie, wie Sie Jupyter-Notebooks mit der MLOpsPython-Codevorlage und Azure Machine Learning in Python-Skripts konvertieren, damit sie leichter getestet und automatisiert werden können. In der Regel wird dieser Prozess verwendet, um Experiment-/Trainingscode aus einem Jupyter-Notebook in Python-Skripts zu konvertieren. Diese Skripts können dann für Tests und zur CI/CD-Automatisierung in Ihrer Produktionsumgebung verwendet werden.

Für ein Projekt für maschinelles Lernen (Machine Learning) sind Experimente erforderlich, bei denen Hypothesen mit flexiblen Tools wie Jupyter Notebook und realen Datasets überprüft werden. Wenn das Modell für die Produktion bereit ist, sollte der Modellcode in einem Repository für Produktionscode platziert werden. In einigen Fällen muss der Modellcode in Python-Skripts konvertiert werden, damit er in ein Repository für Produktionscode eingefügt werden kann. In diesem Tutorial geht es um einen empfohlenen Ansatz für den Export von Experimentcode in Python-Skripts.

In diesem Tutorial lernen Sie Folgendes:

  • Bereinigen von unwichtigem Code
  • Umgestalten von Jupyter Notebook-Code in Funktionen
  • Erstellen von Python-Skripts für verwandte Aufgaben
  • Erstellen von Komponententests

Voraussetzungen

  • Generieren Sie die MLOpsPython-Vorlage, und verwenden Sie die Notebooks experimentation/Diabetes Ridge Regression Training.ipynb und experimentation/Diabetes Ridge Regression Scoring.ipynb. Diese Notebooks werden als Beispiel für die Konvertierung vom Experiment in die Produktion verwendet. Sie finden diese Notebooks unter https://github.com/microsoft/MLOpsPython/tree/master/experimentation.
  • Installieren von nbconvert. Befolgen Sie die Installationsanleitung auf der Seite Installation im Abschnitt Installing nbconvert (nbconvert installieren).

Entfernen des unwichtigen Codes

Code, der im Rahmen der Experimente geschrieben wird, dient nur explorativen Zwecken. Daher ist der erste Schritt beim Konvertieren von Experimentcode in Produktionscode das Entfernen dieses unwichtigen Codes. Nach dem Entfernen des unwichtigen Codes kann der restliche Code zudem besser verwaltet werden. In diesem Abschnitt entfernen Sie Code aus dem Notebook experimentation/Diabetes Ridge Regression Training.ipynb. Die Anweisungen zum Ausgeben der Form von X und y und die Zelle zum Aufrufen von features.describe dienen nur der Untersuchung von Daten und können entfernt werden. Nach dem Entfernen von unwichtigem Code sollte experimentation/Diabetes Ridge Regression Training.ipynb wie der folgende Code ohne Markdown aussehen:

from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import joblib
import pandas as pd

sample_data = load_diabetes()

df = pd.DataFrame(
    data=sample_data.data,
    columns=sample_data.feature_names)
df['Y'] = sample_data.target

X = df.drop('Y', axis=1).values
y = df['Y'].values

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=0)
data = {"train": {"X": X_train, "y": y_train},
        "test": {"X": X_test, "y": y_test}}

args = {
    "alpha": 0.5
}

reg_model = Ridge(**args)
reg_model.fit(data["train"]["X"], data["train"]["y"])

preds = reg_model.predict(data["test"]["X"])
mse = mean_squared_error(preds, y_test)
metrics = {"mse": mse}
print(metrics)

model_name = "sklearn_regression_model.pkl"
joblib.dump(value=reg, filename=model_name)

Umgestalten von Code in Funktionen

Als zweite Maßnahme muss der Jupyter-Code in Funktionen umgestaltet werden. Die Umgestaltung von Code in Funktionen vereinfacht die Komponententests und verbessert die Verwaltbarkeit des Codes. In diesem Abschnitt gestalten Sie Folgendes um:

  • Notebook „Diabetes Ridge Regression Training“ (experimentation/Diabetes Ridge Regression Training.ipynb)
  • Notebook „Diabetes Ridge Regression Scoring“ (experimentation/Diabetes Ridge Regression Scoring.ipynb)

Umgestalten des Notebooks „Diabetes Ridge Regression Training“ in Funktionen

Führen Sie unter experimentation/Diabetes Ridge Regression Training.ipynb die folgenden Schritte aus:

  1. Erstellen Sie eine Funktion split_data, um den Datenrahmen in Test- und Trainingsdaten zu unterteilen. Die Funktion sollte den Datenrahmen df als Parameter verwenden und ein Verzeichnis mit den Schlüsseln train und test zurückgeben.

    Verschieben Sie den Code unter der Überschrift Aufteilen der Daten in Trainings-/Validierungssätze in die Funktion split_data, und ändern Sie ihn so, dass das Objekt data zurückgegeben wird.

  2. Erstellen Sie eine Funktion train_model, bei der die Parameter data und args verwendet werden und ein trainiertes Modell zurückgegeben wird.

    Verschieben Sie den Code unter der Überschrift Modell mit Trainingssatz trainieren in die Funktion train_model, und ändern Sie ihn so, dass das Objekt reg_model zurückgegeben wird. Entfernen Sie das Verzeichnis args. Die Werte stammen vom Parameter args.

  3. Erstellen Sie eine Funktionget_model_metrics, bei der die Parameter reg_model und data verwendet werden und das Modell ausgewertet wird. Anschließend wird ein Verzeichnis mit Metriken für das trainierte Modell zurückgegeben.

    Verschieben Sie den Code unter der Überschrift Modell mit Validierungssatz überprüfen in die Funktion get_model_metrics, und ändern Sie ihn so, dass das Objekt metrics zurückgegeben wird.

Die drei Funktionen sollten wie folgt lauten:

# Split the dataframe into test and train data
def split_data(df):
    X = df.drop('Y', axis=1).values
    y = df['Y'].values

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=0)
    data = {"train": {"X": X_train, "y": y_train},
            "test": {"X": X_test, "y": y_test}}
    return data


# Train the model, return the model
def train_model(data, args):
    reg_model = Ridge(**args)
    reg_model.fit(data["train"]["X"], data["train"]["y"])
    return reg_model


# Evaluate the metrics for the model
def get_model_metrics(reg_model, data):
    preds = reg_model.predict(data["test"]["X"])
    mse = mean_squared_error(preds, data["test"]["y"])
    metrics = {"mse": mse}
    return metrics

Führen Sie in experimentation/Diabetes Ridge Regression Training.ipynb die folgenden Schritte aus:

  1. Erstellen Sie eine neue Funktion mit dem Namen main, für die keine Parameter verwendet werden und nichts zurückgegeben wird.

  2. Verschieben Sie den Code unter der Überschrift „Daten laden“ in die Funktion main.

  3. Fügen Sie der Funktion main Aufrufe für die neu geschriebenen Funktionen hinzu:

    # Split Data into Training and Validation Sets
    data = split_data(df)
    
    # Train Model on Training Set
    args = {
        "alpha": 0.5
    }
    reg = train_model(data, args)
    
    # Validate Model on Validation Set
    metrics = get_model_metrics(reg, data)
    
  4. Verschieben Sie den Code unter der Überschrift „Modell speichern“ in die Funktion main.

Die Funktion main sollte wie der folgende Code aussehen:

def main():
    # Load Data
    sample_data = load_diabetes()

    df = pd.DataFrame(
        data=sample_data.data,
        columns=sample_data.feature_names)
    df['Y'] = sample_data.target

    # Split Data into Training and Validation Sets
    data = split_data(df)

    # Train Model on Training Set
    args = {
        "alpha": 0.5
    }
    reg = train_model(data, args)

    # Validate Model on Validation Set
    metrics = get_model_metrics(reg, data)

    # Save Model
    model_name = "sklearn_regression_model.pkl"

    joblib.dump(value=reg, filename=model_name)

Zu diesem Zeitpunkt sollte das Notebook keinen Code mehr enthalten, der nicht in einer Funktion enthalten ist (mit Ausnahme von Importanweisungen in der ersten Zelle).

Fügen Sie eine Anweisung hinzu, die die Funktion main aufruft.

main()

Nach der Umgestaltung sollte experimentation/Diabetes Ridge Regression Training.ipynb wie der folgende Code ohne Markdown aussehen:

from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import pandas as pd
import joblib


# Split the dataframe into test and train data
def split_data(df):
    X = df.drop('Y', axis=1).values
    y = df['Y'].values

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=0)
    data = {"train": {"X": X_train, "y": y_train},
            "test": {"X": X_test, "y": y_test}}
    return data


# Train the model, return the model
def train_model(data, args):
    reg_model = Ridge(**args)
    reg_model.fit(data["train"]["X"], data["train"]["y"])
    return reg_model


# Evaluate the metrics for the model
def get_model_metrics(reg_model, data):
    preds = reg_model.predict(data["test"]["X"])
    mse = mean_squared_error(preds, data["test"]["y"])
    metrics = {"mse": mse}
    return metrics


def main():
    # Load Data
    sample_data = load_diabetes()

    df = pd.DataFrame(
        data=sample_data.data,
        columns=sample_data.feature_names)
    df['Y'] = sample_data.target

    # Split Data into Training and Validation Sets
    data = split_data(df)

    # Train Model on Training Set
    args = {
        "alpha": 0.5
    }
    reg = train_model(data, args)

    # Validate Model on Validation Set
    metrics = get_model_metrics(reg, data)

    # Save Model
    model_name = "sklearn_regression_model.pkl"

    joblib.dump(value=reg, filename=model_name)

main()

Umgestalten des Notebooks „Diabetes Ridge Regression Scoring“ in Funktionen

Führen Sie unter experimentation/Diabetes Ridge Regression Scoring.ipynb die folgenden Schritte aus:

  1. Erstellen Sie eine neue Funktion mit dem Namen init, für die keine Parameter verwendet werden und nichts zurückgegeben wird.
  2. Kopieren Sie den Code unter der Überschrift „Load Model“ (Modell laden) in die Funktion init.

Die Funktion init sollte wie der folgende Code aussehen:

def init():
    model_path = Model.get_model_path(
        model_name="sklearn_regression_model.pkl")
    model = joblib.load(model_path)

Ersetzen Sie nach der Erstellung der Funktion init den gesamten Code unter der Überschrift „Load Model“ (Modell laden) wie folgt durch einen Einzelaufruf von init:

init()

Führen Sie unter experimentation/Diabetes Ridge Regression Scoring.ipynb die folgenden Schritte aus:

  1. Erstellen Sie wie folgt eine neue Funktion mit dem Namen run, bei der raw_data und request_headers als Parameter verwendet werden und ein Wörterbuch mit Ergebnissen zurückgegeben wird:

    {"result": result.tolist()}
    
  2. Kopieren Sie den Code unter den Überschriften „Prepare Data“ (Daten aufbereiten) und „Score Data“ (Daten bewerten) in die Funktion run.

    Die Funktion run sollte wie der folgende Code aussehen (denken Sie an das Entfernen der Anweisungen zum Festlegen der Variablen raw_data und request_headers, die später beim Aufrufen der Funktion run verwendet werden):

    def run(raw_data, request_headers):
        data = json.loads(raw_data)["data"]
        data = numpy.array(data)
        result = model.predict(data)
    
        return {"result": result.tolist()}
    

Ersetzen Sie nach der Erstellung der Funktion run den gesamten Code unter den Überschriften „Prepare Data“ (Daten aufbereiten) und „Score Data“ (Daten bewerten) durch den folgenden Code:

raw_data = '{"data":[[1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]]}'
request_header = {}
prediction = run(raw_data, request_header)
print("Test result: ", prediction)

Mit dem obigen Code werden die Variablen raw_data und request_header festgelegt, die Funktion run mit raw_data und request_header wird aufgerufen, und die Vorhersagen werden ausgegeben.

Nach der Umgestaltung sollte experimentation/Diabetes Ridge Regression Scoring.ipynb wie der folgende Code ohne Markdown aussehen:

import json
import numpy
from azureml.core.model import Model
import joblib

def init():
    model_path = Model.get_model_path(
        model_name="sklearn_regression_model.pkl")
    model = joblib.load(model_path)

def run(raw_data, request_headers):
    data = json.loads(raw_data)["data"]
    data = numpy.array(data)
    result = model.predict(data)

    return {"result": result.tolist()}

init()
test_row = '{"data":[[1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]]}'
request_header = {}
prediction = run(test_row, {})
print("Test result: ", prediction)

Als dritte Maßnahme müssen die verwandten Funktionen in Python-Dateien zusammengeführt werden, um die Wiederverwendung von Code zu vereinfachen. In diesem Abschnitt erstellen Sie Python-Dateien für die folgenden Notebooks:

  • Notebook „Diabetes Ridge Regression Training“ (experimentation/Diabetes Ridge Regression Training.ipynb)
  • Notebook „Diabetes Ridge Regression Scoring“ (experimentation/Diabetes Ridge Regression Scoring.ipynb)

Erstellen einer Python-Datei für das Notebook „Diabetes Ridge Regression Training“

Konvertieren Sie Ihr Notebook in ein ausführbares Skript, indem Sie an einer Eingabeaufforderung die folgende Anweisung ausführen, für die das Paket nbconvertund der Pfad experimentation/Diabetes Ridge Regression Training.ipynb verwendet werden:

jupyter nbconvert "Diabetes Ridge Regression Training.ipynb" --to script --output train

Entfernen Sie alle unerwünschten Kommentare, nachdem das Notebook in train.py konvertiert wurde. Ersetzen Sie den Aufruf von main() am Ende der Datei durch einen bedingten Aufruf wie den folgenden Code:

if __name__ == '__main__':
    main()

Ihre Datei train.py sollte wie im folgenden Code aussehen:

from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import pandas as pd
import joblib


# Split the dataframe into test and train data
def split_data(df):
    X = df.drop('Y', axis=1).values
    y = df['Y'].values

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=0)
    data = {"train": {"X": X_train, "y": y_train},
            "test": {"X": X_test, "y": y_test}}
    return data


# Train the model, return the model
def train_model(data, args):
    reg_model = Ridge(**args)
    reg_model.fit(data["train"]["X"], data["train"]["y"])
    return reg_model


# Evaluate the metrics for the model
def get_model_metrics(reg_model, data):
    preds = reg_model.predict(data["test"]["X"])
    mse = mean_squared_error(preds, data["test"]["y"])
    metrics = {"mse": mse}
    return metrics


def main():
    # Load Data
    sample_data = load_diabetes()

    df = pd.DataFrame(
        data=sample_data.data,
        columns=sample_data.feature_names)
    df['Y'] = sample_data.target

    # Split Data into Training and Validation Sets
    data = split_data(df)

    # Train Model on Training Set
    args = {
        "alpha": 0.5
    }
    reg = train_model(data, args)

    # Validate Model on Validation Set
    metrics = get_model_metrics(reg, data)

    # Save Model
    model_name = "sklearn_regression_model.pkl"

    joblib.dump(value=reg, filename=model_name)

if __name__ == '__main__':
    main()

train.py kann nun über ein Terminal aufgerufen werden, indem python train.py ausgeführt wird. Die Funktionen aus train.py können auch aus anderen Dateien aufgerufen werden.

Die Datei train_aml.py aus dem Verzeichnis diabetes_regression/training im MLOpsPython-Repository ruft die in train.py definierten Funktionen im Kontext eines Azure Machine Learning-Experimentauftrags auf. Die Funktionen können auch in Komponententests aufgerufen werden. Dieses Thema wird später in dieser Anleitung behandelt.

Erstellen einer Python-Datei für das Notebook „Diabetes Ridge Regression Scoring“

Konvertieren Sie Ihr Notebook in ein ausführbares Skript, indem Sie an einer Eingabeaufforderung die folgende Anweisung mit dem Paket nbconvert und dem Pfad experimentation/Diabetes Ridge Regression Scoring.ipynb ausführen:

jupyter nbconvert "Diabetes Ridge Regression Scoring.ipynb" --to script --output score

Entfernen Sie alle unerwünschten Kommentare, nachdem das Notebook in score.py konvertiert wurde. Ihre Datei score.py sollte wie im folgenden Code aussehen:

import json
import numpy
from azureml.core.model import Model
import joblib

def init():
    model_path = Model.get_model_path(
        model_name="sklearn_regression_model.pkl")
    model = joblib.load(model_path)

def run(raw_data, request_headers):
    data = json.loads(raw_data)["data"]
    data = numpy.array(data)
    result = model.predict(data)

    return {"result": result.tolist()}

init()
test_row = '{"data":[[1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]]}'
request_header = {}
prediction = run(test_row, request_header)
print("Test result: ", prediction)

model muss eine globale Variable sein, damit sie im gesamten Skript sichtbar ist. Fügen Sie am Anfang der init-Funktion die folgende Anweisung hinzu:

global model

Nachdem Sie die obige Anweisung hinzugefügt haben, sollte die init-Funktion wie der folgende Code aussehen:

def init():
    global model

    # load the model from file into a global object
    model_path = Model.get_model_path(
        model_name="sklearn_regression_model.pkl")
    model = joblib.load(model_path)

Erstellen von Komponententests für jede Python-Datei

Als vierte Maßnahme erstellen Sie Komponententests für Ihre Python-Funktionen. Komponententests schützen Code vor funktionalen Regressionen und vereinfachen die Verwaltung. In diesem Abschnitt erstellen Sie Komponententests für die Funktionen in train.py.

train.py enthält mehrere Funktionen. Wir erstellen jedoch nur einen Komponententest für die Funktion train_model. Dazu verwenden wir das Pytest-Framework in diesem Tutorial. Pytest ist nicht das einzige Python-Framework für Komponententests, aber eines der am häufigsten verwendeten. Weitere Informationen finden Sie unter Pytest.

Ein Komponententest enthält normalerweise drei Hauptaktionen:

  • Objekt anordnen: Erstellen und Einrichten der benötigten Objekte
  • Aktion für ein Objekt durchführen
  • Erwartetes Ergebnis bestätigen

Der Komponententest ruft train_model mit hartcodierten Daten und Argumenten auf und überprüft, ob train_model das erwartete Verhalten aufweist. Dazu wird das resultierende trainierte Modell verwendet, um eine Vorhersage zu treffen und diese Vorhersage mit einem erwarteten Wert zu vergleichen.

import numpy as np
from code.training.train import train_model


def test_train_model():
    # Arrange
    X_train = np.array([1, 2, 3, 4, 5, 6]).reshape(-1, 1)
    y_train = np.array([10, 9, 8, 8, 6, 5])
    data = {"train": {"X": X_train, "y": y_train}}

    # Act
    reg_model = train_model(data, {"alpha": 1.2})

    # Assert
    preds = reg_model.predict([[1], [2]])
    np.testing.assert_almost_equal(preds, [9.93939393939394, 9.03030303030303])

Nächste Schritte

Nachdem Sie nun wissen, wie ein Experiment in Produktionscode konvertiert wird, können Sie über die folgenden Links auf weitere Informationen und die nächsten Schritte zugreifen: