Tur lanjutan melalui konsep dan contoh Pelacakan MLflow, Proyek, Model, dan Registri Model

Tentang apa postingan ini

Postingan ini merupakantutorial lanjutan tentangMLflow, yang mencakup motivasi dan fitur dasar di balik masing-masing dari empat sub-proyek utamanya, Pelacakan MLflow, Proyek, Model dan Registri Model. Ini juga akan menyentuh konsep MLOps dan ModelOps, untuk “membenarkan” beberapa fitur MLflow.

Tentu saja, dokumentasi resmi merupakan sumber informasi yang sah, sehingga ide di balik postingan ini adalah agar Anda dapat mengetahui cara menggunakan alat ini dan juga memberikan motivasi dan konteks pada setiap sub-proyek tersebut.

Munculnya Pembelajaran Mesin

Agar tertarik dengan artikel ini, saya yakin Anda sudah sangat familiar dengan ide di balik Machine Learning (ML), namun demi kelengkapan, ada baiknya Anda menuliskan beberapa kata tentang mengapa hal ini menjadi begitu penting dan tersebar luas.

Salah satu alasan utama mengapa ML menjadi begitu penting adalah karena ML memungkinkan otomatisasi tugas-tugas yang sebelumnya dilakukan secara manual, seperti analisis data, pengenalan gambar dan ucapan, serta pengambilan keputusan. Hal ini dapat mengarah pada peningkatan efisiensi dan penghematan biaya di berbagai industri, termasuk layanan kesehatan, keuangan, dan transportasi.

Hal ini semakin meluas dalam beberapa tahun terakhir karena kombinasi dari tiga faktor: 1) ketersediaan data dalam jumlah besar, 2) kemajuan dalam daya komputasi, 3) pengembangan algoritma yang lebih canggih.

Manajemen Model

Melatih model hanyalah sebagian dari pekerjaan. Agar model dapat diterapkan sepenuhnya ke produksi dan menghasilkan nilai, tim perlu memperoleh data, mentransformasikannya, bereksperimen dengannya, menghasilkan fitur, menemukan model terbaik, menerapkannya, memantaunya (penyimpangan data dan konsep) dan mengulanginya. keseluruhan proses.

Tugas-tugas ini biasanya dilakukan secara ad-hoc, menghasilkan artefak tersebar yang sulit dikelola dan menyebabkan krisis reproduksibilitas.

Seiring dengan semakin matangnya penggunaan ML, untuk membawanya ke tingkat industri yang tepat, sangat penting untuk memiliki kerangka kerja untuk mengelola model, yang mendefinisikan bidang ModelOps yang baru lahir.

Kerangka kerja seperti itu harus memungkinkan tidak hanya penyimpanan, pembuatan versi, dan pengambilan model, tetapi juga pelacakan evolusinya (hiperparameter, metrik, dll) dan alat untuk mereproduksi tidak hanya pembuatannya, tetapi juga eksperimen yang mengarah pada penciptaannya.

MLflow — Langkah pertama obat mujarab?

Masukkan MLflow, platform sumber terbuka untuk mengelola siklus hidup pembelajaran mesin, yang dibuat oleh Databricks. Ini menyediakan seperangkat alat dan konsep untuk membantu ilmuwan dan insinyur data melacak, mereproduksi, dan menerapkan model pembelajaran mesin.

Fitur utama MLflow meliputi:

  • Pelacakan eksperimen: memungkinkan Anda melacak dan membandingkan berbagai proses model pembelajaran mesin Anda, termasuk parameter, metrik, dan artefak (seperti file atau data model) yang terkait dengan setiap proses.
  • Standarisasi pengemasan model dan eksperimen: memungkinkan Anda menentukan lingkungan, dependensi, dan persyaratan serupa dengan cara yang terstandarisasi, sehingga eksperimen dapat selalu direproduksi di lingkungan yang sama dan model dapat digunakan dengan lancar di berbagai lingkungan berbeda.
  • Manajemen model: memungkinkan Anda menyimpan dan mengelola model di repositori pusat, termasuk pembuatan versi dan penandaan.
  • Penerapan model: memungkinkan Anda menerapkan model di berbagai lingkungan, seperti REST API atau container Docker.

Oleh karena itu, MLflow merupakan inisiatif yang sangat penting karena membantu meningkatkan reproduktifitas dan kolaborasi dalam proyek pembelajaran mesin.

Mempersiapkannya

Hal pertama yang perlu Anda lakukan adalah menginstal MLflow sebagai aplikasi. Anda dapat menemukan langkah-langkahnya di sini, namun pada dasarnya, ini semudah menjalankan pip install sederhana. Untuk tutorial ini, saya menggunakan Python 3.9.12 dan MLflow 2.0.0.

