Tutorial Metaflow: Framework MLOps dari Netflix untuk Data Science

# Tutorial Metaflow: Framework MLOps dari Netflix untuk Data Science Metaflow adalah framework open-source yang dikembangkan oleh Netflix untuk membangun dan mengelola proyek data science secara efis...

By Ruby Abdullah · · tutorial
MetaflowMLOpsNetflixPipelinePython

Tutorial Metaflow: Framework MLOps dari Netflix untuk Data Science

Metaflow adalah framework open-source yang dikembangkan oleh Netflix untuk membangun dan mengelola proyek data science secara efisien. Framework ini memungkinkan data scientist untuk fokus pada pemodelan tanpa harus khawatir tentang infrastruktur, orkestrasi, dan deployment. Dalam tutorial ini, kita akan mempelajari cara menggunakan Metaflow dari dasar hingga fitur-fitur lanjutan.

Mengapa Metaflow?

Dalam dunia data science dan machine learning, salah satu tantangan terbesar bukan membangun model, melainkan mengelola seluruh lifecycle dari eksperimen hingga produksi. Metaflow hadir untuk menyelesaikan masalah ini dengan pendekatan yang pragmatis:

  • Human-centric API: Dirancang agar mudah digunakan oleh data scientist, bukan hanya ML engineers
  • Versioning otomatis: Setiap eksperimen secara otomatis di-track dan bisa direproduksi
  • Skalabilitas transparan: Beralih dari laptop ke cloud tanpa mengubah kode
  • Dependency management: Mengelola environment Python secara otomatis
  • Integration: Bekerja dengan AWS, Azure, GCP, dan Kubernetes

Instalasi

Instalasi Dasar

pip install metaflow

Instalasi dengan Dukungan AWS

pip install metaflow[aws]

Verifikasi Instalasi

import metaflow

print(metaflow.version)

Konfigurasi Awal

Setelah instalasi, jalankan konfigurasi:

metaflow configure show

Untuk konfigurasi AWS S3 sebagai datastore:

metaflow configure aws

Konsep Dasar: Flow dan Step

Metaflow menggunakan konsep Flow dan Step untuk mengorganisasi pipeline data science. Sebuah Flow adalah DAG (Directed Acyclic Graph) yang terdiri dari beberapa Step.

Flow Pertama

from metaflow import FlowSpec, step

class HelloFlow(FlowSpec):

@step

def start(self):

print("Memulai flow pertama!")

self.message = "Hello from Metaflow"

self.next(self.end)

@step

def end(self):

print(f"Pesan: {self.message}")

print("Flow selesai!")

if name == 'main':

HelloFlow()

Jalankan flow:

python helloflow.py run

Memahami Artifact

Setiap variabel yang disimpan sebagai self.x dalam sebuah step akan menjadi artifact yang secara otomatis di-versioning dan bisa diakses kembali.

from metaflow import FlowSpec, step

class ArtifactFlow(FlowSpec):

@step

def start(self):

self.data = [1, 2, 3, 4, 5]

self.modelname = "randomforest"

self.next(self.process)

@step

def process(self):

self.result = sum(self.data) 2

print(f"Model: {self.modelname}")

print(f"Hasil: {self.result}")

self.next(self.end)

@step

def end(self):

print(f"Final result: {self.result}")

if name == 'main':

ArtifactFlow()

Branching dan Join

Metaflow mendukung eksekusi paralel melalui branching. Ini sangat berguna untuk membandingkan beberapa model secara bersamaan.

Contoh Branching

from metaflow import FlowSpec, step

class BranchFlow(FlowSpec):

@step

def start(self):

self.rawdata = list(range(100))

self.next(self.trainrf, self.trainxgb)

@step

def trainrf(self):

from sklearn.ensemble import RandomForestClassifier

from sklearn.datasets import makeclassification

X, y = makeclassification(nsamples=1000, nfeatures=20, randomstate=42)

model = RandomForestClassifier(nestimators=100, randomstate=42)

model.fit(X, y)

self.accuracy = model.score(X, y)

