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

Zautomatyzowane uczenie maszynowe

Algorytmów uczenia maszynowego jest wiele i rzadko da się z góry powiedzieć, który poradzi sobie najlepiej z konkretnymi danymi. Na jakość modelu mocno wpływa też wstępne przetwarzanie danych (ang. preprocessing) - normalizacja, uzupełnianie brakujących wartości i podobne zabiegi. Szukanie najlepszego rozwiązania oznacza więc sprawdzenie wielu kombinacji algorytmu i przekształceń, a to kosztuje czas i moc obliczeniową.

Azure Machine Learning potrafi to porównanie zautomatyzować. Możesz skorzystać z interfejsu graficznego w Azure Machine Learning studio albo z zestawu SDK. Interfejs graficzny jest prostszy, SDK daje większą kontrolę nad ustawieniami. W tym ćwiczeniu użyjesz SDK.

Po co to robimy: celem nie jest wyręczenie się automatem. Chodzi o to, żeby szybko uzyskać punkt odniesienia - wynik, o którym wiadomo, że da się go osiągnąć bez szczególnej wiedzy o danych. Model budowany ręcznie ma sens wtedy, gdy ten punkt odniesienia pobije.

Połączenie z obszarem roboczym

Zacznij od połączenia z obszarem roboczym (ang. workspace) przy użyciu zestawu Azure ML SDK.

Uwaga: jeśli od poprzedniego ćwiczenia wygasła sesja uwierzytelniania z subskrypcją Azure, zobaczysz prośbę 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}")

Przygotowanie danych dla zadania

W tym ćwiczeniu użyjesz zasobu danych (ang. data asset) z wynikami badań pacjentów pod kątem cukrzycy. Uruchom poniższą komórkę, aby go utworzyć (jeśli powstał już we wcześniejszym ćwiczeniu, zarejestrowana zostanie jego nowa wersja).

Uwaga na kolumnę PatientID: pliki z danymi zawierają kolumnę PatientID. To numer nadany przez system rejestracji, a nie wynik badania, więc nie wolno traktować go jako cechy (ang. feature). Zautomatyzowane uczenie maszynowe wykorzystuje wszystkie kolumny poza kolumną docelową - zostawienie identyfikatora dałoby modelowi wartość, którą może po prostu zapamiętać. Powstałby model wyglądający na dokładny przy znanych pacjentach i bezużyteczny przy nowych. Poniższy kod usuwa tę kolumnę.

In [ ]:
# Pakiet mltable wraz z silnikiem odczytu danych. Wystarczy raz na instancję obliczeniową.
%pip install -q -U mltable "azureml-dataprep[pandas]"
In [ ]:
import mltable
from azure.ai.ml.entities import Data
from azure.ai.ml.constants import AssetTypes

# Połącz oba pliki CSV w jedną tabelę
sciezki = [
    {'file': './data/diabetes.csv'},
    {'file': './data/diabetes2.csv'},
]
tbl = mltable.from_delimited_files(paths=sciezki)

# Usuń identyfikator pacjenta. To numer z rejestracji, a nie wynik badania - nie niesie
# żadnej informacji o cukrzycy, ale model potrafi go zapamiętać i wyglądać na dokładny
# na danych, które już widział.
tbl = tbl.drop_columns(['PatientID'])

tbl.save('./diabetes-mltable', colocated=True, overwrite=True)

# Zarejestruj tabelę jako zasób danych
data_asset = Data(
    path='./diabetes-mltable',
    type=AssetTypes.MLTABLE,
    description='diabetes data',
    name='diabetes_mltable',
)
ml_client.data.create_or_update(data_asset)

print('Zasób danych gotowy.')

Przygotowanie danych do zautomatyzowanego uczenia

Dla zautomatyzowanego uczenia maszynowego nie piszesz skryptu trenującego - wystarczy wskazać dane. Zadanie przyjmuje pojedynczy zasób danych typu MLTable i samo wydziela z niego część walidacyjną (albo stosuje walidację krzyżową, ang. cross-validation), więc nie musisz dzielić danych ręcznie. Użyjesz zasobu zarejestrowanego przed chwilą.

In [ ]:
# Pobieramy zarejestrowany zasob danych treningowych
diabetes_data_asset = ml_client.data.get(name="diabetes_mltable", label="latest")
print("Dane gotowe!")

