Tworzenie usługi wnioskowania w czasie rzeczywistym¶
Dotychczasowe ćwiczenia dotyczyły trenowania i rejestrowania modeli. Teraz czas wdrożyć model jako usługę czasu rzeczywistego, z której aplikacje klienckie będą pobierać predykcje dla nowych danych. To właśnie jest wnioskowanie (ang. inference): model niczego już się nie uczy, tylko odpowiada na pytania.
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 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}")
Trenowanie i rejestracja modelu¶
Skrypt scoringowy (ang. scoring script), którego użyjesz w dalszej części ćwiczenia, wczytuje model przez joblib z pliku diabetes_model.pkl. Zarejestrowany model diabetes_model musi więc być zwykłym modelem scikit-learn zapisanym jako zasób typu CUSTOM_MODEL - a nie modelem w formacie MLflow, jaki rejestrują niektóre wcześniejsze ćwiczenia (Lab 3B, Lab 6A).
Dlatego poniższa komórka jest obowiązkowa: trenuje model i rejestruje go we właściwym formacie, niezależnie od tego, które wcześniejsze ćwiczenia zostały wykonane. Bez niej dalsze kroki wdrożenia nie zadziałają.
import pandas as pd
import numpy as np
import joblib
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import roc_auc_score
from azure.ai.ml.entities import Model
from azure.ai.ml.constants import AssetTypes
# wczytaj zbiór danych o cukrzycy
print("Wczytywanie danych...")
diabetes = pd.read_csv('data/diabetes.csv')
# Rozdziel cechy i etykiety
X, y = diabetes[['Pregnancies','PlasmaGlucose','DiastolicBloodPressure','TricepsThickness','SerumInsulin','BMI','DiabetesPedigree','Age']].values, diabetes['Diabetic'].values
# Podziel dane na zbiór 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 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)
# policz AUC
y_scores = model.predict_proba(X_test)
auc = roc_auc_score(y_test, y_scores[:,1])
print('AUC: ' + str(auc))
# Zapisz wytrenowany model do pliku
model_file = 'diabetes_model.pkl'
joblib.dump(value=model, filename=model_file)
# Zarejestruj model
registered_model = Model(
path=model_file,
type=AssetTypes.CUSTOM_MODEL,
name="diabetes_model",
description="A decision tree model that classifies patients by their likelihood of being diabetic.",
tags={"Training context": "Inline Training"},
properties={"AUC": str(auc), "Accuracy": str(acc)},
)
ml_client.models.create_or_update(registered_model)
print('Model wytrenowany i zarejestrowany.')
Wdrożenie modelu w zarządzanym punkcie końcowym online¶
Model klasyfikujący pacjentów według prawdopodobieństwa cukrzycy jest już wytrenowany i zarejestrowany. Taki model mógłby pracować w przychodni, w której na badanie w kierunku cukrzycy kieruje się tylko pacjentów uznanych za zagrożonych. Żeby to było możliwe, wdrożysz model w zarządzanym punkcie końcowym (ang. endpoint) online, obsługującym wnioskowanie w czasie rzeczywistym.
Zacznij od sprawdzenia, jakie modele są zarejestrowane w obszarze roboczym.
print("Zarejestrowane modele:")
for model in ml_client.models.list():
print(f"\t{model.name}")
# Tak samo jak przy zasobach danych: wersja i typ należą do konkretnej wersji
najnowszy = ml_client.models.get(name="diabetes_model", label="latest")
print(f"\n{najnowszy.name}: najnowsza wersja {najnowszy.version}, typ {najnowszy.type}")
Teraz pobierz model, który ma zostać wdrożony. Jeśli podasz samą nazwę modelu, zwrócona zostanie jego najnowsza wersja.
model = ml_client.models.get(name="diabetes_model", label="latest")
print(model.name, 'wersja', model.version)
Wdrożenie (ang. deployment) potrzebuje kilku plików z kodem i konfiguracją, więc najpierw utwórz na nie folder.
import os
folder_name = 'diabetes_service'
# Utwórz folder na pliki wdrożenia
experiment_folder = './' + folder_name
os.makedirs(folder_name, exist_ok=True)
print(folder_name, '- folder utworzony.')
Punkt końcowy potrzebuje kodu w Pythonie, który wczyta dane wejściowe, załaduje model, wyliczy predykcje i je zwróci. Ten kod zapiszesz w skrypcie scoringowym (nazywanym też skryptem wejściowym), wdrażanym razem z modelem:
%%writefile $folder_name/score_diabetes.py
import json
import joblib
import numpy as np
import os
# Wywoływana raz, przy inicjalizacji wdrożenia
def init():
global model
# Ustal ścieżkę do pliku wdrożonego modelu i wczytaj go
# (zmienną AZUREML_MODEL_DIR ustawia punkt końcowy online; wskazuje ona
# folder z plikami zarejestrowanego modelu)
model_path = os.path.join(os.getenv("AZUREML_MODEL_DIR"), "diabetes_model.pkl")
model = joblib.load(model_path)
# Wywoływana przy każdym przychodzącym żądaniu
def run(raw_data):
# Pobierz dane wejściowe jako tablicę numpy
data = np.array(json.loads(raw_data)['data'])
# Poproś model o predykcję
predictions = model.predict(data)
# Zamień każdą predykcję (0 lub 1) na odpowiadającą jej nazwę klasy
classnames = ['not-diabetic', 'diabetic']
predicted_classes = []
for prediction in predictions:
predicted_classes.append(classnames[prediction])
# Zwracamy listę predykcji - serwer inferencyjny sam zamieni ją na JSON
return predicted_classes
Punkt końcowy działa w kontenerze, który przy uruchomieniu musi doinstalować wymagane pakiety Pythona. Skrypt scoringowy korzysta z scikit-learn, więc utworzysz plik środowiska conda z listą potrzebnych pakietów.
%%writefile $folder_name/diabetes_env.yml
name: diabetes-env
dependencies:
- python=3.8
- numpy
- scikit-learn
- pip
- pip:
- azureml-inference-server-http
# Wyświetl zawartość pliku .yml
with open(folder_name + "/diabetes_env.yml", "r") as f:
print(f.read())
Wszystko jest gotowe do wdrożenia. Utworzysz punkt końcowy o nazwie diabetes-endpoint z jednym wdrożeniem o nazwie blue. Całość składa się z czterech kroków:
- Utworzenie zarządzanego punktu końcowego online.
- Utworzenie zarządzanego wdrożenia, które wskazuje model, skrypt scoringowy, środowisko oraz zasoby obliczeniowe. Zasoby obliczeniowe dla zarządzanych punktów końcowych zapewnia i utrzymuje Azure - nie trzeba ich samodzielnie tworzyć ani nimi administrować.
- Skierowanie całego ruchu punktu końcowego do nowego wdrożenia.
- Sprawdzenie stanu wdrożenia.
Więcej informacji: Szczegóły wdrażania modeli opisuje dokumentacja.
Wdrożenie chwilę potrwa: najpierw budowany jest obraz kontenera ze środowiskiem, potem przydzielane są zasoby obliczeniowe, a na końcu trafia na nie model. Po pomyślnym zakończeniu stan wdrożenia (provisioning state) to Succeeded.
from azure.ai.ml.entities import (
ManagedOnlineEndpoint,
ManagedOnlineDeployment,
Environment,
CodeConfiguration,
)
endpoint_name = "diabetes-endpoint"
# Utwórz zarządzany punkt końcowy online
endpoint = ManagedOnlineEndpoint(
name=endpoint_name,
description="Real-time diabetes classification service",
auth_mode="key",
)
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
# Zdefiniuj środowisko, w którym uruchamiany jest skrypt scoringowy
env = Environment(
conda_file=f"{folder_name}/diabetes_env.yml",
image="mcr.microsoft.com/azureml/openmpi5.0-ubuntu24.04",
)
# Utwórz zarządzane wdrożenie online
blue_deployment = ManagedOnlineDeployment(
name="blue",
endpoint_name=endpoint_name,
model=model,
environment=env,
code_configuration=CodeConfiguration(code=folder_name, scoring_script="score_diabetes.py"),
instance_type="Standard_DS2_v2",
instance_count=1,
)
ml_client.online_deployments.begin_create_or_update(blue_deployment).result()
# Skieruj cały ruch punktu końcowego do wdrożenia blue
endpoint.traffic = {"blue": 100}
ml_client.online_endpoints.begin_create_or_update(endpoint).result()
print("Wdrożenie zakończone.")
Jeśli wdrożenie się powiodło, jego stan to Succeeded. Jeśli nie - poniższy kod sprawdzi stan i pobierze logi wdrożenia, co pomaga znaleźć przyczynę problemu.
endpoint = ml_client.online_endpoints.get(name=endpoint_name)
print(endpoint.provisioning_state)
logs = ml_client.online_deployments.get_logs(
name="blue", endpoint_name=endpoint_name, lines=50
)
print(logs)
# Jeśli trzeba coś poprawić i wdrożyć ponownie, może się okazać, że najpierw należy usunąć wdrożenie:
# ml_client.online_deployments.begin_delete(name="blue", endpoint_name=endpoint_name)
Zajrzyj do obszaru roboczego w Azure Machine Learning studio i otwórz stronę Endpoints - zobaczysz na niej punkty końcowe wdrożone w obszarze roboczym.
Nazwy punktów końcowych online możesz też wypisać z poziomu kodu:
for online_endpoint in ml_client.online_endpoints.list():
print(online_endpoint.name)
Korzystanie z punktu końcowego¶
Punkt końcowy jest wdrożony, więc można już z niego korzystać w aplikacji klienckiej.
import json
x_new = [[2,180,74,24,21,23.9091702,1.488172308,22]]
print ('Pacjent: {}'.format(x_new[0]))
# Zapisz dane przykładowego pacjenta jako plik żądania w formacie JSON
request_file_name = "sample-data.json"
with open(request_file_name, "w") as f:
json.dump({"data": x_new}, f)
# Wywołaj punkt końcowy, przekazując plik żądania (punkt końcowy przyjmuje też dane w postaci binarnej)
predictions = ml_client.online_endpoints.invoke(
endpoint_name=endpoint_name,
deployment_name="blue",
request_file=request_file_name,
)
# Odczytaj przewidzianą klasę - jest tylko jedna, więc bierzemy pierwszą.
predicted_classes = json.loads(predictions)
print(predicted_classes[0])
Do punktu końcowego można też wysłać wyniki badań wielu pacjentów naraz i otrzymać predykcję dla każdego z nich.
import json
# Tym razem dane wejściowe to tablica dwóch zestawów cech
x_new = [[2,180,74,24,21,23.9091702,1.488172308,22],
[0,148,58,11,179,39.19207553,0.160829008,45]]
# Zapisz dane przykładowych pacjentów jako plik żądania w formacie JSON
request_file_name = "sample-data.json"
with open(request_file_name, "w") as f:
json.dump({"data": x_new}, f)
# Wywołaj punkt końcowy, przekazując plik żądania
predictions = ml_client.online_endpoints.invoke(
endpoint_name=endpoint_name,
deployment_name="blue",
request_file=request_file_name,
)
# Odczytaj przewidziane klasy.
predicted_classes = json.loads(predictions)
for i in range(len(x_new)):
print ("Pacjent {}".format(x_new[i]), predicted_classes[i] )
Powyższy kod łączy się z zarządzanym punktem końcowym online przy użyciu Azure ML SDK i pobiera z niego predykcje modelu. W praktyce z modelu korzystają zwykle aplikacje biznesowe, które nie używają Azure ML SDK, tylko wysyłają do punktu końcowego zwykłe żądania HTTP.
Sprawdź więc, pod jaki adres URL takie aplikacje mają wysyłać żądania:
endpoint = ml_client.online_endpoints.get(name=endpoint_name)
scoring_uri = endpoint.scoring_uri
print(scoring_uri)
Znając adres URI punktu końcowego, aplikacja może wysłać żądanie HTTP z danymi pacjenta w formacie JSON i odebrać w odpowiedzi przewidziane klasy. Punkt końcowy powstał z ustawieniem auth_mode="key", więc żądanie musi zawierać nagłówek Authorization z poprawnym kluczem.
import requests
import json
# Pobierz klucz uwierzytelniający do punktu końcowego
keys = ml_client.online_endpoints.get_keys(name=endpoint_name)
primary_key = keys.primary_key
x_new = [[2,180,74,24,21,23.9091702,1.488172308,22],
[0,148,58,11,179,39.19207553,0.160829008,45]]
# Zamień tablicę na dokument JSON, który da się przesłać w żądaniu
input_json = json.dumps({"data": x_new})
# Ustaw typ zawartości i nagłówek uwierzytelniający
headers = {
'Content-Type':'application/json',
'Authorization': f'Bearer {primary_key}',
}
response = requests.post(scoring_uri, input_json, headers = headers)
predicted_classes = json.loads(response.json())
for i in range(len(x_new)):
print ("Pacjent {}".format(x_new[i]), predicted_classes[i] )
Model jest wdrożony w zarządzanym punkcie końcowym online z uwierzytelnianiem kluczem. Takie punkty końcowe same zajmują się równoważeniem obciążenia, skalowaniem i monitorowaniem. W zastosowaniach produkcyjnych, gdzie poświadczenia powinny wygasać, punkt końcowy można utworzyć z auth_mode="aml_token" albo auth_mode="aad_token" zamiast auth_mode="key".
Więcej informacji: O wdrażaniu modeli w punktach końcowych online przeczytasz w dokumentacji.
Sprzątanie¶
Zarządzany punkt końcowy utrzymuje przypisane mu zasoby obliczeniowe (i nalicza za nie koszt) aż do usunięcia. Jeśli punkt końcowy nie jest już potrzebny, uruchom poniższą komórkę, aby go usunąć:
# ml_client.online_endpoints.begin_delete(name=endpoint_name)