MLflow Experiment Tracking: The Complete Production Guide
MLflow experiment tracking explained in depth — runs, artifacts, autolog internals, remote backends, and production gotchas senior ML engineers actually face..
20+ years shipping production ML systems and the infrastructure behind them. Everything here is grounded in real deployments.
- ✓Deep production experience
- ✓Understanding of internals and trade-offs
- ✓Experience debugging complex systems
- MLflow Tracking records hyperparameters, metrics, artifacts, and code version per training run
- Backend store: SQLite locally, Postgres for production; artifact store: local or S3/GCS
- Autolog hooks into model training libraries and captures params/metrics without manual instrumentation
- Runs are grouped under experiments for logical organization
- Performance: Autolog adds ~15-30ms per batch step, negligible for long training
- Production gotcha: Default file store corrupts under concurrent access — always use a database backend for team usage
MLflow Experiment Tracking is a component of the MLflow ecosystem that records and queries experiments: runs, parameters, metrics, artifacts, and code versions. It's built around a REST API that logs data to a backend store (SQLite, PostgreSQL, MySQL) and artifacts to an artifact store (local, S3, GCS, HDFS).
The fundamental unit is a 'run' — one execution of your ML code. Each run is associated with an 'experiment' (logical group). You can tag runs, compare metrics across runs, and download artifacts programmatically. The tracking server renders a web UI for human exploration, but the real value comes from the API: automating model selection, regression detection, and pipeline orchestration.
Why does this matter in production? Without tracking, your team wastes hours reproducing old results. With tracking, you can query 'give me all runs where F1 > 0.9 and training time < 2 hours' — then promote the best model automatically. The tracking server is the source of truth for your ML lifecycle.
Common misconception: 'I'll only use it for logging hyperparameters.' That's leaving 80% of value on the table. You should also log the code version (git commit), dataset hash, environment (conda.yaml), and model signature. This turns an experiment into a fully reproducible artifact.
Imagine you're baking a hundred batches of cookies, tweaking the recipe each time — more sugar here, less flour there, a new oven temperature. Without notes, you'd never know which batch won the taste test or how you made it. MLflow is that notebook. Every time your model trains, MLflow writes down exactly what ingredients you used, how long it baked, and how good the result tasted — so you can recreate the winner or prove to your boss which recipe is best.
Machine learning is fundamentally an iterative science. You run dozens of experiments — swapping optimizers, tuning regularization, trying new feature sets — and somewhere in that chaos is the model that actually ships to production. Without systematic tracking, that winning run disappears into a sea of Jupyter notebooks and poorly named pickle files. Teams waste days rediscovering results, can't reproduce models when regulators ask, and can't explain why Model v7 beats Model v3. This is not a tooling nicety; it's a production safety net.
MLflow's experiment tracking module solves the reproducibility crisis by giving every training run a unique identity: a timestamped record of hyperparameters, metrics at every epoch, the code version that produced them, and the model artifact itself. It does this with a deceptively simple API that integrates into any Python training loop — PyTorch, TensorFlow, scikit-learn, XGBoost — without restructuring your code. Behind the scenes it talks to a pluggable backend: a local SQLite file on your laptop, a Postgres database in staging, or a managed service like Databricks MLflow in production.
By the time you finish this article you'll understand how MLflow's tracking server actually stores data, how to design experiment hierarchies that scale to a team of ten data scientists, how to use autolog without getting burned by its edge cases, and how to query runs programmatically to automate model promotion pipelines. This goes well beyond the quickstart — we're building the mental model you need to debug MLflow in production at 2 a.m.
What is Experiment Tracking with MLflow?
MLflow Experiment Tracking is a component of the MLflow ecosystem that records and queries experiments: runs, parameters, metrics, artifacts, and code versions. It's built around a REST API that logs data to a backend store (SQLite, PostgreSQL, MySQL) and artifacts to an artifact store (local, S3, GCS, HDFS).
The fundamental unit is a 'run' — one execution of your ML code. Each run is associated with an 'experiment' (logical group). You can tag runs, compare metrics across runs, and download artifacts programmatically. The tracking server renders a web UI for human exploration, but the real value comes from the API: automating model selection, regression detection, and pipeline orchestration.
Why does this matter in production? Without tracking, your team wastes hours reproducing old results. With tracking, you can query 'give me all runs where F1 > 0.9 and training time < 2 hours' — then promote the best model automatically. The tracking server is the source of truth for your ML lifecycle.
Common misconception: 'I'll only use it for logging hyperparameters.' That's leaving 80% of value on the table. You should also log the code version (git commit), dataset hash, environment (conda.yaml), and model signature. This turns an experiment into a fully reproducible artifact.
import mlflow mlflow.set_tracking_uri("http://localhost:5000") mlflow.set_experiment("home-price-prediction") with mlflow.start_run(): mlflow.log_param("learning_rate", 0.01) mlflow.log_param("model_type", "xgboost") for epoch in range(10): # simulate training loop loss = 1.0 / (epoch + 1) mlflow.log_metric("loss", loss, step=epoch) mlflow.log_artifact("model.pkl") mlflow.set_tag("trained_by", "alice")
- Each run is an atomic log entry: you cannot modify metrics retroactively (only add new steps).
- Artifacts are versioned by timestamp — overwriting an artifact creates a new version.
- The UI is a read-only viewer; write operations always go through the API.
- This append-only design enables reproducibility — no one can delete evidence of a bad run.
Architecture: Backend Store, Artifact Store, and the Tracking Server
MLflow's architecture has three components: the tracking server (optional, defaults to local file store), the backend store (database for metadata), and the artifact store (blob storage for files).
The tracking server is a Flask app that exposes a REST API. If you run mlflow ui, it starts a minimal server with a file-based backend. For production, you run a standalone server with a database backend (Postgres recommended) and a remote artifact store (S3/GCS).
Backend store: Stores experiment and run metadata (IDs, params, metrics, tags). Supports SQLAlchemy-compatible databases. Using Postgres allows concurrent writers and transactional consistency.
Artifact store: Stores model files, plots, datasets. Can be local (not recommended for teams), S3 with presigned URLs, GCS, or Azure Blob. Artifacts are referenced by URI in the backend store.
Communication: Clients (your training script) send HTTP requests to the tracking server. The server persists metadata to the backend store and streams artifacts to the artifact store. The UI queries the backend store directly.
The practical impact: If your team grows beyond five people, you need a Postgres backend. The default SQLite backend corrupts under concurrent writes. Also, the artifact store must be accessible from both the tracking server and the training machines — otherwise uploads fail.
export MLFLOW_TRACKING_URI="postgresql://user:pass@db:5432/mlflow" export MLFLOW_ARTIFACT_URI="s3://mlflow-artifacts/team-alpha" mlflow server \ --backend-store-uri $MLFLOW_TRACKING_URI \ --default-artifact-root $MLFLOW_ARTIFACT_URI \ --host 0.0.0.0 \ --port 5000 \ --workers 4 \ --static-prefix /mlflow
sqlite:///mlruns.db) does not support concurrent writes. If two training jobs start runs simultaneously, the database can become corrupted. Always use Postgres in a multi-user or multi-job environment.mlflow artifacts list --run-id test.Autolog: Power and Pitfalls
MLflow's function automatically logs parameters, metrics, and models from popular ML frameworks (scikit-learn, TensorFlow, PyTorch, XGBoost, LightGBM). One line of code and you get comprehensive tracking.autolog()
How it works: MLflow patches framework-specific functions (e.g., in scikit-learn) to intercept training events. It logs model.fit() parameters, model hyperparameters, and per-epoch metrics. It also logs the trained model artifact.fit()
The convenience is seductive. But autolog has defaults that hurt: - log_models=True - logs the model after each fit call, which on large models or hyperparameter sweeps fills storage quickly. - log_datasets=True - logs dataset metadata, which can fail if the dataset is a generator or if it contains non-serializable objects. - It logs metrics per step, which in dense frameworks (PyTorch Lightning) can generate millions of rows in minutes.
Autolog also silently conflicts with manual logging. If you call log_metric after autolog already logged a metric with the same name, you get duplicate entries in the UI. The fix: either use autolog exclusively or disable it for specific frameworks using mlflow.autolog(disable_for_unsupported_versions=False).
The production rule: Use autolog for quick exploration. For production pipelines, write explicit logging wrappers.
import mlflow from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import GridSearchCV mlflow.autolog(log_models=False, log_datasets=False) param_grid = {'n_estimators': [10, 50, 100], 'max_depth': [3, 5]} clf = RandomForestRegressor() gs = GridSearchCV(clf, param_grid, cv=3) gs.fit(X_train, y_train) # Only log the best model manually best = gs.best_estimator_ with mlflow.start_run(run_name="best-model"): mlflow.sklearn.log_model(best, "model")
mlflow.autolog(max_tuning_runs=50) or use early stopping.Designing Experiment Hierarchies for Teams
MLflow organizes runs into experiments. An experiment has a name, an ID, and a set of runs. The default experiment is 'Default' — which is a recipe for chaos.
For teams, you need a naming convention and a lifecycle policy. Common pattern: - One experiment per project or per model type. Example: experiments named 'recommender-v4', 'fraud-detection'. - Use tags (team, purpose, status) to filter runs. - Use nested runs if your pipeline has stages: parent run represents the pipeline, child runs represent individual training or evaluation steps.
You can programmatically create experiments with . Set tags at experiment creation time to mark team ownership.mlflow.create_experiment()
Another important design choice: should you archive old experiments or delete them? Archiving moves them out of the default view but retains data for audit. Use only for test experiments. For production, archive by renaming the experiment with a prefix like mlflow.experiments.delete_experiment()_archived/.
The biggest failure I've seen: a team of 15 data scientists all using the same 'Default' experiment. They had 2000+ runs, couldn't find anything, and metrics comparison was unusable. A naming convention took 30 minutes to implement and solved it.
import mlflow experiment_name = "fraud-detection-v5" try: experiment_id = mlflow.create_experiment( experiment_name, artifact_location="s3://mlflow-artifacts/fraud-detection-v5", tags={"team": "ds-payments", "status": "active"} ) except mlflow.exceptions.MlflowException: experiment_id = mlflow.get_experiment_by_name(experiment_name).experiment_id mlflow.set_experiment(experiment_name) with mlflow.start_run(): mlflow.log_param("model_type", "xgboost") mlflow.set_tag("purpose", "online-training")
_archived/<name> or use the new lifecycle_stage tag. You can filter them out in the UI by searching for tags.lifecycle_stage != 'deleted'.team tag._archived/.Programmatic Run Queries and Model Promotion
MLflow's Tracking API isn't just for logging — you can query runs to automate model selection, regression detection, and deployment decisions.
— returns a Pandas DataFrame of runs matching a filter.mlflow.search_runs()mlflow.get_run(run_id)— retrieve a single run's metadata.mlflow.get_metric_history(run_id, key)— get all logged values of a metric over steps.
You can filter by parameter values, metric thresholds, tags, and time. Use SQL-like syntax: "metrics.f1_score > 0.9" and "params.model_type = 'xgboost'".
Practical pipeline: After a hyperparameter sweep, find the best run by metric, then promote its model artifact to a staging registry (e.g., MLflow Model Registry or a custom S3 path).
Seen many engineers manually copy run IDs from the UI — then promotion scripts break because the run ID was mistyped. Always use the API to fetch the best run.
Another pattern: regression detection. Before promoting a new model, compare its metrics to the current production model's metrics (stored in a specific tagged run). If F1 drops by more than 2%, fail the pipeline.
Production insight: The API can be slow with millions of runs if you don't filter effectively. Always limit to a specific experiment and use indexed columns (experiment ID, start time).search_runs()
import mlflow mlflow.set_tracking_uri("http://mlflow-server:5000") experiment_id = mlflow.get_experiment_by_name("fraud-detection-v5").experiment_id # Find best run by validation AUC best_runs = mlflow.search_runs( experiment_ids=[experiment_id], filter_string="metrics.val_auc > 0.95", order_by=["metrics.val_auc DESC"], max_results=1 ) if not best_runs.empty: best_run_id = best_runs.iloc[0]['run_id'] model_uri = f"runs:/{best_run_id}/model" # Promote: register to Model Registry or copy artifact mlflow.register_model(model_uri, "fraud-detection-prod")
CREATE INDEX idx_metrics_run_key_step ON metrics(run_uuid, metric_key, step);. This speeds up search_runs with metric filters dramatically.search_runs() with filters.Logging Parameters, Metrics, and Models: The Bare Minimum That Actually Matters
Beginners treat mlflow.log_param like a diary. They log everything: notebook version, moon phase, coffee intake. That's noise, not signal. You log parameters to reproduce a run, metrics to compare it, and artifacts (usually the model) to deploy it. Nothing else.
Parameters are the knobs you turn: learning rate, max depth, batch size. Log them before training starts so a crash doesn't lose them. Metrics are the scoreboard: accuracy, F1, RMSE. Log them after each epoch or at the end. Models are the product. Log them with mlflow.sklearn.log_model (or your framework's flavor) so you can load them later without guessing which pickle file is which.
Here's the common footgun: MLflow's autolog does this automatically, but it also logs 47 obscure hyperparameters you never set. If you're trying to compare runs manually, those extra parameters make the UI unusable. Explicit logging is boring but predictable. Boring is better than broken in production.
// io.thecodeforge — ml-ai tutorial import mlflow from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score with mlflow.start_run(run_name="rf_prod_v2"): # Log parameters BEFORE training params = {"n_estimators": 200, "max_depth": 10, "min_samples_split": 5} mlflow.log_params(params) model = RandomForestClassifier(**params) model.fit(X_train, y_train) preds = model.predict(X_test) acc = accuracy_score(y_test, preds) mlflow.log_metric("accuracy", acc) # Log model with signature + input example (this matters) mlflow.sklearn.log_model( model, artifact_path="random_forest", signature=mlflow.models.infer_signature(X_test, preds), input_example=X_test[:5] )
infer_signature with real test data, not a mock.MLflow UI Not Launching: Why It Happens and How to Fix It in 10 Seconds
You run mlflow ui in the terminal, get a happy port message, open localhost:5000, and see... nothing. Or worse, a white screen that spins forever. This isn't a bug. It's a tracking server configuration mismatch.
MLflow's UI reads from the backend store you configured. If you set MLFLOW_TRACKING_URI to a remote server or a specific file path, the default mlflow ui command doesn't know about it. It starts an in-memory SQLite database at ./mlruns. If that directory doesn't exist or your runs are stored elsewhere, the UI serves an empty dataset.
Second most common cause: a zombie process on port 5000. MacOS Monterey and later have AirPlay Receiver fighting for that port. Check with lsof -i :5000. If it's AirPlay, either kill it or change MLflow's port: mlflow ui --port 5050.
Third: Docker networking. If you're running MLflow in a container, the UI binds to 127.0.0.1 inside the container, not your host. Set --host 0.0.0.0 in the command.
Fix is always the same: confirm the backend store path matches reality. ls -la the mlruns folder. If it's empty, you're looking in the wrong place.
// io.thecodeforge — ml-ai tutorial # Check what MLflow thinks the tracking URI is python -c "import mlflow; print(mlflow.get_tracking_uri())" # Expected: file:///Users/you/project/mlruns # Check if the UI can find that dir ls -la $(python -c "import mlflow; print(mlflow.get_tracking_uri())" | sed 's/file:\/\///') # Launch UI with explicit backend store mlflow ui --backend-store-uri sqlite:///mlflow.db --port 5050 --host 0.0.0.0
mlflow ui from the same directory where you ran your training script. Or export MLFLOW_TRACKING_URI in your shell profile so the UI picks it up without arguments.MLflow Setup and Installation
Before tracking anything, you need a running MLflow server. A bad setup wastes hours—do it right. The core components are a backend store (SQLite for prototyping, PostgreSQL for teams) and an artifact store (local disk, S3, or GCS). Run mlflow server --backend-store-uri sqlite:///mlflow.db --default-artifact-root ./artifacts to start. That exposes a UI at localhost:5000. For production, use environment variables to set MLFLOW_TRACKING_URI and MLFLOW_S3_ENDPOINT_URL. Always pin the MLflow version in your dependencies—breaking API changes across minor versions are common. Test connectivity with a simple call before any experiment. Without a stable server, your experiment history is unreliable.mlflow.set_tracking_uri()
// io.thecodeforge — ml-ai tutorial import mlflow # Start server manually in terminal: # mlflow server --backend-store-uri sqlite:///mlflow.db --default-artifact-root ./artifacts mlflow.set_tracking_uri("http://localhost:5000") experiment_id = mlflow.create_experiment( "production_validation", artifact_location="s3://my-bucket/artifacts" ) print(f"Experiment {experiment_id} ready")
Enable MLflow Autologging
Autologging logs metrics, parameters, and models automatically without manual log_param or log_metric calls. It works for scikit-learn, TensorFlow, PyTorch, and XGBoost. Call once before training—no more forgotten logging. The why: manual logging is error-prone and skipped under pressure. Autologging ensures every experiment has a complete audit trail. The how: run mlflow.autolog()mlflow.autolog(log_models=True, log_input_examples=True). For frameworks like XGBoost, set . Watch for pitfalls: autologging logs every intermediate iteration, which floods your dashboard. Filter with mlflow.xgboost.autolog()mlflow.autolog(silent=True) for final runs. Production teams combine autologging with manual overrides for custom metrics like business KPIs.
// io.thecodeforge — ml-ai tutorial import mlflow from sklearn.ensemble import RandomForestRegressor mlflow.autolog(log_models=True, log_input_examples=True) model = RandomForestRegressor(n_estimators=100, max_depth=5) model.fit(X_train, y_train) # Metrics, params, and model logged automatically
log_models to False during hyperparameter sweeps to avoid artifact store bloat.Prerequisites
Before diving into MLflow experiment tracking, you must understand the foundational concepts. First, ensure Python 3.8+ is installed with pip or conda for package management. You need a running MLflow Tracking Server—either locally via mlflow server or on a cloud VM—plus accessible storage for the Backend Store (SQLite, PostgreSQL, or MySQL) and Artifact Store (local filesystem, S3, GCS, or Azure Blob). Familiarity with basic ML workflows (training, validation, model serialization) and shell commands is assumed. For production teams, knowledge of Docker, Kubernetes, or CI/CD pipelines helps, as MLflow integrates with these for scalable tracking. Finally, install the MLflow Python client (pip install mlflow) and verify it with a quick import mlflow; print(mlflow.__version__). Without these prerequisites—especially the backend store and artifact store—your experiments will fail to persist, wasting hours of debugging. Set up infrastructure first, then experiment.
// io.thecodeforge — ml-ai tutorial import mlflow import sqlite3 import boto3 # Verify MLflow version print(f"MLflow version: {mlflow.__version__}") # Check DB connectivity (example: SQLite) try: conn = sqlite3.connect('mlflow.db') print("SQLite Backend Store: OK") conn.close() except Exception as e: print(f"DB Error: {e}") # Check artifact store (example: S3) try: s3 = boto3.client('s3') s3.list_buckets() print("S3 Artifact Store: OK") except Exception as e: print(f"S3 Error: {e}")
Stage 6: Monitoring and Maintenance
Experiment tracking doesn't end when a model is promoted to production. Stage 6 ensures your MLflow system remains reliable and your models stay relevant. Monitoring involves tracking data drift, model degradation, and infrastructure health. Use MLflow's Model Registry to version deployed models and log performance metrics over time via periodic inference jobs. For example, log from production serving endpoints back to an MLflow run as a nested run under the model version. Maintenance requires cleaning stale experiments—delete runs older than six months using to keep the backend store lean. Also, rotate artifact store credentials and backup the backend database weekly. Set up alerts for failed runs or slow queries on the Tracking Server. If a model's accuracy drops, trigger a new experiment run automatically. Without Stage 6, your experiment tracking becomes historical noise; failing to monitor drift leads to silent failures in production.mlflow.delete_run()
// io.thecodeforge — ml-ai tutorial import mlflow import datetime with mlflow.start_run(run_name="production_monitor"): mlflow.log_param("model_version", 3) mlflow.log_metric("prediction_count", 10000) mlflow.log_metric("mean_error", 0.023) mlflow.log_metric("data_drift_score", 0.12) # Clean old runs old_time = datetime.datetime.now() - datetime.timedelta(days=180) for run in mlflow.search_runs(experiment_ids=["0"], filter_string=f"end_time < '{old_time.isoformat()}'"): mlflow.delete_run(run.run_id)
The Autolog That Swallowed a Team's Storage
mlflow.autolog(log_models=False) to disable model artifact logging at each step. Use max_tuning_runs and log_metrics per epoch instead of per batch. Add an index on run_uuid and step in the metrics table. Limit concurrent active runs by using a tracking server queue.- Autolog's default per-step logging is a storage bomb in distributed tuning. Always set
log_models=Falseand log per epoch. - A database backend needs indexing and connection pooling for production throughput.
- Run comparison UI performance degrades with >10 metrics per run — use a custom dashboard for large-scale analysis.
mlflow.start_run() hangs indefinitelycurl -I <tracking_uri>/api/2.0/experiments/list. If slow, the backend DB may be locked or overloaded. Kill stale runs with mlflow gc to release the lock.mlflow.log_metric(metric_key, value, step=...) with explicit steps. Run SELECT run_uuid, metric_key, COUNT() FROM metrics GROUP BY run_uuid, metric_key HAVING COUNT() > 1 to detect duplicates.MLFLOW_S3_UPLOAD_EXTRA_ARGS with ServerSideEncryption and use a dedicated artifact store bucket with proper lifecycle policies.`curl -s http://<host>:5000/api/2.0/mlflow/experiments/list | python -m json.tool``lsof -i :5000`mlflow server --backend-store-uri postgresql://... --default-artifact-root s3://... --host 0.0.0.0 --port 5000Check what autolog logs: `mlflow.autolog(log_input_examples=False, log_models=False, log_datasets=False)`View current run metrics via CLI: `mlflow experiments csv --experiment-id <id> --output metrics.csv`mlflow.autolog() with ms.mlflow.log_param() and ms.mlflow.log_metric() in the training loop.`mlflow artifacts download -r <run_id> -d /tmp/artifacts`Check MLflow config: `mlflow config view` or `print(mlflow.get_tracking_uri())`export MLFLOW_S3_ENDPOINT_URL=https://s3.us-east-1.amazonaws.com and verify IAM role.Search with order: `mlflow runs list --experiment-id <id> --order-by start_time DESC`Query via API: `curl -X POST http://<host>:5000/api/2.0/mlflow/runs/search -H "Content-Type: application/json" -d '{"experiment_ids":["<id>"],"order_by":["attributes.start_time DESC"]}'`--serving-cache-ttl 0 to disable caching.Delete duplicate via API: `curl -X POST http://<host>:5000/api/2.0/mlflow/experiments/delete?experiment_id=<dup_id>`Rename experiment: `mlflow experiments rename <id> "New Name"`mlflow.set_experiment(unique_name(e)).| Feature | SQLite (default) | PostgreSQL | MySQL |
|---|---|---|---|
| Concurrent writes | Not supported (single-user only) | Full support | Full support |
| Query performance (>10K runs) | Slow (no indexes on default schema) | Fast with proper indexes | Fast with proper indexes |
| Ease of setup | Zero config | Requires database setup (5 min) | Requires database setup (5 min) |
| Recommendation | Local prototyping only | Production team use | Alternative production |
| File | Command / Code | Purpose |
|---|---|---|
| mlflow_basic_logging.py | mlflow.set_tracking_uri("http://localhost:5000") | What is Experiment Tracking with MLflow? |
| start_production_mlflow_server.sh | export MLFLOW_TRACKING_URI="postgresql://user:pass@db:5432/mlflow" | Architecture |
| autolog_with_tuning.py | from sklearn.ensemble import RandomForestRegressor | Autolog |
| experiment_setup.py | experiment_name = "fraud-detection-v5" | Designing Experiment Hierarchies for Teams |
| best_run_promotion.py | mlflow.set_tracking_uri("http://mlflow-server:5000") | Programmatic Run Queries and Model Promotion |
| LoggingExample.py | from sklearn.ensemble import RandomForestClassifier | Logging Parameters, Metrics, and Models |
| UIFix.sh | python -c "import mlflow; print(mlflow.get_tracking_uri())" | MLflow UI Not Launching |
| SetupServer.py | mlflow.set_tracking_uri("http://localhost:5000") | MLflow Setup and Installation |
| EnableAutolog.py | from sklearn.ensemble import RandomForestRegressor | Enable MLflow Autologging |
| prerequisites_check.py | print(f"MLflow version: {mlflow.__version__}") | Prerequisites |
| monitor_model.py | with mlflow.start_run(run_name="production_monitor"): | Stage 6 |
Key takeaways
Interview Questions on This Topic
How does MLflow autolog work under the hood? Explain the mechanism for intercepting framework training calls.
fit(), PyTorch Lightning's Trainer.fit()). It uses Python's mock.patch or similar monkey-patching to wrap the original method. Before the method executes, it logs parameters from the model constructor arguments. After each step, it captures metrics from the framework's logging hooks (e.g., Scikit-learn's score()). After the method completes, it logs the trained model artifact using the appropriate flavor (e.g., mlflow.sklearn.log_model()). The patching is registered at the module level; calling mlflow.autolog() for a specific framework registers these patches globally. The context manager mlflow.start_run() is typically required to wrap the patched method; if no run is active, autolog will not log anything in some frameworks.When you scale MLflow to a team of 10 data scientists, what are the infrastructure considerations for the tracking server?
Explain how to use MLflow to compare two different models (say XGBoost and Neural Network) trained on the same dataset for a regression problem.
param:boost_rounds vs param:layer_size). Log common metrics: mse, rmse, mae at equivalent steps. Then use the MLflow UI or search_runs() to compare metrics across runs. To compare fairly, log the dataset version or hash as a tag. For cross-model comparison, consider using mlflow.evaluate() to compute consistent metrics (including fairness metrics) outside the training loop. Example: mlflow.search_runs(experiment_ids=[exp_id], order_by=['metrics.rmse ASC']) gives you the best run regardless of model type.Frequently Asked Questions
Experiment Tracking with MLflow is a central logbook for all your machine learning runs. Every time you train a model, MLflow records what you used (hyperparameters), how it performed (metrics), what code version it used, and the model file itself. You can later compare runs to find the best one, reproduce it, or audit it.
Set up a production tracking server with PostgreSQL as backend and S3/GCS as artifact store. Deploy on a server (or use Databricks managed MLflow). Configure each team member's MLFLOW_TRACKING_URI environment variable. Use experiment names per project. Consider adding authentication via a reverse proxy.
The UI loads metrics for all runs in an experiment on the 'Compare' page. If you have thousands of runs with per-step metrics, the database queries are slow. Solutions: (1) limit runs per experiment (archive old ones), (2) log metrics per epoch not per step, (3) add database indexes on run_uuid, metric_key, step, (4) use the API and custom dashboards for large-scale analysis.
Yes, MLflow provides a REST API and Java client. You can log runs from any language by sending HTTP requests to the tracking server. The Python client is the most feature-rich, but the API is language-agnostic. For R, there's an mlflow R package. For others, use the REST API or a wrapper process that calls the Python client via subprocess.
Use the CLI: mlflow gc to delete runs in 'deleted' state (runs must be moved to deleted first via API: mlflow.delete_run(run_id)). For artifacts, delete the corresponding blob store objects. For automated cleanup, write a script that queries runs older than N days, deletes them via API, and removes artifacts. Set up a cron job in your infrastructure.
20+ years shipping production ML systems and the infrastructure behind them. Everything here is grounded in real deployments.
That's MLOps. Mark it forged?
8 min read · try the examples if you haven't