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

Interpretowanie modeli

Azure Machine Learning pozwala zajrzeć do środka modelu przy użyciu wyjaśniacza (ang. explainer) - narzędzia, które liczy, jak mocno każda cecha (ang. feature) wpłynęła na przewidzianą etykietę. Wyjaśniaczy jest kilka rodzajów, dobranych do różnych typów algorytmów, ale sposób korzystania z nich jest w każdym przypadku podobny.

Po co to robimy: sama skuteczność nie mówi, dlaczego model decyduje tak, a nie inaczej. Bez tej wiedzy nie da się ani obronić decyzji przed pacjentem czy audytorem, ani zauważyć, że model oparł się na czymś przypadkowym.

Wyjaśnianie modelu

Zacznij od modelu wytrenowanego poza Azure Machine Learning. Uruchom poniższą komórkę, aby wytrenować klasyfikator oparty na drzewie decyzyjnym.

In [ ]:
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 sklearn.metrics import roc_curve

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

# rozdzielamy cechy i etykiety
features = ['Pregnancies','PlasmaGlucose','DiastolicBloodPressure','TricepsThickness','SerumInsulin','BMI','DiabetesPedigree','Age']
labels = ['zdrowy', 'chory']
X, y = data[features].values, data['Diabetic'].values

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

# trenujemy model drzewa decyzyjnego
print('Trenowanie modelu drzewa decyzyjnego')
model = DecisionTreeClassifier().fit(X_train, y_train)

# liczymy skutecznosc
y_hat = model.predict(X_test)
acc = np.average(y_hat == y_test)
print('Skutecznosc:', acc)

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

print('Model wytrenowany.')

Podczas trenowania policzone zostały metryki na odłożonej części danych, więc wiesz już, jak dokładnie model przewiduje. To jednak nie odpowiada na pytanie, które cechy i jak mocno wpływają na jego decyzje.

Instalacja biblioteki do interpretacji

Żeby to sprawdzić, zainstaluj bibliotekę o otwartym kodzie interpret-community. Pozwala ona interpretować wiele typowych rodzajów modeli całkowicie lokalnie, bez łączenia się z Azure Machine Learning - działa nawet dla modeli, które nigdy nie były trenowane w zadaniu Azure ML ani rejestrowane w obszarze roboczym.

Pip może ostrzec o konflikcie wersji semver z pakietem responsibleai. Ten pakiet nie jest tutaj używany - pulpit odpowiedzialnej sztucznej inteligencji budują komponenty działające w chmurze, nie to jądro. Ostrzeżenie można zignorować.

In [ ]:
# Wersja shap musi pasować do interpret-community - przypinamy najwyższą obsługiwaną.
# Jeśli w tym jądrze był już wczytany starszy shap, uruchom ponownie jądro notatnika.
%pip install -q --upgrade interpret-community "shap==0.46.0"

Utworzenie wyjaśniacza dla modelu

Biblioteka jest już zainstalowana, więc utwórz wyjaśniacz odpowiedni dla tego modelu. Użyjesz wyjaśniacza tabelarycznego (ang. Tabular Explainer). Działa on metodą „czarnej skrzynki" - nie zagląda do wnętrza algorytmu, tylko bada, jak zmienia się predykcja przy zmianie wartości cech. Pod spodem wywołuje odpowiedni wyjaśniacz z biblioteki SHAP.

Dlaczego „czarna skrzynka" to zaleta: ten sam wyjaśniacz zadziała dla drzewa decyzyjnego, lasu losowego i sieci neuronowej. Nie musisz zmieniać podejścia za każdym razem, gdy zmienisz algorytm.

In [ ]:
from interpret.ext.blackbox import TabularExplainer

# pola "features" i "classes" sa opcjonalne
tab_explainer = TabularExplainer(model, 
                             X_train, 
                             features=features, 
                             classes=labels)
print(tab_explainer, "- gotowy!")

Globalna istotność cech

Zacznij od całościowego spojrzenia na model, czyli od istotności cech (ang. feature importance) wyliczonej na całym zbiorze treningowym. Odpowiada ona na pytanie: które cechy mają największy wpływ na predykcje tego modelu w ogóle.

In [ ]:
# mozesz tu podac dane treningowe albo testowe
global_tab_explanation = tab_explainer.explain_global(X_train)

# pobieramy cechy uszeregowane wedlug istotnosci
global_tab_feature_importance = global_tab_explanation.get_feature_importance_dict()
for feature, importance in global_tab_feature_importance.items():
    print(feature,":", importance)

Cechy są uszeregowane - najważniejsza znajduje się na pierwszym miejscu.

Lokalna istotność cech

Masz już obraz całości, ale co z pojedynczymi przypadkami? Teraz wygenerujesz wyjaśnienia lokalne, opisujące konkretne predykcje. Pokazują one, jak poszczególne cechy wpłynęły na decyzję o przypisaniu każdej z możliwych etykiet. Model jest dwuklasowy (chory / zdrowy), więc dla każdej obserwacji zobaczysz wpływ cech osobno dla obu etykiet. Sprawdzisz dwa pierwsze przypadki ze zbioru testowego.

