MLOps
MLflow
Python
MLOps
experiment tracking
model versioning
scikit-learn

Manage Your Machine Learning Lifecycle with MLflow in Python

Learn how to use MLflow to track experiments, version models, and manage the complete machine learning lifecycle in Python, from training to deployment.

February 5, 2026·7 min read

If you have trained more than three machine learning models, you already know the chaos: folders named final_model, final_model_v2, final_model_THIS_ONE, parameter files scattered across notebook comments, and the nagging question of which hyperparameters you used in that model that worked so well two weeks ago. MLflow exists precisely to solve that problem. In this article you'll track experiments with parameters, metrics, and artifacts, query the best run programmatically, and promote a model to production with the Model Registry — all in a few lines of Python.

The Chaos Without Experiment Tracking

As an ML project grows, the lack of a tracking system creates concrete problems:

  • Broken reproducibility: you cannot recreate a result because you don't remember which data version you used or what value max_depth had.
  • Impossible comparison: comparing 15 runs manually by reviewing scattered notes is error-prone.
  • Difficult collaboration: a teammate cannot pick up your work if artifacts and parameters only exist on your machine.
  • Lost knowledge: the model you discarded as "bad" might have been the best for a different use case, but you have no way to recover it.

A tracking system is not a luxury; it is basic engineering hygiene for serious ML projects.

MLflow: An Overview

MLflow is an open-source platform created by Databricks to manage the complete ML lifecycle. It is divided into four main components:

  • MLflow Tracking: records and queries experiments — parameters, metrics, artifacts, and models.
  • MLflow Projects: packages ML code in a reproducible format that can run on any platform.
  • MLflow Models: defines a standard format for packaging models with support for multiple "flavors" (sklearn, tensorflow, pytorch, etc.).
  • MLflow Model Registry: a centralized model store with versioning, staging, and annotations.

For most individual or small-team projects, you will start with Tracking and, when you have models in production, add the Registry.

Installation and Setup

Installation is straightforward:

bash
pip install mlflow scikit-learn pandas numpy

To launch the local web interface:

bash
mlflow ui

This starts a server at http://127.0.0.1:5000 where you can visualize all your experiments. By default, MLflow stores data in an mlruns/ directory in your current working directory.

Tracking Experiments with Python

We will use the Iris dataset and a Random Forest to illustrate all the tracking features. The goal is to train several configurations and automatically log everything.

python
import mlflow
import mlflow.sklearn
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score

# Load data
iris = load_iris()
X = pd.DataFrame(iris.data, columns=iris.feature_names)
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

# Set experiment name
mlflow.set_experiment("iris-random-forest")

# Configurations to try
configs = [
    {"n_estimators": 50,  "max_depth": 3, "min_samples_split": 2},
    {"n_estimators": 100, "max_depth": 5, "min_samples_split": 5},
    {"n_estimators": 200, "max_depth": 10, "min_samples_split": 2},
]

for config in configs:
    with mlflow.start_run():
        # Log parameters
        mlflow.log_param("n_estimators", config["n_estimators"])
        mlflow.log_param("max_depth", config["max_depth"])
        mlflow.log_param("min_samples_split", config["min_samples_split"])
        mlflow.log_param("test_size", 0.2)
        mlflow.log_param("random_state", 42)

        # Train model
        model = RandomForestClassifier(
            n_estimators=config["n_estimators"],
            max_depth=config["max_depth"],
            min_samples_split=config["min_samples_split"],
            random_state=42
        )
        model.fit(X_train, y_train)

        # Evaluate
        y_pred = model.predict(X_test)
        acc  = accuracy_score(y_test, y_pred)
        f1   = f1_score(y_test, y_pred, average="weighted")
        prec = precision_score(y_test, y_pred, average="weighted")
        rec  = recall_score(y_test, y_pred, average="weighted")

        # Log metrics
        mlflow.log_metric("accuracy", acc)
        mlflow.log_metric("f1_score", f1)
        mlflow.log_metric("precision", prec)
        mlflow.log_metric("recall", rec)

        # Log feature importance as artifact
        feature_importance = pd.DataFrame({
            "feature": iris.feature_names,
            "importance": model.feature_importances_
        }).sort_values("importance", ascending=False)
        feature_importance.to_csv("feature_importance.csv", index=False)
        mlflow.log_artifact("feature_importance.csv")

        # Save the model with an input example for automatic documentation
        mlflow.sklearn.log_model(
            model,
            "random_forest_model",
            input_example=X_train.iloc[:3]
        )

        print(f"Run complete — accuracy: {acc:.4f}, f1: {f1:.4f}")

