Васеъ кардани Spark бо MLflow

Салом, Хабровиён. Тавре ки мо аллакай навишта будем, дар ин моҳ OTUS якбора ду курси омӯзиши мошинро оғоз мекунад, яъне асос и пешрафта. Дар ин бобат мо мубодилаи маводи муфидро идома медиҳем.

Мақсади ин мақола ин аст, ки дар бораи таҷрибаи аввалини мо бо MLflow.

Мо баррасиро оғоз мекунем MLflow аз сервери пайгирии он ва prolog ҳамаи такрори омӯзиш. Сипас мо таҷрибаи пайваст кардани Spark бо MLflow бо истифода аз UDF мубодила хоҳем кард.

Мавзӯъ

Мо дар он ҳастем Саломатии Алфа мо омӯзиши мошинсозӣ ва зеҳни сунъиро истифода мебарем, то ба одамон дар бораи саломатӣ ва некӯаҳволии онҳо ғамхорӣ кунанд. Ин аст, ки чаро моделҳои омӯзиши мошинсозӣ дар асоси маҳсулоти маълумоте, ки мо таҳия мекунем ва чаро MLflow, платформаи кушодаасос, ки тамоми ҷанбаҳои давраи омӯзиши мошинро фаро мегирад, диққати моро ба худ ҷалб кард.

MLflow

Ҳадафи асосии MLflow таъмин намудани қабати иловагӣ дар болои омӯзиши мошинсозӣ мебошад, ки ба олимони маълумот имкон медиҳад, ки қариб бо ҳама китобхонаҳои омӯзиши мошинсозӣ кор кунанд (h2o, керас, мезананд, питорч, склерн и тензорфил), кори худро ба зинаи оянда мебарад.

MLflow се ҷузъро таъмин мекунад:

  • Поиш - сабт ва дархостҳо барои таҷрибаҳо: рамз, маълумот, конфигуратсия ва натиҷаҳо. Раванди эҷоди моделро риоя кардан хеле муҳим аст.
  • Лоиҳаҳо - Формати бастабандӣ барои кор дар ҳама гуна платформа (масалан, SageMaker)
  • моделҳои формати маъмул барои пешниҳоди моделҳо ба абзорҳои гуногуни густариш аст.

MLflow (алфа дар вақти навиштан) як платформаи кушодаасос аст, ки ба шумо имкон медиҳад, ки давраи омӯзиши мошинсозӣ, аз ҷумла озмоиш, дубора истифода ва густаришро идора кунед.

Танзими MLflow

Барои истифодаи MLflow, шумо бояд аввал тамоми муҳити Python-ро насб кунед, барои ин мо истифода хоҳем кард PyEnv (барои насб кардани Python дар Mac, нигаред дар ин ҷо). Ҳамин тавр, мо метавонем муҳити маҷозӣ эҷод кунем, ки дар он ҳамаи китобхонаҳои барои кор заруриро насб мекунем.

```
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
```

Китобхонаҳои лозимиро насб кунед.

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

Эзоҳ: Мо PyArrow-ро барои идора кардани моделҳо ба монанди UDFs истифода мебарем. Версияҳои PyArrow ва Numpy бояд ислоҳ карда шаванд, зеро версияҳои охирин бо ҳамдигар ихтилоф доштанд.

Оғози UI Tracking UI

Tracking MLflow ба мо имкон медиҳад, ки таҷрибаҳоро бо Python ва РЕТ API. Илова бар ин, шумо метавонед дар куҷо нигоҳ доштани артефактҳои моделиро муайян кунед (localhost, Amazon S3, Захираи Azure Blob, Захираи абрии Google ё Сервери SFTP). Азбаски мо AWS-ро дар Alpha Health истифода мебарем, S3 анбори артефактҳо хоҳад буд.

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

MLflow истифодаи нигаҳдории доимии файлро тавсия медиҳад. Нигоҳдории файл дар он ҷоест, ки сервер метамаълумотро иҷро мекунад ва озмоиш мекунад. Ҳангоми оғоз кардани сервер, боварӣ ҳосил кунед, ки он ба нигоҳдории файлҳои доимӣ ишора мекунад. Дар ин ҷо, ба хотири таҷриба, мо танҳо истифода мебарем /tmp.