Przygotowanie zasobu obliczeniowego

Zadanie uruchomisz na klastrze aml-cluster, utworzonym we wcześniejszym ćwiczeniu (jeśli go nie ma, poniższy kod go utworzy). Klaster pozwala liczyć wiele prób równolegle - każda sprawdza inną kombinację algorytmu i przekształceń danych.

In [ ]:
from azure.ai.ml.entities import AmlCompute

cluster_name = "aml-cluster"

try:
    # Pobieramy klaster, jesli juz istnieje
    training_cluster = ml_client.compute.get(cluster_name)
    print('Znaleziono istniejacy klaster - uzywamy go.')
except Exception:
    # Jesli nie istnieje - tworzymy go
    compute_config = AmlCompute(
        name=cluster_name,
        type="amlcompute",
        size="Standard_D2as_v4",
        min_instances=0,
        max_instances=4,
        idle_time_before_scale_down=300,
    )
    training_cluster = ml_client.compute.begin_create_or_update(compute_config).result()

print(f"Zasob obliczeniowy '{training_cluster.name}' jest gotowy do uzycia.")

Konfiguracja zautomatyzowanego uczenia maszynowego

Teraz skonfigurujesz samo zadanie. Tworzy je funkcja automl.classification(), a metoda set_limits() określa, ile prób wykonać i jak długo mogą trwać.

Dlaczego limity są ważne: bez nich zadanie potrafi liczyć godzinami i zużyć budżet klastra. max_trials ogranicza liczbę prób, timeout_minutes całe zadanie, a trial_timeout_minutes pojedynczą próbę - dzięki temu jeden algorytm, który utknął, nie zablokuje pozostałych.

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

classification_job = automl.classification(
    compute="aml-cluster",
    experiment_name="diabetes-automl",
    training_data=Input(type=AssetTypes.MLTABLE, path=diabetes_data_asset.id),
    target_column_name="Diabetic",
    primary_metric="AUC_weighted",
)

# Inżynieria cech: "auto" pozwala AutoML samodzielnie dobrać przekształcenia
classification_job.set_featurization(mode="auto")

# Wszystkie limity sa opcjonalne
classification_job.set_limits(
    timeout_minutes=30,
    trial_timeout_minutes=10,
    max_trials=6,
    max_concurrent_trials=4,
)

print("Gotowe do uruchomienia zadania AutoML.")

Uruchomienie zadania

Wszystko gotowe - uruchom zadanie.

Uwaga: potrwa to dłuższą chwilę. Najpierw trzeba przygotować klaster, co może wymagać zatrzymania węzłów pracujących jeszcze nad poprzednim zadaniem. Potem zadanie ruszy, a postęp będzie wyświetlany w miarę kończenia kolejnych prób. Stan zasobu obliczeniowego i zadania możesz też śledzić w Azure Machine Learning studio.

In [ ]:
returned_job = ml_client.jobs.create_or_update(classification_job)
print(f"Wyslano zadanie: {returned_job.name}")

# Strumieniujemy dziennik zadania do notatnika w trakcie jego dzialania
ml_client.jobs.stream(returned_job.name)

Wskazanie najlepszego modelu

Po zakończeniu zadania możesz obejrzeć jego próby (zadania podrzędne, ang. child jobs) - z poziomu kodu albo w Azure Machine Learning studio, gdzie po otwarciu zadania znajdziesz je na kartach Models i Child jobs wraz z użytym algorytmem i uzyskanymi metrykami.

Porównaj próby, a następnie użyj MLflow, żeby wskazać tę, która dała najlepszy model.

In [ ]:
# Porownujemy proby (zadania podrzedne) uruchomione przez AutoML
for child in ml_client.jobs.list(parent_job_name=returned_job.name):
    print(f"{child.name}: {child.display_name} (stan: {child.status})")

Pobierz teraz najlepszą próbę i model, który z niej powstał.

In [ ]:
import mlflow
from mlflow.tracking import MlflowClient

mlflow.set_tracking_uri(ml_client.workspaces.get(ml_client.workspace_name).mlflow_tracking_uri)
mlflow_client = MlflowClient()