self.modeltype = "RandomForest"

print(f"RF Accuracy: {self.accuracy:.4f}")

self.next(self.join)

@step

def trainxgb(self):

from sklearn.ensemble import GradientBoostingClassifier

from sklearn.datasets import makeclassification

X, y = makeclassification(nsamples=1000, nfeatures=20, randomstate=42)

model = GradientBoostingClassifier(nestimators=100, randomstate=42)

model.fit(X, y)

self.accuracy = model.score(X, y)

self.modeltype = "GradientBoosting"

print(f"XGB Accuracy: {self.accuracy:.4f}")

self.next(self.join)

@step

def join(self, inputs):

best = max(inputs, key=lambda x: x.accuracy)

self.bestmodel = best.modeltype

self.bestaccuracy = best.accuracy

print(f"Model terbaik: {self.bestmodel} ({self.bestaccuracy:.4f})")

self.next(self.end)

@step

def end(self):

print(f"Pipeline selesai. Model terpilih: {self.bestmodel}")

if name == 'main':

BranchFlow()

Parameters dan Konfigurasi

Metaflow mendukung parameter yang bisa diatur saat runtime tanpa mengubah kode.

from metaflow import FlowSpec, step, Parameter

class ParamFlow(FlowSpec):

learningrate = Parameter(

'learningrate',

help='Learning rate untuk training',

default=0.01

)

nestimators = Parameter(

'nestimators',

help='Jumlah estimator',

default=100,

type=int

)

datapath = Parameter(

'datapath',

help='Path ke dataset',

default='data/train.csv'

)

@step

def start(self):

print(f"Learning Rate: {self.learningrate}")

print(f"N Estimators: {self.nestimators}")

print(f"Data Path: {self.datapath}")

self.next(self.train)

@step

def train(self):

from sklearn.ensemble import GradientBoostingClassifier

from sklearn.datasets import makeclassification

X, y = makeclassification(nsamples=1000, randomstate=42)

model = GradientBoostingClassifier(

learningrate=self.learningrate,

nestimators=self.nestimators,

randomstate=42

)

model.fit(X, y)

self.score = model.score(X, y)

print(f"Score: {self.score:.4f}")

self.next(self.end)

@step

def end(self):

print(f"Training selesai dengan score: {self.score:.4f}")

if name == 'main':

ParamFlow()

Jalankan dengan parameter kustom:

python paramflow.py run --learningrate 0.1 --nestimators 200

Foreach: Parallel Processing

foreach memungkinkan Anda menjalankan step yang sama untuk banyak item secara paralel.
from metaflow import FlowSpec, step

class ForeachFlow(FlowSpec):

@step

def start(self):

self.datasets = ['dataseta', 'datasetb', 'datasetc']

self.next(self.process, foreach='datasets')

@step

def process(self):

datasetname = self.input

print(f"Memproses: {datasetname}")

import random

random.seed(hash(datasetname) % 232)

self.accuracy = random.uniform(0.7, 0.99)

self.dataset = datasetname

print(f"{datasetname}: accuracy = {self.accuracy:.4f}")

self.next(self.join)

@step

def join(self, inputs):

self.results = {

inp.dataset: inp.accuracy for inp in inputs

}

best = max(inputs, key=lambda x: x.accuracy)

self.bestdataset = best.dataset

self.bestaccuracy = best.accuracy

print("\nHasil semua dataset:")

for name, acc in self.results.items():

print(f" {name}: {acc:.4f}")

print(f"\nTerbaik: {self.bestdataset} ({self.bestaccuracy:.4f})")

self.next(self.end)

@step

def end(self):

print("Pipeline foreach selesai!")

if name == 'main':

ForeachFlow()

Fitur Lanjutan

Decorator @conda dan @pypi

Kelola dependency per step menggunakan decorator:

from metaflow import FlowSpec, step, condabase, pypi

@condabase(python='3.10')

class MLFlow(FlowSpec):

@pypi(packages={'scikit-learn': '1.4.0', 'pandas': '2.2.0'})

@step

def start(self):