Setelah terinstal, Anda siap meluncurkannya. Namun sebelum itu, penting untuk memahami dua konsep, proses,eksperimendan artefak.

Penjalanan pada dasarnya adalah eksekusi beberapa bagian kode. Misalnya, saat Anda melatih suatu model, hal itu akan menghasilkan proses yang berisi hal-hal seperti hyperparameter, metrik, versi kode, dll. Proses ini disimpan ke dalam "database yang dijalankan". Lebih lanjut tentang ini segera.

Eksperimen dapat didefinisikan sebagai sekumpulan proses, atau, dengan kata lain, proses dikelompokkan dalam eksperimen.

Artefak dapat dilihat sebagai hasil eksekusi. Setelah model Anda dilatih, model tersebut mungkin menghasilkan file acar yang berisi model yang dikemas. Ini adalah contoh artefak. MLflow menyimpan artefak ke dalam repositori artefak. Informasi lebih lanjut tentang artefak juga akan segera hadir.

Sekarang setelah Anda mengetahui tentang proses, eksperimen, dan artefak, Anda dapat meluncurkan server MLflow dengan menjalankan:

mlflow server \
    --backend-store-uri sqlite:///mlflow.db \
    --default-artifact-root file:/path/to/your/artifacts \
    --host 0.0.0.0

Ini pada dasarnya mengatakan bahwa Anda meluncurkan server MLflow menggunakan database SQLite sebagai tempat penyimpanan untuk dijalankan, dan jalur tertentu dalam sistem file Anda sebagai tempat penyimpanan artefak. Server Anda akan menerima koneksi masuk dari mana saja (0.0.0.0). Ini akan tersedia pada port 5000. Sebagai saran, nanti Anda dapat menyelidiki konten database SQLite dan repositori artefak untuk pemahaman yang lebih baik.

Sekarang setelah Anda meluncurkan server MLflow, Anda dapat membuka http://localhost:5000 dan melihat sesuatu seperti ini:

Informasi lebih lanjut tentang proses dan repositori artefak dapat ditemukan di sini.

Pelacakan Aliran ML

Untuk memandu kami, bayangkan Anda sedang mengerjakan proyek ML yang berfokus pada data diabetes (tidak perlu memahami secara pasti apa yang ingin kami lakukan dengannya :)). Saat melakukan riset, Anda ingin melacak hal-hal seperti hyperparameter yang telah Anda coba, metrik yang Anda dapatkan, notebook atau potongan kode mana yang menjalankan eksperimen tersebut, kapan eksperimen tersebut dijalankan, oleh siapa, dll. Pelacakan MLflow siap membantu Anda.

Setelah melakukan instalasi, Anda dapat menjalankan potongan kode berikut:

import mlflow

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestRegressor

# loads the diabetes dataset
db = load_diabetes()
X_train, X_test, y_train, y_test = train_test_split(db.data, db.target)

# run description (just metadata)
desc = "the simplest possible example"

# connects to the Mlflow tracking server that you started above
mlflow.set_tracking_uri("http://localhost:5000")

# executes the run
with mlflow.start_run(run_name="no_artifacts_logged", description=desc) as run:
  rf = RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
  rf.fit(X_train, y_train)

Bagian ini pada dasarnya memuat kumpulan data diabetes dari Scikit-learn, membaginya menjadi kumpulan data pelatihan dan pengujian, menyetel URI pelacakan agar mengarah ke server MLflow lokal Anda, dan terakhir, memulai proses yang namanya no_artifacts_logged dan deskripsi ditentukan oleh variabel desc. Di dalam proses, Anda melatih RandomForestRegressor dengan serangkaian hyperparameter tertentu.

Setelah mengeksekusi potongan kode ini, Anda akan mendapatkan yang berikut ini di UI Anda:

Apa artinya? Ini sangat sederhana. Seperti disebutkan sebelumnya, MLflow mengelompokkan semua data eksperimen ke dalam dua konsep yang sangat intuitif: eksperimen dan berjalan. Lebih khusus lagi, proses dikelompokkan ke dalam eksperimen. Setiap proses dapat berisi, misalnya, kumpulan hyperparameter yang berbeda, seperti yang akan kita lihat selanjutnya. Selain itu, jika Anda tidak menentukan nama eksperimen, proses yang sedang Anda jalankan akan dicatat dalam eksperimen Default, yang dibuat secara otomatis oleh MLflow untuk Anda.

Jika Anda mengklik nama proses, Anda akan melihat sesuatu seperti berikut:

Ini adalah jumlah minimum yang bisa Anda peroleh, namun ini sudah memberi Anda tingkat pengorganisasian yang sangat baik.

Meskipun Anda tidak mencatat parameter, tag metrik, atau artefak apa pun, Anda dapat melihat pengguna mana yang menjalankan proses tersebut, waktu terjadinya, durasi, status, dan yang paling penting, dari mana asalnya (0_tracking_default_no_artifacts. py). Jika Anda memiliki kode yang dilacak oleh Git, Anda bahkan dapat melihat asal komit Git.

