Praca ze środowiskami¶
W tym momencie kursu masz za sobą sporo eksperymentów w obszarze roboczym Azure Machine Learning, a w niektórych trzeba było wskazać konkretne pakiety Pythona wymagane tam, gdzie wykonuje się kod eksperymentu. W tym ćwiczeniu przyjrzysz się środowiskom dokładniej, korzystając z Azure Machine Learning SDK v2.
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.
from importlib.metadata import version
from azure.ai.ml import MLClient
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
ml_client = MLClient.from_config(credential=credential)
print(f"Azure ML {version('azure-ai-ml')} gotowe do pracy z obszarem roboczym {ml_client.workspace_name}")
Przygotowanie danych do eksperymentu¶
W tym ćwiczeniu pracujesz na zbiorze z wynikami badań pacjentów pod kątem cukrzycy. Ćwiczenie dotyczy jednak środowisk, a nie zasobów danych, więc zamiast uzależniać się od typu i wersji współdzielonego zasobu diabetes_mltable z wcześniejszych ćwiczeń (zarejestrowanego jako mltable), każde zadanie dostanie lokalny plik data/diabetes.csv bezpośrednio, jako wejście typu uri_file. SDK wysyła ten plik automatycznie przy zleceniu zadania.
Utworzenie skryptu trenującego¶
Uruchom dwie poniższe komórki, aby utworzyć:
- Folder na pliki nowego eksperymentu
- Plik skryptu trenującego, który uczy model przy użyciu scikit-learn i rysuje krzywą ROC przy użyciu matplotlib.
import os
# Utwórz folder na pliki eksperymentu
experiment_folder = 'diabetes_training_logistic'
os.makedirs(experiment_folder, exist_ok=True)
print(experiment_folder, '- folder utworzony')
%%writefile $experiment_folder/diabetes_training.py
# Import bibliotek
import argparse
import os
import pandas as pd
import numpy as np
import joblib
import mlflow
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve
import matplotlib.pyplot as plt
# Ustaw hiperparametr regularyzacji (przekazany do skryptu jako argument)
parser = argparse.ArgumentParser()
parser.add_argument('--training-data', type=str, dest='training_data', help='ścieżka do danych uczących')
parser.add_argument('--regularization', type=float, dest='reg_rate', default=0.01, help='wskaźnik regularyzacji')
args = parser.parse_args()
reg = args.reg_rate
# wczytaj dane o cukrzycy (przekazane jako wejście)
print("Wczytywanie danych...")
diabetes = pd.read_csv(args.training_data)
# Rozdziel cechy (ang. features) i etykietę (ang. label)
X, y = diabetes[['Pregnancies','PlasmaGlucose','DiastolicBloodPressure','TricepsThickness','SerumInsulin','BMI','DiabetesPedigree','Age']].values, diabetes['Diabetic'].values
# Podziel dane na zbiór uczący 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 wskaźnikiem regularyzacji', reg)
mlflow.log_metric('Regularization Rate', float(reg))
model = LogisticRegression(C=1/reg, solver="liblinear").fit(X_train, y_train)
# policz skuteczność
y_hat = model.predict(X_test)
acc = np.average(y_hat == y_test)
print('Skuteczność:', acc)
mlflow.log_metric('Accuracy', float(acc))
# policz 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', float(auc))
# narysuj krzywą ROC
fpr, tpr, thresholds = roc_curve(y_test, y_scores[:,1])
fig = plt.figure(figsize=(6, 4))
# Narysuj przekątną odpowiadającą losowemu zgadywaniu
plt.plot([0, 1], [0, 1], 'k--')
# Narysuj FPR i TPR osiągnięte przez model
plt.plot(fpr, tpr)
plt.xlabel('Odsetek fałszywie pozytywnych (FPR)')
plt.ylabel('Odsetek prawdziwie pozytywnych (TPR)')
plt.title('Krzywa ROC')
mlflow.log_figure(fig, "ROC.png")
plt.show()
os.makedirs('outputs', exist_ok=True)
# plik zapisany w folderze outputs jest automatycznie dołączany do wyników zadania
joblib.dump(value=model, filename='outputs/diabetes_model.pkl')
Zdefiniowanie środowiska¶
Gdy skrypt Pythona wykonuje się w Azure Machine Learning jako zadanie, środowisko określa kontekst jego wykonania - bazowy obraz kontenera oraz pakiety conda i pip dostępne w trakcie działania skryptu. Azure Machine Learning udostępnia gotowe (ang. curated) środowiska z wieloma popularnymi pakietami, ale można też zdefiniować własne.
from azure.ai.ml.entities import Environment
# Zdefiniuj zależności conda dla eksperymentu
conda_spec = {
"name": "diabetes-experiment-env",
"channels": ["conda-forge"],
"dependencies": [
"python=3.10",
"scikit-learn",
"ipykernel",
"matplotlib",
"pandas",
"pip",
{
"pip": [
# Bez pakietu mlflow - wtyczka dociąga zgodną wersję sama.
# Dodanie go tutaj zrywa logowanie artefaktów.
"azureml-mlflow",
"pyarrow",
]
},
],
}
# Utwórz środowisko Pythona dla eksperymentu
diabetes_env = Environment(
name="diabetes-experiment-env",
description="A custom environment for training the diabetes model",
conda_file=conda_spec,
image="mcr.microsoft.com/azureml/openmpi5.0-ubuntu24.04",
)
print(diabetes_env.name, '- środowisko zdefiniowane.')
Teraz możesz użyć tego środowiska w eksperymencie, przypisując je do zadania typu command.
Poniższy kod przypisuje utworzone środowisko do zadania command i zleca je na klastrze aml-cluster. W trakcie działania zadania obserwuj strumieniowane logi - przy pierwszym uruchomieniu zobaczysz, jak budowane jest środowisko conda. Kolejne zadania korzystają z gotowego obrazu.
Dlaczego nie
local: zadanie można też uruchomić na samej instancji obliczeniowej, podająccompute="local". Ta ścieżka wymaga jednak osobnego uwierzytelnienia sesji notatnika i na instancji obliczeniowej często kończy się błędemSSO failure. Klaster od tego nie zależy.
from azure.ai.ml import command, Input
from azure.ai.ml.constants import AssetTypes
job = command(
code=experiment_folder,
command="python diabetes_training.py --training-data ${{inputs.diabetes}} --regularization 0.1",
inputs={"diabetes": Input(type=AssetTypes.URI_FILE, path="data/diabetes.csv")},
environment=diabetes_env, # anonimowa wersja środowiska - SDK ostrzeże, że podana nazwa nie zostanie użyta
compute="aml-cluster",
display_name="diabetes-train-logistic",
experiment_name="diabetes-training",
)
returned_job = ml_client.jobs.create_or_update(job)
ml_client.jobs.stream(returned_job.name)
Zadanie skorzystało ze środowiska, w którym znalazły się wszystkie potrzebne pakiety. Zapisane metryki i wyniki zadania obejrzysz w Azure Machine Learning studio - w tym model wytrenowany przy użyciu scikit-learn i wykres krzywej ROC wygenerowany przez matplotlib - albo pobierzesz je przez MLflow, tak jak poniżej.
import mlflow
mlflow.set_tracking_uri(ml_client.workspaces.get(ml_client.workspace_name).mlflow_tracking_uri)
mlflow_run = mlflow.get_run(returned_job.name)
print("Metryki:")
for key, value in mlflow_run.data.metrics.items():
print(f" {key}: {value}")
print(f"\nZadanie w Azure Machine Learning studio: {returned_job.studio_url}")
Rejestrowanie środowiska¶
Skoro definicja środowiska z potrzebnymi pakietami już powstała, warto zarejestrować je w obszarze roboczym i używać ponownie w kolejnych zadaniach.
# Zarejestruj środowisko
registered_env = ml_client.environments.create_or_update(diabetes_env)
print(f"Zarejestrowane środowisko: {registered_env.name}, wersja {registered_env.version}")
Zwróć uwagę, że środowisko rejestruje się pod nazwą nadaną przy jego tworzeniu - tutaj diabetes-experiment-env.
Zarejestrowane środowisko możesz wykorzystać w dowolnym skrypcie o tych samych wymaganiach. Dla przykładu utwórz folder i skrypt, który trenuje model cukrzycy innym algorytmem:
import os
# Utwórz folder na pliki eksperymentu
experiment_folder = 'diabetes_training_tree'
os.makedirs(experiment_folder, exist_ok=True)
print(experiment_folder, '- folder utworzony')
%%writefile $experiment_folder/diabetes_training.py
# Import bibliotek
import argparse
import os
import pandas as pd
import numpy as np
import joblib
import mlflow
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve
import matplotlib.pyplot as plt
parser = argparse.ArgumentParser()
parser.add_argument('--training-data', type=str, dest='training_data', help='ścieżka do danych uczących')
args = parser.parse_args()
# wczytaj dane o cukrzycy (przekazane jako wejście)
print("Wczytywanie danych...")
diabetes = pd.read_csv(args.training_data)
# Rozdziel cechy (ang. features) i etykietę (ang. label)
X, y = diabetes[['Pregnancies','PlasmaGlucose','DiastolicBloodPressure','TricepsThickness','SerumInsulin','BMI','DiabetesPedigree','Age']].values, diabetes['Diabetic'].values
# Podziel dane na zbiór uczący i testowy
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30, random_state=0)
# Wytrenuj model drzewa decyzyjnego
print('Trenowanie modelu drzewa decyzyjnego')
model = DecisionTreeClassifier().fit(X_train, y_train)
# policz skuteczność
y_hat = model.predict(X_test)
acc = np.average(y_hat == y_test)
print('Skuteczność:', acc)
mlflow.log_metric('Accuracy', float(acc))
# policz 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', float(auc))
# narysuj krzywą ROC
fpr, tpr, thresholds = roc_curve(y_test, y_scores[:,1])
fig = plt.figure(figsize=(6, 4))
# Narysuj przekątną odpowiadającą losowemu zgadywaniu
plt.plot([0, 1], [0, 1], 'k--')
# Narysuj FPR i TPR osiągnięte przez model
plt.plot(fpr, tpr)
plt.xlabel('Odsetek fałszywie pozytywnych (FPR)')
plt.ylabel('Odsetek prawdziwie pozytywnych (TPR)')
plt.title('Krzywa ROC')
mlflow.log_figure(fig, "ROC.png")
plt.show()
os.makedirs('outputs', exist_ok=True)
joblib.dump(value=model, filename='outputs/diabetes_model.pkl')
Teraz pobierz zarejestrowane środowisko i skonfiguruj z nim nowe zadanie, które uruchomi alternatywny skrypt trenujący. Tym razem skrypt nie ma parametrów, bo klasyfikator drzewa decyzyjnego nie wymaga tutaj żadnych wartości hiperparametrów.
from azure.ai.ml import command, Input
from azure.ai.ml.constants import AssetTypes
registered_env = ml_client.environments.get(name="diabetes-experiment-env", label="latest")
job = command(
code=experiment_folder,
command="python diabetes_training.py --training-data ${{inputs.diabetes}}",
inputs={"diabetes": Input(type=AssetTypes.URI_FILE, path="data/diabetes.csv")},
environment=registered_env,
compute="aml-cluster",
display_name="diabetes-train-tree",
experiment_name="diabetes-training",
)
returned_job = ml_client.jobs.create_or_update(job)
ml_client.jobs.stream(returned_job.name)
Tym razem zadanie rusza szybciej, bo pasujący obraz środowiska został już zbudowany przy poprzednim uruchomieniu i trafił do pamięci podręcznej. Co ważniejsze, na innym środowisku obliczeniowym zbudowałoby się i zostało użyte dokładnie to samo środowisko - to właśnie zapewnia powtarzalny kontekst wykonania skryptu trenującego.
Zajrzyj do metryk i wyników zadania.
mlflow_run = mlflow.get_run(returned_job.name)
print("Metryki:")
for key, value in mlflow_run.data.metrics.items():
print(f" {key}: {value}")
print(f"\nZadanie w Azure Machine Learning studio: {returned_job.studio_url}")
Ten model wygląda na nieco lepszy od regresji logistycznej, więc warto go zarejestrować.
from azure.ai.ml.entities import Model
from azure.ai.ml.constants import AssetTypes
model = Model(
path=f"azureml://jobs/{returned_job.name}/outputs/artifacts/paths/outputs/diabetes_model.pkl",
type=AssetTypes.CUSTOM_MODEL,
name="diabetes_model",
description="A diabetes classification model",
tags={"Training context": "Command job + custom environment (Decision Tree)"},
properties={
"AUC": str(mlflow_run.data.metrics.get("AUC")),
"Accuracy": str(mlflow_run.data.metrics.get("Accuracy")),
},
)
ml_client.models.create_or_update(model)
for m in ml_client.models.list(name="diabetes_model"):
print(m.name, 'wersja:', m.version)
for tag_name in m.tags:
print('\t', tag_name, ':', m.tags[tag_name])
for prop_name in m.properties:
print('\t', prop_name, ':', m.properties[prop_name])
print('\n')
Przegląd zarejestrowanych środowisk¶
Poza środowiskami definiowanymi samodzielnie Azure Machine Learning udostępnia gotowe (ang. curated) środowiska dla typowych scenariuszy trenowania i wnioskowania. Poniższy kod wypisuje własne środowiska zarejestrowane w obszarze roboczym:
for env in ml_client.environments.list():
print("Nazwa:", env.name)
Gotowe środowiska utrzymuje centralnie Microsoft w systemowym rejestrze azureml - nie w obszarze roboczym - i odwołuje się do nich ścieżką azureml://registries/azureml/environments/<nazwa>/labels/latest. Połącz się z tym rejestrem i obejrzyj jedno z dostępnych tam gotowych środowisk razem z pakietami, które zawiera.
from azure.ai.ml import MLClient
# Połącz się z zarządzanym przez Microsoft rejestrem "azureml", w którym leżą gotowe środowiska
registry_client = MLClient(credential=credential, registry_name="azureml")
curated_env_name = "sklearn-1.5"
curated_env = registry_client.environments.get(name=curated_env_name, label="latest")
print("Nazwa:", curated_env.name)
print("Wersja:", curated_env.version)
print("Obraz:", curated_env.image)
print(f"Pełna referencja: azureml://registries/azureml/environments/{curated_env.name}/labels/latest")
Więcej informacji: O środowiskach w Azure Machine Learning przeczytasz w dokumentacji Azure Machine Learning