import sklearn

import pandas as pd

print(f"sklearn: {sklearn.version}")

print(f"pandas: {pd.version}")

self.next(self.end)

@step

def end(self):

print("Selesai!")

if name == 'main':

MLFlow()

Decorator @resources

Atur resource komputasi untuk setiap step:

from metaflow import FlowSpec, step, resources

class GPUFlow(FlowSpec):

@resources(cpu=4, memory=8000)

@step

def start(self):

self.data = list(range(1000000))

self.next(self.train)

@resources(cpu=8, memory=16000, gpu=1)

@step

def train(self):

print("Training dengan GPU...")

self.modeltrained = True

self.next(self.end)

@step

def end(self):

print(f"Model trained: {self.modeltrained}")

if name == 'main':

GPUFlow()

Retry dan Error Handling

from metaflow import FlowSpec, step, retry, catch

class RobustFlow(FlowSpec):

@retry(times=3)

@step

def start(self):

self.data = self.loaddata()

self.next(self.train)

def loaddata(self):

import random

if random.random() < 0.3:

raise Exception("Koneksi terputus!")

return [1, 2, 3, 4, 5]

@catch(var='trainerror')

@step

def train(self):

self.result = sum(self.data) 2

self.next(self.end)

@step

def end(self):

if hasattr(self, 'trainerror') and self.trainerror:

print(f"Training gagal: {self.trainerror}")

else:

print(f"Hasil: {self.result}")

if name == 'main':

RobustFlow()

Client API: Mengakses Hasil Eksperimen

Salah satu fitur paling powerful dari Metaflow adalah Client API untuk mengakses hasil eksperimen sebelumnya:

from metaflow import Flow, Run, Step

flow = Flow('BranchFlow')

for run in flow.runs():

print(f"Run ID: {run.id}")

print(f" Status: {run.successful}")

print(f" Waktu: {run.createdat}")

if run.successful:

endstep = Step(f'BranchFlow/{run.id}/end')

for task in endstep.tasks():

print(f" Best Model: {task['bestmodel'].data}")

print(f" Best Accuracy: {task['bestaccuracy'].data}")

print()

Mengakses Run Terakhir

from metaflow import Flow

run = Flow('BranchFlow').latestsuccessfulrun

print(f"Run terakhir: {run.id}")

print(f"Best model: {run.data.bestmodel}")

print(f"Best accuracy: {run.data.bestaccuracy}")

Proyek Lengkap: Pipeline ML End-to-End

Berikut contoh pipeline ML lengkap menggunakan Metaflow:

from metaflow import FlowSpec, step, Parameter, retry, catch

import json

class MLPipelineFlow(FlowSpec):

testsize = Parameter('testsize', default=0.2, type=float)

randomstate = Parameter('randomstate', default=42, type=int)

@step

def start(self):

print("=== ML Pipeline Dimulai ===")

self.next(self.loaddata)

@retry(times=2)

@step

def loaddata(self):

from sklearn.datasets import loadbreastcancer

import pandas as pd

data = loadbreastcancer()

self.df = pd.DataFrame(data.data, columns=data.featurenames)

self.target = list(data.target)

self.featurenames = list(data.featurenames)

print(f"Dataset loaded: {self.df.shape}")

print(f"Features: {len(self.featurenames)}")

self.next(self.preprocess)

@step

def preprocess(self):

from sklearn.modelselection import traintestsplit

from sklearn.preprocessing import StandardScaler

import numpy as np

X = self.df.values

y = np.array(self.target)

Xtrain, Xtest, ytrain, ytest = traintestsplit(

X, y, testsize=self.testsize, randomstate=self.randomstate

)

scaler = StandardScaler()

self.Xtrain = scaler.fittransform(Xtrain).tolist()

self.Xtest = scaler.transform(Xtest).tolist()

self.ytrain = ytrain.tolist()

self.ytest = ytest.tolist()

self.scalermean = scaler.mean.tolist()

self.scalerscale = scaler.scale.tolist()

print(f"Train: {len(self.Xtrain)}, Test: {len(self.Xtest)}")