Ini adalah awal yang sangat baik, namun mengelompokkan segala sesuatu di bawah eksperimen Default jelas merupakan ide yang berantakan, jadi mari kita mulai eksperimen baru, dengan menambahkan baris berikut ke kode yang kita jalankan di atas dan jalankan lagi:

mlflow.set_experiment("mlflow_tracking_examples")

UI Anda sekarang akan menampilkan sesuatu seperti ini:

Sekarang kita sudah mengaturnya dengan lebih baik, mari kita mulai menangkap beberapa detail yang lebih berguna seperti hyperparameter dan metrik, dan mari kita juga menambahkan beberapa tag, dengan mengubah eksekusi eksekusi menjadi ini:

with mlflow.start_run(run_name="params_no_artifacts_logged") as run:

  params = {"n_estimators":100, "max_depth":6, "max_features":3}

  rf = RandomForestRegressor(**params)
  rf.fit(X_train, y_train)

  mlflow.log_params(params)
  mlflow.log_param("my_extra_param", "extra_param_value")
  mlflow.log_metric("my_metric", 0.8)
  mlflow.set_tag("my_tag", "my_tag_value")

Setelah itu, inilah yang akan Anda lihat di dalam proses:

Sekarang, selain metadata sebelumnya seperti sumber, kami juga menangkap parameter pelatihan, metrik yang dihasilkan, dan juga beberapa tag untuk referensi di masa mendatang. Ini merupakan kemajuan besar. Hanya dengan menggunakan apa yang telah kita lihat sejauh ini, Anda sudah dapat membuat eksperimen, mencoba berbagai hyperparameter, melacak hasilnya, dan mengetahui dari notebook atau modul mana hyperparameter tersebut berasal. Ini sudah jauh lebih baik daripada mempertahankan Wiki.

Tapi kami bisa berbuat lebih baik. Kita juga dapat menyimpan model sebenarnya yang dihasilkan oleh proses tersebut, dengan melakukan ini:

with mlflow.start_run(run_name="logged_artifacts") as run:
  params = {"n_estimators":100, "max_depth":6, "max_features":3}

  rf = RandomForestRegressor(**params)
  rf.fit(X_train, y_train)

  mlflow.log_params(params)
  mlflow.sklearn.log_model(
      sk_model=rf,
      artifact_path="random_forest_regressor"
  )

Potongan ini mencatat model terlatih dalam artefak bernama random_forest_regressor, dan akan terlihat seperti ini di UI Anda:

Ini merupakan tingkat kenyamanan yang sangat baik. Kini Anda tidak hanya memiliki model terlatih yang dikelola untuk Anda (file model.pkl), namun Anda juga memiliki dependensinya yang secara otomatis ditangkap dalam tiga bentuk berbeda, yaitu conda.yaml, python_env.yaml dan requirements.txt(kita akan membicarakan tentang MLmodel nanti). Selain itu, Anda bahkan memiliki dua contoh salin dan tempel yang tersedia untuk Anda, satu menggunakan Spark dan yang lainnya menggunakan Pandas.

Kami juga dapat mencatat contoh masukan di samping artefak sehingga, misalnya, siapa pun dapat menguji penerapan:

with mlflow.start_run(run_name="logged_artifacts_with_signature") as run:
  params = {"n_estimators":100, "max_depth":6, "max_features":3}
  
  rf = RandomForestRegressor(**params)
  rf.fit(X_train, y_train)

  signature = infer_signature(X_train, rf.predict(X_test))
  input_example = X_train[0]

  mlflow.log_params(params)
  mlflow.sklearn.log_model(
      sk_model=rf,
      artifact_path="random_forest_regressor",
      input_example=input_example,
      signature=signature
  )

Setelah itu, inilah yang akan Anda dapatkan:

File MLmodel adalah deskripsi model. Ini mengelompokkan nama artefak, konfigurasi ketergantungan, ragam (lebih lanjut tentang ini nanti) dan tanda tangan model, yaitu apa yang didapat sebagai masukan dan dihasilkan sebagai keluaran.

Namun menangkap setiap metrik dan hyperparameter bisa sangat membosankan dan rawan kesalahan. Selain itu, menghasilkan tanda tangan model, meskipun sederhana, merupakan kode boiler-plate yang dapat ditingkatkan, sama seperti secara eksplisit memberi tahu MLflow untuk mencatat model. Jadi mari kita berbuat lebih baik. Coba ganti kode sebelumnya dengan ini:

import mlflow

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestRegressor

db = load_diabetes()
X_train, X_test, y_train, y_test = train_test_split(db.data, db.target)

# connect to mlflow
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("mlflow_tracking_examples")

