تمديد شرارة مع MLflow

مرحبًا سكان خابروفسك. كما كتبنا بالفعل، ستطلق OTUS هذا الشهر دورتين للتعلم الآلي في وقت واحد، وهما قاعدة и المتقدمة. وفي هذا الصدد، نواصل تبادل المواد المفيدة.

الغرض من هذه المقالة هو الحديث عن تجربتنا الأولى في الاستخدام MLflow.

سنبدأ المراجعة MLflow من خادم التتبع الخاص به وقم بتسجيل جميع تكرارات الدراسة. ثم سنشارك تجربتنا في توصيل Spark بـ MLflow باستخدام UDF.

السياق

نحن مشتركون صحة ألفا نحن نستخدم التعلم الآلي والذكاء الاصطناعي لتمكين الناس من تولي مسؤولية صحتهم ورفاهيتهم. ولهذا السبب تقع نماذج التعلم الآلي في قلب منتجات علوم البيانات التي نقوم بتطويرها، ولهذا السبب انجذبنا إلى MLflow، وهي منصة مفتوحة المصدر تغطي جميع جوانب دورة حياة التعلم الآلي.

MLflow

الهدف الرئيسي من MLflow هو توفير طبقة إضافية فوق التعلم الآلي والتي من شأنها أن تسمح لعلماء البيانات بالعمل مع أي مكتبة للتعلم الآلي تقريبًا (h2o, keras, com.mleap, pytorch, sklearn и tensorflow)، والارتقاء بعملها إلى المستوى التالي.

يوفر MLflow ثلاثة مكونات:

  • تتبع الشحنة – التسجيل وطلبات التجارب: الكود والبيانات والتكوين والنتائج. من المهم جدًا مراقبة عملية إنشاء النموذج.
  • المشاريع – تنسيق التعبئة والتغليف للتشغيل على أي منصة (على سبيل المثال. SageMaker)
  • الموديلات – تنسيق مشترك لتقديم النماذج إلى أدوات النشر المختلفة.

MLflow (في ألفا وقت كتابة هذا التقرير) عبارة عن نظام أساسي مفتوح المصدر يسمح لك بإدارة دورة حياة التعلم الآلي، بما في ذلك التجريب وإعادة الاستخدام والنشر.

إعداد MLflow

لاستخدام MLflow، عليك أولاً إعداد بيئة Python بأكملها، ولهذا سنستخدم PyEnv (لتثبيت بايثون على ماك، راجع هنا). بهذه الطريقة يمكننا إنشاء بيئة افتراضية حيث سنقوم بتثبيت جميع المكتبات اللازمة لتشغيلها.

