Etendante Spark kun MLflow

Saluton, Ĥabrovskaj loĝantoj. Kiel ni jam skribis, ĉi-monate OTUS lanĉas du kursojn de maŝinlernado samtempe, nome bazo и progresinta. Ĉi-rilate, ni daŭre kundividas utilan materialon.

La celo de ĉi tiu artikolo estas paroli pri nia unua sperto de uzado MLflow.

Ni komencos la recenzon MLflow de ĝia spura servilo kaj ensalutu ĉiujn ripetojn de la studo. Tiam ni dividos nian sperton pri konekti Spark kun MLflow uzante UDF.

Kunteksto

Ni estas en Alfa Sano Ni uzas maŝinlernadon kaj artefaritan inteligentecon por rajtigi homojn zorgi pri ilia sano kaj bonfarto. Tial maŝinlernado-modeloj estas ĉe la koro de la datumsciencaj produktoj, kiujn ni disvolvas, kaj tial ni estis allogitaj al MLflow, malfermfonta platformo kiu kovras ĉiujn aspektojn de la maŝinlernada vivociklo.

MLflow

La ĉefa celo de MLflow estas provizi plian tavolon aldone al maŝinlernado, kiu permesus al datumsciencistoj labori kun preskaŭ ajna maŝinlernada biblioteko (h2o, keras, mleap, pitorĉo, sklearn и tensorflow), portante ŝian laboron al la sekva nivelo.

MLflow disponigas tri komponentojn:

  • spurado – registrado kaj petoj por eksperimentoj: kodo, datumoj, agordo kaj rezultoj. Monitori la procezon de kreado de modelo estas tre grava.
  • projektoj - Paka formato por funkcii sur ajna platformo (ekz. SageMaker)
  • modeloj - komuna formato por sendi modelojn al diversaj disfaldaj iloj.

MLflow (en alfa en la momento de skribado) estas malfermfonta platformo, kiu ebligas al vi administri la maŝinlerndan vivociklon, inkluzive de eksperimentado, reuzo kaj deplojo.

Agordi MLflow

Por uzi MLflow vi devas unue agordi vian tutan Python-medion, por tio ni uzos PyEnv (por instali Python en Mac, kontrolu tie). Tiel ni povas krei virtualan medion kie ni instalos ĉiujn bibliotekojn necesajn por funkcii ĝin.

```
pyenv install 3.7.0
pyenv global 3.7.0 # Use Python 3.7
mkvirtualenv mlflow # Create a Virtual Env with Python 3.7
workon mlflow
```

Ni instalu la bezonatajn bibliotekojn.

```
pip install mlflow==0.7.0 
            Cython==0.29  
            numpy==1.14.5 
            pandas==0.23.4 
            pyarrow==0.11.0
```

Noto: Ni uzas PyArrow por ruli modelojn kiel UDF. La versioj de PyArrow kaj Numpy devis esti fiksitaj ĉar ĉi-lastaj versioj konfliktis unu kun la alia.

Lanĉu Spuradon UI

MLflow Tracking permesas al ni ensaluti kaj pridemandi eksperimentojn uzante Python kaj RESTA API. Krome, vi povas determini kie stoki modelajn artefaktojn (localhost, Amazon S3, Azure Blob Stokado, Google Nubo-StokadoSFTP-servilo). Ĉar ni uzas AWS ĉe Alpha Health, nia konservado de artefaktoj estos S3.

# Running a Tracking Server
mlflow server 
    --file-store /tmp/mlflow/fileStore 
    --default-artifact-root s3://<bucket>/mlflow/artifacts/ 
    --host localhost
    --port 5000

MLflow rekomendas uzi konstantan dosierstokadon. Dosiera stokado estas kie la servilo stokos kurajn kaj eksperimentajn metadatumojn. Komencante la servilon, certigu, ke ĝi montras al la konstanta dosiervendejo. Ĉi tie por la eksperimento ni simple uzos /tmp.

Memoru, ke se ni volas uzi la mlflow-servilon por ruli malnovajn eksperimentojn, ili devas ĉeesti en la dosierstokado. Tamen, eĉ sen tio ni povus uzi ilin en la UDF, ĉar ni bezonas nur la vojon al la modelo.

Noto: Memoru, ke Spurado-UI kaj la modelkliento devas havi aliron al la artefakto-loko. Tio estas, sendepende de la fakto, ke la Tracking UI loĝas en EC2-instanco, dum funkciado de MLflow loke, la maŝino devas havi rektan aliron al S3 por skribi artefaktajn modelojn.

