Materiały kursów
Materiały / Azure ML (polski)
Instrukcja do tego ćwiczeniaPobierz notatnik (.ipynb)

Trenowanie modeli

Celem uczenia maszynowego jest wytrenowanie modeli predykcyjnych, z których korzystają potem aplikacje. W Azure Machine Learning modele trenujesz skryptami, używając popularnych bibliotek - Scikit-Learn, TensorFlow, PyTorch i innych. Takie skrypty treningowe uruchamiasz jako zadania (job), dzięki czemu metryki i pliki wyjściowe - a przede wszystkim sam wytrenowany model - są śledzone. Powstały model możesz następnie zarejestrować w obszarze roboczym.

Połączenie z obszarem roboczym

Na początek połącz się z obszarem roboczym przy użyciu Azure ML SDK v2.

Uwaga: Jeśli od poprzedniego ćwiczenia wygasła uwierzytelniona sesja z subskrypcją Azure, pojawi się prośba o ponowne zalogowanie.

In [ ]:
from azure.ai.ml import MLClient
from azure.identity import DefaultAzureCredential

credential = DefaultAzureCredential()
ml_client = MLClient.from_config(credential=credential)

print(f"Azure ML gotowe do pracy z obszarem roboczym {ml_client.workspace_name}")

Tworzenie skryptu treningowego

Model na danych o cukrzycy wytrenuje skrypt Pythona, więc zacznij od utworzenia folderu na skrypt i pliki z danymi.

In [ ]:
import os, shutil

# Utwórz folder na pliki eksperymentu
training_folder = 'diabetes-training'
os.makedirs(training_folder, exist_ok=True)

# Skopiuj plik z danymi do podfolderu data, aby skrypt wczytywał go
# tą samą ścieżką względną, której używa w tym repozytorium
os.makedirs(os.path.join(training_folder, 'data'), exist_ok=True)
shutil.copy('data/diabetes.csv', os.path.join(training_folder, 'data', 'diabetes.csv'))

Teraz utwórz skrypt treningowy i zapisz go w tym folderze.

Skrypt wywołuje mlflow.sklearn.autolog(), dzięki czemu parametry i metryki zapisują się same - nie trzeba wypisywać każdej z osobna. Model zapisujemy już jawnie, do nazwanego wyjścia zadania: autolog potrafi to zrobić za nas, ale ta droga wymaga zgodności wersji MLflow z wtyczką Azure ML, a jawny zapis działa zawsze.

In [ ]:
%%writefile $training_folder/diabetes_training.py
# Import bibliotek
import argparse

import pandas as pd
import numpy as np
import mlflow
import mlflow.sklearn
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score

# Azure ML podaje tu katalog, w ktorym ma wyladowac gotowy model
parser = argparse.ArgumentParser()
parser.add_argument('--model_output', type=str, dest='model_output',
                    required=True,
                    help='katalog nazwanego wyjscia zadania na gotowy model')
args = parser.parse_args()

# Automatyczny zapis parametrow i metryk. Modelem zajmujemy sie sami - patrz
# komentarz na koncu skryptu.
mlflow.sklearn.autolog(log_models=False, log_datasets=False)

# Ustaw wspolczynnik regularyzacji
reg = 0.01
mlflow.log_param('regularization_rate', reg)

# wczytaj zbior danych o cukrzycy
print("Wczytywanie danych...")
diabetes = pd.read_csv('data/diabetes.csv')

# Rozdziel cechy (ang. features) i etykiety (ang. labels)
X, y = diabetes[['Pregnancies','PlasmaGlucose','DiastolicBloodPressure','TricepsThickness','SerumInsulin','BMI','DiabetesPedigree','Age']].values, diabetes['Diabetic'].values

# Podziel dane na zbior treningowy i testowy
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30, random_state=0)

# Wytrenuj model regresji logistycznej
print('Trenowanie modelu regresji logistycznej ze wspolczynnikiem regularyzacji', reg)
model = LogisticRegression(C=1/reg, solver="liblinear").fit(X_train, y_train)

# oblicz skutecznosc (accuracy)
y_hat = model.predict(X_test)
acc = np.average(y_hat == y_test)
print('Accuracy:', acc)
mlflow.log_metric('Accuracy', acc)

# oblicz AUC
y_scores = model.predict_proba(X_test)
auc = roc_auc_score(y_test,y_scores[:,1])
print('AUC: ' + str(auc))
mlflow.log_metric('AUC', auc)

# Zapisujemy model do nazwanego wyjscia zadania. Przez log_model() sie nie
# da: azureml-mlflow obsluguje MLflow najwyzej w wersji 2.16.
mlflow.sklearn.save_model(sk_model=model, path=args.model_output)
print('Model zapisany w:', args.model_output)

Uruchomienie skryptu jako zadania

