ium/IUM_08.MLFlow.ipynb

52 KiB

MLflow


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

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
%%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
        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.

Wygląd interfejsu webowego

Porównywanie wyników

Logowanie

  • logowania metryk i parametrów można dokonać m.in. poprzez wywołania Python-owego API: mlflow.log_param() i mlflow.log_metric(). Więcej dostępnych funkcji: link

  • wywoł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
%%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
%%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
  • Składnia

    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
    
!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] (do 16 V 12:00)

  1. Dodaj do swojego projektu logowanie parametrów i metryk za pomocą MLflow (polecenia mlflow.log_param i mlflow.log_metric
  2. Dodaj plik MLProject definiujący polecenia do trenowania i testowania, ich parametry wywołania oraz środowisko (użyj zdefiniowanego wcześniej obrazu 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 dysku
  • log_model() - zapisuje model razem z innymi informacjami (metrykami, parametrami). W zależności od ustawień "tracking_uri" może być to lokalny folder w mlruns/ 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 "falovor"

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

  1. [2 pkt] Dodaj do joba treningowego wywołania MLflow, tak, żeby przy każdym uruchomieniu stworzyć i zarchiwizować katalog z modelem. Plik MLmodel powinien on zawierać pola:
  • signature

  • input_example

    Folder powinien również zawierać środowisko - conda lub docker, umożliwiająceo uruchomienie projektu.

  1. [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 parametry niech będą przykładowe dane wejściowe z input_example
  1. [1 pkt] Zarejestruj swój model w MLflow registry (dan do połączenia z rejstrem podam po jego pomyślnym skonfigurowaniu, nie później niż w środę 19.05.2021

  2. [6 pkt] Stwórz na Jenkinsie projekt s123456-predict-s654321-from-registry, który zrealizuje to samo zadanie co s123456-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: Podane na MS Teams
  • Tracking URI:

    • Python: mlflow.set_tracking_uri("http://172.17.0.1:5000")
    • CLI: export MLFLOW_TRACKING_URI=http://172.17.0.1:5000
  • Żeby klient MLflow działający w kontenerze docker mógł zapisywać i pdczytywać artefakty, muszą Państwo podmonotwać katalog /tmp/mlrunsMożna to zrobić za pomocą flagi -v, którą można przekazać tak, jak pokazano tutaj: https://www.jenkins.io/doc/book/pipeline/docker/#caching-data-for-containers

  • 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")