Skip to content

intermediate ~90 min updated 2026-06-01

MLOps Pipeline Basics

Build a minimal but real MLOps workflow: train a scikit-learn classifier, track parameters and metrics with MLflow, register the best model, and serve it as a REST API locally.

Objective

Train and compare scikit-learn models with MLflow tracking, promote the best run to the Model Registry, and serve it for real-time predictions. This is the core experiment-to-production loop every MLOps stack automates.

Prerequisites

  • Python 3.10 or newer
  • pip and the venv module
  • Basic Python and ML familiarity (train/test split, accuracy)
  • About 2 GB free disk for the virtualenv and model environments

Architecture

A training script runs experiments against the Iris dataset, logging parameters, metrics, and the serialized model to a local MLflow tracking server backed by SQLite. The best run is registered in the Model Registry under a version and alias. mlflow models serve then loads the registered model behind a REST scoring endpoint.

 train.py (scikit-learn)
     | log params/metrics/model
     v
+----------------------+      register       +-------------------+
| MLflow Tracking      | ------------------> | Model Registry    |
| server :5000         |                     | iris-clf@champion |
| backend: sqlite      |                     +---------+---------+
| artifacts: ./mlruns  |                               | load
+----------+-----------+                               v
           ^                                  mlflow models serve :5001
        UI in browser                         POST /invocations -> prediction

Steps

1. Set up the environment and tracking server

mkdir mlops-lab && cd mlops-lab
python3 -m venv .venv && source .venv/bin/activate
pip install mlflow scikit-learn pandas

mlflow server \
  --backend-store-uri sqlite:///mlflow.db \
  --default-artifact-root ./mlruns \
  --host 127.0.0.1 --port 5000 &
sleep 5

Open http://127.0.0.1:5000 to confirm the UI loads.

2. Write the training script

# train.py
import sys
import mlflow
import mlflow.sklearn
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, f1_score
from sklearn.model_selection import train_test_split

mlflow.set_tracking_uri("http://127.0.0.1:5000")
mlflow.set_experiment("iris-classification")

X, y = load_iris(return_X_y=True, as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42, stratify=y
)

model_name = sys.argv[1] if len(sys.argv) > 1 else "rf"

if model_name == "rf":
    n_estimators = int(sys.argv[2]) if len(sys.argv) > 2 else 100
    model = RandomForestClassifier(n_estimators=n_estimators, random_state=42)
    params = {"model": "random_forest", "n_estimators": n_estimators}
else:
    model = LogisticRegression(max_iter=500)
    params = {"model": "logistic_regression", "max_iter": 500}

with mlflow.start_run():
    mlflow.log_params(params)
    model.fit(X_train, y_train)
    preds = model.predict(X_test)
    acc = accuracy_score(y_test, preds)
    f1 = f1_score(y_test, preds, average="macro")
    mlflow.log_metric("accuracy", acc)
    mlflow.log_metric("f1_macro", f1)
    mlflow.sklearn.log_model(model, name="model",
                             input_example=X_train.head(2))
    print(f"{params['model']}: accuracy={acc:.4f} f1={f1:.4f}")

3. Run several experiments

python train.py logreg
python train.py rf 50
python train.py rf 200

Compare runs in the UI: experiment iris-classification, sort by accuracy.

4. Register the best model

# register.py
import mlflow
from mlflow.tracking import MlflowClient

mlflow.set_tracking_uri("http://127.0.0.1:5000")
client = MlflowClient()

exp = client.get_experiment_by_name("iris-classification")
best = client.search_runs(
    [exp.experiment_id], order_by=["metrics.accuracy DESC"], max_results=1
)[0]
print("Best run:", best.info.run_id, "accuracy:", best.data.metrics["accuracy"])

mv = mlflow.register_model(f"runs:/{best.info.run_id}/model", "iris-clf")
client.set_registered_model_alias("iris-clf", "champion", mv.version)
print(f"Registered iris-clf version {mv.version} as @champion")
python register.py

5. Serve the registered model

export MLFLOW_TRACKING_URI=http://127.0.0.1:5000
mlflow models serve -m "models:/iris-clf@champion" \
  --port 5001 --env-manager local &
sleep 10

6. Score live predictions

curl -s -X POST http://127.0.0.1:5001/invocations \
  -H "Content-Type: application/json" \
  -d '{
    "dataframe_split": {
      "columns": ["sepal length (cm)","sepal width (cm)","petal length (cm)","petal width (cm)"],
      "data": [[5.1, 3.5, 1.4, 0.2], [6.7, 3.0, 5.2, 2.3]]
    }
  }'

Expected output

$ python train.py rf 200
random_forest: accuracy=0.9111 f1=0.9106

$ python register.py
Best run: 4f3a2b1c0d9e8f7a accuracy: 0.9333
Registered iris-clf version 1 as @champion

$ curl -s -X POST http://127.0.0.1:5001/invocations ...
{"predictions": [0, 2]}

Class 0 is setosa and class 2 is virginica — both correct for those measurements.

Troubleshooting

  • Connection refused from train.py: the tracking server is not up. Check the background job (jobs), and re-run the mlflow server command; SQLite locks can occur if a previous server is still running.
  • RESOURCE_ALREADY_EXISTS when registering: the model name exists from a previous attempt — that is fine; register_model creates a new version automatically. Delete old versions in the UI if you want a clean slate.
  • mlflow models serve fails resolving the environment: omit env recreation with --env-manager local (as shown) so it uses your active virtualenv.
  • Invalid input 400 from /invocations: the JSON must use the dataframe_split (or instances) key and column names matching training. Copy the request body exactly.
  • UI shows no experiment: train.py used a different tracking URI. Ensure mlflow.set_tracking_uri("http://127.0.0.1:5000") matches the server port.

Cleanup

kill %1 %2 2>/dev/null || true   # stop server and model endpoint
deactivate
cd .. && rm -rf mlops-lab