self.next(self.trainlr, self.trainrf, self.trainsvm)

@step

def trainlr(self):

from sklearn.linearmodel import LogisticRegression

from sklearn.metrics import accuracyscore, f1score

import numpy as np

Xtrain = np.array(self.Xtrain)

ytrain = np.array(self.ytrain)

Xtest = np.array(self.Xtest)

ytest = np.array(self.ytest)

model = LogisticRegression(maxiter=1000, randomstate=self.randomstate)

model.fit(Xtrain, ytrain)

ypred = model.predict(Xtest)

self.accuracy = accuracyscore(ytest, ypred)

self.f1 = f1score(ytest, ypred)

self.modelname = "LogisticRegression"

print(f"LR - Accuracy: {self.accuracy:.4f}, F1: {self.f1:.4f}")

self.next(self.compare)

@step

def trainrf(self):

from sklearn.ensemble import RandomForestClassifier

from sklearn.metrics import accuracyscore, f1score

import numpy as np

Xtrain = np.array(self.Xtrain)

ytrain = np.array(self.ytrain)

Xtest = np.array(self.Xtest)

ytest = np.array(self.ytest)

model = RandomForestClassifier(

nestimators=100, randomstate=self.randomstate

)

model.fit(Xtrain, ytrain)

ypred = model.predict(Xtest)

self.accuracy = accuracyscore(ytest, ypred)

self.f1 = f1score(ytest, ypred)

self.modelname = "RandomForest"

print(f"RF - Accuracy: {self.accuracy:.4f}, F1: {self.f1:.4f}")

self.next(self.compare)

@step

def trainsvm(self):

from sklearn.svm import SVC

from sklearn.metrics import accuracyscore, f1score

import numpy as np

Xtrain = np.array(self.Xtrain)

ytrain = np.array(self.ytrain)

Xtest = np.array(self.Xtest)

ytest = np.array(self.ytest)

model = SVC(kernel='rbf', randomstate=self.randomstate)

model.fit(Xtrain, ytrain)

ypred = model.predict(Xtest)

self.accuracy = accuracyscore(ytest, ypred)

self.f1 = f1score(ytest, ypred)

self.modelname = "SVM"

print(f"SVM - Accuracy: {self.accuracy:.4f}, F1: {self.f1:.4f}")

self.next(self.compare)

@step

def compare(self, inputs):

results = []

for inp in inputs:

results.append({

'model': inp.modelname,

'accuracy': inp.accuracy,

'f1': inp.f1

})

results.sort(key=lambda x: x['f1'], reverse=True)

print("\n=== Perbandingan Model ===")

for r in results:

print(f" {r['model']}: Accuracy={r['accuracy']:.4f}, F1={r['f1']:.4f}")

best = results[0]

self.bestmodelname = best['model']

self.bestaccuracy = best['accuracy']

self.bestf1 = best['f1']

self.allresults = results

print(f"\nModel Terbaik: {self.bestmodelname}")

self.next(self.end)

@step

def end(self):

print("\n=== ML Pipeline Selesai ===")

print(f"Model terpilih: {self.bestmodelname}")

print(f"Accuracy: {self.bestaccuracy:.4f}")

print(f"F1 Score: {self.bestf1:.4f}")

if name == 'main':

MLPipelineFlow()

Jalankan pipeline:

python mlpipeline.py run

Dengan parameter kustom:

python mlpipeline.py run --testsize 0.3 --randomstate 123

Deployment dengan Argo Workflows

Metaflow mendukung deployment ke Argo Workflows untuk production scheduling:

python mlpipeline.py argo-workflows create

python mlpipeline.py argo-workflows trigger

Visualisasi dengan Metaflow Cards

Metaflow Cards memungkinkan Anda membuat visualisasi otomatis:

from metaflow import FlowSpec, step, card, current

from metaflow.cards import Markdown, Table, Image

class CardFlow(FlowSpec):

@card

@step

def start(self):

self.metrics = {

'accuracy': 0.95,

'precision': 0.93,

'recall': 0.97

}

