Hej, Habr-besökare. Som vi redan har skrivit, lanserar OTUS tvÄ kurser i maskininlÀrning den hÀr mÄnaden, nÀmligen och . I samband med detta fortsÀtter vi att dela anvÀndbart material.
Syftet med denna artikel Àr att berÀtta om vÄr första erfarenhet av .
Vi kommer att börja översikten med dess tracking-server och logga alla iterationer av forskningen. DÀrefter delar vi vÄr erfarenhet av att koppla Spark med MLflow med hjÀlp av UDF.
Kontext
En kort historia om containrar: frÄn chroot till Kubernetes anvÀnder maskininlÀrning och artificiell intelligens för att ge mÀnniskor möjlighet att ta hand om sin hÀlsa och sitt vÀlbefinnande. DÀrför ligger maskininlÀrningsmodellerna till grund för de databehandlingsprodukter vi utvecklar, och dÀrför blev vi intresserade av MLflow - en plattform med öppen kÀllkod som omfattar alla aspekter av maskininlÀrningens livscykel.
MLflow
Huvudsyftet med MLflow Àr att tillhandahÄlla ett extra lager ovanpÄ maskininlÀrning som skulle lÄta datavetare arbeta praktiskt med vilket maskininlÀrningsbibliotek som helst (, , , , och ), för att ta dess funktioner till en ny nivÄ.
MLflow erbjuder tre komponenter:
- Tracking â inspelning och förfrĂ„gningar om experiment: kod, data, konfiguration och resultat. Att följa utvecklingen av en modell Ă€r mycket viktigt.
- Projekt â Paketformat för körning pĂ„ vilken plattform som helst (till exempel, )
- Models â ett gemensamt format för att skicka modeller till olika distributionsverktyg.
MLflow (vid tidpunkten för artikelns skrivande i alpha-version) Àr en plattform med öppen kÀllkod som möjliggör hantering av livscykeln för maskininlÀrning, inklusive experiment, ÄteranvÀndning och distribution.
Konfigurering av MLflow
För att anvÀnda MLflow mÄste hela Python-miljön först konfigureras, för detta kommer vi att anvÀnda (för att installera Python pÄ Mac, titta gÀrna ). SÄ kan vi skapa en virtuell miljö dÀr vi installerar alla nödvÀndiga bibliotek för att köra.
```
pyenv install 3.7.0
pyenv global 3.7.0 # AnvÀnd Python 3.7
mkvirtualenv mlflow # Skapa en virtuell miljö med Python 3.7
workon mlflow
```Vi installerar de nödvÀndiga biblioteken.
```
pip install mlflow==0.7.0
Cython==0.29
numpy==1.14.5
pandas==0.23.4
pyarrow==0.11.0
```Obs: vi anvÀnder PyArrow för att köra sÄdana modeller som UDF. Versionerna av PyArrow och Numpy behövde justeras eftersom de senaste versionerna krockade med varandra.
Kör Tracking UI
MLflow Tracking lÄter oss logga och göra förfrÄgningar pÄ experiment med hjÀlp av Python och API. Utöver detta kan vi definiera var modellarterna ska lagras (localhost, , , eller ). Eftersom vi pÄ Alpha Health anvÀnder AWS, kommer S3 att fungera som lagring för modellarterna.
# Running a Tracking Server
mlflow server
--file-store /tmp/mlflow/fileStore
--default-artifact-root s3://<bucket>/mlflow/artifacts/
--host localhost
--port 5000 MLflow rekommenderar att anvÀnda permanent fil lagring. Fil lagring Àr en plats dÀr servern kommer att lagra metadata för körningar och experiment. NÀr servern startas, se till att den pekar pÄ permanent fil lagring. HÀr för experimentet kommer vi att anvÀnda /tmp.
Kom ihÄg att om vi vill anvÀnda mlflow-servern för att köra gamla experiment, mÄste de finnas i fil lagringen. Men Àven utan detta skulle vi kunna anvÀnda dem i UDF, eftersom vi bara behöver sökvÀgen till modellen.
Obs: TÀnk pÄ att Tracking UI och modelklienten mÄste ha Ätkomst till platsen för artefakten. Det vill sÀga, oavsett att Tracking UI ligger i en EC2-instans, mÄste maskinen ha direkt Ätkomst till S3 för att skriva modellarterna lokalt nÀr MLflow startas.

