3. Narzędzia zarządzania eksperymentami#
W ramach materiałów zajmiemy się zagadnieniem analizy nacechowania wypowiedzi, dla którego zbudujemy potok przetwarzania dla tego zadania oraz wykorzystamy narzędzia do zarządzania eksperymentami: DVC oraz MLFlow. Na potrzeby laboratorium tym razem wykorzystamy zbiór clarin-pl/polemo2, który jest również udostępniony na repozytorium huggingface. Zbiór polemo2 jest przeznaczony do analizy nacechowania wypowiedzi zawiera 8216 recenzji napisanych w języku polskim pochodzących z różnych domen: hotels (tripadvisor.com), medicine (znanylekarz.com), school (polwro.pl), products (ceneo.pl). Do rencenzji przypisano cztery klasy nacechowania wypowiedzi:
zero - neutralna
minus - negatywna
plus - pozytywna
amb - nieokreślona
3.1. DVC#
DVC można najkrócej opisać jako system kontroli wersji dla danych lub jako git dla danych i modeli. Jest to narzędzie o otwartych źródłach, które pozwala nam m.in.:
wersjonować dane i modele (do śledzenia wersji plików lub folderów dvc wykorzystuje sumy kontrolne
md5)zarządzać potokami przetwarzania i zapewnia interfejs do śledzenia metryk
przesyłać dane na zewnetrzne serwery oraz zarządzać nimi lokalnie
Important
DVC jest dalej w trakcie rozwoju, dlatego poniższa instrukcja ogranicza się do zastosowania DVC w wersji 3.0. Nowsze wersje biblioteki mogą wprowadzić duże zmiany do poszczególnych funkcjonalności. Najnowsza wersja dokumentacji jest dostępna pod adresem https://dvc.org/doc
3.1.1. Instalacja#
DVC możemy najprościej zainstalować poprzez menadżera paczek pip
pip install dvc
Dodatkowo do obsługi zewnętrzych serwerów wymaga zainstalowania dodatkowych zależności. Wspierane konfiguracje to s3, gs, azure, oss, ssh lub all - instaluje wszystkie opcjonalne zależności. Jeżeli chcemy zainstalować dodatkowe zależności np. dla s3, polecenie przyjmie następującą postać:
pip install dvc[s3]
W ramach materiałów nie będziemy wykorzystywać zewnętrznego serwera, dlatego wystarczy nam podstawowa wersja.
Important
Poniższa instrukcja dotyczy wykorzystania DVC w wersji 3.0 i została przetestowana z wykorzystaniem wersji 3.59.1
3.1.2. Inicjalizacja repozytorium dvc#
Inicjalizacja repozytorium dvc występuję z wykorzystaniem komendy dvc init, przechodząc do głównego katalogu projektu.
$ dvc init
Initialized DVC repository.
You can now commit the changes to git.
+---------------------------------------------------------------------+
| |
| DVC has enabled anonymous aggregate usage analytics. |
| Read the analytics documentation (and how to opt-out) here: |
| <https://dvc.org/doc/user-guide/analytics> |
| |
+---------------------------------------------------------------------+
What's next?
------------
- Check out the documentation: <https://dvc.org/doc>
- Get help and share ideas: <https://dvc.org/chat>
- Star us on GitHub: <https://github.com/iterative/dvc>
Important
dvc init domyślnie wykonuje inicjalizację w głównym folderze, jeżeli chcemy umieścić go poza głównym folderem musimy skorzystać z flagi --subdir, jednak zazwyczaj nie jest to potrzebne. Pełną listę konfiguracji komendy dvc init znajdziemy w dokumentacji. LINK
Sprawdźmy, jakie pliki są śledzone przez git.
$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .dvc/.gitignore
new file: .dvc/config
new file: .dvcignore
Untracked files:
(use "git add <file>..." to include in what will be committed)
requirements.txt
Pliki trackowane przez repozytorium git
.dvc/.gitignore- Deklaracja plików/folderów, które mają nie być trackowane przez gita.dvc/config- Konfiguracja dvc, tutaj umieszczana jest np. konfiguracja zewnętrznego serwera.dvcignore- Deklaracja plików/folderów, która mają nie być trackowane przez dvc
Pliki nietrackowane przez git:
.dvc/cache/- Folder cache, tutaj zawierają się lokalne pliki repozytorium. Więcej informacji: [LINK].dvc/tmp/- Pliki tymczasowe
Szczegółowy opis plików znajduje się pod adresem: [LINK]
Important
Po inicjalizacji dvc obowiązkowo commitujemy zmiany w repo. Jeżeli tego nie zrobimy, część późniejszych poleceń, np. dvc params diff --all nie będzie działała (bug). Dodatkowo, jeżeli któraś komenda nie działa i jest to oczywisty bug po stronie dvc, zaktualizowanie indeksu przy pomocy git add lub commit zmian w plikach generowanych przez dvc potrafi pomóc.
3.1.3. Dodanie pierwszego etapu potoku przetwarzania do DVC#
3.1.3.1. Przygotowanie skryptu#
Najpierw zaczniemy od przygotowania skryptu do pobrania zbioru danych. Konfigurację dla tego skryptu umieścimy w zewnętrzntym pliku params.yaml w głównym katalogu. Parametrami skryptu będą domeny dla danego podziału danych (train, dev i test) oraz typ tekstów (zdania lub pełne recenzje). Działanie tego skryptu będzie polegać na pobraniu odpowiedniej konfiguracji zbioru danych z repozytorium i jego transformacja go do prostszego formatu. Przetworzony plik umieśćmy w folderze data/
scripts/download_data.py
import pickle
import datasets
import yaml
def main():
with open("params.yaml", "r") as f:
cfg = yaml.safe_load(f)
dataset = datasets.load_dataset(
"clarin-pl/polemo2-official", **cfg["download_data"], trust_remote_code=True, verification_mode="no_checks"
)
dataset = {
"train": {"X": dataset["train"]["text"], "y": dataset["train"]["target"]},
"test": {"X": dataset["test"]["text"], "y": dataset["test"]["target"]},
"labels": dataset["train"].features["target"].names,
}
with open("data/dataset.pkl", "wb") as f:
pickle.dump(obj=dataset, file=f)
main()
Jako domyślną konfigurację wykorzystajmy recenzje pochodzące z hoteli i zdania.
params.yaml
download_data:
train_domains:
- hotels
dev_domains:
- hotels
test_domains:
- hotels
text_cfg: sentence
3.1.3.2. Dodanie skryptu do DVC#
Etapy przetwarzania dodajemy do potoku za pomocą komendy dvc stage add [LINK], lub manualnie modyfikując plik dvc.yaml.
Głównymi elementami danego etapu definiowanego w dvc są:
zależności etapu (dane wejściowe, skrypty pythonowe etc.), definiowane za pomocą parametru
-dparametry (w celu śledzenia zmian), definiowane za pomocą parametru
-ppliki wyjściowe, definiowane za pomocą parametru
-oskrypt który będziemy wykonywać podawany na końcu polecania w naszym przypadku będzie to
python3 scripts/download_data.py
Wróćmy do naszego skryptu pobierającego zbiór danych. Nazwę etapu przypisujemy za pomocą parametru -n.
Zdefiniujmy teraz elementy etapu:
Zależności:
Skrypt
scripts/download_data.py
Parametry:
train_domains
test_domains
dev_domains
text_cfg
Wyjście:
Plik
data/dataset.pkl
Skrypt do wykonania:
PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/download_data.py
Zbierzmy teraz wszystko w ramach polecenia i wykonajmy je.
$ dvc stage add \
-n download_data \
-d scripts/download_data.py \
-o data/dataset.pkl \
-p download_data.train_domains \
-p download_data.dev_domains \
-p download_data.test_domains \
-p download_data.text_cfg \
'PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/download_data.py'
Creating 'dvc.yaml'
Adding stage 'download_data' in 'dvc.yaml'
To track the changes with git, run:
git add data/.gitignore dvc.yaml
Important
Przy pracy z repozytorium DVC szczególną uwagę należy zwrócić na pliki .gitignore generowane podczas dodawania następnych etapów. Pozwala to uniknąć dodania nieporządanych plików do repozytorium git.
Important
Plik params.yaml umieszczony w głównym katalogu repozytorium lub w odpowiednim podfolderze, jeżeli dvc było inicjalizowane z flagą –subdir jest domyślną ścieżką do definicji parametrów w dvc. Pozwala to na odwoływanie się do parametrów umieszczonych w tym pliku bezpośrednio. Jeżeli nasza konfiguracja byłaby w innej lokalizacji np. w configs/download_data.yaml musielibyśmy ją podać. Przykładowa dekleracja parametru miałaby wtedy postać
-p configs/download_data.yaml:train_domains
Wszystkie elementy potoku przetwarzania są definiowane w pliku dvc.yaml. Spójrzmy na jego sygnaturę w tym momencie.
dvc.yaml
stages:
download_data:
cmd: PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/download_data.py
deps:
- scripts/download_data.py
params:
- download_data.dev_domains
- download_data.test_domains
- download_data.text_cfg
- download_data.train_domains
outs:
- data/dataset.pkl
Hint
Kolejne etapy możemy również dodawać lub edytować bezpośrednio modyfikąc plik dvc.yaml. Jednak wtedy musimy sami zadbać o poprawność definicji oraz o zarządzanie plikami .gitignore
3.1.3.3. Sprawdzenie statusu potoku#
Stan potoku możemy sprawdzić za pomocą polecenia dvc status. Daje nam to pogląd na zmiany, które występują w naszym potoku.
$ dvc status
download_data:
changed deps:
modified: scripts/download_data.py
new: params.yaml
changed outs:
deleted: data/dataset.pkl
W tym przypadku dvc status pokazuje nam w naszym etapie download_data tutaj zmiany zarówno w zależnościach, parametrach jak i w pliku wyjściowym.
3.1.3.4. Wykonywanie etapów#
Do wykonania etapu lub etapów służy polecenie dvc repro. Domyślna konfiguracja polecenia dvc repro oznacza przejrzenie całego potoku i wykonanie tylko tych etapów w których wykryto zmiany. Możemy również wykonać jeden etap lub wybraną część potoku przekazując nazwy etapów. Pełna dokumentacja: [LINK]
Przydatne parametry:
--dry- pozwala podejrzeć które etapy mają byc wykonane-f- wymuszenie wykonania wszystkich lub wybranych potoków, nawet jeżelidvcnie reportuje zmian-s- wykonanie pojedynczego etapu z pominięciem poprzednich, nawet jeżelidvcreportuje zmiany
Wykonujmy teraz reprodukcję potoku
$ dvc repro
Running stage 'download_data':
> PYTHONPATH=. python3 scripts/download_data.py
Using custom data configuration default-d3f574c876938804
Reusing dataset pol_emo2
Generating lock file 'dvc.lock'
Updating lock file 'dvc.lock'
To track the changes with git, run:
git add dvc.lock
Use `dvc push` to send your updates to remote storage.
Stan potoku jest zapisywany w pliku dvc.lock
schema: '2.0'
stages:
download_data:
cmd: PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/download_data.py
deps:
- path: scripts/download_data.py
hash: md5
md5: 8a84f31122a097d0f267d616cc41c32a
size: 631
params:
params.yaml:
download_data.dev_domains:
- hotels
download_data.test_domains:
- hotels
download_data.text_cfg: sentence
download_data.train_domains:
- hotels
outs:
- path: data/dataset.pkl
hash: md5
md5: 35c2088e2229c17deb87abb690475c75
size: 2263646
3.1.4. Wysyłanie i pobieranie danych z dvc#
Important
Wymaga konfiguracji serwera zewnętrznego. Więcej szczegółów na temat konfiguracji serwera: [LINK]
Pliki śledzone przez dvc takie jak np. pliki wyjściowe możemy przesyłać na zewnętrzny serwer za pomocą polecenia
dvc push, a pobierać lokalnie za pomocą komendy dvc pull. Podobnie jak w przypadku dvc repro zakres operacji push i pull obejmuje domyślnie cały potok lub jego wybraną część poprzez przekazanie nazw(y) etapów jako argumentu polecenia.
3.1.5. Kolejne etapy przetwarzania#
Dodajmy teraz kolejne etapy przetwarzania extract_features i evaluate_model.
extract_features dodamy podobnie jak wcześniej download_data
$ dvc stage add \
-n extract_features \
-d scripts/extract_features.py \
-d data/dataset.pkl \
-o data/featurized.pkl \
'PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/extract_features.py'
Adding stage 'extract_features' in 'dvc.yaml'
To track the changes with git, run:
git add dvc.yaml data/.gitignore
Po wykonaniu powyższego polecenia, plik dvc.yaml powinien wyglądać następująco
$ cat dvc.yaml
stages:
download_data:
cmd: PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/download_data.py
deps:
- scripts/download_data.py
params:
- download_data.dev_domains
- download_data.test_domains
- download_data.text_cfg
- download_data.train_domains
outs:
- data/dataset.pkl
extract_features:
cmd: PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/extract_features.py
deps:
- data/dataset.pkl
- scripts/extract_features.py
outs:
- data/featurized.pkl
Kolejny etap evaluate_model zdefiniujemy manualnie w pliku dvc.yaml. Dodamy też mechanizm śledzenie metryk. Pełny opis jest dostępny pod adresem [LINK].
Gdybyśmy chcieli wykorzystać dvc stage add, plik zawierający metryki powinniśmy określić za pomocą flagi -m lub -M. Obecnie wspierany jest format json, toml lub yaml.
Etap przetwarzania evaluate_model chcemy uzależnić od parametrów max_iter i random_state. Dodajemy je do pliku params.yaml, podobnie jak wcześniej parametry dla etapu download_data:
evaluate_model:
random_state: 441
max_iter: 200
Następnie modyfikujemy plik dvc.yaml dodając nowy etap przetwarzania evaluate_model:
evaluate_model:
cmd: PYTHONPATH=$(pwd):$PYTHONPATH python3 scripts/evaluate_model.py
deps:
- data/featurized.pkl
- scripts/evaluate_model.py
params:
- evaluate_model.max_iter
- evaluate_model.random_state
metrics:
- data/results.json:
cache: false
Jak widać, zależnościami tego etapu są pliki data/featurized.pkl i scripts/evaluate_model.py. Skrypt scripts/evaluate_model.py generuje też plik data/results.json z metrykami.
Ponieważ jest on nieduży, możemy go bez problemu śledzić przy pomocy gita. Dodajemy zatem element cache: false po określeniu pliku z metrykami, aby DVC go nie śledziło.
Zreprodukujmy teraz cały potok
$ dvc repro
Stage 'download_data' didn't change, skipping
Running stage 'extract_features':
> PYTHONPATH=. python3 scripts/extract_features.py
Updating lock file 'dvc.lock'
Running stage 'evaluate_model':
> PYTHONPATH=. python3 scripts/evaluate_model.py
Updating lock file 'dvc.lock'
To track the changes with git, run:
git add dvc.lock
Use `dvc push` to send your updates to remote storage.
3.1.6. Przeglądanie potoku przetwarzania, parametrów i metryk#
Mając już zdefiniowany potok przetwarzania możemy go zwizualizować w konsoli za pomocą komendy dvc dag [LINK]
$ dvc dag
+---------------+
| download_data |
+---------------+
*
*
*
+------------------+
| extract_features |
+------------------+
*
*
*
+----------------+
| evaluate_model |
+----------------+
Możemy również śledzić graph zależności pomiędzy wyjściami za pomocą flagi --outs
$ dvc dag --outs
+------------------+
| data/dataset.pkl |
+------------------+
*
*
*
+---------------------+
| data/featurized.pkl |
+---------------------+
*
*
*
+-------------------+
| data/results.json |
+-------------------+
Listę etapów możemy podejrzeć za pomocą polecenia dvc stage list [LINK]
$ dvc stage list
download_data Outputs data/dataset.pkl
extract_features Outputs data/featurized.pkl
evaluate_model Reports data/results.json
Metryki możemy teraz wyświetlić bezpośrednio z konsoli za pomocą komendy dvc metrics show [LINK]
$ dvc metrics show
Path test_accuracy test_f1-score test_precision test_recall
data/results.json 0.74245 0.67593 0.7481 0.64806
Parametry możemy wyświetlić za pomocą dvc params diff --all [LINK]
$ dvc params diff --all
Path Param Old New
params.yaml download_data.dev_domains ['hotels'] ['hotels']
params.yaml download_data.test_domains ['hotels'] ['hotels']
params.yaml download_data.text_cfg sentence sentence
params.yaml download_data.train_domains ['hotels'] ['hotels']
params.yaml evaluate_model.max_iter 200 200
params.yaml evaluate_model.random_state 441 441
3.1.7. Wprowadzanie tymczasowych zmian#
Czasami potrzebujemy wprowadzić szybkie zmiany, ale niekoniecznie chcemy je przesłać później na repozytorium. W celu ćwiczenia zmieńmy parametry download_data.text_cfg na text oraz evaluate_model.max_iter na 50.
Zmiany możemy sprawdzić za również za pomocą dvc params diff.
$ dvc params diff
Path Param Old New
params.yaml download_data.text_cfg sentence text
params.yaml evaluate_model.max_iter 200 50
Zamiast wszystkich elementów, polecenie teraz wyświetla nam tylko te w których dokonana byłą zmiana.
Parametry się zgadzają, dokonajmy reprodukcji całego potoku za pomocą dvc repro. Zmiany wymagają wykonania całego potoku od nowa.
Po wykonaniu się wszystkich elementu potoku, możemy sprawdzić zmiany.ożemy sprawdzić jak zmieniły się metryki do tego wykorzystamy polecenia dvc metrics diff
$ dvc metrics diff
Path Metric Old New Change
data/results.json accuracy 0.74165 0.81013 0.06848
data/results.json f1-score 0.67661 0.80187 0.12526
Czas na cofnięcie zmian, zacznijmy od odrzucenia zmian w repozytorium git, zmienione pliki to results.json, params.yaml i dvc.lock.
$ git restore .
Teraz zostaje nam cofnięcie zmian w plikach dvc do tego wykorzystamy komendę checkout, która działa w podobny sposób jak git checkout. Przydatna jest również w sytuacji, kiedy przełączamy się pomiędzy różnymi branchami.
$ dvc checkout
3.1.8. Inne przydatne komendy DVC#
dvc add - Pozwala na śledzenie pliku / folderu poprzez utworzenie pliku
{nazwa_pliku}.dvc.
Przykład
$ dvc add data/
data.dvc
outs:
- md5: 48c6715169a5ed3b78e278f5f2c6055e.dir
size: 57675468
nfiles: 9
path: data
dvc commit - Pozwala uaktalnić stan danego etapu.
dvc commitjest szczególnie przydatny w sytuacjach:Kiedy wprowadziliśmy zmiany w zależnościach etapu, które nie mają wpływu na pliki wyjściowe etapu
Kiedy wykonaliśmy skrypt danego etapu z pominięciem polecenia
dvc repro
dvc remove - Usuwa dany etap
3.1.9. Kod do części I#
Rozwiązanie części I znajduje się pod adresem: [LINK]
3.2. MLFlow#
MLFlow jest dedykowanym narzędziem do zarządzania eksperymentami. Jego funkcjonalnośc obejmuje następujące moduły:
MLFlow Tracking- zarządzanie eksperymentami, logowanie parametrych, metryk, wykresów i innych artefaktówMLFlow Projects- przygotowanie środowiska eksperymentalnego dla projektu, wykonywanie eksperymentówMLFlow Models- Wdrażanie modeliMLFlow Registry- Rejestr modeli
Dokumentacja: [LINK]
W ramach zajęć skupimy się na module MLFlow Tracking
3.2.1. Instalacja#
Biblioteka mlflow również możemy zainstalować za pomocą menadżera paczek
pip install mlflow
3.2.2. MLFlow Tracking#
Moduł jest oparty o monitorowanie przebiegów uczenia (ang. runs), które opcjonalnie możemy grupować w eksperymenty.
W ramach pojedynczego przebiegu zbierane są takie komponenty jak:
Wersja kodu (aktualny hash commitu z gita)
Czas rozpoczęcia i zakończenia przebiegu
Źródło uruchomienia np. nazwa skryptu do uczenia
Parametry
Metryki
Artefakty - pliki wyjściowe w dowolnym formacie, można logować obrazy, wykresy, modele itp.
Do logowania eksperymenty potrzebujemy zdefiniowania dwóch magazynów danych:
backend store- przechowuje dane dotyczące ekspetymentów (przebiegi, parametry, metryki, tagi, metadane itp.)
Wspierane typy magazynów danych:lokalna ścieżka
baza danych: mysql, mssql, sqlite lub postgresql
artifact store- przechowuje artefakty (pliki, modele, wykresy, obrazy itp.) Wspierane typy magazynów danych: lokalna ścieżka, Amazon S3, Azure Blob Storage, Google Cloud Storage, serwer FTP i SFTP, NFS i HDFS
Więcej informacji na temat magazynów danych mlflow: [LINK]
3.2.2.1. Ustawienia potrzebne do relizacji laboratorium#
W ramach laboratorium wykorzystamy baze danych sqllite, a artefakty będziemy logować do folderu artifacts
3.2.3. Uruchomienie serwera do śledzenia przebiegów uczenia#
Pierwszym krokiem, który musimy zdefiniować jest uruchomienie serwera, który będzie nam obsługiwał proces śledzenia przebiegów uczenia. Do tego wykorzystamy komendę mlflow server. Aplikacja domyślnie uruchamia się na porcie 5000. Szczegółowa dokumentacja: [LINK]
$ mlflow server --backend-store-uri sqlite:///mlflow.db --default-artifact-root ./artifacts --host 0.0.0.0
Następnym krokiem jest przekazanie adresu serwisu do śledzenia do biblioteki mlflow. Możemy umieścić bezpośrednie odwołanie w kodzie:
mlflow.set_tracking_uri("http://localhost:5000")
lub za pomocą zmiennej środowiskowej
MLFLOW_TRACKING_URI=http://localhost:5000
3.2.4. Monitorowanie przebiegów uczenia#
Nowy przebieg tworzymy za pomocą metody mlflow.start_run(). Najprościej jest umieścić go w ramach bloku kodu with.
with mlflow.start_run():
...
3.2.4.1. Automatyczne logowanie#
MLflow dostarcza moduły, które pozwolają automatycznie logować parametry, sam model oraz metryki, odbywa się to za pomocą metody autolog(). Metoda autolog() musi być wywołana przed rozpocząciem procesu uczenia.
Important
Funkcjanolność związana z autologowaniem za pomocą metody autolog() jest w fazie eksperymentalnej i zależy od wykorzystywanej biblioteki.
Autolog obecnie nie wspiera automatycznego wykrywania modelu, musimy zaimportować odpowiedni moduł przeznaczony dla naszego modelu. W naszym przypadku będzie to sklearn. mlflow.sklearn.autolog().
Szczegóły dotyczące autologowania i lista obecnie wspieranych bibliotek znajduję się pod linkiem: [LINK]
3.2.4.2. Ręczne logowanie#
Logowanie parametrów:
mlflow.log_paramlubmlflow.log_params
Przykłady:mlflow.log_param("train_domains", cfg["download_data"]["train_domains"])mlflow.log_params(cfg["download_data"])
Logowanie metryk
mlflow.log_metriclubmlflow.log_metrics
Przykłady:mlflow.log_metric("accuracy", metrics["accuracy"])mlflow.log_metrics(metrics=metrics)
Logowanie wykresów
mlflow.log_figurePrzykłady:mlflow.log_figure(fig, artifact_file="metrics.png")
Logowanie modeli
mlflow.{moduł_wykorzystywanej_biblioteki}.log_modelPrzykłady:mlflow.sklearn.log_model(clf, "model", registered_model_name="LogisticRegression")
3.2.5. Dodanie MLFlow do skryptu#
Dodajmy teraz obsługę mlflow do wcześniej przygotowane skryptu evaluate_model.
Elementy, które będziemy logować:
autologowanie
logowanie parametru
train_domainsitest_domainslogowanie metryk
accuracyi macrof1-scorelogowanie wykresu przedstawiającego metryki
precision,recall,f1-scoreper klasa
Bazowa wersja skryptu evaluate_model
import json
import pickle
import yaml
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
def main():
with open("data/featurized.pkl", "rb") as f:
dataset = pickle.load(f)
with open("params.yaml", "r") as f:
cfg = yaml.safe_load(f)
clf = LogisticRegression(**cfg["evaluate_model"])
clf.fit(dataset["train"]["X"], dataset["train"]["y"])
y_pred = clf.predict(dataset["test"]["X"])
report = classification_report(
y_pred=y_pred, y_true=dataset["test"]["y"], output_dict=True
)
test_metrics = {
"test_accuracy": report["accuracy"],
"test_f1-score": report["macro avg"]["f1-score"],
"test_precision": report["macro avg"]["precision"],
"test_recall": report["macro avg"]["recall"],
}
with open("data/results.json", "w") as f:
json.dump(obj=test_metrics, fp=f)
main()
Kod po dodaniu obsługi mlflow
import json
import pickle
+ from typing import Dict, Any, List
+ import matplotlib.pyplot as plt
+ import mlflow
+ import pandas as pd
+ import seaborn as sns
import yaml
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
+ def plot_metrics_per_class(report: Dict[str, Any], labels: List[str]):
+ report = pd.DataFrame(
+ [
+ {
+ "class": labels[class_id],
+ "metric": metric,
+ "value": report[str(class_id)][metric],
+ }
+ for class_id in range(len(labels))
+ for metric in ["precision", "recall", "f1-score"]
+ ]
+ )
+
+ sns.barplot(data=report, x="class", y="value", hue="metric")
+
+ plt.title("Metrics per class")
+ fig = plt.gcf()
+ return fig
def main():
with open("data/featurized.pkl", "rb") as f:
dataset = pickle.load(f)
with open("params.yaml", "r") as f:
cfg = yaml.safe_load(f)
+ mlflow.set_tracking_uri("http://localhost:5100")
+ with mlflow.start_run():
+ mlflow.sklearn.autolog()
clf = LogisticRegression(**cfg["evaluate_model"])
clf.fit(dataset["train"]["X"], dataset["train"]["y"])
y_pred = clf.predict(dataset["test"]["X"])
report = classification_report(
y_pred=y_pred,
y_true=dataset["test"]["y"],
output_dict=True,
)
test_metrics = {
"test_accuracy": report["accuracy"],
"test_f1-score": report["macro avg"]["f1-score"],
"test_precision": report["macro avg"]["precision"],
"test_recall": report["macro avg"]["recall"],
}
+ fig = plot_metrics_per_class(report, labels=dataset["labels"])
+ mlflow.log_params(cfg["download_data"])
+ mlflow.log_metrics(metrics=metrics)
+ mlflow.log_figure(fig, artifact_file="metrics.png")
with open("data/results.json", "w") as f:
json.dump(obj=metrics, fp=f)
main()
3.3. Weights & Biases#
Innym popularnym narzędziem do zarządzania eksperymentami jest Weights & Biases (Wandb). Do wykorzystania narzędzia niezbędne jest założenie konta na stronie wandb.ai i wygenerowanie klucza API w User settings -> API keys (na stronie).
Dokumentacja do biblioteki wandb: [LINK]
3.3.1. Instalacja#
Bibliotekę wandb możemy zainstalować za pomocą menadżera paczek
pip install wandb
3.3.2. Śledzenie eksperymentów#
W ramach laboratorium skupimy się na wykorzystaniu weights and biases do śledzenia przebiegu eksperymentów. Narzędzie umożliwia monitorowanie przebiegów uczenia (ang. runs), które są grupowane w projekty.
W ramach pojedynczego przebiegu są (lub mogą być) zbierane takie informacje jak:
Informacje o środowisku w którym wykonano eksperyment (system, hardware, wersja pythona)
Wersja kodu
Czas rozpoczęcia i trwania przebiegu
Źródło uruchomienia np. nazwa skryptu do uczenia
Parametry
Metryki
Artefakty - pliki wyjściowe w dowolnym formacie, można logować obrazy, wykresy, modele itp.
Logowanie danych odbywa się w chmurze na stronie wandb.ai
3.3.3. Monitorowanie przebiegów uczenia#
Pierwszym krokiem jest uwierzytelnienie komputera na którym przeprowadzamy eksperymenty. Możemy tym celu ustawić zmienną środowiskową
export WANDB_API_KEY=<wygenerowany klucz>
lub wykonać w kodzie polecenie
import wandb
wandb.login()
Następnym krokiem jest inicjalizacja przebiegu w kodzie.
with wandb.init(
project=<nazwa projektu>,
job_type=<wykonywane zadanie (nieobowiązkowe)>,
config={
(konfiguracja, np. parametry modelu)
}
) as run:
3.3.3.1. Automatyczne logowanie#
Wandb dostarcza moduły, które pozwolają zaautomatyzować logowanie popularnych metryk i analiz. W przykładzie posłużymy się funkcją wandb.sklearn.plot_classifier, jednak inne przydatne funkcje obejmują logowanie macierzy pomyłek wandb.sklearn.plot_confusion_matrix, istotności cech wandb.sklearn.plot_feature_importances, czy krzywej kalibracyjnej wandb.sklearn.plot_calibration_curve.
wandb.sklearn.plot_classifier(
clf,
X_train,
X_test,
y_train,
y_test,
y_pred,
y_probas,
labels,
model_name=<nazwa modelu>,
feature_names=<nazwy cech>,
)
Szczegóły dotyczące integracji wandb z sklearn znajduję się pod linkiem: [LINK]
3.3.3.2. Ręczne logowanie#
Ręczne logowanie odbywa się przy pomocy metody wandb.log, np.
wandb.log({**test_metrics, "fig": wandb.Image(fig)})
lub w przypadku zdefiniowania kontekstu jak wyżej, możemy użyć
run.log({**test_metrics, "fig": wandb.Image(fig)})
Skrypt z przykładu z MLFlow wykorzystujący Wandb wyglądałby następująco. Wartości przypisywane parametrom project i job_type powinny być zdefiniowane w miejscu mniej narażonym na przeoczenie, jendak dla prostoty przykładu znajdują się w wywołaniu wandb.init.
import json
import pickle
from typing import Dict, Any, List
import matplotlib.pyplot as plt
import wandb
import pandas as pd
import seaborn as sns
import yaml
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
def plot_metrics_per_class(report: Dict[str, Any], labels: List[str]):
report = pd.DataFrame(
[
{
"class": labels[class_id],
"metric": metric,
"value": report[str(class_id)][metric],
}
for class_id in range(len(labels))
for metric in ["precision", "recall", "f1-score"]
]
)
sns.barplot(data=report, x="class", y="value", hue="metric")
plt.title("Metrics per class")
fig = plt.gcf()
return fig
def main():
wandb.login()
with open("data/featurized.pkl", "rb") as f:
dataset = pickle.load(f)
with open("params.yaml", "r") as f:
cfg = yaml.safe_load(f)
with wandb.init(
project="pdiow-l3-wandb",
job_type="eval-model",
config={
**cfg["download_data"],
**cfg["evaluate_model"]
}
) as run:
clf = LogisticRegression(**cfg["evaluate_model"])
clf.fit(dataset["train"]["X"], dataset["train"]["y"])
y_pred = clf.predict(dataset["test"]["X"])
y_prob = clf.predict_proba(dataset["test"]["X"])
wandb.sklearn.plot_classifier(
clf,
dataset["train"]["X"],
dataset["test"]["X"],
dataset["train"]["y"],
dataset["test"]["y"],
y_pred,
y_prob,
labels=dataset["labels"],
)
report = classification_report(
y_pred=y_pred,
y_true=dataset["test"]["y"],
output_dict=True,
)
test_metrics = {
"test_accuracy": report["accuracy"],
"test_f1-score": report["macro avg"]["f1-score"],
"test_precision": report["macro avg"]["precision"],
"test_recall": report["macro avg"]["recall"],
}
fig = plot_metrics_per_class(report, labels=dataset["labels"])
run.log({**test_metrics, "fig": wandb.Image(fig)})
with open("data/results2.json", "w") as f:
json.dump(obj=test_metrics, fp=f)
main()
3.4. Kod do części II#
Pełny kod pokrywający część II znajduje się pod adresem: [LINK]
3.5. Eksperymenty w DVC 3.0#
Ostatnim omawianym elementem jest definiowanie eksperymentów w ramach DVC w wersji 3.0
Eksperymenty oparte są o moduł dvc exp pozwalają na łatwą zmianę konfiguracji potoku przetwarzania oraz umożliwiają definicję kilku konfiguracji. Aktualny opis interfejsu experiments znajduje się pod adresem [LINK]
Eksperymenty są uruchamiane za pomocą komendy dvc exp run [LINK] zmienione parametry podajemy za pomocą flagi -S.
Zmieńmy teraz liczbę iteracji na 50.
$ dvc exp run -S evaluate_model.max_iter=50
...
Reproduced experiment(s): exp-b605e
Experiment results have been applied to your workspace.
To promote an experiment to a Git branch run:
dvc exp branch <exp>
Teraz możemy podejrzeć zmainę metryk za pomocą dvc exp diff
$ dvc exp diff
Path Metric Value Change
data/results.json accuracy 0.73964 -0.0020121
data/results.json f1-score 0.66816 -0.0084543
Path Param Value Change
params.yaml evaluate_model.max_iter 50 -150
Alternatywnie możemy zdefiniować kolejkę eksperymentów i dopiero później je wykonać. Dodajmy teraz inne konfigurację. W celu zakolejkowania eksperymentu wykorzystujemy flagę --queue.
$ dvc exp run --queue -S evaluate_model.max_iter=300 -S download_data.train_domains="[hotels,medicine]"
Queued experiment 'dd51be5' for future execution.
$ dvc exp run --queue -S evaluate_model.max_iter=300 -S download_data.train_domains="[medicine,products]"
Queued experiment '9887d0f' for future execution.
$ dvc exp run --queue -S evaluate_model.max_iter=300 -S download_data.train_domains="[all]"
Queued experiment 'db4350f' for future execution.
Uruchumienie eksperymentów
$ dvc exp run --run-all
Podsumowanie wyników eksperymentów możemy uzyskać za pomocą polecenia dvc exp show [LINK]
$ dvc exp show
Możemy ograniczyć listę wyświetlanych kolumn przy pomocy flagi --drop, określając nazwy kolumn do odrzucenia.
$ dvc exp show --drop 'Created|State|Executor|data.*|scripts.*'
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Experiment test_accuracy test_f1-score download_data.train_domains evaluate_model.max_iter
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
workspace 0.74165 0.67661 ['hotels'] 200
main 0.74165 0.67661 ['hotels'] 200
├── c552408 [sober-monk] 0.72716 0.66199 ['hotels', 'medicine'] 300
├── 35d1f73 [owned-rubs] 0.57223 0.51545 ['medicine', 'products'] 300
├── b12c2be [whole-vase] 0.73119 0.6675 ['all'] 300
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Hint
Jeżeli tabela nie mieści się na ekranie, dvc exp show domyślnie wyświetli ją używając pagera. Aby z niego wyjść, należy nacisnąć q na klawiaturze.
Wyświetlanie listy eksperymentów
$ dvc exp list
main:
c552408 [sober-monk]
35d1f73 [owned-rubs]
b12c2be [whole-vase]
Możemy teraz wybrać najlepszy eksperyment, akurat w tym przypadku żaden z przeprowadzonych eksperymentów nie okazał się lepszy od konfiguracji zastosowanej na głównym branchu. Więc pozostawimy bez zmian. Ale jeżeli chcielibyśmy zaaktualizować potok na bazie któregoś z przeprowadzonych elementów możemy wykorzystać komendę dvc exp apply podając identyfikator eksperymentu
Do usunięcia eksperymentu służy komenda dvc exp remove, możemy również usunąć stare eksperymenty za pomocą dvc exp clean. Dodatkowo aby wyczyścić pamięc podręczną musimy również wykonać polecenie dvc gc [LINK]