# this is the magical stuff
mlflow.autolog(log_input_examples=True, log_model_signatures=True)

# train the model
rf = RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
rf.fit(X_train, y_train)

Perhatikan bahwa impor, pemisahan kumpulan data, koneksi ke URI pelacakan, penyiapan nama eksperimen, dan pelatihan model tetap sama. Satu-satunya perbedaan adalah baris yang dimulai dengan mlflow.autolog. Dan tambahan sederhana itu memberi kita ini:

UI Anda sekarang tidak hanya menampilkan tanda tangan metode dan beberapa contoh masukan, tetapi juga 17 parameter, 5 metrik, dan 2 tag (berisi detail tentang estimator). Itu semua adalah parameter yang digunakan Scikit-learn saat melatih model, dan juga metrik yang dihasilkan. Semua itu dengan satu baris kode. Jika itu tidak membantu, saya tidak tahu apa lagi yang bisa membantu.

Namun satu hal, Autolog hanya mendukung beberapa perpustakaan, yang biasanya dicurigai di dunia ML. Anda dapat menemukan daftarnya di sini.

Sekarang, satu hal yang mungkin Anda perhatikan adalah nama acak yang diterima proses Anda:

Milik saya diberi nama treasured-snipe-405, yang meskipun lucu, tidak terlalu membantu. Hal baiknya adalah autolog berfungsi dengan proses reguler seperti di bawah ini, jadi kami masih baik-baik saja.

mlflow.autolog(log_model_signatures=True, log_input_examples=True)

with mlflow.start_run(run_name="autolog_with_named_run") as run:
  rf = RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
  rf.fit(X_train, y_train)

Namun dalam kehidupan nyata, kita jarang terpaku pada hyperparameter default. Sebaliknya kami mencoba opsi yang berbeda. Namun, jika kami harus membuat satu proses untuk setiap kumpulan hyperparameter, manfaat pengelompokan proses dalam eksperimen akan segera hilang. MLflow menyelesaikannya dengan konsep proses bersarang. Mari kita ubah kodenya menjadi seperti ini:

with mlflow.start_run(run_name="main_run_for_nested") as run:
  for estimators in range(20, 100, 20):
    with mlflow.start_run(run_name=f"nested_{estimators}_estimators", nested=True) as nested:
      rf = RandomForestRegressor(n_estimators=estimators, max_depth=6, max_features=3)
      rf.fit(X_train, y_train)

Proses utama disebut main_run_for_nested. Di dalamnya, kita membuat proses baru dengan parameter bersarang yang disetel ke True,dan di dalam proses bertingkat, kita mengubah jumlah estimator. Inilah yang kami dapatkan:

Seperti yang bisa kita lihat, kita memiliki empat proses yang disarangkan ke dalam proses utama. Lebih penting lagi, setiap proses yang disarangkan menghasilkan kumpulan hyperparameter, metrik, tag, dan artefaknya sendiri (yang terakhir agak sia-sia karena kami hanya tertarik pada hasil terbaik). Menyelidiki proses bersarang nested_80_estimators, kita mendapatkan:

Meskipun ini berguna, kami tidak mengoptimalkan hyperparameter secara manual. Sebagai gantinya kami menggunakan hal-hal seperti pencarian grid, pencarian acak atau Hyperopt.

Meskipun kita tahu bahwa pencarian grid jauh lebih buruk daripada pencarian acak dan khususnya Hyperopt, sebagai contoh, kita akan menggunakannya untuk melihat bagaimana MLflow menangkap parameter berbeda yang digunakan dalam setiap proses, dengan menjalankan perintah berikut:

params = {
  "n_estimators": [33, 66, 200],
  "max_depth": [2, 4, 6],
  "max_features": [3, 4, 5]
}

rf = RandomForestRegressor()
searcher = GridSearchCV(estimator=rf, param_grid=params)

with mlflow.start_run(run_name="autolog_with_grid_search") as run:
  searcher.fit(X_train, y_train)

Potongan kode ini persis sama dengan yang Anda gunakan dalam kehidupan nyata, satu-satunya perbedaan adalah kode tersebut dieksekusi dalam proses. Hasil darinya adalah:

Kita dapat melihat bahwa model ini menghasilkan proses yang disarangkan dengan cara yang sama, namun, artefak model akhir hanya dikaitkan dengan proses utama (yang jauh lebih efisien), bersama dengan skor terbaik, 0,487.

Fitur lain yang disediakan MLflow adalah memungkinkan perbandingan proses yang berbeda berdasarkan parameternya. Jika Anda memilih semua berjalan seperti ini:

lalu klik tombol bandingkan, Anda mendapatkan sesuatu seperti di bawah ini, yang bagus untuk analisis lebih lanjut:

Proyek MLflow