Etendante Spark kun MLflow
Spurado de UI stokas artefaktojn en S3 sitelo

Kurantaj modeloj

Tuj kiam la Spura servilo funkcias, vi povas komenci trejni la modelojn.

Kiel ekzemplo, ni uzos la vinmodifon de la ekzemplo MLflow en Sklearn.

MLFLOW_TRACKING_URI=http://localhost:5000 python wine_quality.py 
  --alpha 0.9
  --l1_ration 0.5
  --wine_file ./data/winequality-red.csv

Kiel ni jam diskutis, MLflow permesas vin registri modelajn parametrojn, metrikojn kaj artefaktojn, por ke vi povu spuri kiel ili evoluas per ripetoj. Ĉi tiu funkcio estas ekstreme utila ĉar tiel ni povas reprodukti la plej bonan modelon kontaktante la Spuran servilon aŭ komprenante kiu kodo faris la bezonatan ripeton uzante la git hash-protokolojn de kommits.

with mlflow.start_run():

    ... model ...

    mlflow.log_param("source", wine_path)
    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)

    mlflow.set_tag('domain', 'wine')
    mlflow.set_tag('predict', 'quality')
    mlflow.sklearn.log_model(lr, "model")

Etendante Spark kun MLflow
Vinaj ripetoj

Servila parto por la modelo

La spurservilo MLflow, lanĉita per la komando "mlflow-servilo", havas REST-API por spuri kurojn kaj skribi datumojn al la loka dosiersistemo. Vi povas specifi la spuran servilon-adreson uzante la mediovariablon "MLFLOW_TRACKING_URI" kaj la MLflow-spurada API aŭtomate kontaktos la spuran servilon ĉe ĉi tiu adreso por krei/ricevi lanĉajn informojn, protokolojn, ktp.

fonto: Dokumentoj// Funkcianta spurservilon

Por provizi la modelon per servilo, ni bezonas kurantan spuran servilon (vidu lanĉan interfacon) kaj la Run ID de la modelo.

Etendante Spark kun MLflow
Kuru ID

# Serve a sklearn model through 127.0.0.0:5005
MLFLOW_TRACKING_URI=http://0.0.0.0:5000 mlflow sklearn serve 
  --port 5005  
  --run_id 0f8691808e914d1087cf097a08730f17 
  --model-path model

Por servi modelojn uzante la MLflow-servfunkciecon, ni bezonos aliron al la Spurado-UI por ricevi informojn pri la modelo simple specifante --run_id.

Post kiam la modelo kontaktas la Spuran servilon, ni povas akiri novan modelan finpunkton.

# Query Tracking Server Endpoint
curl -X POST 
  http://127.0.0.1:5005/invocations 
  -H 'Content-Type: application/json' 
  -d '[
	{
		"fixed acidity": 3.42, 
		"volatile acidity": 1.66, 
		"citric acid": 0.48, 
		"residual sugar": 4.2, 
		"chloridessssss": 0.229, 
		"free sulfur dsioxide": 19, 
		"total sulfur dioxide": 25, 
		"density": 1.98, 
		"pH": 5.33, 
		"sulphates": 4.39, 
		"alcohol": 10.8
	}
]'

> {"predictions": [5.825055635303461]}

Kurantaj modeloj de Spark