Дар хотир доред, ки агар мо хоҳем, ки сервери mlflow-ро барои иҷро кардани таҷрибаҳои кӯҳна истифода барем, онҳо бояд дар мағозаи файл мавҷуд бошанд. Аммо, ҳатто бидуни ин, мо метавонем онҳоро дар UDF истифода барем, зеро ба мо танҳо роҳ ба модел лозим аст.

Эзоҳ: Дар хотир доред, ки UI Tracking UI ва муштарии модел бояд ба макони артефакт дастрасӣ дошта бошанд. Яъне, новобаста аз он, ки UI Tracking UI дар мисоли EC2 ҷойгир аст, ҳангоми ба таври маҳаллӣ иҷро кардани MLflow, мошин бояд барои навиштани моделҳои артефакт ба S3 дастрасии мустақим дошта бошад.

Васеъ кардани Spark бо MLflow
Пайгирии UI артефактҳоро дар сатили S3 нигоҳ медорад

Моделҳои иҷрошаванда

Ҳамин ки сервери Tracking кор мекунад, шумо метавонед ба омӯзиши моделҳо шурӯъ кунед.

Ҳамчун мисол, мо тағир додани шаробро аз мисоли MLflow дар Склерн.

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

Тавре ки мо гуфта будем, MLflow ба шумо имкон медиҳад, ки параметрҳо, ченакҳо ва артефактҳои моделиро сабт кунед, то шумо метавонед пайгирӣ кунед, ки чӣ гуна онҳо ҳамчун итератсия инкишоф меёбанд. Ин хусусият бениҳоят муфид аст, зеро он ба мо имкон медиҳад, ки модели беҳтаринро тавассути тамос бо сервери Tracking дубора тавлид кунем ё фаҳмем, ки кадом код бо истифода аз сабтҳои git hash-и ӯҳдадориҳо такрори лозимиро иҷро кардааст.

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

Васеъ кардани Spark бо MLflow
такрори шароб

Қисми қафо барои модел

Сервери пайгирии MLflow, ки бо фармони "server mlflow" оғоз шудааст, дорои REST API барои пайгирии иҷроҳо ва навиштани маълумот ба системаи файлии маҳаллӣ мебошад. Шумо метавонед суроғаи сервери пайгириро бо истифода аз тағирёбандаи муҳити зисти "MLFLOW_TRACKING_URI" муайян кунед ва API пайгирии MLflow ба таври худкор бо сервери пайгирӣ дар ин суроға тамос мегирад, то иттилооти оғозёбӣ, ченакҳои сабти ном ва ғ.

Манбаъ: Ҳуҷҷатҳо// Иҷрои сервери пайгирӣ

Барои таъмин кардани модел бо сервер, ба мо сервери пайгирии иҷрошаванда (ба интерфейси оғозёбӣ нигаред) ва ID Run модел лозим аст.

Васеъ кардани Spark бо MLflow
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

Барои хидматрасонии моделҳо бо истифода аз функсионалии хидматрасонии MLflow, ба мо лозим аст, ки ба UI Tracking UI дастрасӣ дошта бошем, то дар бораи модел бо роҳи муайян кардани маълумот маълумот гирем. --run_id.

Вақте ки модел бо сервери пайгирӣ тамос мегирад, мо метавонем нуқтаи ниҳоии модели навро ба даст орем.

# 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]}

Моделҳои иҷрошаванда аз Spark