Apa yang baru saja kami lihat adalah peningkatan besar dibandingkan cara kami melakukan ModelOps sebelumnya. Kini kami dapat melacak berbagai eksperimen, mengidentifikasi kapan eksperimen tersebut dijalankan, oleh siapa, bagian kode mana yang berisi logika, hyperparameter yang digunakan, metrik yang dihasilkan, dll.

Tapi masih ada sesuatu yang hilang. Bagaimana dengan, misalnya, versi kerangka kerja? Bagaimana jika versi yang berbeda memberikan hasil yang berbeda? Bagaimana dengan waktu eksekusi, yang seharusnya memakan waktu beberapa menit, namun kini malah memakan waktu beberapa jam? Dengan kata lain, dengan apa yang telah kita lihat, kita dapat melacak eksperimen tersebut, namun kita tidak dapat mereplikasi eksperimen tersebut dengan aman, karena kita tidak mengetahui apa pun tentang lingkungan tempat eksperimen tersebut dijalankan.

Jangan takut, Proyek MLflow akan datang untuk menyelamatkan. Menurut dokumentasi, Proyek MLflow adalah format untuk mengemas kode ilmu data dengan cara yang dapat digunakan kembali dan direproduksi, terutama berdasarkan konvensi. Selain itu, komponen Projects menyertakan API dan alat baris perintah untuk menjalankan proyek, sehingga memungkinkan untuk menyatukan proyek ke dalam alur kerja. Dengan kata lain, ini adalah cara bagi Anda untuk mengemas seluruh lingkungan sehingga eksperimen dan artefak lainnya dapat direproduksi sepenuhnya. Anda dapat menentukan, di samping kode Anda, semua dependensi beserta versinya, parameternya, perintah yang dijalankan, dll.

MLflow memberikan contoh proyek di https://github.com/mlflow/mlflow-example. Setelah mengakses URL tersebut, jika Anda membuka file MLproject, Anda akan menemukan yang berikut:

name: tutorial
conda_env: conda.yaml
entry_points:
  main:
    parameters:
      alpha: {type: float, default: 0.5}
      l1_ratio: {type: float, default: 0.1}
    command: "python train.py {alpha} {l1_ratio}"

Artinya, nama proyeknya adalah tutorial, dependensinya ditentukan di conda.yaml, dan dijalankan dengan memanggil python train.py menggunakan parameter alpha dan l1_ratio.

Seperti disebutkan di atas, Anda memiliki dua cara untuk menjalankannya: melalui API dan melalui baris perintah.

Untuk menjalankannya melalui API, Anda dapat menggunakan MLflow run dengan melakukan seperti di bawah ini:

import mlflow.pyfunc
import os

os.environ["MLFLOW_CONDA_HOME"] = "/path/to/your/conda/home"

mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("mlflow_projects_example")

parameters = {'alpha': 0.5, 'l1_ratio': 0.01}
ml_project_uri ="https://github.com/mlflow/mlflow-example"

res_sub = mlflow.run(ml_project_uri, parameters=parameters, run_name="test_run")

print("status = ", res_sub.get_status())
print("run_id = ", res_sub.run_id)

Potongan kode tersebut akan mengambil proyek dari Github ke lokasi sementara, membuat lingkungan Conda dan menginstal dependensi jika belum ada (jika tidak maka akan digunakan kembali), dan menjalankan perintah. Setelah itu, eksperimen baru bernama mlflow_projects_example akan dibuat, dan setelah mengeklik proses bernama test_run di dalamnya, UI Anda akan menampilkan sesuatu seperti di bawah ini:

Anda dapat melihat bahwa kali ini, ini tidak hanya melacak nomor komit Git, tetapi juga menunjukkan perintah sebenarnya yang digunakan untuk menjalankan proses. Selain itu, Anda juga dapat melihat nilai yang Anda tentukan untuk parameter alpha dan l1_ratio dan, jika Anda mengeklik Sumber, Anda akan diarahkan ke Repositori proyek Github. Cukup mudah, bukan?

Anda dapat membuatnya lebih sederhana dengan menggunakan baris perintah:

mlflow run https://github.com/mlflow/mlflow-example -P alpha=0.5 -P l1_ratio=0.01

Model MLflow

Jadi ini sudah sangat bagus. Anda tidak hanya dapat melacak eksperimen Anda sepenuhnya, yaitu parameter, metrik, tag, jumlah penerapan, sumber, artefak, dll, tetapi juga mereproduksi setiap langkah yang diambil untuk menghasilkan eksperimen, termasuk lingkungan.

Namun masih ada beberapa pertanyaan. Misalnya, bagaimana dengan rasa pyfunc itu? Bagaimana saya bisa mengeksekusi model yang disimpan sebagai artefak di dalam proses? Kita telah melihat bahwa kita dapat mendaftarkan model yang dihasilkan oleh beberapa kerangka kerja umum, namun bagaimana jika saya menggunakan kerangka lain? Bisakah saya menggunakan MLflow untuk menyajikan model untuk saya?

