Track and Register ML Models with MLflow
Build a local MLOps pipeline to track, compare, and register ML models.
Introduction
30 Second Summary
When data scientists build machine learning models, they rarely train just one and call it done. They experiment with dozens of configurations, compare results, and pick the winner. Without a system to track all of this, things get messy fast.
In this project, you will build a local MLOps pipeline that trains multiple models, tracks every experiment automatically, and promotes the best-performing model to production using MLflow.
What You'll Build
A complete local MLOps workflow that trains, tracks, versions, and serves machine learning models using MLflow and scikit-learn.
By the end of this project, you'll have:
- A training script that trains and logs multiple Logistic Regression models to MLflow.
- An MLflow tracking server with a visual UI showing your experiment runs, metrics, and model artifacts.
- A registered model in the Model Registry with versioning and a "champion" alias that marks your best model for production use.
- An inference script that loads the champion model from the registry and makes predictions on new data.
- Secret Mission: Build an automated promotion pipeline that adds a Random Forest model and promotes the best performer to champion.
Are there any prerequisites?
You only need basic Python knowledge (variables, functions, imports). No prior machine learning or MLOps experience is required.
Before We Start
Before We Start...
Set Up Your Environment
Before you can train and track machine learning models, you need a working environment with the right tools installed.
In this step, you'll install Python, create a project folder with an isolated virtual environment, install your dependencies, and start the MLflow tracking server.
In this step, get ready to:
- Install Python 3.11+ and create a project folder with a virtual environment.
- Install MLflow and scikit-learn.
- Start the MLflow tracking server and verify the UI.
Install Python and create your project folder
This project requires Python 3.11 or higher because scikit-learn 1.8.0 only supports Python 3.11+.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
- Check your Python version by running this command:
macOS
python3 --version
Windows
python --version
Linux
python3 --version
You should see output in the format Python 3.x.x where the version number tells you what is installed.
✔️ I see version 3.11 or higher
Python is ready to go. Skip ahead to creating your project folder below.
ⓧ I see an older version
Your system has Python installed, but it is an older version. You need 3.11 or higher for this project.
On macOS, the system-installed Python is often an older version. Even after installing a newer version, python3 may still point to the old one.
- Download and install Python 3.11+ from python.org/downloads.
- After installing, close and reopen your terminal.
- On macOS, try running python3.11 --version or python3.12 --version to confirm the new version is available.
- On Windows, the python.org installer updates the default python command automatically.
Use the versioned binary on macOS
If python3 still shows the old version after installing, use python3.11 or python3.12 directly for the rest of this project. Alternatively, use pyenv to manage multiple Python versions.
ⓧ Command not found
Python is not installed on your machine yet.
- Download and install Python 3.11+ from python.org/downloads.
- On Windows, check the box that says Add Python to PATH during installation.
- After installing, close and reopen your terminal.
- Run the version check command again to confirm it works.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir mlflow-tracking
cd mlflow-tracking
- Confirm the folder exists by running:
ls
The folder will be empty for now. That is expected.
- Create a virtual environment by running this command:
python3 -m venv .venv
What is a virtual environment?
A virtual environment is an isolated Python installation inside your project folder. It keeps your project's packages separate from your system Python so different projects don't conflict with each other.
- Activate the virtual environment by running this command:
macOS
source .venv/bin/activate
Windows
.venv\Scripts\activate
Linux
source .venv/bin/activate
You should see (.venv) appear at the beginning of your terminal prompt. This confirms the virtual environment is active.
Don't see (.venv) in your prompt?
On Windows, you may need to allow script execution first. Run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned in PowerShell, then try the activate command again.
On macOS, make sure you used source before the path. Without it, the command won't work.
Help me activate my Python virtual environment.
Install MLflow and scikit-learn
With your virtual environment active, you can now install the two main packages for this project.
- Install both packages by running this command:
pip install mlflow==3.12.0 scikit-learn==1.8.0
What are these packages?
MLflow is an open-source platform for tracking machine learning experiments, managing models, and deploying them. It provides a visual UI for comparing runs.
scikit-learn is a popular machine learning library with built-in datasets and algorithms. You'll use it to train models that MLflow tracks.
The installation may take a minute as pip downloads the packages and their dependencies.
- Verify MLflow installed correctly by running:
mlflow --version
You should see the version 3.12.0 printed in your terminal.
Seeing 'command not found' after installing?
Make sure your virtual environment is still active. Look for (.venv) in your prompt. If it disappeared, run the activate command from the previous substep again.
If the install command itself failed, check that you're using Python 3.11+. Older Python versions cannot install scikit-learn==1.8.0.
Help me fix my MLflow installation.
Start the MLflow tracking server
MLflow includes a built-in tracking server with a web UI. You'll start it now and keep it running in this terminal while you work in a second terminal later.
- Start the MLflow tracking server by running this command:
mlflow server --port 5000
What does this command do?
This starts a local web server on port 5000 that stores experiment data in a local SQLite database. The server will keep running in this terminal window until you stop it with Ctrl+C.
Your terminal will show server logs and appear "frozen." This is normal. The server is running and waiting for requests.
- Open your browser and go to http://localhost:5000.
You should see the MLflow UI with an empty experiments page. This is your experiment tracking dashboard.
Browser showing 'unable to connect'?
Make sure the mlflow server command is still running in your terminal. If you see an error about port 5000 being in use, another process is occupying that port.
On macOS, AirPlay Receiver sometimes uses port 5000. You can disable it in System Settings under General, then AirDrop & Handoff. Alternatively, use mlflow server --port 5001 instead.
Help me get the MLflow server running.
Keep this terminal open
Leave the MLflow server running in this terminal window. In the next step, you'll open a new terminal to write and run your training scripts.
Your environment is ready. Next up, you'll write a training script and watch MLflow automatically capture every detail of your experiment.
Train Your First Model with Autologging
Your MLflow server is running and ready to receive data. Now it's time to train your first machine learning model and watch MLflow track everything automatically.
In this step, you'll create a training script that trains multiple Logistic Regression models on the classic Iris flower dataset. MLflow will log every parameter, metric, and model artifact so you can compare runs later.
In this step, get ready to:
- Create a training script that loads data and defines model configurations.
- Train multiple models and log each run to MLflow.
- Explore your experiment results in the MLflow UI.
Create train.py with data and configurations
Every ML experiment starts with data. The Iris dataset is a classic beginner dataset built into scikit-learn. It contains measurements of 150 flowers across three species.
- Create a new file called train.py in your project folder.
- Add the following code to set up imports, connect to MLflow, load the data, and define three hyperparameter configurations:
import mlflow
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# Connect to local MLflow server
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("Iris Classification")
# Load data
X, y = datasets.load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Define hyperparameter configurations to compare
configs = [
{"solver": "lbfgs", "max_iter": 200, "C": 1.0},
{"solver": "lbfgs", "max_iter": 1000, "C": 0.5},
{"solver": "lbfgs", "max_iter": 500, "C": 2.0},
]
What does this code do?
The first two lines after imports connect your script to the MLflow server running at http://localhost:5000 and create (or select) an experiment called Iris Classification. All runs will be grouped under this experiment.
The train_test_split function splits the data into 80% for training and 20% for testing. The configs list defines three different hyperparameter combinations for Logistic Regression. Each combination varies max_iter (how many iterations the model trains) and C (how strongly the model regularizes).
- Save train.py.
Seeing an import error?
Make sure your virtual environment is activated before running the script. If mlflow or sklearn can't be found, the packages may not be installed in the active environment.
Help me debug my import error in train.py.
Add the training loop with MLflow logging
Now you need a loop that trains a model for each configuration and logs everything to MLflow. Each iteration creates a separate "run" in your experiment.
- Add the following training loop below your configs list in train.py:
for i, params in enumerate(configs):
with mlflow.start_run(run_name=f"logistic-regression-{i+1}"):
# Log parameters
mlflow.log_params(params)
# Train model
lr = LogisticRegression(**params, random_state=42)
lr.fit(X_train, y_train)
# Evaluate
y_pred = lr.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
mlflow.log_metric("accuracy", accuracy)
# Log model
mlflow.sklearn.log_model(sk_model=lr, name="iris_model")
print(f"Run {i+1}: accuracy={accuracy:.4f}, C={params['C']}, max_iter={params['max_iter']}")
What does this code do?
- mlflow.start_run() creates a new tracked run. Each run gets a descriptive name like logistic-regression-1.
- mlflow.log_params(params) records the hyperparameters (solver, max_iter, C) so you can see exactly what each run used.
- mlflow.log_metric("accuracy", accuracy) saves the accuracy score so you can compare performance across runs.
- mlflow.sklearn.log_model() saves the trained model itself as an artifact, so you can reload and use it later.
Why manual logging instead of autologging?
MLflow offers mlflow.sklearn.autolog() which automatically logs parameters, metrics, and models with zero code. It's convenient for quick experiments.
Manual logging teaches you exactly what's being tracked and gives you full control over which metrics and artifacts are recorded. This understanding transfers to any ML framework, not just scikit-learn.
- Save train.py.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before running it.
ⓧ I'd like to double check the full code
import mlflow
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# Connect to local MLflow server
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("Iris Classification")
# Load data
X, y = datasets.load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Define hyperparameter configurations to compare
configs = [
{"solver": "lbfgs", "max_iter": 200, "C": 1.0},
{"solver": "lbfgs", "max_iter": 1000, "C": 0.5},
{"solver": "lbfgs", "max_iter": 500, "C": 2.0},
]
for i, params in enumerate(configs):
with mlflow.start_run(run_name=f"logistic-regression-{i+1}"):
# Log parameters
mlflow.log_params(params)
# Train model
lr = LogisticRegression(**params, random_state=42)
lr.fit(X_train, y_train)
# Evaluate
y_pred = lr.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
mlflow.log_metric("accuracy", accuracy)
# Log model
mlflow.sklearn.log_model(sk_model=lr, name="iris_model")
print(f"Run {i+1}: accuracy={accuracy:.4f}, C={params['C']}, max_iter={params['max_iter']}")
Run the script and explore the MLflow UI
Time to run your training script and see MLflow in action. Make sure your MLflow server is still running in its terminal window.
- Open a new terminal (keep the MLflow server running in the other one).
- Activate your virtual environment if it's not already active.
- Run the training script by running this command:
python train.py
You should see output showing three runs with their accuracy scores, similar to this:
Run 1: accuracy=1.0000, C=1.0, max_iter=200
Run 2: accuracy=1.0000, C=0.5, max_iter=1000
Run 3: accuracy=1.0000, C=2.0, max_iter=500
What should I see?
Your accuracy values may differ slightly from the example above. The Iris dataset is small and well-separated, so Logistic Regression often achieves very high accuracy. The important thing is that all three runs completed without errors.
Seeing a connection error?
If you see an error mentioning a connection failure to localhost:5000, your MLflow server is not running. Switch to the terminal where you started it and re-run mlflow server --port 5000.
Help me fix my MLflow connection error when running train.py.
Now explore your results in the MLflow UI.
- Switch to your browser and open http://localhost:5000.
- In the left sidebar, click on the Iris Classification experiment.
- You should see three runs listed in the table: logistic-regression-1, logistic-regression-2, and logistic-regression-3.
- Click on any run to view its details. You'll see the logged parameters (solver, max_iter, C), the accuracy metric, and the saved model artifact.
What is a run in MLflow?
A run is a single execution of your training code with a specific set of hyperparameters. MLflow records everything about that execution: what parameters you used, what metrics you achieved, and the model itself.
Grouping runs into experiments lets you compare different approaches side by side. This is the foundation of systematic ML experimentation.
Your first experiment is tracked and visible in the MLflow UI. Next up, you'll compare these runs side by side and identify the best-performing model.
Compare Multiple Experiments
In the previous step, you trained three Logistic Regression models and logged every run to MLflow.
Training models is only half the job. The real power of experiment tracking is comparing runs side by side to find which hyperparameters produce the best results. In this step, you will use the MLflow UI to compare your three runs and identify the winning configuration.
In this step, get ready to:
- Review how your training script structures multiple experiment runs.
- Compare accuracy across runs using the MLflow UI table view.
- Identify the best-performing model by its accuracy score.
Review the experiment configurations
Your train.py script already trains three models with different hyperparameter configurations. Each run is tracked separately in MLflow so you can compare them.
- Switch back to train.py in your editor and review the configs list:
# Define hyperparameter configurations to compare
configs = [
{"solver": "lbfgs", "max_iter": 200, "C": 1.0},
{"solver": "lbfgs", "max_iter": 1000, "C": 0.5},
{"solver": "lbfgs", "max_iter": 500, "C": 2.0},
]
What do these hyperparameters control?
Each configuration varies two values: max_iter (how many optimization steps the model takes to learn) and C (how strictly the model fits the training data).
A higher C means the model tries harder to classify every training sample correctly, which can improve accuracy or cause overfitting. A lower C makes the model more conservative.
- Now review the loop that trains each configuration and logs it as a named run:
for i, params in enumerate(configs):
with mlflow.start_run(run_name=f"logistic-regression-{i+1}"):
# Log parameters
mlflow.log_params(params)
# Train model
lr = LogisticRegression(**params, random_state=42)
lr.fit(X_train, y_train)
# Evaluate
y_pred = lr.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
mlflow.log_metric("accuracy", accuracy)
# Log model
mlflow.sklearn.log_model(sk_model=lr, name="iris_model")
print(f"Run {i+1}: accuracy={accuracy:.4f}, C={params['C']}, max_iter={params['max_iter']}")
What makes this code comparison-ready?
- mlflow.start_run(run_name=...) creates a separate tracked run for each config, with a human-readable name like logistic-regression-1.
- mlflow.log_params(params) records the exact hyperparameters so you can see what was different between runs.
- mlflow.log_metric("accuracy", accuracy) records the result so you can sort and compare performance.
- mlflow.sklearn.log_model(...) saves the trained model as an artifact, so you can retrieve the best one later.
Compare runs in the MLflow UI
Your three runs are already logged from when you ran the script in the previous step. Now you will use the MLflow UI to compare them visually.
- Return to the MLflow UI in your browser at http://localhost:5000.
- Click on the Iris Classification experiment in the left sidebar.
- You should see your three runs listed: logistic-regression-1, logistic-regression-2, and logistic-regression-3.
Only seeing one run or no runs?
If your runs are missing, make sure your MLflow server is still running in its terminal window. If it stopped, restart it with mlflow server --port 5000 and refresh the page.
If you only see one run, you may need to re-run your training script. Run python train.py again in your project terminal.
help me figure out why my MLflow runs are not showing up
- Click the Columns button above the runs table.
- Add the accuracy metric column by selecting it under Metrics.
- Add the C and max_iter parameter columns by selecting them under Parameters.
You should now see a table with each run's name, accuracy score, and the hyperparameters used.
- Click the accuracy column header to sort the runs by accuracy in descending order.
Why is this comparison view useful?
In a real ML project, you might run hundreds of experiments with different hyperparameters. The table view lets you quickly sort by any metric and see which configuration performed best without digging through logs or terminal output.
This is the same workflow ML engineers use in production to decide which model to deploy.
Identify the best run
With the table sorted by accuracy, the top row shows your best-performing model.
- Note which run has the highest accuracy score. Check the C and max_iter values for that run.
- Click on the best run's name to open its detail page.
- On the run detail page, review the Parameters section to confirm the hyperparameters.
- Scroll down to the Artifacts section to see the saved iris_model artifact.
What should I see?
The run detail page shows everything MLflow captured: the exact parameters used, the accuracy metric, the model artifact, and metadata like the run duration. The iris_model artifact in the Artifacts section is the serialized trained model that you will register in the next step.
Keep track of your best run
You do not need to memorize the run ID. In the next step, you will write a script that programmatically finds the best run by querying MLflow. This is more reliable than manually copying IDs.
You have identified your best model by comparing experiments in the MLflow UI. Next, you will register that model in MLflow's Model Registry and assign it a "champion" alias so it can be loaded for predictions.
Register Your Best Model
You've compared three model configurations and identified the best performer in the MLflow UI. Now it's time to formally register that model so it can be tracked, versioned, and referenced by name.
In production ML teams, trained models don't just sit in experiment logs. They get promoted to a Model Registry, a central catalog where each model has a name, version history, and deployment status. This is how teams know which model is "the one" serving predictions in production.
In this step, get ready to:
- Register the best-performing model in MLflow's Model Registry.
- Set a "champion" alias to mark it as the production model.
- Verify the registration in the MLflow UI.
Create your model registration script
Instead of manually clicking through the UI, you'll register your model programmatically. This approach is repeatable and can be automated in a real deployment pipeline.
- Create a new file called register_model.py in your project folder.
- Add the following code to connect to MLflow, find the best run, and register the model:
import mlflow
from mlflow import MlflowClient
# Connect to local MLflow server
mlflow.set_tracking_uri("http://localhost:5000")
client = MlflowClient()
# Find the best run in the experiment
experiment = client.get_experiment_by_name("Iris Classification")
runs = client.search_runs(
experiment_ids=[experiment.experiment_id],
order_by=["metrics.accuracy DESC"],
max_results=1,
)
best_run = runs[0]
print(f"Best run: {best_run.info.run_id}, accuracy: {best_run.data.metrics['accuracy']:.4f}")
# Register the best model
model_uri = f"runs:/{best_run.info.run_id}/iris_model"
model_name = "IrisClassifier"
result = mlflow.register_model(model_uri=model_uri, name=model_name)
print(f"Registered model '{model_name}' version {result.version}")
What does this code do?
This script automates finding and registering your best model:
- MlflowClient() creates a client that can query experiments, runs, and the Model Registry directly.
- search_runs() queries all runs in the experiment, sorted by accuracy in descending order, and returns the top result.
- The model_uri points to the specific model artifact inside the best run using the runs:/ scheme.
- mlflow.register_model() takes that artifact and creates a new registered model named "IrisClassifier" in the Registry, starting at version 1.
Set the champion alias and add a tag
A registered model can have multiple versions over time. Aliases let you point to a specific version by a human-readable name like "champion" instead of a version number.
- Add the following code at the bottom of register_model.py:
# Set the champion alias
client.set_registered_model_alias(model_name, "champion", result.version)
print(f"Set 'champion' alias to version {result.version}")
# Add a tag to the model version
client.set_model_version_tag(model_name, result.version, "validation_status", "approved")
print("Tagged model version as approved")
What does this code do?
set_registered_model_alias() assigns the name "champion" to version 1 of your model. Any script that loads models:/IrisClassifier@champion will always get this version until you reassign the alias.
set_model_version_tag() adds a key-value tag (validation_status: approved) to the model version. Tags are metadata labels that help teams track whether a model has been reviewed, tested, or cleared for deployment.
- Save register_model.py.
- Run the script by executing this command in a new terminal (keep the MLflow server running in the other one):
python register_model.py
- You should see output showing the best run ID, its accuracy, the registered model name and version, the alias assignment, and the tag confirmation.
Seeing a connection error?
Make sure your MLflow tracking server is still running in the other terminal window. If it stopped, restart it with mlflow server --port 5000 before running the script again.
If you see an error about the experiment not being found, double-check that you ran train.py successfully in the previous step. Help me debug my register_model.py script
✔️ Awesome, I've got everything!
Great. Make sure you've saved register_model.py before moving on.
ⓧ I'd like to double check the full code
import mlflow
from mlflow import MlflowClient
# Connect to local MLflow server
mlflow.set_tracking_uri("http://localhost:5000")
client = MlflowClient()
# Find the best run in the experiment
experiment = client.get_experiment_by_name("Iris Classification")
runs = client.search_runs(
experiment_ids=[experiment.experiment_id],
order_by=["metrics.accuracy DESC"],
max_results=1,
)
best_run = runs[0]
print(f"Best run: {best_run.info.run_id}, accuracy: {best_run.data.metrics['accuracy']:.4f}")
# Register the best model
model_uri = f"runs:/{best_run.info.run_id}/iris_model"
model_name = "IrisClassifier"
result = mlflow.register_model(model_uri=model_uri, name=model_name)
print(f"Registered model '{model_name}' version {result.version}")
# Set the champion alias
client.set_registered_model_alias(model_name, "champion", result.version)
print(f"Set 'champion' alias to version {result.version}")
# Add a tag to the model version
client.set_model_version_tag(model_name, result.version, "validation_status", "approved")
print("Tagged model version as approved")
Verify in the MLflow UI
- Switch back to your browser at http://localhost:5000.
- Click the **Models** link in the top navigation bar to open the Model Registry.
- You should see IrisClassifier listed as a registered model.
- Click on IrisClassifier to view its details.
- Confirm that version 1 is listed with the champion alias and the validation_status: approved tag.
Why aliases instead of version numbers?
Aliases decouple your deployment code from specific version numbers. When you train a better model next week, you can reassign the "champion" alias to the new version without changing any downstream code that loads the model.
This is the same pattern production teams use for blue-green deployments and canary releases.
Your model is now officially registered, versioned, and tagged in the Model Registry. Next up, you'll load this champion model and use it to make predictions on new data.
Load and Use the Champion Model
Your best model is now registered in the Model Registry with a "champion" alias. But a registered model only has value if you can load it and use it.
In this step, you will write an inference script that pulls the champion model directly from the registry using its alias. This is how production systems serve models. They never hardcode a run ID or file path.
In this step, get ready to:
- Create an inference script that loads the champion model by alias.
- Run predictions on new sample data and verify the results.
Create predict.py and load the champion model
Instead of referencing a specific run ID or file path, you will load the model using its alias URI. This means any script or service that uses the champion alias automatically picks up whichever model version is currently promoted.
- Create a new file called predict.py in your project folder.
- Add the following code to connect to MLflow and load the champion model:
import mlflow
import numpy as np
# Connect to local MLflow server
mlflow.set_tracking_uri("http://localhost:5000")
# Load the champion model using the alias
model_name = "IrisClassifier"
model_uri = f"models:/{model_name}@champion"
print(f"Loading model from: {model_uri}")
model = mlflow.pyfunc.load_model(model_uri)
What does this code do?
The alias URI models:/IrisClassifier@champion tells MLflow to look up whichever model version currently holds the "champion" alias. You set this alias in the previous step.
The mlflow.pyfunc.load_model() function returns a generic Python model wrapper. It works with any ML framework MLflow supports, not just scikit-learn.
Why use an alias instead of a version number?
Aliases decouple your inference code from specific model versions. When you train a better model next week, you promote it to "champion" and every service that references that alias automatically uses the new version. No code changes needed.
Add prediction logic and run the script
With the model loaded, you need sample data to predict on. The Iris dataset has four features per sample: sepal length, sepal width, petal length, and petal width.
- Add the following code below the model loading section in predict.py:
# Sample data for prediction (sepal length, sepal width, petal length, petal width)
samples = np.array([
[5.1, 3.5, 1.4, 0.2], # Expected: setosa (0)
[6.2, 2.9, 4.3, 1.3], # Expected: versicolor (1)
[7.7, 3.0, 6.1, 2.3], # Expected: virginica (2)
])
# Make predictions
predictions = model.predict(samples)
# Map class indices to species names
species = ["setosa", "versicolor", "virginica"]
print("\nPredictions:")
for i, (sample, pred) in enumerate(zip(samples, predictions)):
print(f" Sample {i+1}: {sample} -> {species[int(pred)]}")
What does this code do?
Three hand-picked samples represent one flower from each Iris species. The model predicts a class index (0, 1, or 2) for each sample.
The species list maps those numeric indices back to human-readable names so you can verify the model predicts the correct species for each sample.
- Save predict.py.
- Run the script by executing this command:
python predict.py
- You should see output showing the model URI being loaded, followed by three predictions mapping each sample to a species name (setosa, versicolor, virginica).
Seeing a model not found error?
Make sure your MLflow tracking server is still running in the other terminal. The script connects to http://localhost:5000 to fetch the model.
Confirm that you ran register_model.py successfully in the previous step. The "champion" alias must exist on the IrisClassifier model for the load to work.
help me debug a model not found error when loading from the MLflow registry
✔️ Awesome, I've got everything!
Great. Double check you have saved predict.py and confirmed the predictions output correctly.
ⓧ I'd like to double check the full code
import mlflow
import numpy as np
# Connect to local MLflow server
mlflow.set_tracking_uri("http://localhost:5000")
# Load the champion model using the alias
model_name = "IrisClassifier"
model_uri = f"models:/{model_name}@champion"
print(f"Loading model from: {model_uri}")
model = mlflow.pyfunc.load_model(model_uri)
# Sample data for prediction (sepal length, sepal width, petal length, petal width)
samples = np.array([
[5.1, 3.5, 1.4, 0.2], # Expected: setosa (0)
[6.2, 2.9, 4.3, 1.3], # Expected: versicolor (1)
[7.7, 3.0, 6.1, 2.3], # Expected: virginica (2)
])
# Make predictions
predictions = model.predict(samples)
# Map class indices to species names
species = ["setosa", "versicolor", "virginica"]
print("\nPredictions:")
for i, (sample, pred) in enumerate(zip(samples, predictions)):
print(f" Sample {i+1}: {sample} -> {species[int(pred)]}")
You now have a complete MLOps workflow: training, tracking, registering, and serving models from a single system. Next up is the Secret Mission, where you will automate the promotion process so the best model always becomes the champion.
Secret mission
Automated Model Promotion
Your current workflow requires you to manually pick and promote the best model. What if you could add a completely different model type to the competition and have a script automatically crown the winner?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- MLflow tracking server (running process in your terminal).
- Project folder with Python scripts (train.py, register_model.py, predict.py, auto_promote.py).
- Virtual environment (.venv) with installed packages (MLflow, scikit-learn).
- MLflow SQLite database (mlflow.db) storing your experiment data.
Keep everything running
No action needed. Choose this if you are still actively building or want to keep experimenting with new models and hyperparameters.
- Your MLflow server continues running in its terminal window. Access the UI at http://localhost:5000 any time.
- All experiment data, registered models, and aliases persist in mlflow.db.
- Your virtual environment stays active with MLflow and scikit-learn installed.
Pause - I'll come back to this later
Shut down running processes to free up memory. Your files and data stay on disk so you can pick up where you left off.
- Switch to the terminal running the MLflow server and press Ctrl+C to stop it.
- Deactivate your virtual environment by running:
deactivate
When you come back, reactivate the environment and restart the server:
cd ~/Desktop/mlflow-tracking
source .venv/bin/activate
mlflow server --port 5000
On Windows, use .venv\Scripts\activate instead.
Delete - I don't want to use this again
Remove all project resources and start fresh. This deletes your scripts, experiment data, and installed packages.
- Switch to the terminal running the MLflow server and press Ctrl+C to stop it.
- Deactivate the virtual environment by running:
deactivate
- Delete the entire project folder by running this on macOS/Linux:
cd ~/Desktop
rm -rf mlflow-tracking
- On Windows, run this in PowerShell:
cd ~\Desktop
Remove-Item -Recurse -Force mlflow-tracking
This removes all Python scripts, the virtual environment, and the mlflow.db database containing your experiment history.
Nice Work!
Nice Work!
You just built a real MLOps workflow from scratch. You trained multiple machine learning models, tracked every experiment, compared results visually, versioned your best model in a registry, and loaded it for inference using an alias.
You've learned how to:
- Train multiple ML models and track every experiment with MLflow, comparing performance across hyperparameter configurations in the MLflow UI.
- Register your best model in a Model Registry with versioning and descriptive tags.
- Assign a champion alias and load the production model for inference using a registry URI.
- Built an automated promotion pipeline that finds the best-performing model across multiple algorithms and promotes it to champion.
Ready to quiz yourself?