Tracking UI lagrar artefakterna i en S3-bucket
Körning av modeller
SÄ snart Tracking-servern Àr igÄng kan vi börja trÀna modeller.
Som exempel kommer vi att anvÀnda en modifiering av wine frÄn MLflow-exemplet i .
MLFLOW_TRACKING_URI=http://localhost:5000 python wine_quality.py
--alpha 0.9
--l1_ration 0.5
--wine_file ./data/winequality-red.csvSom vi redan har nÀmnt, lÄter MLflow oss logga parametrar, metrik och artefakter av modeller sÄ att vi kan spÄra hur de utvecklas över iterationerna. Denna funktion Àr extremt anvÀndbar eftersom vi dÄ kan reproducera den bÀsta modellen genom att gÄ till Tracking-servern eller förstÄ vilken kod som körde den nödvÀndiga iterationen genom att anvÀnda loggar av git hash-kommentarerna.
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") 
Wine-iterationer
Serverdelen för modellen
MLflow-trackingserver som körs med kommandot âmlflow serverâ har ett REST API för att spĂ„ra körningar och registrera data i det lokala filsystemet. Du kan ange adressen till trackingservern med miljövariabeln «MLFLOW_TRACKING_URI», och MLflows tracking API kommer automatiskt att ansluta till trackingservern pĂ„ denna adress för att skapa/hĂ€mta information om körningar, loggmetrik, etc.
KĂ€lla:
För att tillhandahÄlla modellen med en server behöver vi en aktiv trackingserver (se startgrÀnssnittet) och modellens Run ID.