Untuk menyelesaikan tugas tersebut, kita memerlukan cara standar untuk mengemas model. Kita memerlukan standar untuk menginformasikan lokasi file model, ketergantungan apa yang dimilikinya, cara pembuatannya, apa yang diperlukan sebagai masukan dan apa yang dihasilkan sebagai keluaran, dll. Dengan kata lain, kita perlu menstandarkan pengemasan model dengan cara yang sama seperti kita menstandardisasi eksekusi eksperimen dengan Proyek MLflow. Kita dapat melakukannya dengan menggunakan Model MLflow.

Menurut dokumentasi, Model MLflow adalah format standar untuk mengemas model pembelajaran mesin yang dapat digunakan dalam berbagai alat hilir — misalnya, penyajian real-time melalui REST API atau inferensi batch di Apache Spark. Formatnya menentukan konvensi yang memungkinkan Anda menyimpan model dalam “rasa” berbeda yang dapat dipahami oleh berbagai alat hilir. Jadi mari kita periksa format ini:

File di atas memberitahukan jalur ke artefak di dalam proses, yaitu model, nama yang sama dengan direktori tempat file MLmodel berada; rasanya, sklearn dan python_function (yang akan segera Anda lihat lebih lanjut); nama file model, model.pkl; file dependensi, conda.yaml dan python_env.yaml; format serialisasi, cloudpickle; versi Python, framework dan MLflow, masing-masing 3.9.12, 1.2.0 dan 2.0.0; contoh masukan beserta formatnya; metadata lain seperti waktu pembuatan.

Itu sudah cukup informasi bagi kami untuk mengunduh dan menyajikan modelnya. Misalnya, dengan menggunakan kembali pelatihan dan pengujian yang sama dari kumpulan data diabetes, kita dapat melakukan:

mlflow.autolog(log_model_signatures=True, log_input_examples=True)

with mlflow.start_run(run_name="autolog_with_named_run") as run:
  rf = RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
  rf.fit(X_train, y_train)

  run_id = run.info.run_id

# get model path from run id 
#(run_id can also be retrieved using the API or the UI)
model_path = f"runs:/{run_id}/model"
print(f"Loading model from: {model_path}")

# load using sklearn flavor
loaded_model = mlflow.sklearn.load_model(model_path)

print("Showing predictions")
print(loaded_model.predict(X_test))

Potongan kode ini menghasilkan proses, mengambil id proses darinya dan kemudian memuat model yang dihasilkan dari proses tersebut (menggunakan jalur runs:/run_id/model) kembali ke aplikasi sebagai model Scikit-learn (mlflow.sklearn.load_model).

Ada tiga hal yang perlu mendapat perhatian. Pertama, kita dapat mengeksekusi model langsung dari proses, yang sangat memudahkan, misalnya, untuk pengujian lebih lanjut. Kedua, kami tidak perlu menentukan apa pun tentang cara memuat model, berkat standarisasi yang disediakan oleh MLflow Models. Ketiga, kami secara eksplisit menginformasikan bahwa ini adalah jenis model Scikit-learn.

Poin terakhir ini agak memprihatinkan. Bayangkan Anda sedang bereksperimen dengan berbagai jenis kerangka kerja, dan Anda memiliki sepotong kode yang mengambil model dan menerapkannya di suatu tempat. Percobaan pertama Anda memang menggunakan Scikit-learn, tetapi selanjutnya Anda mencoba PyTorch lalu Tensorflow. Untuk mengakomodasi skenario ini, Anda perlu memberikan pernyataan if-else tambahan pada kode penerapan Anda (yang akan berubah untuk setiap kerangka kerja baru, termasuk aplikasi khusus Anda), atau lebih buruk lagi, Anda harus terus mengubahnya.

Selain itu, semuanya sudah cukup abstrak. Deskripsi model, mekanisme untuk memuat dan memanggilnya, dll. Selanjutnya, pada akhirnya memanggil model tidak berbeda dengan memanggil suatu fungsi, dalam hal ini fungsi Python. Jadi dalam hal ini, kita dapat melakukan ini:

loaded_model = mlflow.pyfunc.load_model(model_path)

Dengan kata lain, alih-alih mengatakan bahwa ini adalah model dengan cita rasa Scikit, kami sekarang mengatakan bahwa ini adalah fungsi Python, atau pyfunc, dan itu mengabstraksi semuanya. Jika sekarang Anda mengubah cita rasa model Anda, katakanlah, dari Scikit ke Pytorch, Anda dapat menyebutnya sama saja. Sangat nyaman, bukan?