Do tej pory skrypty wykonywały się bezpośrednio w notatniku. Teraz uruchomisz skrypt treningowy jako zadanie command na środowisku obliczeniowym. Dzięki temu Azure Machine Learning śledzi przebieg, a jego metryki, pliki wyjściowe i zarejestrowany model zobaczysz w Azure Machine Learning studio.

Skorzystasz z utworzonego wcześniej klastra aml-cluster oraz z gotowego środowiska, w którym scikit-learn i MLflow są już zainstalowane.

In [ ]:
from azure.ai.ml import command, Output
from azure.ai.ml.constants import AssetTypes

# skonfiguruj zadanie
job = command(
    code=training_folder,
    command="python diabetes_training.py --model_output ${{outputs.model_output}}",
    outputs={
        # Nazwane wyjscie: Azure ML przygotuje katalog i zapamieta, ze lezy
        # w nim model w formacie MLflow.
        "model_output": Output(type=AssetTypes.MLFLOW_MODEL),
    },
    environment="azureml://registries/azureml/environments/sklearn-1.5/labels/latest",
    compute="aml-cluster",
    display_name="diabetes-training",
    experiment_name="diabetes-training",
)

# zlec zadanie
returned_job = ml_client.jobs.create_or_update(job)

# wyswietlaj na biezaco logi zadania w trakcie jego dzialania
ml_client.jobs.stream(returned_job.name)

W trakcie działania zadania możesz otworzyć je w Azure Machine Learning studio pod linkiem wypisanym poniżej.

In [ ]:
print(returned_job.services["Studio"].endpoint)

Po zakończeniu zadania metryki i parametry zapisane w jego trakcie pobierzesz również klientem MLflow.

In [ ]:
from mlflow.tracking import MlflowClient

client = MlflowClient()
job_run = client.get_run(returned_job.name)

print("Metryki:")
for key, value in job_run.data.metrics.items():
    print(key, value)

print("\nParametry:")
for key, value in job_run.data.params.items():
    print(key, value)

Rejestrowanie wytrenowanego modelu

Skrypt zapisał model do wyjścia zadania o nazwie model_output. Azure ML wie, że leży tam model w formacie MLflow, bo tak zadeklarowaliśmy to wyjście - wystarczy więc wskazać je przy rejestracji.

Rejestracja nadaje modelowi nazwę i kolejny numer wersji. Dzięki temu da się go później odtworzyć, a przy wdrożeniu w jednym z następnych ćwiczeń nie będzie potrzebny osobny skrypt oceniający.

In [ ]:
from azure.ai.ml.entities import Model
from azure.ai.ml.constants import AssetTypes

# Rejestrujemy model z nazwanego wyjscia zadania (a nie z artefaktow MLflow)
model = Model(
    path=f"azureml://jobs/{returned_job.name}/outputs/model_output",
    name="diabetes_model",
    type=AssetTypes.MLFLOW_MODEL,
    description="Diabetes classification model trained with scikit-learn.",
    tags={"training_context": "command job"},
)
registered_model = ml_client.models.create_or_update(model)
print(f"Zarejestrowany model: {registered_model.name}, wersja: {registered_model.version}")

# Wypisz wszystkie wersje zarejestrowanego modelu
for m in ml_client.models.list(name="diabetes_model"):
    print(m.name, 'wersja:', m.version)

Skrypt treningowy z parametrami

Zadanie treningowe staje się dużo elastyczniejsze, gdy skrypt przyjmuje parametry - można wtedy powtarzać to samo trenowanie z różnymi ustawieniami bez modyfikowania kodu. Tutaj dodasz parametr sterujący współczynnikiem regularyzacji regresji logistycznej.

Znowu zacznij od utworzenia folderu na sparametryzowany skrypt i dane treningowe.

In [ ]:
import os, shutil

# Utwórz folder na pliki eksperymentu
training_folder = 'diabetes-training-params'
os.makedirs(training_folder, exist_ok=True)

# Skopiuj plik z danymi do podfolderu data, aby skrypt wczytywał go
# tą samą ścieżką względną, której używa w tym repozytorium
os.makedirs(os.path.join(training_folder, 'data'), exist_ok=True)
shutil.copy('data/diabetes.csv', os.path.join(training_folder, 'data', 'diabetes.csv'))

Utwórz teraz skrypt, w którym współczynnik regularyzacji jest hiperparametrem przyjmowanym z wiersza poleceń.

In [ ]:
%%writefile $training_folder/diabetes_training.py
# Import bibliotek
import argparse

import pandas as pd
import numpy as np
import mlflow
import mlflow.sklearn
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score

# Azure ML podaje tu katalog, w ktorym ma wyladowac gotowy model
parser = argparse.ArgumentParser()
parser.add_argument('--model_output', type=str, dest='model_output',
                    required=True,
                    help='katalog nazwanego wyjscia zadania na gotowy model')
