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
-d
parametry (w celu śledzenia zmian), definiowane za pomocą parametru
-p
pliki wyjściowe, definiowane za pomocą parametru
-o
skrypt 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żelidvc
nie reportuje zmian-s
- wykonanie pojedynczego etapu z pominięciem poprzednich, nawet jeżelidvc
reportuje 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 commit
jest 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_param
lubmlflow.log_params
Przykłady:mlflow.log_param("train_domains", cfg["download_data"]["train_domains"])
mlflow.log_params(cfg["download_data"])
Logowanie metryk
mlflow.log_metric
lubmlflow.log_metrics
Przykłady:mlflow.log_metric("accuracy", metrics["accuracy"])
mlflow.log_metrics(metrics=metrics)
Logowanie wykresów
mlflow.log_figure
Przykłady:mlflow.log_figure(fig, artifact_file="metrics.png")
Logowanie modeli
mlflow.{moduł_wykorzystywanej_biblioteki}.log_model
Przykł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_domains
itest_domains
logowanie metryk
accuracy
i macrof1-score
logowanie wykresu przedstawiającego metryki
precision
,recall
,f1-score
per 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]