Sekarang, bagaimana jika Anda ingin menggunakan framework yang tidak didukung? Atau mungkin Anda ingin menambahkan logika tambahan pada prediksi? Dalam kasus tersebut, Anda bisa membuat model khusus dengan memperluas mlflow.pyfunc.PythonModel. Kelas ini pada dasarnya memaparkan 2 metode: load_context dan predict. Yang pertama dapat digunakan untuk memuat artefak, sedangkan yang kedua cukup jelas.

Dengan mengambil dokumentasi contoh dari MLflow Model Registry (berikutnya), mari daftarkan model menggunakan perpustakaan analisis sentimen VADER lalu muat model tersebut untuk menganalisis beberapa pernyataan:

import mlflow
import pandas as pd
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer

# create an MLflow-compliant model by extending PythonModel
class TextAnalyzerModel(mlflow.pyfunc.PythonModel):

  def __init__(self):
    super().__init__()
    self._analyser = SentimentIntensityAnalyzer()

  def _preprocess(self):
    pass

  def _score(self, txt):
    prediction_scores = self._analyser.polarity_scores(txt)
    return prediction_scores

  def predict(self, context, model_input):
    model_output = model_input.apply(lambda col: self._score(col))
    return model_output

# connect to mlflow and set experiment
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("sentiment_analysis")

# enable autolog
mlflow.autolog(log_model_signatures=True, log_input_examples=True)

model_artifact_path = "vader_model"
model = TextAnalyzerModel()

# execute run
with mlflow.start_run(run_name="Vader Sentiment Analysis") as run:
  mlflow.log_param("algorithm", "VADER")
  mlflow.pyfunc.log_model(artifact_path=model_artifact_path, 
                          python_model=model)
  run_id = run.info.run_id

Setelah mengeksekusi potongan kode ini, Anda akan melihat eksperimen baru di UI yang disebut analisis_sentimen. Jika Anda memeriksa konten Analisis Sentimen Vader di dalamnya, Anda akan melihat sesuatu seperti:

Jika Anda memeriksa persyaratannya, Anda akan melihat bahwa pustaka vaderSentiment telah diambil, seperti yang diharapkan:

Sekarang kita dapat memuat model dan menganalisis beberapa pertanyaan:

model_uri = f"runs:/{run_id}/vader_model"

loaded_model = mlflow.pyfunc.load_model(model_uri)

queries = ["This is a bad movie. You don't want to see it! :-)",
           "Ricky Gervais is smart, witty, and creative!!!!!! :D",
           "LOL, this guy fell off a chair while sleeping and snoring in a meeting",
           "Men shoots himself while trying to steal a dog, OMG",
           "Yay!! Another good phone interview. I nailed it!!",
           "This is INSANE! I can't believe it. How could you do such a horrible thing?"]

for q in queries:
  m_input = pd.DataFrame([q])
  scores = loaded_model.predict(m_input)
  print(f"<{q}> -- {str(scores[0])}")

Registri Model MLflow

Pada titik ini kami memiliki sarana untuk melacak eksperimen dengan hyperparameter dan metrik (Eksperimen MLflow), untuk mereproduksi eksperimen dengan tepat, termasuk lingkungan (Proyek MLflow), dan untuk mengemas model sehingga dapat digunakan dengan lancar (Model MLflow). Namun bagaimana dengan pengelolaan model itu sendiri? Misalnya, jika Anda terus mengembangkan suatu model, Anda harus mengetahui versi mana yang sedang diproduksi. Ketika Anda memiliki versi baru yang siap untuk diuji, Anda perlu mempromosikannya. Jika versi tertentu telah dinonaktifkan, Anda memerlukan mekanisme untuk mencerminkan fakta ini.

Meskipun dapat memuat model dari proses sangat bagus untuk pengujian, kami memerlukan registri khusus untuk membawanya ke tingkat produksi, dan inilah yang disediakan oleh MLflow Model Registry. Menurut dokumentasi, Komponen Registri Model MLflow adalah penyimpanan model terpusat, kumpulan API, dan UI, untuk secara kolaboratif mengelola siklus hidup penuh Model MLflow. Ini menyediakan silsilah model (yang mana eksperimen dan pengoperasian MLflow menghasilkan model), pembuatan versi model, transisi tahapan (misalnya dari pementasan ke produksi), dan anotasi. Dengan kata lain, setelah artefak model diproduksi, registri model akan menyediakan sarana untuk menyimpannya, mengembangkannya, dan mengaksesnya melalui API, klien, dan UI.

Misalnya, melanjutkan contoh kumpulan data diabetes, ubah proses menjadi seperti ini:

with mlflow.start_run(run_name="log_and_register") as run:
  rf = RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
  rf.fit(X_train, y_train)

  mlflow.sklearn.log_model(
    sk_model=rf,
    artifact_path="sklearn-model",
    registered_model_name="my_registered_model_1" # the magic is here
  )