parser.add_argument('--reg_rate', type=float, dest='reg', default=0.01)
args = parser.parse_args()

# Automatyczny zapis parametrow i metryk. Modelem zajmujemy sie sami - patrz
# komentarz na koncu skryptu.
mlflow.sklearn.autolog(log_models=False, log_datasets=False)

reg = args.reg
mlflow.log_param('regularization_rate', reg)

# wczytaj zbior danych o cukrzycy
print("Wczytywanie danych...")
diabetes = pd.read_csv('data/diabetes.csv')

# Rozdziel cechy (ang. features) i etykiety (ang. labels)
X, y = diabetes[['Pregnancies','PlasmaGlucose','DiastolicBloodPressure','TricepsThickness','SerumInsulin','BMI','DiabetesPedigree','Age']].values, diabetes['Diabetic'].values

# Podziel dane na zbior treningowy i testowy
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30, random_state=0)

# Wytrenuj model regresji logistycznej
print('Trenowanie modelu regresji logistycznej ze wspolczynnikiem regularyzacji', reg)
model = LogisticRegression(C=1/reg, solver="liblinear").fit(X_train, y_train)

# oblicz skutecznosc (accuracy)
y_hat = model.predict(X_test)
acc = np.average(y_hat == y_test)
print('Accuracy:', acc)
mlflow.log_metric('Accuracy', acc)

# oblicz AUC
y_scores = model.predict_proba(X_test)
auc = roc_auc_score(y_test,y_scores[:,1])
print('AUC: ' + str(auc))
mlflow.log_metric('AUC', auc)

# Zapisujemy model do nazwanego wyjscia zadania. Przez log_model() sie nie
# da: azureml-mlflow obsluguje MLflow najwyzej w wersji 2.16.
mlflow.sklearn.save_model(sk_model=model, path=args.model_output)
print('Model zapisany w:', args.model_output)

Przekazanie parametrów do zadania

Poprzednio skrypt treningowy działał z domyślnym współczynnikiem regularyzacji. Teraz uruchomisz wersję sparametryzowaną i przekażesz argument --reg_rate w poleceniu zadania (command). Dzięki temu to samo trenowanie powtórzysz z różnymi wartościami regularyzacji bez edytowania skryptu.

In [ ]:
from azure.ai.ml import command, Output
from azure.ai.ml.constants import AssetTypes

# skonfiguruj zadanie, przekazujac wspolczynnik regularyzacji jako argument
job = command(
    code=training_folder,
    command="python diabetes_training.py --reg_rate 0.1 --model_output ${{outputs.model_output}}",
    outputs={
        # Nazwane wyjscie: Azure ML przygotuje katalog i zapamieta, ze lezy
        # w nim model w formacie MLflow.
        "model_output": Output(type=AssetTypes.MLFLOW_MODEL),
    },
    environment="azureml://registries/azureml/environments/sklearn-1.5/labels/latest",
    compute="aml-cluster",
    display_name="diabetes-training",
    experiment_name="diabetes-training",
)

# zlec zadanie
returned_job = ml_client.jobs.create_or_update(job)

# wyswietlaj na biezaco logi zadania w trakcie jego dzialania
ml_client.jobs.stream(returned_job.name)

Tak jak poprzednio, możesz pobrać metryki i parametry zapisane przez zadanie.

In [ ]:
from mlflow.tracking import MlflowClient

client = MlflowClient()
job_run = client.get_run(returned_job.name)

print("Metryki:")
for key, value in job_run.data.metrics.items():
    print(key, value)

print("\nParametry:")
for key, value in job_run.data.params.items():
    print(key, value)

Rejestrowanie nowej wersji modelu

Powstał nowy wytrenowany model, więc możesz zarejestrować go jako kolejną wersję diabetes_model w obszarze roboczym.

In [ ]:
from azure.ai.ml.entities import Model
from azure.ai.ml.constants import AssetTypes

# Rejestrujemy model z nazwanego wyjscia zadania (a nie z artefaktow MLflow)
model = Model(
    path=f"azureml://jobs/{returned_job.name}/outputs/model_output",
    name="diabetes_model",
    type=AssetTypes.MLFLOW_MODEL,
    description="Diabetes classification model trained with scikit-learn.",
    tags={"training_context": "command job"},
)
registered_model = ml_client.models.create_or_update(model)
print(f"Zarejestrowany model: {registered_model.name}, wersja: {registered_model.version}")

# Wypisz wszystkie wersje zarejestrowanego modelu
for m in ml_client.models.list(name="diabetes_model"):
    print(m.name, 'wersja:', m.version)

W menu File wybierz Close and Halt, aby zamknąć ten notatnik. Następnie wróć do instrukcji ćwiczenia.