Here is what each key call does:

  • mlflow.set_experiment("iris-random-forest"): groups all runs under the same experiment. If it does not exist, MLflow creates it.
  • mlflow.start_run(): opens a run context. When the with block exits, the run closes automatically.
  • mlflow.log_param(key, value): records a hyperparameter or configuration (typically values that do not change during training).
  • mlflow.log_metric(key, value): records an evaluation metric (can be called multiple times with step to plot training curves).
  • mlflow.log_artifact(path): uploads any file to the artifact store.
  • mlflow.sklearn.log_model(model, name): serializes and saves the model with enough metadata to load it with any compatible flavor.

The MLflow UI

After running the script, open http://127.0.0.1:5000 in your browser. You will see:

  • Experiment list in the left panel.
  • Runs table with all runs, showing parameters and metrics in configurable columns.
  • Filters and sorting to quickly find the best run by any metric.
  • Individual run view with all parameters, metrics, downloadable artifacts, and the model URI.

The UI also renders metric charts over time when you log them with step, which is very useful for iterative training (epochs in neural networks, for example).

Comparing Runs and Finding the Best Model

You can compare runs from the UI by selecting several and clicking "Compare". You can also do it programmatically:

python
from mlflow.tracking import MlflowClient

client = MlflowClient()

# Get the experiment by name
experiment = client.get_experiment_by_name("iris-random-forest")
experiment_id = experiment.experiment_id

# Find the best run by accuracy
best_run = client.search_runs(
    experiment_ids=[experiment_id],
    order_by=["metrics.accuracy DESC"],
    max_results=1
)[0]

print(f"Best run ID: {best_run.info.run_id}")
print(f"Accuracy: {best_run.data.metrics['accuracy']:.4f}")
print(f"Parameters: {best_run.data.params}")

Loading a Logged Model and Making Predictions

Once the best run is identified, loading the model is straightforward:

python
import mlflow.sklearn
import pandas as pd

# Use the run_id from the best run
run_id = best_run.info.run_id
model_uri = f"runs:/{run_id}/random_forest_model"

# Load the model
loaded_model = mlflow.sklearn.load_model(model_uri)

# New data to predict
new_data = pd.DataFrame({
    "sepal length (cm)": [5.1, 6.7],
    "sepal width (cm)":  [3.5, 3.0],
    "petal length (cm)": [1.4, 5.2],
    "petal width (cm)":  [0.2, 2.3]
})

predictions = loaded_model.predict(new_data)
species = ["setosa", "versicolor", "virginica"]
print([species[p] for p in predictions])
# ['setosa', 'virginica']

Registering the Model in the Model Registry

For teams managing production models, the Registry adds a governance layer. MLflow 3 replaced the stage system (Production, Staging, Archived) with model aliases, which are more flexible: you can have multiple aliases per version and reassign them without state transition restrictions.

python
# Register the best model
result = mlflow.register_model(
    model_uri=f"runs:/{run_id}/random_forest_model",
    name="IrisClassifier"
)

# Assign the "champion" alias to the production version
client.set_registered_model_alias(
    name="IrisClassifier",
    alias="champion",
    version=result.version
)

# Always load the version with the champion alias
prod_model = mlflow.sklearn.load_model("models:/IrisClassifier@champion")

With this setup, your inference pipeline always points to the @champion alias without hardcoding run IDs or version numbers. When you retrain and have a better model, you simply move the alias to the new version.

Conclusion

MLflow transforms the way you work with machine learning: from a collection of disorganized scripts and files to a reproducible, auditable workflow. With just four calls (log_param, log_metric, log_artifact, log_model) you get complete experiment tracking.

The natural next step is to integrate MLflow into a CI/CD pipeline: run automatic training when new data arrives, log the run, and automatically promote the model to the Registry if it surpasses a quality threshold. Tools like GitHub Actions or Azure Pipelines integrate well with the MLflow API for this purpose.

The code in this article is designed to run directly; you only need to install the dependencies and you will have a functional experimentation system in minutes.

Once your experiments are tracked, the next step is wrapping your training code in a reliable scheduled workflow: Orchestrate ML Pipelines with Prefect. And when your @champion model is ready to receive traffic, see Serve a Machine Learning Model with FastAPI and Docker.


About the author

Rodrigo Arenas is a software architect and machine learning engineer in Medellín, Colombia. He builds products, platforms, and open-source software for AI, Data Engineering, and Machine Learning — creator of Ciaren, sklearn-genetic-opt, and PyWorkforce.



Rodrigo Arenas — ML Engineer & Open Source Maintainer

Working on something similar? I can help you take it to production.

Building an AI or data platform?

Products, platforms, and open-source software for AI, Data Engineering, and Machine Learning.

View Projects
Vision

Building an ecosystem of products and open-source software for AI, Data Engineering, and Machine Learning.

Projects
sklearn-genetic-optPyWorkforceCiaren

Iterixs (Currently building)

© 2026 Rodrigo Arenas