Kita sudah melihat metode log_model, namun, dengan memberikan nama model, metode tersebut akan dimasukkan ke dalam registri model. Di UI, pada tab Model, Anda akan melihat:

Ini menunjukkan nama model yang Anda berikan saat pendaftaran model, versi terbarunya, dan juga informasi tentang staging, yang akan kita lihat selanjutnya. Jika Anda mengeklik Versi 1, Anda akan melihat ini:

Hal ini menunjukkan dua hal menarik. Yang pertama adalah informasi tentang tahap, yang saat ini Tidak Ada. Yang kedua adalah Source Run. Seperti yang Anda lihat, ini adalah nama proses yang sama yang menghasilkan model ini, yang menyediakan silsilah lengkap, yaitu dari model yang Anda dapatkan hingga proses tersebut, dan dari proses tersebut Anda mendapatkan kode sumbernya.

Untuk menggunakan model versi ini, langsung dari registri model, Anda hanya perlu mengubah jalurnya, yaitu mengubah sumber dari runs menjadi models :

model_name = "my_registered_model_1"
model_version = 1
model_path = f"models:/{model_name}/{model_version}"

model = mlflow.pyfunc.load_model(model_version_uri)

Tapi ini tidak jauh berbeda dengan memuat langsung dari proses, karena kita masih perlu mengetahui versinya. Dalam lingkungan produksi, mengubah versi setiap kali Anda ingin menerapkan model baru sama dengan mengubah nama cabang setiap kali Anda menerapkan proyek reguler dari Git, bukan hanya menerapkan cabang master.

Memiliki tahapan berguna untuk mengatasi masalah itu. Jika Anda memperluasnya, Anda akan melihat:

Itu memberi Anda kemampuan manajemen yang jauh lebih baik. Pertama, Anda dapat membedakan dengan jelas versi mana yang sedang dalam tahap produksi (Production), mana yang sedang diuji (Staging), mana yang sudah dinonaktifkan (Diarsipkan ) dan yang baru saja dibuat (Tidak ada). Dengan cara ini, penerapan produksi Anda cukup memuat ulang model dalam tahap produksi, tanpa perlu mengubah kode. Dan melakukan itu sangatlah mudah:

model_name = "my_registered_model_1"
model_path = f"models:/{model_name}/production"

model = mlflow.pyfunc.load_model(model_version_uri)

Sekarang, Anda dapat menyimpan versi yang berbeda, dengan lingkungan berbeda yang memuat versi yang sesuai. Dan modelnya bahkan tidak perlu menggunakan kerangka kerja yang sama, karena mereka dipanggil melalui pyfunc.

Dengan bagian terakhir ini, kita memiliki lingkungan ModelOps yang lengkap, menyediakan pelacakan, reproduksibilitas, standardisasi, garis keturunan, sentralisasi dan evolusi. Ceri di atas kue hadir dengan kemampuan terakhir: menyajikan.

Untuk "melayani model melalui REST API", langsung dari registri model, pada baris perintah, jalankan perintah berikut:

mlflow models serve --env-manager conda --model-uri models:/my_registered_model_1/production --port 5001

Ini akan membuat lingkungan conda untuk Anda, dengan dependensi yang disediakan oleh MLflow Models, dan memulai REST API yang melayani model secara lokal pada port 5001. Jika Anda tidak ingin lingkungan conda dibuat, Anda dapat menentukan — env-manager local (Anda dapat mempelajari lebih lanjut di sini).

Setelah itu, Anda dapat mengambil contoh masukan, yang juga disediakan oleh Model MLflow, seperti yang telah kita lihat sebelumnya, dan menjalankan sesuatu seperti:

import requests
import json

query_json = {
  "inputs": [
    [
      -0.09269547780327612,
      -0.044641636506989144,
      0.028284032228378497,
      -0.015998975220305175,
      0.03695772020942014,
      0.02499059336410222,
      0.05600337505832251,
      -0.03949338287409329,
      -0.005142189801713891,
      -0.0010776975004659671
    ]
  ]
}
query = json.dumps(query_json)

headers = {'Content-Type': 'application/json'}
request_uri = 'http://127.0.0.1:5001/invocations'

response = requests.post(request_uri, data=query, headers=headers)

print(response.content)

Dan itu saja! ModelOps sumber terbuka yang canggih. Semoga postingan ini membuat Anda bersemangat :)

Referensi

Referensi mutlak untuk MLflow tentu saja adalah dokumentasinya. Selain itu, saya telah menggunakan contoh dari kumpulan data diabetes Scikit-learn. Akhirnya, saya belajar banyak dari Ben Wilson, Noah Gift, Alfredo Deza dan Chip Huyen, jadi tautkan juga buku-buku mereka (yang sangat bagus) di bawah ini.