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

Praca z magazynami danych

W poprzednich ćwiczeniach powstało połączenie z obszarem roboczym Azure ML przy użyciu SDK v2 i uruchomienie prostego zadania na danych z lokalnego pliku CSV. Teraz czas na dane przechowywane w chmurze.

Ważne: Kod w tym notatniku zakłada, że masz wykonane dwa pierwsze kroki z Lab 4A. Jeśli jeszcze ich nie ma - zrób je teraz.

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

Przegląd magazynów danych w obszarze roboczym

Obszar roboczy zawiera kilka magazynów danych (ang. datastore), w tym magazyn aml_data utworzony we wcześniejszym kroku.

Uruchom poniższy kod, aby pobrać domyślny magazyn danych, a potem wypisać wszystkie magazyny z zaznaczeniem, który z nich jest domyślny.

In [ ]:
# Pobierz domyślny magazyn danych
default_ds = ml_client.datastores.get_default()

# Wypisz wszystkie magazyny danych, zaznaczając, który jest domyślny
for ds in ml_client.datastores.list():
    print(ds.name, "- domyślny =", ds.name == default_ds.name)

Pobranie magazynu danych do pracy

Dalsza część ćwiczenia korzysta z magazynu aml_data, więc trzeba pobrać go po nazwie:

In [ ]:
aml_datastore = ml_client.datastores.get(name="aml_data")
print(f"{aml_datastore.name}: {aml_datastore.type} ({aml_datastore.account_name})")

Gdzie trafiają wysłane pliki

Magazyn jest wybrany, więc pora umieścić w nim dane. Tu czeka pierwsza niespodzianka.

W SDK v2 wysyłka plików z dysku dzieje się przy okazji rejestrowania zasobu danych (ang. data asset): gdy obiekt Data dostaje lokalną ścieżkę path, SDK sam wysyła pliki do chmury. Zawsze jednak do magazynu domyślnego - klasa Data nie ma parametru pozwalającego wskazać inny. Nie jest to przeoczenie, tylko świadoma decyzja: w SDK v1 istniała metoda Datastore.upload_files(), w v2 jej odpowiednika nie ma.

Co z tego wynika praktycznie: żeby wstawić pliki z dysku do konkretnego magazynu, sięga się po azcopy albo po stronę Data → Datastores → Browse w Azure Machine Learning studio. Z poziomu SDK da się natomiast zapisać do wybranego magazynu wynik zadania - i właśnie to zrobimy z magazynem aml_data w dalszej części ćwiczenia.

Zarejestruj zasób danych typu uri_folder na podstawie lokalnego folderu data, w którym leżą pliki diabetes.csv i diabetes2.csv:

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

diabetes_data_folder = Data(
    path="./data",
    type=AssetTypes.URI_FOLDER,
    description="Diabetes data files (folder)",
    name="diabetes_data_folder",
)

diabetes_data_folder = ml_client.data.create_or_update(diabetes_data_folder)
print(f"Zarejestrowano zasób danych: {diabetes_data_folder.name} (wersja {diabetes_data_folder.version})")

Trenowanie modelu na danych z magazynu danych

Zarejestrowany zasób danych przekazuje się do zadania jako wejście (ang. input) - obiekt Input, który wskazuje zasób danych po nazwie i wersji. Azure ML podmontowuje wtedy dane na środowisku obliczeniowym albo je tam pobiera, dzięki czemu skrypt czyta je tak samo, niezależnie od tego, gdzie faktycznie wykonuje się zadanie.

Więcej informacji: Szczegóły pracy z danymi opisuje artykuł Read and write data in a job w dokumentacji Azure ML.

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

data_input = Input(type=AssetTypes.URI_FOLDER, path=f"azureml:{diabetes_data_folder.name}:{diabetes_data_folder.version}")
print(data_input)

Aby skrypt trenujący mógł skorzystać z wejścia z danymi, musi mieć przewidziany dla niego parametr. Uruchom dwie poniższe komórki, aby utworzyć:

  1. Folder o nazwie diabetes_training_from_datastore
  2. Skrypt, który trenuje model klasyfikacji na danych ze wszystkich plików CSV w folderze wskazanym przez przekazane wejście.
In [ ]:
import os

# Utwórz folder na pliki zadania
experiment_folder = 'diabetes_training_from_datastore'
os.makedirs(experiment_folder, exist_ok=True)
print(experiment_folder, '- folder utworzony.')
In [ ]:
%%writefile $experiment_folder/diabetes_training.py
# Import bibliotek
import os
import argparse
import mlflow
import pandas as pd
import numpy as np
import joblib
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

