áááºá¹ááá¬áá« Khabrovsk áá±ááá¯ááºáá°áá»á¬ážá áá»áœááºáá±á¬áºáá±ážááŒá®ážáá¬ážá¡ááá¯ááºážá ááá¯áááœáẠOTUS ááẠá
ááºááá¯ááºážááá¯ááºáᬠáááºáá°ááŸá¯áááºáááºážááŸá
áºáá¯ááᯠáá
áºááŒáá¯ááºááẠá
áááºááœáá·áºááŸá
áºáá±ááŒá®ááŒá
áºáááºá
á€áá±á¬ááºážáá«ážá áááºááœááºáá»ááºááŸá¬ áá»áœááºá¯ááºááá¯á·á ááááá¯á¶áž á¡ááœá±á·á¡ááŒá¯á¶ááᯠá¡áá¯á¶ážááŒá¯ááŒááºážá¡ááŒá±á¬ááºáž ááœá±ážááœá±ážáááºááŒá
áºáá«áááºá
áá¯á¶ážáááºáá»ááºááᯠá
áááºáá«áááºá
á áá¬ážá ááº
áá»áœááºá¯ááºááá¯á·áááŸááááº
MLflow
MLflow á á¡ááááááºááŸááºážáá»ááºááŸá¬ áá±áá¬áááá¹áá¶ááá¬ááŸááºáá»á¬ážá¡á¬áž á
ááºáááºáá°ááŸá¯á
á¬ááŒáá·áºááá¯ááºááá¯ááºážáá®ážáá«ážááœáẠáá±áá¬áááá¹áá¶ááá¬ááŸááºáá»á¬áž á¡áá¯ááºáá¯ááºááá¯ááºá
á±ááá·áº á
ááºáááºáá°ááŸá¯á¡áá±á«áºáááºáááºá¡ááœáŸá¬áá
áºáᯠáá¶á·ááá¯ážáá±ážáááºááŒá
áºááẠ(
MLflow ááẠá¡á áááºá¡ááá¯ááºážáá¯á¶ážáá¯ááᯠáá¶á·ááá¯ážáá±ážáááº-
- ááŒá±áá¬áá±á¬áẠ- á ááºážáááºááŸá¯áá»á¬ážá¡ááœáẠááŸááºáááºážáááºááŒááºážááŸáá·áº áá±á¬ááºážááá¯áá»ááºáá»á¬áž- áá¯ááºá áá±áá¬á ááœá²á·á ááºážááŸá¯áá¯á¶á á¶ááŸáá·áº ááááºáá»á¬ážá áá±á¬áºáááºáááºáá®ážááŸá¯ áá¯ááºáááºážá ááºááᯠá á±á¬áá·áºááŒáá·áºáá±á·áá¬ááẠá¡ááœááºá¡áá±ážááŒá®ážáá«áááºá
- á
á®áá¶ááááºážáá»á¬áž - áááºááá·áºááááºáá±á¬ááºážáá±á«áºááœááºáááá¯áá¯ááºáá±á¬ááºááẠáá¯ááºááá¯ážááŸá¯áá¯á¶á
ᶠ(á¥ááá¬á
SageMaker ) - áá±á¬áºááẠ- á¡áá»áá¯ážáá»áá¯ážáá±á¬ á¡áá¯á¶ážáá»áááááá¬áá»á¬ážááá¯á· áá±á¬áºáááºáá»á¬áž áááºááœááºážááŒááºážá¡ááœáẠáá¯á¶áá±á¬áºáááºáá áºáá¯á
MLflow (áá±ážáá±áá»áááºááœáẠá¡ááºááºáá¬ááŒáá·áº) ááẠá ááºážáááºááŒááºážá ááŒááºáááºá¡áá¯á¶ážááŒá¯ááŒááºážááŸáá·áº á¡áá¯á¶ážáá»ááŒááºážá¡áá«á¡ááẠá ááºáááºáá°ááŸá¯áááá¶ááá¬ááᯠá á®áá¶ááá·áºááœá²ááẠááœáá·áºááŒá¯ááá·áº open source platform áá áºáá¯ááŒá áºáááºá
MLflow ááᯠá áá áºááá·áºááœááºážááŒááºážá
MLflow ááá¯á¡áá¯á¶ážááŒá¯ááẠáááºááẠáááºá Python áááºáááºážáá»ááºáá
áºáá¯áá¯á¶ážááᯠáŠážá
áœá¬ á
áááºáááºááŸááºááẠááá¯á¡ááºáááºá áááºážááᯠáá»áœááºá¯ááºááá¯á·á¡áá¯á¶ážááŒá¯áá«áááºá
```
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
```
ááŸááºáá»ááº- UDF áá²á·ááá¯á·áá±á¬ áá±á¬áºáááºáá»á¬ážááᯠáá¯ááºáá±á¬ááºááẠáá»áœááºá¯ááºááá¯á·ááẠPyArrow ááᯠá¡áá¯á¶ážááŒá¯áááºá PyArrow ááŸáá·áº Numpy áá¬ážááŸááºážáá»á¬ážááẠáá±á¬ááºááœááºáá¬ážááŸááºážáá»á¬áž áá áºáá¯ááŸáá·áºáá áºáᯠááœá²ááœá²áá±áá±á¬ááŒá±á¬áá·áº ááŒá¯ááŒááºáááºááá¯á¡ááºáá«áááºá
ááŒá±áá¬áá¶ááŒááºáž UI ááá¯ááœáá·áºáá«á
MLflow ááŒá±áá¬áá¶ááŒááºážááẠáá»áœááºá¯ááºááá¯á·á¡á¬áž Python ááŸáá·áº á¡áá¯á¶ážááŒá¯á á
ááºážáááºááŸá¯áá»á¬ážááᯠááŸááºáááºážááŸáá·áº áá±ážááŒááºážááá¯ááºá
á±áá«áááºá
# 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 ááŸáá·áº áá±á¬áºáááºáá±á¬ááºáááºááẠááŸá±ážáá±á¬ááºážáá á¹á ááºážáááºáá±áá¬ááá¯á· áááºáá±á¬ááºááœáá·áºááŸáááááºááᯠááŸááºáá¬ážáá¬ážáá«á ááá¯ááá¯áááºááŸá¬á ááŒá±áá¬áᶠUI ááẠEC2 á á¶ááá°áá¬ááœáẠááŸááá±áááºááŒá áºá á± MLflow ááᯠá ááºááœááºážááœáẠá¡áá¯á¶ážááŒá¯ááá·áºá¡áá«á á ááºááẠááŸá±ážáá±á¬ááºážáá á¹á ááºážáá±á¬áºáááºáá»á¬ážáá±ážááẠS3 ááá¯á· ááá¯ááºááá¯ááºáááºáá±á¬ááºááœáá·áºááŸáááá«áááºá
UI ááá¯ááŒá±áá¬áá¶ááŒááºážááẠS3 áá¯á¶ážáá²ááœáẠááŸá±ážáá±á¬ááºážáá
á¹á
ááºážáá»á¬ážááᯠááááºážáááºážáá¬ážáááºá
á¡ááŒá±ážáá±á¬áºáááºáá»á¬áž
ááŒá±áá¬áá¶ááŒááºážáá¬áá¬ááẠá¡áá¯ááºáá¯ááºáááºááŸáá·áºáááŒáá¯ááºáááºá áááºááẠáá±á¬áºáááºáá»á¬ážááᯠá áááºáá±á·áá»áá·áºááá¯ááºáááºá
á¥ááá¬á¡áá±ááŒáá·áºá áá»áœááºá¯ááºááá¯á·ááẠ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 ááẠááá·áºá¡á¬áž áá±á¬áºáááºááá·áºáááºáá»ááºáá»á¬ážá áááºááá áºáá»á¬ážááŸáá·áº á¡áá¯ááá¬áá á¹á ááºážáá»á¬ážááᯠááŸááºáááºážáááºááẠááœáá·áºááŒá¯áá¬ážáá±á¬ááŒá±á¬áá·áº áááºážááá¯á·ááẠáááºááá²áá² áá¯ááºáá±á¬ááºááŸá¯áá»á¬ážá¡áá±á«áº áááºááŒá±áá¬áá¶ááá¯ááºáááºááŒá áºáááºá á€áááºážááŒáá·áº áá»áœááºá¯ááºááá¯á·ááẠááŒá±áá¬áá¶áá¬áá¬ááᯠáááºááœááºááŒááºáž ááá¯á·ááá¯áẠááœááºááá·áºáá»á¬ážá 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")
ááá¯ááºáá«áááºááá¯á·
áá±á¬áºáááºá¡ááœáẠáá¬áá¬á¡ááá¯ááºáž
âmlflow áá¬áá¬â ááœáŸááºááŒá¬ážáá»ááºááᯠá¡áá¯á¶ážááŒá¯á á áááºááá·áº MLflow ááŒá±áá¬áá¶áá¬áá¬ááœáẠáá¯ááºáá±á¬ááºáá»ááºáá»á¬ážááᯠááŒá±áá¬áá¶ááŒááºážááŸáá·áº áá±áááœááºáž ááá¯ááºá áá áºááá¯á· áá±áá¬áá»á¬ážáá±ážáá¬ážááŒááºážá¡ááœáẠREST API ááŸááááºá "MLFLOW_TRACKING_URI" ááᯠá¡áá¯á¶ážááŒá¯á áááºáááºážáá»ááºááŒá±á¬ááºážáá²ááá¯ááºáá±á¬ ááŒá±áá¬áá¶áá¬áá¬ááááºá á¬ááᯠáááºáááºááŸááºááá¯ááºááŒá®áž MLflow ááŒá±áá¬áá¶ááŒááºáž API ááẠááœáŸáá·áºáááºááŸá¯á¡áá»ááºá¡áááºá ááŸááºáááºážááá¯ááºážáá¬ááŸá¯áá»á¬áž ááŒá¯áá¯ááºáááº/áááºáá¶ááẠá€ááááºá á¬ááŸá ááŒá±áá¬áá¶áá¬áá¬ááᯠá¡ááá¯á¡áá»á±á¬ááºáááºááœááºáá±ážáááºááŒá áºáá«áááºá
source:
Docs// ááŒá±áá¬áá¶áá¬áá¬ááᯠáá¯ááºáá±á¬ááºáá±áááºá
áá±á¬áºáááºááᯠáá¬áá¬áá áºáᯠáá¶á·ááá¯ážáá±ážáááºá áá»áœááºá¯ááºááá¯á·ááẠáááºáááºáá±ááá·áº ááŒá±áá¬áá¶áá¬áᬠ(á áááºááá·áº áá»ááºááŸá¬ááŒááºááᯠááŒáá·áºáá«) ááŸáá·áº áá±á¬áºáááºá Run ID ááá¯á¡ááºáá«áááºá
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 ááá¯á· áááºáá±á¬ááºááœáá·áº ááá¯á¡ááºáááºááŒá
áºáá«áááºá --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 á០á¡ááŒá±ážáá±á¬áºáááºáá»á¬áž
ááŒá±áá¬áá¶áá¬áá¬ááẠáá±á¬áºáááºáá»á¬ážááᯠá¡áá»áááºááŸáá·áºáááŒá±ážáá® áááºáá±á¬ááºááŸá¯áá±ážááá¯ááºáá±á¬ááºá¡á±á¬áẠá¡á
áœááºážáááºáá±á¬áºáááºáž áááºážááá¯á·ááᯠáá±á·áá»áá·áºáá±ážááŒá®áž áá¬áá¬áá¯ááºáá±á¬ááºááá¯ááºá
áœááºážááᯠá¡áá¯á¶ážááŒá¯áá« (á¡áááºážá¡ááŒá
áº-
áááºááẠáá±á·áá»áá·áºáá±ážááᯠá¡á±á¬á·ááºááá¯ááºážááœáẠááá¯ážááá¯ážááŸááºážááŸááºáž ááŒá¯áá¯ááºááŒá®áž áááºááá±áá¬á¡á¬ážáá¯á¶ážááœáẠá¡ááœááºáá±á¬áºáááºááᯠá¡áá¯á¶ážááŒá¯áááºáᯠááŒááºáá±á¬ááºááŒáá·áºáá«á á€áá±áá¬ááœáẠSpark ááŸáá·áº MLflow áá±á¬ááºááá±áááºá
PySpark + Jupyter + Spark ááᯠááá·áºááœááºážáá«á
source:
PySpark - Jupyter á áááºááá¯ááºáá«á
Spark dataframes ááœáẠMLflow áá±á¬áºáááºáá»á¬ážááᯠáááºááá¯á·á¡áá¯á¶ážááŒá¯ááŒá±á¬ááºážááŒááááºá PySpark ááŸáá·áº á¡áá°áááœáá¯ááºáá±á¬ááºááẠJupyter ááŸááºá á¯á á¬á¡á¯ááºáá»á¬ážááᯠá áá áºááá·áºááœááºážááẠááá¯á¡ááºáá«áááºá
áá±á¬ááºáá¯á¶ážááœááºáááºááŒáááºáá±á¬áá¬ážááŸááºážááᯠááá·áºááœááºážááŒááºážááŒáá·áº á
áááºáá«á
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
áá»áœááºá¯ááºááá¯á·ááẠáá»áœááºá¯ááºááá¯á·áááŸááºá
á¯á
á¬á¡á¯ááºáá»á¬ážááᯠá¡ááá¯ááŸááá±á¬ááá¯ááºááœá²ááœáẠááááºážáááºážááá¯ááºáá«áááºá
PySpark á០Jupyter ááá¯ááœáá·áºááŒááºážá
áá»áœááºá¯ááºááá¯á·ááẠJupiter ááᯠPySpark áááá¯ááºáá¬á¡ááŒá ẠáááºááŸááºááá¯ááºáá±á¬ááŒá±á¬áá·áº ááᯠPySpark áá¡ááŒá±á¡áá±ááœáẠJupyter ááŸááºá á¯á á¬á¡á¯ááºááᯠáá¯á¶ážááá¯ááºáá«ááŒá®á
(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
á¡áááºááœááºáá±á¬áºááŒáá²á·ááá·áºá¡ááá¯ááºážá 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)
PySpark - ááá¯ááºá¡áááºá¡ááœá±áž ááá·áºááŸááºážáá»ááºáá»á¬ážááᯠáá¯ááºááŒááºááŒááºážá
á€á¡áá»ááºá¡ááá áá»áœááºá¯ááºááá¯á·ááẠááá¯ááºáá±áá¬á¡ááœá²áá áºáá¯áá¯á¶ážááœáẠááá¯ááºá¡áááºá¡ááœá±ážááá·áºááŸááºážáá»ááºáá»á¬ážááᯠáá¯ááºáá±á¬ááºááŒá®áž PySpark ááᯠMLflow ááŒáá·áº áááºááá¯á·á¡áá¯á¶ážááŒá¯ááááºááᯠááŒá±á¬ááá¯áá¬ážáá«áááºá ááá¯á·áá±á¬áº áááºááẠScala Spark á០Python MLflow modules ááá¯á¡áá¯á¶ážááŒá¯ááẠááá¯á¡ááºáá«á á¡áááºáááºážá
Scala ááŸáá·áº Python á¡ááŒá¬áž Spark áááºá ááºááŸá¯ááᯠááá¯ááºážááŒá¬ážááŒááºážááŒáá·áºáááºáž áááºážááᯠá ááºážáááºáá²á·áááºá ááá¯ááá¯áááºááŸá¬á áá»áœááºá¯ááºááá¯á·ááẠ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 ááẠAlpha áá¬ážááŸááºážááœááºááŸááá±á¬áºáááºážá áááºážááẠá¡ááœááºá¡áá¬ážá¡áá¬ááŸááá¯á¶ááááºá á ááºáááºáá°ááŸá¯áá±á¬ááºáá»á¬ážá áœá¬ááᯠáá¯ááºáá±á¬ááºááá¯ááºááŒá®áž á¡áá¯á¶ážááŸááºáá áºáá¯áááºážá០áááºážááá¯á·ááᯠá á¬ážáá¯á¶ážááá¯ááºáá¯á¶áá»áŸááŒáá·áº á¡ááŒá¶ááŒá¯áá°á áá áºáá»á¬ážááᯠáá±á¬ááºáá áºááá·áºááá¯á· áá°áá±á¬ááºááœá¬ážáááºááŒá áºáááºá
ááá¯á·á¡ááŒááºá MLflow ááẠData Engineers ááŸáá·áº Data Science áá»áœááºážáá»ááºáá°áá»á¬ážá¡á¬áž áááºážááá¯á·ááŒá¬ážááœáẠáá¯á¶á¡ááœáŸá¬áá áºáá¯á á®áá¬ážáᬠááá¯ááá¯áá®ážáááºá á±áá«áááºá
ဠMLflow ááᯠá á°ážá ááºážáá±á·áá¬ááŒá®ážáá±á¬ááºá áá»áœááºá¯ááºááá¯á·ááẠáááºážááᯠáá»áœááºá¯ááºááá¯á·á Spark ááá¯ááºááá¯ááºážáá»á¬ážááŸáá·áº á¡ááŒá¶ááŒá¯áá±ážá áá áºáá»á¬ážá¡ááœáẠááŸá±á·ááá¯á· áááºáááºáá»á®áááºáááºáᯠáá¯á¶ááŒááºáá«áááºá
ááá¯ááºááá¯ááŸá±á¬ááºááŸá¯ááᯠááá¯ááºá
áá
áºá¡á
á¬áž áá±áá¬áá±á·á
áºááŸáá·áº áááºáá°ááŒá¯ááŒááºážááẠáá±á¬ááºážááœááºáá«áááºá áááºážááẠáá»áœááºá¯ááºááá¯á·á¡á¬áž áá°áá®áá±á¬ááá¯ááºááá¯ááŸá±á¬ááºááŸá¯ááᯠá¡áá¯á¶ážááŒá¯ááá¯ááºááá·áº á¡áá¯á¶ážááŸááºáá»á¬ážá
áœá¬ááᯠáá±ážááá·áºáááºá á¥ááá¬á¡á¬ážááŒáá·áºá áá»á¬ážá
áœá¬áá±á¬á¥ááá¬áá»á¬ážááá¯áá¯á¶ážáá«á
á¡ááŸá áºáá»á¯ááºááá»áŸáẠáá»áœááºá¯ááºááá¯á·ááá¯ááºáááºážááᯠáá±áá¬ááá¯ááá¯á áááºáááºá á¬ážááœááºááŒá áºá¡á±á¬áẠááŒá¯áá¯ááºáá±ážááá·áºá¡ááœáẠMLFlow á¡ááá¯ááºážá¡ááá¯ááºážááᯠáá»á±ážáá°ážáááºááŒá±á¬ááºáž ááŒá±á¬ááŒá¬ážááá¯áá«áááºá
á¡áááºá áááºááẠMLflow ááŸáá·áº áá á¬ážáá±áá«áá áá»áœááºá¯ááºááá¯á·áᶠá á¬áá±ážááŒá®áž áááºá¡áá¯á¶ážááŒá¯áá¯á¶ááᯠááŒá±á¬ááŒááẠáááœáá·áºáá¯ááºáá«ááŸáá·áºá áááºážááᯠáá¯ááºáá¯ááºáá±ážááœáẠáááºá¡áá¯á¶ážááŒá¯áá«á ááá¯ááááºá
áááºáááºážáá»á¬ážá¡ááŒá±á¬ááºážááá¯ááá¯áá±á·áá¬áá«
ááá¯ááŒá®ážáááºáá«:
ááŒá®ážááŒááºáááºáá°ááŸá¯ááŒá¿áá¬áá»á¬ážááœáẠá¡áááá¡á áááºá¡ááá¯ááºážááœá²ááŒááºážá áááºááŒá¬ááŒááºážááᯠá¡áá¯á¶ážáá»ááŒááºážáá¡áá¹ááá¬ááºááŸáá·áºááááá¬ážáá»ááºáá»á¬áž Docker ááŒáá·áº á ááºáááºáá°ááŸá¯áá¯á¶á á¶ááᯠá¡áá¯á¶ážáá»ááŒááºáž â á¡ááá¯ááºáž á Docker ááŒáá·áº á ááºáááºáá°ááŸá¯áá¯á¶á á¶ááᯠá¡áá¯á¶ážáá»ááŒááºáž â á¡ááá¯ááºáž á
source: www.habr.com