```
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 لتشغيل نماذج مثل UDF. يلزم إصلاح إصدارات PyArrow وNumpy لأن الإصدارات الأخيرة تتعارض مع بعضها البعض.

إطلاق واجهة المستخدم للتتبع

يتيح لنا تتبع MLflow تسجيل التجارب والاستعلام عنها باستخدام Python و REST واجهة برمجة التطبيقات. بالإضافة إلى ذلك، يمكنك تحديد مكان تخزين العناصر النموذجية (المضيف المحلي، الأمازون S3, تخزين Azure Blob, جوجل سحابة التخزين أو خادم سفتب). نظرًا لأننا نستخدم 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، لأننا نحتاج فقط إلى المسار إلى النموذج.

ملاحظة: ضع في اعتبارك أن واجهة مستخدم التتبع والعميل النموذجي يجب أن يكون لديهما حق الوصول إلى موقع العنصر. وهذا يعني، بغض النظر عن حقيقة وجود واجهة مستخدم التتبع في مثيل EC2، عند تشغيل MLflow محليًا، يجب أن يتمتع الجهاز بإمكانية الوصول المباشر إلى S3 لكتابة النماذج الاصطناعية.

تمديد شرارة مع MLflow
تقوم واجهة مستخدم التتبع بتخزين العناصر في حاوية 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
تكرارات النبيذ

جزء الخادم للنموذج

يحتوي خادم تتبع MLflow، الذي تم إطلاقه باستخدام أمر "mlflow server"، على واجهة برمجة تطبيقات REST لتتبع عمليات التشغيل وكتابة البيانات إلى نظام الملفات المحلي. يمكنك تحديد عنوان خادم التتبع باستخدام متغير البيئة "MLFLOW_TRACKING_URI" وستتصل واجهة برمجة تطبيقات تتبع MLflow تلقائيًا بخادم التتبع على هذا العنوان لإنشاء/تلقي معلومات الإطلاق ومقاييس السجل وما إلى ذلك.

المصدر: المستندات// تشغيل خادم التتبع

لتزويد النموذج بخادم، نحتاج إلى خادم تتبع قيد التشغيل (انظر واجهة التشغيل) ومعرف التشغيل الخاص بالنموذج.

تمديد شرارة مع MLflow
تشغيل معرف

# 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، سنحتاج إلى الوصول إلى واجهة مستخدم التتبع لتلقي معلومات حول النموذج ببساطة عن طريق تحديد --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]}

تشغيل الموديلات من سبارك

على الرغم من أن خادم التتبع قوي بما يكفي لخدمة النماذج في الوقت الفعلي وتدريبها واستخدام وظيفة الخادم (المصدر: mlflow // المستندات // النماذج # المحلية)، يعد استخدام Spark (الدفعة أو التدفق) حلاً أكثر قوة بسبب التوزيع.

تخيل أنك قمت ببساطة بالتدريب دون الاتصال بالإنترنت ثم قمت بتطبيق نموذج الإخراج على جميع بياناتك. هذا هو المكان الذي يتألق فيه Spark وMLflow.

قم بتثبيت PySpark + Jupyter + Spark

المصدر: ابدأ PySpark - Jupyter

لإظهار كيفية تطبيق نماذج MLflow على إطارات بيانات Spark، نحتاج إلى إعداد دفاتر ملاحظات Jupyter للعمل مع PySpark.

ابدأ بتثبيت أحدث إصدار ثابت أباتشي سبارك:

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

تمديد شرارة مع 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)

تمديد شرارة مع MLflow
PySpark - إخراج تنبؤات جودة النبيذ

حتى هذه اللحظة، تحدثنا عن كيفية استخدام PySpark مع MLflow، وتشغيل تنبؤات جودة النبيذ على مجموعة بيانات النبيذ بأكملها. ولكن ماذا لو كنت بحاجة إلى استخدام وحدات Python MLflow من Scala Spark؟

لقد اختبرنا ذلك أيضًا عن طريق تقسيم سياق Spark بين Scala وPython. أي أننا سجلنا MLflow UDF في Python واستخدمناه من Scala (نعم، ربما ليس الحل الأفضل، ولكن ما لدينا).

سكالا سبارك + إم إل فلو

لهذا المثال سوف نضيف توري نواة إلى كوكب المشتري الموجود.

تثبيت سبارك + توري + 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 على التقريب بين مهندسي البيانات والمتخصصين في علوم البيانات، مما يضع طبقة مشتركة بينهم.

بعد هذا الاستكشاف لـ MLflow، نحن واثقون من أننا سنمضي قدمًا ونستخدمه في خطوط أنابيب Spark وأنظمة التوصية الخاصة بنا.

سيكون من الجيد مزامنة تخزين الملفات مع قاعدة البيانات بدلاً من نظام الملفات. يجب أن يمنحنا هذا نقاط نهاية متعددة يمكنها استخدام نفس مساحة تخزين الملفات. على سبيل المثال، استخدم مثيلات متعددة مقطع موسيقي سريع и أثينا مع نفس الغراء metastore.

للتلخيص، أود أن أشكر مجتمع MLFlow على جعل عملنا مع البيانات أكثر إثارة للاهتمام.

إذا كنت تتلاعب بـ MLflow، فلا تتردد في الكتابة إلينا وإخبارنا كيف تستخدمه، بل وأكثر من ذلك إذا كنت تستخدمه في الإنتاج.

تعرف على المزيد حول الدورات:
التعلم الالي. دورة اساسية
التعلم الالي. دورة متقدمة

اقرأ أكثر:

المصدر: www.habr.com

إضافة تعليق