# Odczytaj parametry
parser = argparse.ArgumentParser()
parser.add_argument('--regularization', type=float, dest='reg_rate', default=0.01, help='wskaźnik regularyzacji')
parser.add_argument('--data-folder', type=str, dest='data_folder', help='wejście z folderem danych')
parser.add_argument('--output-folder', type=str, dest='output_folder',
                    required=True, help='folder na połączone dane')
args = parser.parse_args()
reg = args.reg_rate

# Rozpocznij przebieg MLflow, aby zapisywać metryki (śledzenie MLflow jest wbudowane w zadania Azure ML)
mlflow.start_run()

# wczytaj dane o cukrzycy z folderu przekazanego na wejściu
data_folder = args.data_folder
print("Wczytywanie danych z", data_folder)
# Wczytaj wszystkie pliki i połącz ich zawartość w jedną ramkę danych
all_files = os.listdir(data_folder)
diabetes = pd.concat((pd.read_csv(os.path.join(data_folder, csv_file)) for csv_file in all_files))

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

os.makedirs('outputs', exist_ok=True)
# pliki zapisane w folderze outputs są automatycznie dołączane do wyników zadania
joblib.dump(value=model, filename='outputs/diabetes_model.pkl')

# Zapisz połączone dane do folderu wskazanego przez wyjście zadania.
# To wyjście wskazuje magazyn aml_data, więc plik wyląduje właśnie tam.
os.makedirs(args.output_folder, exist_ok=True)
sciezka_polaczonych = os.path.join(args.output_folder, 'diabetes_combined.csv')
diabetes.to_csv(sciezka_polaczonych, index=False)
print('Połączone dane zapisane w:', sciezka_polaczonych)

mlflow.end_run()

Skrypt wczytuje dane uczące z wejścia przekazanego mu jako parametr, więc zostaje już tylko skonfigurować zadanie tak, żeby przy zleceniu to wejście przekazało.

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

job = command(
    code=experiment_folder,
    command="python diabetes_training.py --regularization 0.1 --data-folder ${{inputs.data_folder}} --output-folder ${{outputs.combined}}",
    inputs={
        "data_folder": Input(type=AssetTypes.URI_FOLDER, path=f"azureml:{diabetes_data_folder.name}:{diabetes_data_folder.version}")
    },
    outputs={
        # Wyjscie wskazuje konkretny magazyn - tak w SDK v2 zapisuje sie
        # dane tam, gdzie chcemy, a nie tam, gdzie akurat wypadnie domyslnie.
        "combined": Output(
            type=AssetTypes.URI_FOLDER,
            path="azureml://datastores/aml_data/paths/diabetes-combined/",
        ),
    },
    environment="azureml://registries/azureml/environments/sklearn-1.5/labels/latest",
    compute="aml-cluster",
    display_name="diabetes-training-datastore",
    experiment_name="diabetes-training",
)

# zleć zadanie
returned_job = ml_client.jobs.create_or_update(job)
ml_client.jobs.stream(returned_job.name)

Przy pierwszym uruchomieniu zadania zbudowanie środowiska może chwilę potrwać - kolejne uruchomienia są już szybsze.

W trakcie działania zadania (albo po jego zakończeniu) szczegóły obejrzysz w Azure Machine Learning studio, łącznie z kartą Outputs + logs - zajrzyj do pliku user_logs/std_log.txt, żeby sprawdzić, że pliki z danymi zostały wczytane. Metryki zapisane przez MLflow możesz też pobrać z poziomu kodu:

Gdzie szukać wyniku: po zakończeniu zadania otwórz w Azure Machine Learning studio Data → Datastores → aml_data → Browse. W folderze diabetes-combined znajdziesz plik diabetes_combined.csv - dowód, że zadanie zapisało dane do wskazanego magazynu, a nie do domyślnego.

In [ ]:
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(key, value)

print(f"\nSzczegóły przebiegu w Studio: {returned_job.studio_url}")

Tak jak poprzednio, model wytrenowany przez zadanie możesz zarejestrować.

In [ ]:
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",
    name="diabetes_model",
    type=AssetTypes.CUSTOM_MODEL,
    description="Diabetes classification model",
    tags={"Training context": "Command job (using Datastore)"},
)
registered_model = ml_client.models.create_or_update(model)

# Wypisz zarejestrowane modele
print("Zarejestrowane modele:")
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])

W tym ćwiczeniu poznajesz kilka sposobów pracy z danymi udostępnianymi przez magazyny danych.

Azure Machine Learning daje jeszcze jeden poziom abstrakcji nad danymi - zasoby danych. Nimi zajmiesz się w następnym ćwiczeniu.