current.card.append(Markdown("# Hasil Training"))

current.card.append(

Table([

['Metrik', 'Nilai'],

['Accuracy', '0.95'],

['Precision', '0.93'],

['Recall', '0.97']

])

)

self.next(self.end)

@step

def end(self):

print("Selesai!")

if name == 'main':

CardFlow()

Lihat card setelah run:

python cardflow.py card view start

Best Practices

1. Desain Step yang Modular

Setiap step sebaiknya melakukan satu tugas spesifik. Ini memudahkan debugging dan memungkinkan retry per step.

@step

def loaddata(self):

...

self.next(self.validatedata)

@step

def validatedata(self):

...

self.next(self.preprocess)

2. Gunakan Artifact dengan Bijak

Simpan hanya data yang diperlukan. Hindari menyimpan objek besar yang tidak perlu diakses di step berikutnya.

@step

def train(self):

self.metrics = {'accuracy': 0.95}

self.next(self.end)

3. Manfaatkan Parameter

Jadikan hyperparameter dan konfigurasi sebagai Parameter agar eksperimen mudah direproduksi.

4. Gunakan Branching untuk Eksperimen

Bandingkan beberapa pendekatan secara paralel menggunakan branching, lalu pilih yang terbaik di step join.

5. Tambahkan Retry untuk Step yang Rawan Gagal

Step yang mengakses API eksternal atau memproses data besar sebaiknya diberi retry.

@retry(times=3)

@step

def fetchdata(self):

...

6. Gunakan Namespace untuk Organisasi

METAFLOWUSER=production python flow.py run

METAFLOWUSER=experiment python flow.py run

7. Monitoring dengan Client API

Buat script monitoring untuk memantau status pipeline:

from metaflow import Flow

flow = Flow('MLPipelineFlow')

for run in list(flow.runs())[:5]:

status = "Berhasil" if run.successful else "Gagal"

print(f"Run {run.id}: {status} ({run.created_at})")

Kesimpulan

Metaflow adalah framework MLOps yang dirancang untuk menyederhanakan workflow data science dari eksperimen hingga produksi. Dengan fitur-fitur seperti versioning otomatis, branching paralel, dependency management, dan Client API, Metaflow memungkinkan data scientist untuk fokus pada pemodelan tanpa terbebani oleh kompleksitas infrastruktur.

Keunggulan utama Metaflow:

  • Kemudahan penggunaan: API yang intuitif dan Pythonic
  • Reprodusibilitas: Setiap eksperimen otomatis di-track
  • Skalabilitas: Transisi mulus dari laptop ke cloud
  • Fleksibilitas: Mendukung berbagai cloud provider dan orchestrator
  • Monitoring: Client API dan Cards untuk observability

Untuk langkah selanjutnya, Anda bisa mengeksplorasi integrasi Metaflow dengan AWS Step Functions, Kubernetes, atau Apache Airflow untuk production deployment yang lebih kompleks. Dokumentasi resmi di docs.metaflow.org menyediakan panduan lengkap untuk setiap fitur.

Artikel Terkait

Tutorial ClearML: Platform MLOps Open-Source untuk Experiment Tracking dan Pipeline Automation

Tutorial ClearML: Platform MLOps Open-Source untuk Experiment Tracking dan Pipeline Automation ClearML adalah platform M...

Tutorial Kedro: Pipeline Data Science yang Reproducible dan Terstruktur

Kedro: Pipeline Data Science yang Reproducible dan Mudah Dirawat Sebagian besar proyek data science dimulai dari satu no...

ZenML: Framework Pipeline MLOps yang Modular dan Cloud-Agnostic

ZenML: Framework Pipeline MLOps yang Modular dan Cloud-Agnostic Pendahuluan Membangun model machine learning yang akurat...

Tutorial Lengkap Comet ML: Platform MLOps untuk Experiment Tracking dan Model Management

Tutorial Lengkap Comet ML: Platform MLOps untuk Experiment Tracking dan Model Management Dalam dunia machine learning mo...