์๋
ํ์ธ์, Khabrovites. ์ฐ๋ฆฌ๊ฐ ์ด๋ฏธ ์ด ๊ฒ์ฒ๋ผ ์ด๋ฒ ๋ฌ OTUS๋ ๊ธฐ๊ณ ํ์ต์ ๋ํ ๋ ๊ฐ์ง ๊ณผ์ ์ ํ ๋ฒ์ ์์ํฉ๋๋ค.
์ด ๊ธฐ์ฌ์ ๋ชฉ์ ์ ์ฐ๋ฆฌ์ ์ฒซ ๋ฒ์งธ ๊ฒฝํ์ ๋ํด ์ด์ผ๊ธฐํ๋ ๊ฒ์
๋๋ค.
๋ฆฌ๋ทฐ ์์ํ๊ฒ ์ต๋๋ค
๋ฌธ๋งฅ
์ฐ๋ฆฌ๋
MLํ๋ก์ฐ
MLflow์ ์ฃผ์ ๋ชฉํ๋ ๋ฐ์ดํฐ ๊ณผํ์๊ฐ ๊ฑฐ์ ๋ชจ๋ ๊ธฐ๊ณ ํ์ต ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์์
ํ ์ ์๋๋ก ๊ธฐ๊ณ ํ์ต ์์ ์ถ๊ฐ ๊ณ์ธต์ ์ ๊ณตํ๋ ๊ฒ์
๋๋ค.
MLflow๋ ์ธ ๊ฐ์ง ๊ตฌ์ฑ ์์๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ถ์ โ ์คํ ๊ธฐ๋ก ๋ฐ ์์ฒญ: ์ฝ๋, ๋ฐ์ดํฐ, ๊ตฌ์ฑ ๋ฐ ๊ฒฐ๊ณผ. ๋ชจ๋ธ์ ๋ง๋๋ ๊ณผ์ ์ ๋ฐ๋ฅด๋ ๊ฒ์ด ๋งค์ฐ ์ค์ํฉ๋๋ค.
- ํ๋ก์ ํธ โ ๋ชจ๋ ํ๋ซํผ์์ ์คํํ ์ ์๋ ํจํค์ง ํ์(์:
์ธ์ด์ง ๋ฉ์ด์ปค ) - ๋ชจ๋ธ ๋ค์ํ ๋ฐฐํฌ ๋๊ตฌ์ ๋ชจ๋ธ์ ์ ์ถํ๊ธฐ ์ํ ์ผ๋ฐ์ ์ธ ํ์์ ๋๋ค.
MLflow(์์ฑ ๋น์ ์ํ)๋ ์คํ, ์ฌ์ฌ์ฉ ๋ฐ ๋ฐฐํฌ๋ฅผ ํฌํจํ์ฌ ๊ธฐ๊ณ ํ์ต ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌํ ์ ์๋ ์คํ ์์ค ํ๋ซํผ์ ๋๋ค.
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 ํด์ ๋ก๊ทธ๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ๋ฐ๋ณต์ ์ํํ ์ฝ๋๋ฅผ ์ดํดํจ์ผ๋ก์จ ์ต์์ ๋ชจ๋ธ์ ์ฌํํ ์ ์๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ์ ์ฉํฉ๋๋ค.
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 server" ๋ช ๋ น์ผ๋ก ์์๋ MLflow ์ถ์ ์๋ฒ์๋ ์คํ์ ์ถ์ ํ๊ณ ๋ฐ์ดํฐ๋ฅผ ๋ก์ปฌ ํ์ผ ์์คํ ์ ์ฐ๊ธฐ ์ํ REST API๊ฐ ์์ต๋๋ค. "MLFLOW_TRACKING_URI" ํ๊ฒฝ ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ์ถ์ ์๋ฒ์ ์ฃผ์๋ฅผ ์ง์ ํ ์ ์์ผ๋ฉฐ MLflow ์ถ์ API๋ ์๋์ผ๋ก ์ด ์ฃผ์์์ ์ถ์ ์๋ฒ์ ์ฐ๊ฒฐํ์ฌ ์คํ ์ ๋ณด, ๋ก๊น ๋ฉํธ๋ฆญ ๋ฑ์ ์์ฑ/๊ฐ์ ธ์ต๋๋ค.
์ถ์ฒ :
Docs// ์ถ์ ์๋ฒ ์คํ
๋ชจ๋ธ์ ์๋ฒ๋ฅผ ์ ๊ณตํ๋ ค๋ฉด ์คํ ์ค์ธ ์ถ์ ์๋ฒ(์์ ์ธํฐํ์ด์ค ์ฐธ์กฐ)์ ๋ชจ๋ธ์ ์คํ 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 ๋ฐ์ดํฐ ํ๋ ์์ 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 - ์์ธ ํ์ง ์์ธก
์ง๊ธ๊น์ง ์ ์ฒด ์์ธ ๋ฐ์ดํฐ ์ธํธ์์ ์์ธ ํ์ง ์์ธก์ ์คํํ์ฌ MLflow์ ํจ๊ป PySpark๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์ด์ผ๊ธฐํ์ต๋๋ค. ํ์ง๋ง Scala Spark์์ Python MLflow ๋ชจ๋์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ํด์ผ ํ ๊น์?
Scala์ Python ๊ฐ์ Spark ์ปจํ ์คํธ๋ฅผ ๋ถํ ํ์ฌ ์ด๋ฅผ ํ ์คํธํ์ต๋๋ค. ์ฆ, Python์ MLflow UDF๋ฅผ ๋ฑ๋กํ๊ณ Scala์์ ์ฌ์ฉํ์ต๋๋ค(์, ์ต์์ ์๋ฃจ์ ์ ์๋์ง๋ง ์ฐ๋ฆฌ๊ฐ ๊ฐ์ง๊ณ ์๋ ๊ฒ).
์ค์นผ๋ผ ์คํํฌ + 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 ํ์ดํ๋ผ์ธ ๋ฐ ์ถ์ฒ ์์คํ ์ ์ฌ์ฉํ ๊ฒ์ ๋๋ค.
ํ์ผ ์์คํ
๋์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ์ผ ์คํ ๋ฆฌ์ง๋ฅผ ๋๊ธฐํํ๋ ๊ฒ์ด ์ข์ ๊ฒ์
๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋์ผํ ํ์ผ ๊ณต์ ๋ฅผ ์ฌ์ฉํ ์ ์๋ ์ฌ๋ฌ ์๋ํฌ์ธํธ๊ฐ ์ ๊ณต๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด ์ฌ๋ฌ ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์์ฝํ๋ฉด ๋ฐ์ดํฐ ์์ ์ ๋์ฑ ํฅ๋ฏธ๋กญ๊ฒ ๋ง๋ค์ด์ค MLFlow ์ปค๋ฎค๋ํฐ์ ๊ฐ์ฌ์ ๋ง์ ์ ํ๊ณ ์ถ์ต๋๋ค.
MLflow๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ธ์ ๋ ์ง ์ ํฌ์๊ฒ ํธ์ง๋ฅผ ๋ณด๋ด ์ฌ์ฉ ๋ฐฉ๋ฒ์ ์๋ ค์ฃผ์ญ์์ค. ํ๋ก๋์ ์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ ๋์ฑ ๊ทธ๋ ์ต๋๋ค.
๊ณผ์ ์ ๋ํด ์์ธํ ์์๋ณด๊ธฐ:
๋ ์ฝ์ด๋ณด๊ธฐ:
๊ฐ๋ ํ์ต ๋ฌธ์ ์ ์ฃผ์ฑ๋ถ ๋ถ์์ ์ ์ฉํ ๋์ ์ํ ๋ฐ ์ฃผ์ ์ฌํญ Docker๋ก ๊ธฐ๊ณ ํ์ต ๋ชจ๋ธ ๋ฐฐํฌ - 1๋ถ Docker๋ก ๊ธฐ๊ณ ํ์ต ๋ชจ๋ธ ๋ฐฐํฌ - 2๋ถ
์ถ์ฒ : habr.com