Сарфи назар аз он, ки сервери Tracking ба қадри кофӣ тавоност, ки ба моделҳо дар вақти воқеӣ хидмат кунад, онҳоро таълим диҳед ва аз функсияҳои сервер истифода баред (манбаъ: mlflow // ҳуҷҷатҳо // моделҳо #маҳаллӣ), истифодаи Spark (маҷмӯа ё ҷараён) бо сабаби тақсимоти он ҳалли боз ҳам пурқувваттар аст.

Тасаввур кунед, ки шумо танҳо омӯзиши офлайнӣ анҷом додаед ва сипас модели баромадро ба ҳамаи маълумоти худ татбиқ кардед. Дар ин ҷо Spark ва MLflow ба худ меоянд.

PySpark + Jupyter + Spark насб кунед

Манбаъ: Оғози PySpark - Jupyter

Барои нишон додани он ки чӣ тавр мо моделҳои MLflow-ро ба dataframes Spark истифода мебарем, мо бояд ноутбукҳои Jupyter-ро барои кор бо PySpark насб кунем.

Бо насб кардани версияи охирини устувор оғоз кунед 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̀

PySpark ва Jupyter -ро дар муҳити виртуалӣ насб кунед:

pip install pyspark jupyter

Тағйирёбандаҳои муҳити атрофро танзим кунед:

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"

Муайян карда notebook-dir, мо метавонем дафтархои худро дар папкаи дилхох нигох дорем.

Иҷрои Jupyter аз PySpark

Азбаски мо тавонистем Jupiter-ро ҳамчун ронандаи PySpark насб кунем, ҳоло мо метавонем дафтарчаи Jupyter-ро дар контексти 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

Васеъ кардани Spark бо MLflow

Тавре ки дар боло зикр гардид, MLflow вазифаи сабти артефактҳои моделиро дар S3 таъмин мекунад. Ҳамин ки мо модели интихобшударо дар дасти худ дорем, мо имкон дорем, ки онро ҳамчун UDF бо истифода аз модул ворид кунем 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)

Васеъ кардани Spark бо MLflow
PySpark - Пешгӯии сифати шароб

То ин вақт, мо дар бораи чӣ гуна истифода бурдани PySpark бо MLflow тавассути пешгӯии сифати шароб дар тамоми маҷмӯаи маълумоти шароб сӯҳбат кардем. Аммо чӣ мешавад, агар ба шумо лозим аст, ки модулҳои Python MLflow -ро аз Scala Spark истифода баред?

Мо инро инчунин тавассути тақсим кардани контексти Spark байни Scala ва Python озмоиш кардем. Яъне, мо MLflow UDF-ро дар Python ба қайд гирифтем ва онро аз Scala истифода бурдем (бале, шояд ҳалли беҳтарин нест, аммо он чизе ки мо дорем).

Scala Spark + MLflow

Барои ин мисол, мо илова мекунем Торе ядро ба Юпитери мавҷуда.

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
```

Тавре ки шумо аз дафтари замимашуда мебинед, UDF байни Spark ва PySpark тақсим карда мешавад. Мо умедворем, ки ин қисм барои онҳое, ки Scala-ро дӯст медоранд ва мехоҳанд моделҳои омӯзиши мошинро дар истеҳсолот ҷойгир кунанд, муфид хоҳад буд.

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       |
+-----------+--------+-----------+---------+-----------+

Қадамҳои оянда

Гарчанде ки MLflow дар вақти навиштан дар Алфа аст, он хеле умедбахш ба назар мерасад. Танҳо қобилияти кор кардани чаҳорчӯбаҳои омӯзиши мошинҳои сершумор ва истифодаи онҳо аз як нуқтаи ниҳоӣ системаҳои тавсиядиҳандаро ба сатҳи оянда мебарад.

Илова бар ин, MLflow муҳандисони маълумот ва олимони маълумотро ба ҳам наздик мекунад ва дар байни онҳо як қабати умумӣ мегузорад.

Пас аз ин таҳқиқи MLflow, мо боварӣ дорем, ки пеш рафта, онро барои лӯлаҳои Spark ва системаҳои тавсиядиҳандаи худ истифода мебарем.

Хуб мебуд, ки нигоҳдории файлро бо пойгоҳи додаҳо ба ҷои системаи файлӣ ҳамоҳанг созед. Ин бояд ба мо якчанд нуқтаи ниҳоӣ диҳад, ки метавонанд як мубодилаи файлро истифода баранд. Масалан, якчанд мисолҳоро истифода баред Presto и Афина бо ҳамон метамағозаи Glue.

Дар ҷамъбаст, ман мехоҳам ба ҷомеаи MLFlow ташаккур гӯям, ки кори моро бо маълумот ҷолибтар кардааст.

Агар шумо бо MLflow бозӣ кунед, озодона ба мо нависед ва ба мо бигӯед, ки чӣ гуна онро истифода мебаред ва ҳатто бештар аз он, агар шумо онро дар истеҳсолот истифода баред.

Маълумоти бештар дар бораи курсҳо:
омӯзиши мошинсозӣ. Курси асосӣ
омӯзиши мошинсозӣ. курси пешрафта

Бештар:

Манбаъ: will.com

Илова Эзоҳ