54 KiB
Inżynieria uczenia maszynowego
8. MLFlow [laboratoria]
Tomasz Ziętkiewicz (2022)
MLflow
- https://mlflow.org/
- Narzędzie podobne do omawianego na poprzednich zajęciach Sacred
- Nieco inne podejście: mniej ingerencji w istniejący kod
- Bardziej kompleksowe rozwiązanie: 4 komponenty, pierwszy z nich ma funkcjonalność podobną do Sacred
- Działa "z każdym" językiem. A tak naprawdę: Python, R, Java + CLI API + REST API
- Popularna wśród pracodawców - wyniki wyszukiwania ofert pracy: 20 ofert (https://pl.indeed.com/), 36 ofert (linkedin). Sacred: 0
- Integracja z licznymi bibliotekami / chmurami
- Rozwiązanie OpenSource, stworzone przez firmę Databricks
- Dostępna odpłatna wersja "Managed" (w ordóżnieniu od "self-hosted")
Komponenty
MLflow składa się z czterech niezależnych komponentów:
MLflow Tracking - pozwala śledzić zmiany parametrów, kodu, środowiska i ich wpływ na metryki. Jest to funkcjonalność bardzo zbliżona do tej, którą zapewnia Sacred
MLflow Projects - umożliwia "pakowanie" kodu ekserymentów w taki sposób, żeby mogłby być w łatwy sposób zreprodukowane przez innych
MLflow Models - ułatwia "pakowanie" modeli uczenia maszynowego
MLflow Registry - zapewnia centralne miejsce do przechowywania i współdzielenia modeli. Zapewnia narzędzia do wersjonowania i śledzenia pochodzenia tych modeli.
Komponenty te mogą być używane razem bądź oddzielnie.
MLflow Tracking - przykład
(poniższe przykłady kodu trenującego pochodzą z tutoriala MLflow: https://mlflow.org/docs/latest/tutorials-and-examples/tutorial.html)
%%capture null
!pip install mlflow
!pip install sklearn
!mkdir -p IUM_08/examples/sklearn_elasticnet_wine/
%%writefile IUM_08/examples/sklearn_elasticnet_wine/train.py
# The data set used in this example is from http://archive.ics.uci.edu/ml/datasets/Wine+Quality
# P. Cortez, A. Cerdeira, F. Almeida, T. Matos and J. Reis.
# Modeling wine preferences by data mining from physicochemical properties. In Decision Support Systems, Elsevier, 47(4):547-553, 2009.
import os
import warnings
import sys
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import ElasticNet
from urllib.parse import urlparse
import mlflow
import mlflow.sklearn
import logging
logging.basicConfig(level=logging.WARN)
logger = logging.getLogger(__name__)
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("s123456")
def eval_metrics(actual, pred):
rmse = np.sqrt(mean_squared_error(actual, pred))
mae = mean_absolute_error(actual, pred)
r2 = r2_score(actual, pred)
return rmse, mae, r2
if __name__ == "__main__":
warnings.filterwarnings("ignore")
np.random.seed(40)
# Read the wine-quality csv file from the URL
csv_url = (
"http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
)
try:
data = pd.read_csv(csv_url, sep=";")
except Exception as e:
logger.exception(
"Unable to download training & test CSV, check your internet connection. Error: %s", e
)
# Split the data into training and test sets. (0.75, 0.25) split.
train, test = train_test_split(data)
# The predicted column is "quality" which is a scalar from [3, 9]
train_x = train.drop(["quality"], axis=1)
test_x = test.drop(["quality"], axis=1)
train_y = train[["quality"]]
test_y = test[["quality"]]
alpha = float(sys.argv[1]) if len(sys.argv) > 1 else 0.5
#alpha = 0.5
l1_ratio = float(sys.argv[2]) if len(sys.argv) > 2 else 0.5
#l1_ratio = 0.5
with mlflow.start_run() as run:
print("MLflow run experiment_id: {0}".format(run.info.experiment_id))
print("MLflow run artifact_uri: {0}".format(run.info.artifact_uri))
lr = ElasticNet(alpha=alpha, l1_ratio=l1_ratio, random_state=42)
lr.fit(train_x, train_y)
predicted_qualities = lr.predict(test_x)
(rmse, mae, r2) = eval_metrics(test_y, predicted_qualities)
print("Elasticnet model (alpha=%f, l1_ratio=%f):" % (alpha, l1_ratio))
print(" RMSE: %s" % rmse)
print(" MAE: %s" % mae)
print(" R2: %s" % r2)
mlflow.log_param("alpha", alpha)
mlflow.log_param("l1_ratio", l1_ratio)
mlflow.log_metric("rmse", rmse)
mlflow.log_metric("r2", r2)
mlflow.log_metric("mae", mae)
# Infer model signature to log it
# Więcej o sygnaturach: https://mlflow.org/docs/latest/models.html?highlight=signature#model-signature
signature = mlflow.models.signature.infer_signature(train_x, lr.predict(train_x))
tracking_url_type_store = urlparse(mlflow.get_tracking_uri()).scheme
# Model registry does not work with file store
if tracking_url_type_store != "file":
# Register the model
# There are other ways to use the Model Registry, which depends on the use case,
# please refer to the doc for more information:
# https://mlflow.org/docs/latest/model-registry.html#api-workflow
mlflow.sklearn.log_model(lr, "wines-model", registered_model_name="ElasticnetWineModel", signature=signature)
else:
mlflow.sklearn.log_model(lr, "model", signature=signature)
Overwriting IUM_08/examples/sklearn_elasticnet_wine/train.py
! ls -l /tmp/mlruns
### Wtyrenujmy model z domyślnymi wartościami parametrów
! cd ./IUM_08/examples/; python sklearn_elasticnet_wine/train.py
total 4 drwxrwxr-x 3 tomek tomek 4096 maj 19 21:31 1 INFO: 's123456' does not exist. Creating a new experiment MLflow run experiment_id: 2 MLflow run artifact_uri: /tmp/mlruns/2/c15feb5df335490ba990ddd4dd977c1b/artifacts Elasticnet model (alpha=0.500000, l1_ratio=0.500000): RMSE: 0.7931640229276851 MAE: 0.6271946374319586 R2: 0.10862644997792614 Registered model 'ElasticnetWineModel' already exists. Creating a new version of this model... 2021/05/19 22:34:48 INFO mlflow.tracking._model_registry.client: Waiting up to 300 seconds for model version to finish creation. Model name: ElasticnetWineModel, version 2 Created version '2' of model 'ElasticnetWineModel'.
### I jeszcze raz, tym razem ze zmienionymi wartościami parametrów
! cd ./IUM_08/examples/; for l in {1..9}; do for a in {1..9}; do python sklearn_elasticnet_wine/train.py 0.$a 0.$l; done; done
Elasticnet model (alpha=0.100000, l1_ratio=0.100000): RMSE: 0.7128829045893679 MAE: 0.5462202174984664 R2: 0.2799376066653344 Elasticnet model (alpha=0.200000, l1_ratio=0.100000): RMSE: 0.7268133518615142 MAE: 0.5586842416161892 R2: 0.251521166881557 Elasticnet model (alpha=0.300000, l1_ratio=0.100000): RMSE: 0.7347397539240514 MAE: 0.5657315547549873 R2: 0.23510678899596094 Elasticnet model (alpha=0.400000, l1_ratio=0.100000): RMSE: 0.7410782793160982 MAE: 0.5712718681984227 R2: 0.22185255063708875 Elasticnet model (alpha=0.500000, l1_ratio=0.100000): RMSE: 0.7460550348172179 MAE: 0.576381895873763 R2: 0.21136606570632266 Elasticnet model (alpha=0.600000, l1_ratio=0.100000): RMSE: 0.7510866447955419 MAE: 0.5815681289333974 R2: 0.20069264568704714 Elasticnet model (alpha=0.700000, l1_ratio=0.100000): RMSE: 0.7560654760040749 MAE: 0.5868129921328281 R2: 0.19006056603695476 Elasticnet model (alpha=0.800000, l1_ratio=0.100000): RMSE: 0.7609263702116827 MAE: 0.5919470003487062 R2: 0.17961256649282442 Elasticnet model (alpha=0.900000, l1_ratio=0.100000): RMSE: 0.7656313758553691 MAE: 0.5969367233859049 R2: 0.16943586313742276 Elasticnet model (alpha=0.100000, l1_ratio=0.200000): RMSE: 0.7201489594275661 MAE: 0.5525324524014098 R2: 0.26518433811823017 Elasticnet model (alpha=0.200000, l1_ratio=0.200000): RMSE: 0.7336400911821402 MAE: 0.5643841279275428 R2: 0.23739466063584158 Elasticnet model (alpha=0.300000, l1_ratio=0.200000): RMSE: 0.7397486012946922 MAE: 0.5704931175017443 R2: 0.22464242411894242 Elasticnet model (alpha=0.400000, l1_ratio=0.200000): RMSE: 0.7468093030485085 MAE: 0.5777243300021722 R2: 0.2097706278632726 Elasticnet model (alpha=0.500000, l1_ratio=0.200000): RMSE: 0.7543919979968401 MAE: 0.5857669727382302 R2: 0.19364204365178095 Elasticnet model (alpha=0.600000, l1_ratio=0.200000): RMSE: 0.7622123676513404 MAE: 0.5938629318868578 R2: 0.17683724501340814 Elasticnet model (alpha=0.700000, l1_ratio=0.200000): RMSE: 0.7700845840888665 MAE: 0.6024685725504659 R2: 0.15974600028150265 Elasticnet model (alpha=0.800000, l1_ratio=0.200000): RMSE: 0.7778880968569085 MAE: 0.6105907461474273 R2: 0.14263059582492588 Elasticnet model (alpha=0.900000, l1_ratio=0.200000): RMSE: 0.7855450337039626 MAE: 0.6182359127922239 R2: 0.1256689455181047 Elasticnet model (alpha=0.100000, l1_ratio=0.300000): RMSE: 0.7260299544064643 MAE: 0.5571534327625295 R2: 0.2531337966130104 Elasticnet model (alpha=0.200000, l1_ratio=0.300000): RMSE: 0.7357092639331829 MAE: 0.5667609266233857 R2: 0.23308686049079996 Elasticnet model (alpha=0.300000, l1_ratio=0.300000): RMSE: 0.7443224557281489 MAE: 0.5754825491733004 R2: 0.2150247343683439 Elasticnet model (alpha=0.400000, l1_ratio=0.300000): RMSE: 0.7545302211047864 MAE: 0.5862255018460154 R2: 0.19334652749043568 Elasticnet model (alpha=0.500000, l1_ratio=0.300000): RMSE: 0.7657094552843393 MAE: 0.597876674089536 R2: 0.16926645189778677 Elasticnet model (alpha=0.600000, l1_ratio=0.300000): RMSE: 0.7774287676055035 MAE: 0.6102458961382884 R2: 0.14364282001967787 Elasticnet model (alpha=0.700000, l1_ratio=0.300000): RMSE: 0.7876149030178985 MAE: 0.6208628759605734 R2: 0.12105524358911324 Elasticnet model (alpha=0.800000, l1_ratio=0.300000): RMSE: 0.7972426725990548 MAE: 0.6310633254738363 R2: 0.09943554388738107 Elasticnet model (alpha=0.900000, l1_ratio=0.300000): RMSE: 0.806653553139972 MAE: 0.6407940021176486 R2: 0.07804901733081859 Elasticnet model (alpha=0.100000, l1_ratio=0.400000): RMSE: 0.7301757756825391 MAE: 0.5603782497631705 R2: 0.24457984004307665 Elasticnet model (alpha=0.200000, l1_ratio=0.400000): RMSE: 0.7383379454127179 MAE: 0.5696920200435643 R2: 0.22759672468382497 Elasticnet model (alpha=0.300000, l1_ratio=0.400000): RMSE: 0.7501603725852 MAE: 0.5818749078280213 R2: 0.2026629101382652 Elasticnet model (alpha=0.400000, l1_ratio=0.400000): RMSE: 0.7644619587468349 MAE: 0.5966303605775048 R2: 0.17197111491474282 Elasticnet model (alpha=0.500000, l1_ratio=0.400000): RMSE: 0.7794144864140182 MAE: 0.6125287339702588 R2: 0.1392625955410326 Elasticnet model (alpha=0.600000, l1_ratio=0.400000): RMSE: 0.7928446872861473 MAE: 0.626666444473971 R2: 0.10934405701835759 Elasticnet model (alpha=0.700000, l1_ratio=0.400000): RMSE: 0.8064523157995205 MAE: 0.6407990295001776 R2: 0.07850896155515663 Elasticnet model (alpha=0.800000, l1_ratio=0.400000): RMSE: 0.8200264141399415 MAE: 0.6539313398770489 R2: 0.04722706260889009 Elasticnet model (alpha=0.900000, l1_ratio=0.400000): RMSE: 0.8317936823364004 MAE: 0.6647839366878934 R2: 0.01968654319755092 Elasticnet model (alpha=0.100000, l1_ratio=0.500000): RMSE: 0.7308996187375898 MAE: 0.5615486628017713 R2: 0.2430813606733676 Elasticnet model (alpha=0.200000, l1_ratio=0.500000): RMSE: 0.7415652207304311 MAE: 0.573067857646195 R2: 0.22082961765864062 Elasticnet model (alpha=0.300000, l1_ratio=0.500000): RMSE: 0.7573787958793151 MAE: 0.5893143148791096 R2: 0.18724431943947983 Elasticnet model (alpha=0.400000, l1_ratio=0.500000): RMSE: 0.7759342885655987 MAE: 0.6090076377075831 R2: 0.14693206734185604 Elasticnet model (alpha=0.500000, l1_ratio=0.500000): RMSE: 0.7931640229276851 MAE: 0.6271946374319586 R2: 0.10862644997792614 Elasticnet model (alpha=0.600000, l1_ratio=0.500000): RMSE: 0.8112953030727291 MAE: 0.645693705089251 R2: 0.06740807086129252 Elasticnet model (alpha=0.700000, l1_ratio=0.500000): RMSE: 0.8298921852578498 MAE: 0.6629780128961713 R2: 0.024163452726365775 Elasticnet model (alpha=0.800000, l1_ratio=0.500000): RMSE: 0.8320198635059106 MAE: 0.6657357030427604 R2: 0.019153337439844154 Elasticnet model (alpha=0.900000, l1_ratio=0.500000): RMSE: 0.8323808561832262 MAE: 0.6669472047761406 R2: 0.0183020229672054 Elasticnet model (alpha=0.100000, l1_ratio=0.600000): RMSE: 0.7317723392279818 MAE: 0.5627373693033669 R2: 0.24127270524006605 Elasticnet model (alpha=0.200000, l1_ratio=0.600000): RMSE: 0.7454324777911233 MAE: 0.5772117261484206 R2: 0.21268169183406394 Elasticnet model (alpha=0.300000, l1_ratio=0.600000): RMSE: 0.7661028672396263 MAE: 0.5984406933733759 R2: 0.16841259155853305 Elasticnet model (alpha=0.400000, l1_ratio=0.600000): RMSE: 0.787179486885359 MAE: 0.6210967388389844 R2: 0.12202678676193257 Elasticnet model (alpha=0.500000, l1_ratio=0.600000): RMSE: 0.809739471626647 MAE: 0.6442565454817458 R2: 0.07098152823463388 Elasticnet model (alpha=0.600000, l1_ratio=0.600000): RMSE: 0.8317884179944764 MAE: 0.6647524814105722 R2: 0.019698951776764728 Elasticnet model (alpha=0.700000, l1_ratio=0.600000): RMSE: 0.8321519738036909 MAE: 0.6662086037874676 R2: 0.018841829895677176 Elasticnet model (alpha=0.800000, l1_ratio=0.600000): RMSE: 0.8326350511178233 MAE: 0.6676630843299566 R2: 0.01770234373563795 Elasticnet model (alpha=0.900000, l1_ratio=0.600000): RMSE: 0.8332048101440411 MAE: 0.6690717294644856 R2: 0.016357542209390563 Elasticnet model (alpha=0.100000, l1_ratio=0.700000): RMSE: 0.7327938109945942 MAE: 0.5640101718105491 R2: 0.23915303116151632 Elasticnet model (alpha=0.200000, l1_ratio=0.700000): RMSE: 0.7499835110445395 MAE: 0.5819389930665501 R2: 0.20303883413454027 Elasticnet model (alpha=0.300000, l1_ratio=0.700000): RMSE: 0.7747136483567111 MAE: 0.6079678532556209 R2: 0.14961391810397695 Elasticnet model (alpha=0.400000, l1_ratio=0.700000): RMSE: 0.8004478857657858 MAE: 0.6350378679245181 R2: 0.09217977708630032 Elasticnet model (alpha=0.500000, l1_ratio=0.700000): RMSE: 0.829586285479097 MAE: 0.6627028304266674 R2: 0.024882710417618137 Elasticnet model (alpha=0.600000, l1_ratio=0.700000): RMSE: 0.8321502650365332 MAE: 0.6662000872414003 R2: 0.018845859373919027 Elasticnet model (alpha=0.700000, l1_ratio=0.700000): RMSE: 0.832725785743381 MAE: 0.667898097502809 R2: 0.017488244494447747 Elasticnet model (alpha=0.800000, l1_ratio=0.700000): RMSE: 0.8331825395236181 MAE: 0.6692175076829847 R2: 0.016410124803194592 Elasticnet model (alpha=0.900000, l1_ratio=0.700000): RMSE: 0.8331069437643933 MAE: 0.6697424890266508 R2: 0.016588601539516357 Elasticnet model (alpha=0.100000, l1_ratio=0.800000): RMSE: 0.7339712501091269 MAE: 0.5654097809725043 R2: 0.23670603806205326 Elasticnet model (alpha=0.200000, l1_ratio=0.800000): RMSE: 0.7552646505492441 MAE: 0.5873472009739388 R2: 0.19177543499093674 Elasticnet model (alpha=0.300000, l1_ratio=0.800000): RMSE: 0.7836957692333741 MAE: 0.6176788505535867 R2: 0.12978065429593022 Elasticnet model (alpha=0.400000, l1_ratio=0.800000): RMSE: 0.8160164529135189 MAE: 0.650349905850893 R2: 0.05652247327326554 Elasticnet model (alpha=0.500000, l1_ratio=0.800000): RMSE: 0.8320145539945119 MAE: 0.6657081587004348 R2: 0.019165855890777572 Elasticnet model (alpha=0.600000, l1_ratio=0.800000): RMSE: 0.8326325509502465 MAE: 0.6676500690618903 R2: 0.01770824285088779 Elasticnet model (alpha=0.700000, l1_ratio=0.800000): RMSE: 0.8331830329685253 MAE: 0.6692142378162035 R2: 0.016408959758236752 Elasticnet model (alpha=0.800000, l1_ratio=0.800000): RMSE: 0.8330972295348316 MAE: 0.669813814205792 R2: 0.016611535037920344 Elasticnet model (alpha=0.900000, l1_ratio=0.800000): RMSE: 0.8330208354420413 MAE: 0.6704133670619602 R2: 0.016791878033996177 Elasticnet model (alpha=0.100000, l1_ratio=0.900000): RMSE: 0.735314956888905 MAE: 0.566974647785579 R2: 0.23390870203034675 Elasticnet model (alpha=0.200000, l1_ratio=0.900000): RMSE: 0.7613249071370938 MAE: 0.593613372674502 R2: 0.1787529818606436 Elasticnet model (alpha=0.300000, l1_ratio=0.900000): RMSE: 0.7940027723712206 MAE: 0.6284316436541582 R2: 0.10674024649047587 Elasticnet model (alpha=0.400000, l1_ratio=0.900000): RMSE: 0.831784893250733 MAE: 0.6647313794016759 R2: 0.019707259905588637 Elasticnet model (alpha=0.500000, l1_ratio=0.900000): RMSE: 0.8323747376136406 MAE: 0.6669171677143245 R2: 0.018316455219614114 Elasticnet model (alpha=0.600000, l1_ratio=0.900000): RMSE: 0.8332063354920289 MAE: 0.6690618761753936 R2: 0.01635394069773599 Elasticnet model (alpha=0.700000, l1_ratio=0.900000): RMSE: 0.8331078270287657 MAE: 0.6697360518827573 R2: 0.016586516302516174 Elasticnet model (alpha=0.800000, l1_ratio=0.900000): RMSE: 0.8330212125502486 MAE: 0.6704102143580977 R2: 0.016790987837928095 Elasticnet model (alpha=0.900000, l1_ratio=0.900000): RMSE: 0.8329464950658837 MAE: 0.6710843636018047 R2: 0.01696735695860563
### Informacje o przebieagach eksperymentu zostały zapisane w katalogu mlruns
! ls -l IUM_08/examples/mlruns/0 | head
total 16 drwxrwxr-x 6 tomek tomek 4096 maj 17 08:43 375cde31bdd44a45a91fd7cee92ebcda drwxrwxr-x 6 tomek tomek 4096 maj 17 10:38 b395b55b47fc43de876b67f5a4a5dae9 drwxrwxr-x 6 tomek tomek 4096 maj 17 09:15 b3ead42eca964113b29e7e5f8bcb7bb7 -rw-rw-r-- 1 tomek tomek 151 maj 17 08:43 meta.yaml
! ls -l IUM_08/examples/mlruns/0/375cde31bdd44a45a91fd7cee92ebcda
total 20 drwxrwxr-x 3 tomek tomek 4096 maj 17 08:43 artifacts -rw-rw-r-- 1 tomek tomek 423 maj 17 08:43 meta.yaml drwxrwxr-x 2 tomek tomek 4096 maj 17 08:43 metrics drwxrwxr-x 2 tomek tomek 4096 maj 17 08:43 params drwxrwxr-x 2 tomek tomek 4096 maj 17 08:43 tags
### Możemy je obejrzeć w przeglądarce uruchamiając interfejs webowy:
### (powinniśmy to wywołać w normalnej konsoli, w jupyter będziemy mieli zablokowany kernel)
! cd IUM_08/examples/; mlflow ui
[2021-05-16 17:58:43 +0200] [118029] [INFO] Starting gunicorn 20.1.0 [2021-05-16 17:58:43 +0200] [118029] [ERROR] Connection in use: ('127.0.0.1', 5000) [2021-05-16 17:58:43 +0200] [118029] [ERROR] Retrying in 1 second. [2021-05-16 17:58:44 +0200] [118029] [ERROR] Connection in use: ('127.0.0.1', 5000) [2021-05-16 17:58:44 +0200] [118029] [ERROR] Retrying in 1 second. [2021-05-16 17:58:45 +0200] [118029] [ERROR] Connection in use: ('127.0.0.1', 5000) [2021-05-16 17:58:45 +0200] [118029] [ERROR] Retrying in 1 second. [2021-05-16 17:58:46 +0200] [118029] [ERROR] Connection in use: ('127.0.0.1', 5000) [2021-05-16 17:58:46 +0200] [118029] [ERROR] Retrying in 1 second. [2021-05-16 17:58:47 +0200] [118029] [ERROR] Connection in use: ('127.0.0.1', 5000) [2021-05-16 17:58:47 +0200] [118029] [ERROR] Retrying in 1 second. [2021-05-16 17:58:48 +0200] [118029] [ERROR] Can't connect to ('127.0.0.1', 5000) Running the mlflow server failed. Please see the logs above for details.
Instancja na naszym serwerze: http://tzietkiewicz.vm.wmi.amu.edu.pl:5000/#/
Logowanie
logowania metryk i parametrów można dokonać m.in. poprzez wywołania Python-owego API:
mlflow.log_param()
imlflow.log_metric()
. Więcej dostępnych funkcji: linkwywołania te muszą nastąpić po wykonaniu
mlflow.start_run()
, najlepiej wewnątrz bloku:with mlflow.start_run(): #[...] mlflow.log_param("alpha", alpha) mlflow.log_param("l1_ratio", l1_ratio)
jest też możliwość automatycznego logwania dla wybranych bibliotek: https://mlflow.org/docs/latest/tracking.html#automatic-logging
MLflow Projects
- MLflow projects to zestaw konwencji i kilku narzędzi
- ułatwiają one uruchamianie eskperymentów
Konfiguracja projektu
- W pliku
MLproject
zapisuje się konfigurację projektu (specyfikacja) - Zawiera ona:
- odnośnik do środowiska, w którym ma być wywołany eksperyment szczegóły:
- nazwa obrazu Docker
- albo ścieżka do pliku conda.yaml definiującego środowisko wykonania Conda
- parametry, z którymi można wywołać eksperyment
- polecenia służące do wywołania eksperymentu
- odnośnik do środowiska, w którym ma być wywołany eksperyment szczegóły:
%%writefile IUM_08/examples/sklearn_elasticnet_wine/MLproject
name: tutorial
conda_env: conda.yaml #ścieżka do pliku conda.yaml z definicją środowiska
#docker_env:
# image: mlflow-docker-example-environment
entry_points:
main:
parameters:
alpha: {type: float, default: 0.5}
l1_ratio: {type: float, default: 0.1}
command: "python train.py {alpha} {l1_ratio}"
test:
parameters:
alpha: {type: cutoff, default: 0}
command: "python test.py {cutoff}"
Overwriting IUM_08/examples/sklearn_elasticnet_wine/MLproject
Środowisko Conda
- https://docs.conda.io
- Składnia plików conda.yaml definiujących środowisko: https://docs.conda.io/projects/conda/en/4.6.1/user-guide/tasks/manage-environments.html#create-env-file-manually
- Składnia YAML: przystępnie, oficjalnie
%%writefile IUM_08/examples/sklearn_elasticnet_wine/conda.yaml
name: tutorial
channels:
- defaults
dependencies:
- python=3.6 #Te zależności będą zainstalowane za pomocą conda isntall
- pip
- pip: #Te ząś za pomocą pip install
- scikit-learn==0.23.2
- mlflow>=1.0
Overwriting IUM_08/examples/sklearn_elasticnet_wine/conda.yaml
Środowisko docker
- zamiast środowiska Conda możemy również podać nazwę obrazu docker, w którym ma być wywołany eksperyment.
- obraz będzie szukany lokalnie a następnie na DockerHub, lub w innym repozytorium dockera
- składnia specyfikacji ścieżki jest taka sama jak w przypadki poleceń dockera, np. docker pull link
- Można również podać katalogi do podmontowania wewnątrz kontenera oraz wartości zmiennych środowiskowych do ustawienia w kontenerze:
docker_env: image: mlflow-docker-example-environment volumes: ["/local/path:/container/mount/path"] environment: [["NEW_ENV_VAR", "new_var_value"], "VAR_TO_COPY_FROM_HOST_ENVIRONMENT"]
Parametry
Specyfikacja parametrów w pliku MLproject pozwala na ich walidację i używanie wartości domyślnych
Dostępne typy:
- String
- Float - dowolna liczba (MLflow waliduje, czy podana wartość jest liczbą)
- Path - pozwala podawać ścieżki względne (przekształca je na bezwzlędne) do plików lokalnych albo do plików zdalnych (np. do s3://) - zostaną wtedy ściągnięte lokalnie
- URI - podobnie jak path, ale do rozproszonych systemów plików
-
parameter_name: {type: data_type, default: value} # Short syntax parameter_name: # Long syntax type: data_type default: value
Uruchamianie projektu
- Projekt możemy uruchomić przy pomocy polecenia
mlflow run
(dokumentacja) - Spowoduje to przygotowanie środowiska i uruchomienie eksperymentu wewnątrz środowiska
- domyślnie zostanie uruchomione polecenie zdefiniowane w "entry point"
main
. Żeby uruchomić inny "entry point", możemy użyć parametru-e
, np:mlflow run sklearn_elasticnet_wine -e test
- Parametry do naszego polecenia możemy przekazywać przy pomocy flagi
-P
!cd IUM_08/examples/; mlflow run sklearn_elasticnet_wine -P alpha=0.42
2021/05/16 17:59:10 INFO mlflow.projects.utils: === Created directory /tmp/tmprq4mdosv for downloading remote URIs passed to arguments of type 'path' === 2021/05/16 17:59:10 INFO mlflow.projects.backend.local: === Running command 'source /home/tomek/miniconda3/bin/../etc/profile.d/conda.sh && conda activate mlflow-5987e03d4dbaa5faa1a697bb113be9b9bdc39b29 1>&2 && python train.py 0.42 0.1' in run with ID '1860d321ea1545ff8866e4ba199d1712' === Elasticnet model (alpha=0.420000, l1_ratio=0.100000): RMSE: 0.7420620899060748 MAE: 0.5722846717246247 R2: 0.21978513651550236 2021/05/16 17:59:19 INFO mlflow.projects: === Run (ID '1860d321ea1545ff8866e4ba199d1712') succeeded ===
Zadania [10p pkt]
- Dodaj do swojego projektu logowanie parametrów i metryk za pomocą MLflow (polecenia
mlflow.log_param
imlflow.log_metric
- Dodaj plik MLProject definiujący polecenia do trenowania i testowania, ich parametry wywołania oraz środowisko (Conda albo Docker)
MLflow Models
MLflow Models to konwencja zapisu modeli, która ułatwia potem ich załadowanie i użycie
Rodzaje modeli ("flavors") wspierane przez MLflow:
- Python Function (python_function)
- PyTorch (pytorch)
- TensorFlow (tensorflow)
- Keras (keras)
- Scikit-learn (sklearn)
- Spacy(spaCy)
- ONNX (onnx)
- R Function (crate)
- H2O (h2o)
- MLeap (mleap)
- Spark MLlib (spark)
- MXNet Gluon (gluon)
- XGBoost (xgboost)
- LightGBM (lightgbm)
- CatBoost (catboost)
- Fastai(fastai)
- Statsmodels (statsmodels)
Zapisywanie modelu
Model ML można zapisać w MLflow przy pomocy jednej z dwóch funkcji z pakietu odpowiadającego używanej przez nas bibliotece:
save_model()
- zapisuje model na dyskulog_model()
- zapisuje model razem z innymi informacjami (metrykami, parametrami). W zależności od ustawień "tracking_uri" może być to lokalny folder wmlruns/
lub ścieżka na zdalnym serwerze MLflow
mlflow.sklearn.save_model(lr, "my_model")
mlflow.keras.save_model(lr, "my_model")
Wywołanie tej funkcji spowoduje stworzenie katalogu "my_model" zawierającego:
- plik _MLmodel zawierający informacje o sposobach, w jaki model można załadować ("flavors") oraz ścieżki do plików związanych z modelem, takich jak:
- _conda.yaml - opis środowiska potrzebnego do załadowania modelu
- _model.pkl - plik z zserializowanym modelem
Tylko plik _MLmodel jest specjalnym plikiem MLflow - reszta zależy od konkrentego "flavour"
ls IUM_08/examples/my_model
conda.yaml MLmodel model.pkl
! ls -l IUM_08/examples/mlruns/0/b395b55b47fc43de876b67f5a4a5dae9/artifacts/model
total 12 -rw-rw-r-- 1 tomek tomek 153 maj 17 10:38 conda.yaml -rw-rw-r-- 1 tomek tomek 958 maj 17 10:38 MLmodel -rw-rw-r-- 1 tomek tomek 641 maj 17 10:38 model.pkl
# %load IUM_08/examples/mlruns/0/b395b55b47fc43de876b67f5a4a5dae9/artifacts/model/MLmodel
artifact_path: model
flavors:
python_function:
env: conda.yaml
loader_module: mlflow.sklearn
model_path: model.pkl
python_version: 3.9.1
sklearn:
pickled_model: model.pkl
serialization_format: cloudpickle
sklearn_version: 0.24.2
run_id: b395b55b47fc43de876b67f5a4a5dae9
signature:
inputs: '[{"name": "fixed acidity", "type": "double"}, {"name": "volatile acidity",
"type": "double"}, {"name": "citric acid", "type": "double"}, {"name": "residual
sugar", "type": "double"}, {"name": "chlorides", "type": "double"}, {"name": "free
sulfur dioxide", "type": "double"}, {"name": "total sulfur dioxide", "type": "double"},
{"name": "density", "type": "double"}, {"name": "pH", "type": "double"}, {"name":
"sulphates", "type": "double"}, {"name": "alcohol", "type": "double"}]'
outputs: '[{"type": "tensor", "tensor-spec": {"dtype": "float64", "shape": [-1]}}]'
utc_time_created: '2021-05-17 08:38:41.749670'
# %load IUM_08/examples/my_model/conda.yaml
channels:
- defaults
- conda-forge
dependencies:
- python=3.9.1
- pip
- pip:
- mlflow
- scikit-learn==0.24.2
- cloudpickle==1.6.0
name: mlflow-env
Dodatkowe pola w MLmodel
- _utc_time_created - timestamp z czasem stworzenia modelu
- _run_id - ID uruchomienia ("run"), które stworzyło ten model, jeśli model był zapisany za pomocą MLflow Tracking.
- _signature - opisa danych wejściowych i wyjściowych w formacie JSON
- _input_example przykładowe wejście przyjmowane przez model. Można je podać poprzez parametr
input_example
funkcji log_model
import mlflow
import pandas as pd
model = mlflow.sklearn.load_model("IUM_08/examples/mlruns/0/b395b55b47fc43de876b67f5a4a5dae9/artifacts/model")
csv_url = "http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
data = pd.read_csv(csv_url, sep=";")
model.predict(data.drop(["quality"], axis=1).head())
array([5.57688397, 5.50664777, 5.52550482, 5.50431125, 5.57688397])
Serwowanie modeli
!cd IUM_08/examples/; mlflow models --help
Usage: mlflow models [OPTIONS] COMMAND [ARGS]... Deploy MLflow models locally. To deploy a model associated with a run on a tracking server, set the MLFLOW_TRACKING_URI environment variable to the URL of the desired server. Options: --help Show this message and exit. Commands: build-docker **EXPERIMENTAL**: Builds a Docker image whose default... predict Generate predictions in json format using a saved MLflow... prepare-env **EXPERIMENTAL**: Performs any preparation necessary to... serve Serve a model saved with MLflow by launching a webserver on...
!cd IUM_08/examples/; mlflow models serve --help
Usage: mlflow models serve [OPTIONS] Serve a model saved with MLflow by launching a webserver on the specified host and port. The command supports models with the ``python_function`` or ``crate`` (R Function) flavor. For information about the input data formats accepted by the webserver, see the following documentation: https://www.mlflow.org/docs/latest/models.html#built-in-deployment-tools. You can make requests to ``POST /invocations`` in pandas split- or record- oriented formats. Example: .. code-block:: bash $ mlflow models serve -m runs:/my-run-id/model-path & $ curl http://127.0.0.1:5000/invocations -H 'Content-Type: application/json' -d '{ "columns": ["a", "b", "c"], "data": [[1, 2, 3], [4, 5, 6]] }' Options: -m, --model-uri URI URI to the model. A local path, a 'runs:/' URI, or a remote storage URI (e.g., an 's3://' URI). For more information about supported remote URIs for model artifacts, see https://mlflow.org/docs/latest/tracking.html#artifact- stores [required] -p, --port INTEGER The port to listen on (default: 5000). -h, --host HOST The network address to listen on (default: 127.0.0.1). Use 0.0.0.0 to bind to all addresses if you want to access the tracking server from other machines. -w, --workers TEXT Number of gunicorn worker processes to handle requests (default: 4). --no-conda If specified, will assume that MLmodel/MLproject is running within a Conda environment with the necessary dependencies for the current project instead of attempting to create a new conda environment. --install-mlflow If specified and there is a conda environment to be activated mlflow will be installed into the environment after it has been activated. The version of installed mlflow will be the same asthe one used to invoke this command. --help Show this message and exit.
import pandas as pd
csv_url = "http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
data = pd.read_csv(csv_url, sep=";").drop(["quality"], axis=1).head(1).to_json(orient='split')
print(data)
{"columns":["fixed acidity","volatile acidity","citric acid","residual sugar","chlorides","free sulfur dioxide","total sulfur dioxide","density","pH","sulphates","alcohol"],"index":[0],"data":[[7.4,0.7,0.0,1.9,0.076,11.0,34.0,0.9978,3.51,0.56,9.4]]}
!curl http://127.0.0.1:5003/invocations -H 'Content-Type: application/json' -d '{\
"columns":[\
"fixed acidity","volatile acidity","citric acid","residual sugar","chlorides","free sulfur dioxide","total sulfur dioxide","density","pH","sulphates","alcohol"],\
"index":[0],\
"data":[[7.4,0.7,0.0,1.9,0.076,11.0,34.0,0.9978,3.51,0.56,9.4]]}'
[5.576883967129615]
$ cd IUM_08/examples/
$ mlflow models serve -m my_model
2021/05/17 08:52:07 INFO mlflow.models.cli: Selected backend for flavor 'python_function'
2021/05/17 08:52:07 INFO mlflow.pyfunc.backend: === Running command 'source /home/tomek/miniconda3/bin/../etc/profile.d/conda.sh && conda activate mlflow-503f0c7520a32f054a9d168bd099584a9439de9d 1>&2 && gunicorn --timeout=60 -b 127.0.0.1:5003 -w 1 ${GUNICORN_CMD_ARGS} -- mlflow.pyfunc.scoring_server.wsgi:app'
[2021-05-17 08:52:07 +0200] [291217] [INFO] Starting gunicorn 20.1.0
[2021-05-17 08:52:07 +0200] [291217] [INFO] Listening at: http://127.0.0.1:5003 (291217)
[2021-05-17 08:52:07 +0200] [291217] [INFO] Using worker: sync
[2021-05-17 08:52:07 +0200] [291221] [INFO] Booting worker with pid: 291221
MLflow Registry
- umożliwia zapisywanie i ładowanie modeli z centralnego rejestru
- Modele można też serwować bezpośrednio z rejestru:
#!/usr/bin/env sh
# Set environment variable for the tracking URL where the Model Registry resides
export MLFLOW_TRACKING_URI=http://localhost:5000
# Serve the production model from the model registry
mlflow models serve -m "models:/sk-learn-random-forest-reg-model/Production"
- Żeby było to możliwe, musimy mieć uruchomiony serwer MLflow
- Umożliwia zarządzanie wersjami modeli i oznaczanie ich różnymi fazami, np. "Staging", "Production"
Zadania (termin: 14.05 EOD)
- [2 pkt] Dodaj do joba treningowego wywołania MLflow, tak, żeby przy każdym uruchomieniu stworzyć i zarchiwizować katalog z modelem. Plik MLmodel powinien zawierać pola:
signature
input_example
Folder powinien również zawierać definicję środowiska - conda lub docker, umożliwiającego uruchomienie projektu.
- [6 pkt] Wybierz jedną osobę z grupy. Załóżmy, że Twoje ID to s123456 a jej s654321. Stwórz na Jenkinsie projekt
s123456-predict-s654321
, w którym:
- pobierzesz artefakt z zapisanym modelem z joba osoby s654321
- dokonasz na nim predykcji danych wejściowych podanych w formacie json jako parametr zadania Jenkinsowego. Domyślną wartością tego parametru niech będą przykładowe dane wejściowe z
input_example
[1 pkt] Zarejestruj swój model w MLflow registry
[6 pkt] Stwórz na Jenkinsie projekt
s123456-predict-s654321-from-registry
, który zrealizuje to samo zadanie cos123456-predict-s654321
, ale tym razem pobierze model z rejestru MLflow zamiast z artefaktów Jenkinsa
Dane do konfiguracji MLflow registry
Podgląd w przeglądarce: http://tzietkiewicz.vm.wmi.amu.edu.pl/#/
- user:
student
- hasło: IUM@2021
- user:
Tracking URI:
- Python:
mlflow.set_tracking_uri("http://172.17.0.1:5000")
- CLI:
export MLFLOW_TRACKING_URI=http://172.17.0.1:5000
- Python:
Żeby klient MLflow działający w kontenerze docker mógł zapisywać i pdczytywać artefakty, muszą Państwo podmonotwać katalog
/tmp/mlruns
i/mlruns
(ten drugi po to, żeby po restarcie serwera katalog nie został wyczyszczony)- jak podmontować:
docker.image('my-image').inside('-v /tmp/mlruns:/tmp/mlruns' -v /mlruns:/mlruns')
- Przykład działąjącego joba na Jenkinsie zapisującego wyniki do MLFlow: https://tzietkiewicz.vm.wmi.amu.edu.pl:8081/job/gitea-test/job/ium-helloworld/job/master/
- Repo: https://git.wmi.amu.edu.pl/tzietkiewicz/ium-helloworld/src/branch/master
- jak podmontować:
Proszę ustawić nazwę eksperymentu na numer indeksu, dzięki temu każdy z Państwa będzie widział swoje eksperymenty oddzielnie:
mlflow.set_experiment("s123456")