# Zadanie nadrzedne AutoML zapisuje identyfikator najlepszej proby jako znacznik
mlflow_parent_run = mlflow_client.get_run(returned_job.name)
best_child_run_id = mlflow_parent_run.data.tags["automl_best_child_run_id"]
print(f"Identyfikator najlepszej proby: {best_child_run_id}")

best_run = mlflow_client.get_run(best_child_run_id)
best_run_metrics = best_run.data.metrics

print("Metryki najlepszej proby:")
for metric_name, value in best_run_metrics.items():
    print(f"\t{metric_name}: {value}")

Zautomatyzowane uczenie maszynowe może samo dobierać wstępne przetwarzanie danych. Robi to za pomocą potoków przekształceń scikit-learn - nie mylić z potokami Azure Machine Learning. Powstały model zawiera więc nie tylko sam algorytm, ale i kroki przygotowania danych, wykonywane przed każdą predykcją.

Zobacz, co próba po sobie zostawiła - obok samego modelu także gotowy skrypt oceniający i definicję środowiska, w którym model da się uruchomić:

Gdzie zobaczyć nazwę algorytmu: w Azure Machine Learning studio otwórz zadanie AutoML i przejdź na kartę Models + child jobs. Kolumna Algorithm name pokazuje, co wybrała każda próba. SDK v2 nie udostępnia tej informacji: próby AutoML nie są dostępne przez ml_client.jobs.get(), a MLflow nie zapisuje dla nich parametrów. Sam potok odtworzysz tylko w środowisku z pakietami uruchomieniowymi AutoML - dlatego modeli AutoML używa się przez wdrożenie, a nie przez wczytanie w notatniku.

In [ ]:
import mlflow.artifacts

# Adres artefaktów bierzemy z przebiegu. Przez samo list_artifacts(run_id, ścieżka)
# MLflow 3 sięga po API, którego Azure ML nie udostępnia.
adres = mlflow_client.get_run(best_child_run_id).info.artifact_uri

print("Artefakty najlepszej próby:")
for artefakt in mlflow.artifacts.list_artifacts(artifact_uri=f"{adres}/outputs"):
    print(f"\t{artefakt.path}")

Wśród tych plików jest featurization_summary.json - AutoML zapisuje w nim, co zrobił z każdą kolumną danych wejściowych. Pobierz go i obejrzyj:

In [ ]:
import json
import pandas as pd
import mlflow.artifacts

sciezka = mlflow.artifacts.download_artifacts(
    artifact_uri=f"{adres}/outputs/featurization_summary.json"
)

with open(sciezka, encoding="utf-8") as plik:
    podsumowanie = pd.DataFrame(json.load(plik))

# Pomijamy TransformationParams - pełne parametry każdego przekształcenia
kolumny = ["RawFeatureName", "TypeDetected", "Dropped",
           "EngineeredFeatureCount", "Transformations"]
print(podsumowanie[kolumny].to_string(index=False))

Zwróć uwagę na wykryte typy: kolumna Pregnancies zawiera liczby, ale AutoML może uznać ją za kategoryczną - ma niewiele różnych wartości. Wtedy zamiast jednej cechy liczbowej powstaje kilkanaście cech zakodowanych tekstowo. Automat nie wie, że to liczba ciąż; widzi wyłącznie rozkład wartości. Warto sprawdzić, czy wykryte typy zgadzają się z tym, co wiesz o danych - bo od nich zależy, czego model się nauczy.

Na koniec zarejestruj model, który wypadł najlepiej.

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

# Rejestrujemy model z wyjscia MLflow najlepszej proby
model = Model(
    path=f"azureml://jobs/{best_child_run_id}/outputs/artifacts/outputs/mlflow-model/",
    name="diabetes_model_automl",
    description="Best model from an automated ML job",
    type=AssetTypes.MLFLOW_MODEL,
    properties={
        'AUC_weighted': best_run_metrics.get('AUC_weighted'),
        'accuracy': best_run_metrics.get('accuracy'),
    },
)
registered_model = ml_client.models.create_or_update(model)

# Wypisujemy zarejestrowane modele
for m in ml_client.models.list(name="diabetes_model_automl"):
    print(m.name, 'wersja:', m.version)

Więcej informacji: o zautomatyzowanym uczeniu maszynowym przeczytasz w dokumentacji Azure ML.