Run 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 För att hantera modeller med hjÀlp av MLflow serve-funktionalitet behöver vi tillgÄng till Tracking UI för att hÀmta information om modellen genom att helt enkelt ange --run_id.
NÀr modellen kopplas till trackingservern kan vi fÄ en ny modellens slutpunkt.
# 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]}Kör modeller frÄn Spark
Ăven om trackingservern Ă€r tillrĂ€ckligt kraftfull för att betjĂ€na modeller i realtid, deras trĂ€ning och utnyttjande av serve-funktionaliteten (kĂ€lla: ), Ă€r anvĂ€ndningen av Spark (batch eller streaming) en Ă€nnu kraftfullare lösning tack vare dess distribuerade natur.
TÀnk dig att du just har genomfört trÀning offline och sedan tillÀmpat den slutgiltiga modellen pÄ alla dina data. Just hÀr kommer Spark och MLflow att visa sina bÀsta sidor.
Installera PySpark + Jupyter + Spark
KĂ€lla:
För att visa hur vi tillÀmpar MLflow-modeller pÄ Spark-dataramar mÄste vi konfigurera samarbetet mellan Jupyter-notebookar och PySpark.
Börja med att installera den senaste stabila versionen :
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 ~/sparkInstallera PySpark och Jupyter i den virtuella miljön:
pip install pyspark jupyterKonfigurera miljövariabler:
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" Genom att definiera notebook-dir, kan vi lagra vÄra notebooks i önskad mapp.
Kör Jupyter frÄn PySpark
Eftersom vi har kunnat konfigurera Jupyter som PySpark-drivrutin kan vi nu köra Jupyter notebook i kontexten av PySpark.
(mlflow) afranzi:~$ pyspark
[I 19:05:01.572 NotebookApp] sparkmagic-tillÀgg aktiverat!
[I 19:05:01.573 NotebookApp] Serverar anteckningsblock frÄn lokal katalog: /Users/afranzi/Projects/notebooks
[I 19:05:01.573 NotebookApp] Jupyter Notebook körs pÄ:
[I 19:05:01.573 NotebookApp] http://localhost:8888/?token=c06252daa6a12cfdd33c1d2e96c8d3b19d90e9f6fc171745
[I 19:05:01.573 NotebookApp] AnvÀnd Control-C för att stoppa den hÀr servern och stÀnga av alla kÀrnor (tryck tvÄ gÄnger för att hoppa över bekrÀftelsen).
[C 19:05:01.574 NotebookApp]
Kopiera/klistra in den hÀr URL:en i din webblÀsare nÀr du ansluter för första gÄngen,
för att logga in med en token:
http://localhost:8888/?token=c06252daa6a12cfdd33c1d2e96c8d3b19d90e9f6fc171745 
Som nÀmnts ovan tillhandahÄller MLflow en funktion för att logga artefakter av modellen i S3. NÀr vi vÀl har modellen i vÄra hÀnder kan vi importera den som UDF med hjÀlp av modulen mlflow.pyfunc.
import mlflow.pyfunc
model_path = 's3:///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 = [ "fast syra", "flyktig syra", "citronsyra",
"resterande socker", "klorider", "fri svavel-dioxid",
"total svavel-dioxid", "densitet", "pH",
"sulfater", "alkohol"
]
df.withColumn('prediction', wine_udf(*columns)).show(100, False) 
PySpark â Utskrift av vinens kvalitetsprediktion
Hitintills har vi pratat om hur man anvÀnder PySpark med MLflow genom att köra vinets kvalitetsprediktion pÄ hela datasetet. Men vad hÀnder om man mÄste anvÀnda Python-moduler frÄn MLflow i Scala Spark?
Vi har testat det ocksÄ, och delat Spark-kontot mellan Scala och Python. Det vill sÀga att vi registrerade MLflow UDF i Python och anvÀnde den frÄn Scala (ja, kanske inte den bÀsta lösningen, men vad vi har).
Scala Spark + MLflow
För detta exempel kommer vi att lÀgga till till den befintliga Jupiter.
Installera Spark + Toree + Jupyter
pip install toree
jupyter toree install --spark_home=${SPARK_HOME} --sys-prefix
jupyter kernelspec list
```
```
TillgÀngliga kÀrnor:
apache_toree_scala /Users/afranzi/.virtualenvs/mlflow/share/jupyter/kernels/apache_toree_scala
python3 /Users/afranzi/.virtualenvs/mlflow/share/jupyter/kernels/python3
```Som visas i det bifogade anteckningsblocket anvÀnds UDF tillsammans med Spark och PySpark. Vi hoppas att denna del kommer att vara anvÀndbar för dem som Àlskar Scala och vill distribuera maskininlÀrningsmodeller i produktion.
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..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 |
+-----------+--------+-----------+---------+-----------+
Följande steg
Trots att MLflow vid tidpunkten för denna artikel Àr i Alpha-version, ser den lovande ut. Möjligheten att köra flera maskininlÀrningsramverk och anvÀnda dem frÄn en enda slutpunkt tar rekommendationssystem pÄ en ny nivÄ.
Dessutom sammanför MLflow dataingenjörer och datavetare genom att skapa ett gemensamt lager mellan dem.
Efter denna forskning om MLflow Àr vi övertygade om att vi kommer att gÄ vidare och anvÀnda det för vÄra Spark-flöden och i rekommendationssystem.
Det skulle vara bra att synkronisera filsystemet med databasen, istÀllet för att anvÀnda filsystemet. SÄ vi borde fÄ flera slutpunkter som kan anvÀnda samma filsystem. Till exempel, att anvÀnda flera instanser och med samma Glue metastore.
Sammanfattningsvis vill vi tacka MLFlow-gemenskapen för att ni gör vÄrt arbete med data mer intressant.
Om du experimenterar med MLflow, tveka inte att skriva till oss och berÀtta hur du anvÀnder det, sÀrskilt om du anvÀnder det i produktion.
LĂ€s mer om kurserna:
LĂ€s mer:
KĂ€lla: habr.com