Kiedy to jest naprawdę potrzebne: gdy trzeba uzasadnić jedną konkretną decyzję - na przykład wyjaśnić pacjentowi, dlaczego model zakwalifikował go do grupy ryzyka. Globalna istotność cech na takie pytanie nie odpowiada.

In [ ]:
# wybieramy obserwacje, ktore chcemy wyjasnic (dwie pierwsze)
X_explain = X_test[0:2]

# pobieramy predykcje
predictions = model.predict(X_explain)

# pobieramy wyjasnienia lokalne
local_tab_explanation = tab_explainer.explain_local(X_explain)

# pobieramy nazwy cech i ich istotnosc dla kazdej mozliwej etykiety
local_tab_features = local_tab_explanation.get_ranked_local_names()
local_tab_importance = local_tab_explanation.get_ranked_local_values()

for l in range(len(local_tab_features)):
    print('Wklad na rzecz etykiety:', labels[l])
    label = local_tab_features[l]
    for o in range(len(label)):
        print("\tObserwacja", o + 1)
        feature_list = label[o]
        total_support = 0
        for f in range(len(feature_list)):
            print("\t\t", feature_list[f], ':', local_tab_importance[l][o][f])
            total_support += local_tab_importance[l][o][f]
        print("\t\t ----------\n\t\t Suma:", total_support, "Predykcja:", labels[predictions[o]])

Pulpit odpowiedzialnej AI dla zarejestrowanego modelu

Jak widzisz, wyjaśnienia dla modelu wytrenowanego poza Azure ML można wygenerować, używając interpret-community bezpośrednio w notatniku.

Żeby powiązać wyjaśnienia z modelem zarejestrowanym w obszarze roboczym, korzysta się z pulpitu odpowiedzialnej sztucznej inteligencji (ang. Responsible AI dashboard). Daje on więcej niż sama istotność cech: analizę błędów, przykłady kontrfaktyczne („co by było, gdyby") i analizę przyczynową.

Pulpit powstaje jako zadanie potoku złożone z gotowych komponentów. Zbudujesz go kreatorem w Azure Machine Learning studio - klikając, bez pisania kodu. Kreator uruchamia pod spodem dokładnie ten sam potok.

Dlaczego nie z poziomu SDK: komponenty pulpitu są publikowane w rejestrze azureml Microsoftu i nie w każdym obszarze roboczym są dostępne - pobranie ich przez registry_client.components.get() kończy się wtedy błędem Could not find component with name. Kreator w studio tym się nie przejmuje.

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

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

# Kreator wymaga zarejestrowanego modelu MLflow i danych w formacie mltable
model = ml_client.models.get(name="diabetes_model", label="latest")
dane = ml_client.data.get(name="diabetes_mltable", label="latest")

print(f"Model:  {model.name}, wersja {model.version}, typ {model.type}")
print(f"Dane:   {dane.name}, wersja {dane.version}, typ {dane.type}")

Zbudowanie pulpitu w studio

Otwórz Azure Machine Learning studio i wykonaj poniższe kroki. Komórka powyżej wypisała nazwy i wersje, których będziesz potrzebować.

  1. W menu po lewej wybierz Models, a następnie model diabetes_model.
  2. Na karcie Details kliknij Create Responsible AI dashboard (preview).
  3. Training dataset: wybierz diabetes_mltable. Kreator przyjmuje wyłącznie dane w formacie mltable - dlatego właśnie taki zasób powstał w ćwiczeniu 4B.
  4. Test dataset: wybierz ten sam zasób. W prawdziwym projekcie byłby to osobny zbiór, ale tutaj chodzi o poznanie narzędzia.
  5. Modeling task: wybierz Classification.
  6. Dashboard components: wybierz profil Model debugging - obejmuje analizę błędów, przykłady kontrfaktyczne i wyjaśnienia modelu.
  7. Component parameters: jako Target feature wskaż Diabetic i upewnij się, że Generate explanations jest włączone.
  8. Experiment configuration: nadaj pulpitowi nazwę, wskaż eksperyment oraz klaster aml-cluster i kliknij Create.

Zadanie potrwa kilkanaście minut. Postęp śledzisz na stronie eksperymentu.

Przegląd wyjaśnień

Po zakończeniu zadania wróć do modelu diabetes_model i otwórz kartę Responsible AI. Wybierz utworzony pulpit, a w nim:

  1. Obejrzyj wykres Aggregate feature importance - globalną istotność cech.
  2. Przełącz się na Individual feature importance i wybierz pojedynczy punkt danych, aby zobaczyć, co zadecydowało o tej jednej predykcji.
  3. Zajrzyj do sekcji Error analysis - pokazuje, w których podgrupach danych model myli się najczęściej.

Porównaj z wynikami z początku ćwiczenia: uszeregowanie cech powinno być zbliżone do tego, które policzył interpret-community w notatniku. To ta sama metoda, tylko uruchomiona w chmurze i powiązana z zarejestrowanym modelem.

Więcej informacji: o kreatorze pulpitu przeczytasz w artykule Generate Responsible AI insights in the studio UI, o budowaniu go z poziomu SDK - w Generate Responsible AI insights with YAML and Python, a o samym odczytywaniu pulpitu - w Use the Responsible AI dashboard.