Konwertowanie eksperymentów uczenia maszynowego na produkcyjny kod w języku Python
DOTYCZY: Zestaw SDK języka Python w wersji 1
Z tego samouczka dowiesz się, jak przekonwertować notesy Jupyter na skrypty języka Python, aby ułatwić testowanie i automatyzację przy użyciu szablonu kodu MLOpsPython i usługi Azure Machine Learning. Zazwyczaj ten proces służy do wykonywania eksperymentów/ kodu szkoleniowego z notesu Jupyter i konwertowania go na skrypty języka Python. Te skrypty można następnie używać do testowania i automatyzacji ciągłej integracji/ciągłego wdrażania w środowisku produkcyjnym.
Projekt uczenia maszynowego wymaga eksperymentowania, w którym hipotezy są testowane za pomocą elastycznych narzędzi, takich jak Jupyter Notebook przy użyciu rzeczywistych zestawów danych. Gdy model będzie gotowy do produkcji, kod modelu powinien zostać umieszczony w produkcyjnym repozytorium kodu. W niektórych przypadkach kod modelu musi zostać przekonwertowany na skrypty języka Python, aby można je było umieścić w repozytorium kodu produkcyjnego. W tym samouczku omówiono zalecane podejście do eksportowania kodu eksperymentowania do skryptów języka Python.
Z tego samouczka dowiesz się, jak wykonywać następujące czynności:
- Czysty kod beznessential
- Refaktoryzacja kodu notesu Jupyter Notebook do funkcji
- Tworzenie skryptów języka Python dla powiązanych zadań
- Tworzenie testów jednostkowych
Wymagania wstępne
- Wygeneruj szablon MLOpsPython i użyj
experimentation/Diabetes Ridge Regression Training.ipynb
notesów iexperimentation/Diabetes Ridge Regression Scoring.ipynb
. Te notesy są używane jako przykład konwertowania z eksperymentów na środowisko produkcyjne. Te notesy można znaleźć na stronie https://github.com/microsoft/MLOpsPython/tree/master/experimentation. - Zainstaluj system
nbconvert
. Postępuj zgodnie z instrukcjami instalacji w sekcji Instalowanie aplikacji nbconvert na stronie Instalacja .
Usuń cały beznieściowy kod
Część kodu napisana podczas eksperymentowania jest przeznaczona tylko do celów eksploracyjnych. W związku z tym pierwszym krokiem do przekonwertowania kodu eksperymentalnego na kod produkcyjny jest usunięcie tego kodu beznieściowego. Usunięcie kodu beznieściowego spowoduje również, że kod będzie bardziej konserwowalny. W tej sekcji usuniesz kod z notesu experimentation/Diabetes Ridge Regression Training.ipynb
. Instrukcje drukowania kształtu X
i y
i wywołania features.describe
komórek są tylko do eksploracji danych i można je usunąć. Po usunięciu kodu experimentation/Diabetes Ridge Regression Training.ipynb
bez nessential powinien wyglądać podobnie do następującego kodu bez znaczników markdown:
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)
Refaktoryzacja kodu do funkcji
Po drugie kod Jupyter musi zostać refaktoryzowany do funkcji. Refaktoryzacja kodu do funkcji ułatwia testowanie jednostkowe i ułatwia obsługę kodu. W tej sekcji wykonasz refaktoryzację:
- Notes trenowania regresji diabetes Ridge(
experimentation/Diabetes Ridge Regression Training.ipynb
) - Notes analizy punktacji diabetes Ridge Regression (
experimentation/Diabetes Ridge Regression Scoring.ipynb
)
Refaktoryzacja notesu trenowania regresji diabetes Ridge do funkcji
W experimentation/Diabetes Ridge Regression Training.ipynb
pliku wykonaj następujące kroki:
Utwórz funkcję o nazwie
split_data
, aby podzielić ramkę danych na dane testowe i wytrenować. Funkcja powinna przyjąć ramkędf
danych jako parametr i zwrócić słownik zawierający kluczetrain
itest
.Przenieś kod pod nagłówkiem Split Data into Training and Validation Sets (Podział danych do nagłówka Training and Validation Sets) i
split_data
zmodyfikuj go, aby zwrócićdata
obiekt.Utwórz funkcję o nazwie
train_model
, która przyjmuje parametrydata
iargs
zwraca wytrenowany model.Przenieś kod pod nagłówkiem Training Model on Training Set (Model trenowania) do
train_model
funkcji i zmodyfikuj go, aby zwrócićreg_model
obiekt.args
Usuń słownik, a wartości będą pochodzić z parametruargs
.Utwórz funkcję o nazwie
get_model_metrics
, która przyjmuje parametryreg_model
idata
, i ocenia model, a następnie zwraca słownik metryk dla wytrenowanego modelu.Przenieś kod pod nagłówkiem Validate Model on Validation Set do
get_model_metrics
funkcji i zmodyfikuj go, aby zwrócićmetrics
obiekt.
Trzy funkcje powinny być następujące:
# 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
Nadal w pliku experimentation/Diabetes Ridge Regression Training.ipynb
wykonaj następujące kroki:
Utwórz nową funkcję o nazwie
main
, która nie przyjmuje żadnych parametrów i nie zwraca żadnych parametrów.Przenieś kod pod nagłówkiem "Załaduj dane" do
main
funkcji.Dodaj wywołania dla nowo napisanych funkcji do
main
funkcji:# 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)
Przenieś kod pod nagłówkiem "Zapisz model" do
main
funkcji.
Funkcja main
powinna wyglądać podobnie do następującego kodu:
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)
Na tym etapie w notesie nie powinien znajdować się żaden kod, który nie znajduje się w funkcji, poza instrukcjami import w pierwszej komórce.
Dodaj instrukcję, która wywołuje main
funkcję.
main()
Po refaktoryzacji experimentation/Diabetes Ridge Regression Training.ipynb
powinien wyglądać podobnie do następującego kodu bez znaku markdown:
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()
Refactor Diabetes Ridge Regression Scoring notebook into functions (Refaktoryzacja cukrzycy Ridge Regression Scoring notebook do funkcji)
W experimentation/Diabetes Ridge Regression Scoring.ipynb
pliku wykonaj następujące kroki:
- Utwórz nową funkcję o nazwie
init
, która nie przyjmuje żadnych parametrów i nie zwraca żadnych parametrów. - Skopiuj kod pod nagłówkiem "Load Model" (Załaduj
init
model) do funkcji .
Funkcja init
powinna wyglądać podobnie do następującego kodu:
def init():
model_path = Model.get_model_path(
model_name="sklearn_regression_model.pkl")
model = joblib.load(model_path)
Po utworzeniu init
funkcji zastąp cały kod pod nagłówkiem "Load Model" pojedynczym wywołaniem w init
następujący sposób:
init()
W experimentation/Diabetes Ridge Regression Scoring.ipynb
pliku wykonaj następujące kroki:
Utwórz nową funkcję o nazwie
run
, która przyjmujeraw_data
parametry irequest_headers
jako parametry i zwraca słownik wyników w następujący sposób:{"result": result.tolist()}
Skopiuj kod w nagłówkach "Prepare Data" (Przygotowywanie danych) i "Score Data" (Generowanie wyników danych) do
run
funkcji .Funkcja
run
powinna wyglądać podobnie do następującego kodu (Pamiętaj, aby usunąć instrukcje, które ustawiają zmienneraw_data
irequest_headers
, które będą używane później po wywołaniurun
funkcji):def run(raw_data, request_headers): data = json.loads(raw_data)["data"] data = numpy.array(data) result = model.predict(data) return {"result": result.tolist()}
Po utworzeniu run
funkcji zastąp cały kod w nagłówkach "Prepare Data" (Przygotowywanie danych) i "Score Data" (Generowanie wyników danych) następującym kodem:
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)
Poprzedni kod ustawia zmienne raw_data
i , wywołuje run
funkcję za pomocą raw_data
poleceń i request_header
i request_header
i wyświetla przewidywania.
Po refaktoryzacji experimentation/Diabetes Ridge Regression Scoring.ipynb
powinien wyglądać podobnie do następującego kodu bez znaku markdown:
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)
Łączenie powiązanych funkcji w plikach języka Python
Po trzecie, powiązane funkcje należy scalić z plikami języka Python, aby lepiej ułatwić ponowne użycie kodu. W tej sekcji utworzysz pliki języka Python dla następujących notesów:
- Notes trenowania regresji diabetes Ridge(
experimentation/Diabetes Ridge Regression Training.ipynb
) - Notes analizy punktacji diabetes Ridge Regression (
experimentation/Diabetes Ridge Regression Scoring.ipynb
)
Tworzenie pliku języka Python dla notesu trenowania regresji diabetes Ridge
Przekonwertuj notes na skrypt wykonywalny, uruchamiając następującą instrukcję w wierszu polecenia, która używa nbconvert
pakietu i ścieżki experimentation/Diabetes Ridge Regression Training.ipynb
:
jupyter nbconvert "Diabetes Ridge Regression Training.ipynb" --to script --output train
Po przekonwertowaniu notesu na train.py
program usuń wszelkie niepożądane komentarze. Zastąp wywołanie na main()
końcu pliku wywołaniem warunkowym, tak jak w poniższym kodzie:
if __name__ == '__main__':
main()
Plik train.py
powinien wyglądać podobnie do następującego kodu:
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
można teraz wywołać z poziomu terminalu, uruchamiając polecenie python train.py
.
Funkcje z train.py
programu mogą być również wywoływane z innych plików.
train_aml.py
Plik znaleziony w katalogu w diabetes_regression/training
repozytorium MLOpsPython wywołuje funkcje zdefiniowane w train.py
kontekście zadania eksperymentu usługi Azure Machine Learning. Funkcje można również wywoływać w testach jednostkowych, które zostały omówione w dalszej części tego przewodnika.
Tworzenie pliku języka Python dla notesu analizy regresji diabetes Ridge
Przekonwertuj notes na skrypt wykonywalny, uruchamiając następującą instrukcję w wierszu polecenia, który używa nbconvert
pakietu i ścieżki experimentation/Diabetes Ridge Regression Scoring.ipynb
:
jupyter nbconvert "Diabetes Ridge Regression Scoring.ipynb" --to script --output score
Po przekonwertowaniu notesu na score.py
program usuń wszelkie niepożądane komentarze. Plik score.py
powinien wyglądać podobnie do następującego kodu:
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)
Zmienna model
musi być globalna, aby była widoczna w całym skrycie. Dodaj następującą instrukcję na początku init
funkcji:
global model
Po dodaniu poprzedniej instrukcji init
funkcja powinna wyglądać podobnie do następującego kodu:
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)
Tworzenie testów jednostkowych dla każdego pliku języka Python
Po czwarte, utwórz testy jednostkowe dla funkcji języka Python. Testy jednostkowe chronią kod przed regresjami funkcjonalnymi i ułatwiają konserwację. W tej sekcji utworzysz testy jednostkowe dla funkcji w programie train.py
.
train.py
Zawiera wiele funkcji, ale w tym samouczku train_model
utworzymy tylko jeden test jednostkowy dla funkcji przy użyciu platformy Pytest. Narzędzie Pytest nie jest jedyną strukturą testowania jednostkowego języka Python, ale jest jedną z najczęściej używanych platform. Aby uzyskać więcej informacji, odwiedź stronę Pytest.
Test jednostkowy zwykle zawiera trzy główne akcje:
- Rozmieszczanie obiektu — tworzenie i konfigurowanie niezbędnych obiektów
- Działanie na obiekcie
- Potwierdzanie oczekiwanego
Test jednostkowy wywoła train_model
metodę z niektórymi zakodowanymi danymi i argumentami oraz sprawdzi, czy train_model
działa zgodnie z oczekiwaniami, używając wynikowego wytrenowanego modelu w celu przewidywania i porównywania tego przewidywania z oczekiwaną wartością.
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])
Następne kroki
Teraz, gdy rozumiesz sposób konwertowania z eksperymentu na kod produkcyjny, zobacz następujące linki, aby uzyskać więcej informacji i następne kroki:
- MLOpsPython: tworzenie potoku ciągłej integracji/ciągłego wdrażania w celu trenowania, oceniania i wdrażania własnego modelu przy użyciu usług Azure Pipelines i Azure Machine Learning
- Monitorowanie zadań i metryk eksperymentu usługi Azure Machine Learning
- Monitorowanie i zbieranie danych z punktów końcowych usługi internetowej ML