Malgraŭ la fakto, ke la Spura servilo estas sufiĉe potenca por servi modelojn en reala tempo, trejnu ilin kaj uzu la servilon (fonto: mlflow // dokumentoj // modeloj # local), la uzo de Spark (batch aŭ streaming) estas eĉ pli potenca solvo pro distribuo.

Imagu, ke vi simple faris la trejnadon eksterrete kaj poste aplikis la eligmodelon al ĉiuj viaj datumoj. Ĉi tie brilas Spark kaj MLflow.

Instalu PySpark + Jupyter + Spark

fonto: Komencu PySpark - Jupyter

Por montri kiel ni aplikas MLflow-modelojn al Spark-datumkadroj, ni devas agordi Jupyter-kajeroj por labori kune kun PySpark.

Komencu instalante la plej novan stabilan version Apache Spark:

cd ~/Downloads/
tar -xzf spark-2.4.3-bin-hadoop2.7.tgz
mv ~/Downloads/spark-2.4.3-bin-hadoop2.7 ~/
ln -s ~/spark-2.4.3-bin-hadoop2.7 ~/spark̀

Instalu PySpark kaj Jupyter en la virtuala medio:

pip install pyspark jupyter

Agordu mediajn variablojn:

export SPARK_HOME=~/spark
export PATH=$SPARK_HOME/bin:$PATH
export PYSPARK_DRIVER_PYTHON=jupyter
export PYSPARK_DRIVER_PYTHON_OPTS="notebook --notebook-dir=${HOME}/Projects/notebooks"

Decidinte notebook-dir, ni povas konservi niajn kajerojn en la dezirata dosierujo.

Lanĉante Jupyter de PySpark

Ĉar ni povis agordi Jupitero'n kiel PySpark-ŝoforon, ni nun povas ruli Jupiter-notlibron en la kunteksto de PySpark.

(mlflow) afranzi:~$ pyspark
[I 19:05:01.572 NotebookApp] sparkmagic extension enabled!
[I 19:05:01.573 NotebookApp] Serving notebooks from local directory: /Users/afranzi/Projects/notebooks
[I 19:05:01.573 NotebookApp] The Jupyter Notebook is running at:
[I 19:05:01.573 NotebookApp] http://localhost:8888/?token=c06252daa6a12cfdd33c1d2e96c8d3b19d90e9f6fc171745
[I 19:05:01.573 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[C 19:05:01.574 NotebookApp]

    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://localhost:8888/?token=c06252daa6a12cfdd33c1d2e96c8d3b19d90e9f6fc171745

Etendante Spark kun MLflow

Kiel menciite supre, MLflow provizas funkcion por registri modelajn artefaktojn en S3. Tuj kiam ni havas la elektitan modelon en niaj manoj, ni havas la ŝancon importi ĝin kiel UDF uzante la modulon. mlflow.pyfunc.

import mlflow.pyfunc

model_path = 's3://<bucket>/mlflow/artifacts/1/0f8691808e914d1087cf097a08730f17/artifacts/model'
wine_path = '/Users/afranzi/Projects/data/winequality-red.csv'
wine_udf = mlflow.pyfunc.spark_udf(spark, model_path)

df = spark.read.format("csv").option("header", "true").option('delimiter', ';').load(wine_path)
columns = [ "fixed acidity", "volatile acidity", "citric acid",
            "residual sugar", "chlorides", "free sulfur dioxide",
            "total sulfur dioxide", "density", "pH",
            "sulphates", "alcohol"
          ]
          
df.withColumn('prediction', wine_udf(*columns)).show(100, False)

Etendante Spark kun MLflow
PySpark - Eligante vinkvalitajn prognozojn

Ĝis ĉi tiu punkto, ni parolis pri kiel uzi PySpark kun MLflow, kurante vinkvalitajn prognozojn sur la tuta vindatumaro. Sed kio se vi bezonas uzi Python MLflow-modulojn de Scala Spark?

Ni testis ĉi tion ankaŭ dividante la Spark-kuntekston inter Scala kaj Python. Tio estas, ni registris MLflow UDF en Python, kaj uzis ĝin de Scala (jes, eble ne la plej bona solvo, sed kion ni havas).

Scala Spark + MLflow

Por ĉi tiu ekzemplo ni aldonos Toree Kernel en la ekzistantan Jupitero'n.

Instalu Spark + Toree + Jupyter

pip install toree
jupyter toree install --spark_home=${SPARK_HOME} --sys-prefix
jupyter kernelspec list
```
```
Available kernels:
  apache_toree_scala    /Users/afranzi/.virtualenvs/mlflow/share/jupyter/kernels/apache_toree_scala
  python3               /Users/afranzi/.virtualenvs/mlflow/share/jupyter/kernels/python3
```

Kiel vi povas vidi el la kuna kajero, la UDF estas dividita inter Spark kaj PySpark. Ni esperas, ke ĉi tiu parto estos utila al tiuj, kiuj amas Scala kaj volas disfaldi maŝinlernajn modelojn en produktado.

import org.apache.spark.sql.functions.col
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.{Column, DataFrame}
import scala.util.matching.Regex

val FirstAtRe: Regex = "^_".r
val AliasRe: Regex = "[\s_.:@]+".r

def getFieldAlias(field_name: String): String = {
    FirstAtRe.replaceAllIn(AliasRe.replaceAllIn(field_name, "_"), "")
}

def selectFieldsNormalized(columns: List[String])(df: DataFrame): DataFrame = {
    val fieldsToSelect: List[Column] = columns.map(field =>
        col(field).as(getFieldAlias(field))
    )
    df.select(fieldsToSelect: _*)
}

def normalizeSchema(df: DataFrame): DataFrame = {
    val schema = df.columns.toList
    df.transform(selectFieldsNormalized(schema))
}

FirstAtRe = ^_
AliasRe = [s_.:@]+

getFieldAlias: (field_name: String)String
selectFieldsNormalized: (columns: List[String])(df: org.apache.spark.sql.DataFrame)org.apache.spark.sql.DataFrame
normalizeSchema: (df: org.apache.spark.sql.DataFrame)org.apache.spark.sql.DataFrame
Out[1]:
[s_.:@]+
In [2]:
val winePath = "~/Research/mlflow-workshop/examples/wine_quality/data/winequality-red.csv"
val modelPath = "/tmp/mlflow/artifactStore/0/96cba14c6e4b452e937eb5072467bf79/artifacts/model"

winePath = ~/Research/mlflow-workshop/examples/wine_quality/data/winequality-red.csv
modelPath = /tmp/mlflow/artifactStore/0/96cba14c6e4b452e937eb5072467bf79/artifacts/model
Out[2]:
/tmp/mlflow/artifactStore/0/96cba14c6e4b452e937eb5072467bf79/artifacts/model
In [3]:
val df = spark.read
              .format("csv")
              .option("header", "true")
              .option("delimiter", ";")
              .load(winePath)
              .transform(normalizeSchema)

df = [fixed_acidity: string, volatile_acidity: string ... 10 more fields]
Out[3]:
[fixed_acidity: string, volatile_acidity: string ... 10 more fields]
In [4]:
%%PySpark
import mlflow
from mlflow import pyfunc

model_path = "/tmp/mlflow/artifactStore/0/96cba14c6e4b452e937eb5072467bf79/artifacts/model"
wine_quality_udf = mlflow.pyfunc.spark_udf(spark, model_path)

spark.udf.register("wineQuality", wine_quality_udf)
Out[4]:
<function spark_udf.<locals>.predict at 0x1116a98c8>
In [6]:
df.createOrReplaceTempView("wines")
In [10]:
%%SQL
SELECT 
    quality,
    wineQuality(
        fixed_acidity,
        volatile_acidity,
        citric_acid,
        residual_sugar,
        chlorides,
        free_sulfur_dioxide,
        total_sulfur_dioxide,
        density,
        pH,
        sulphates,
        alcohol
    ) AS prediction
FROM wines
LIMIT 10
Out[10]:
+-------+------------------+
|quality|        prediction|
+-------+------------------+
|      5| 5.576883967129615|
|      5|  5.50664776916154|
|      5| 5.525504822954496|
|      6| 5.504311247097457|
|      5| 5.576883967129615|
|      5|5.5556903912725755|
|      5| 5.467882654744997|
|      7| 5.710602976324739|
|      7| 5.657319539336507|
|      5| 5.345098606538708|
+-------+------------------+

In [17]:
spark.catalog.listFunctions.filter('name like "%wineQuality%").show(20, false)

+-----------+--------+-----------+---------+-----------+
|name       |database|description|className|isTemporary|
+-----------+--------+-----------+---------+-----------+
|wineQuality|null    |null       |null     |true       |
+-----------+--------+-----------+---------+-----------+

Sekvaj paŝoj

Kvankam MLflow estas en Alpha versio en la momento de verkado, ĝi aspektas sufiĉe promesplena. Nur la kapablo funkciigi plurajn maŝinlernajn kadrojn kaj konsumi ilin de ununura finpunkto portas rekomendajn sistemojn al la sekva nivelo.

Krome, MLflow alportas Datumajn Inĝenierojn kaj Data Science-specialistojn pli proksime kune, metante komunan tavolon inter ili.

Post ĉi tiu esplorado de MLflow, ni certas, ke ni antaŭeniros kaj uzos ĝin por niaj Spark-duktoj kaj rekomendaj sistemoj.

Estus bone sinkronigi la dosierstokadon kun la datumbazo anstataŭ la dosiersistemo. Ĉi tio devus doni al ni plurajn finpunktojn, kiuj povas uzi la saman dosierstokadon. Ekzemple, uzu plurajn okazojn Preta и Ateneo kun la sama Glue metavendejo.

Por resumi, mi ŝatus danki vin al la komunumo de MLFlow pro ke ni igis nian laboron kun datumoj pli interesa.

Se vi ludas kun MLflow, ne hezitu skribi al ni kaj rakonti al ni kiel vi uzas ĝin, kaj eĉ pli se vi uzas ĝin en produktado.

Lernu pli pri la kursoj:
Maŝina lernado. Baza kurso
Maŝina lernado. Altnivela kurso

Legu pli:

fonto: www.habr.com

Aldoni komenton