MLOpsThe MLOps lifecycle · MLflow 3.17 · DVC 3.67 · FastAPI · Docker · GitHub Actions · Evidently · Python 3.11+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
50 small wins to finish your pathNext lesson →

Logging params and metrics

Logging params and metrics is the pair of calls that put the settings you chose and the numbers you measured into the same run, so a score is never separated from the configuration that produced it.

Last updated: 19 Oct, 2026 · MLOps

The Experiment tracking lesson logged a single metric to prove the run survived. A run worth comparing carries more: the video logs all the parameters of its parameter file first, then the metrics it measured, into one open run. The three calls are log_params, log_metrics and set_tag, and the difference between them is what this lesson is about.

Which value is which

  • A parameter is an input you chose. n_estimators, max_depth and random_state are parameters: they decide what the run does.
  • A metric is a number the run measured. r2, MAE and RMSE are metrics: they are the result, so MLflow stores them as floats you can sort and chart.
  • A tag is a label on the run. A lifecycle stage or an owner is a tag: you filter by it and you never compute with it.

Logging many values at once

Syntax: the plural calls take a dictionary, so the parameter file and the metrics dictionary go in whole. log_param and log_metric remain for a single value.

python
mlflow.log_params({"n_estimators": 100, "max_depth": 12})   # the settings, as a dict
mlflow.log_metrics({"r2": 0.8485, "mae": 4.6996})           # the measurements, as a dict
mlflow.set_tag("stage", "experiment")                       # one label on the run

Pointing MLflow at a local store

The same two required lines open every tracked run: the opt-in MLflow 3.17 wants before it will use a file store, and the tracking URI that names the folder.

python
import os, shutil

os.environ["MLFLOW_ALLOW_FILE_STORE"] = "true"   # 3.17 refuses a local file store without this
shutil.rmtree("mlruns", ignore_errors=True)      # each lesson starts from an empty store
os.makedirs("mlruns/.trash", exist_ok=True)      # MLflow keeps deleted runs in .trash

import mlflow
mlflow.set_tracking_uri("file:./mlruns")
mlflow.set_experiment("student-performance")
start_run, log_params, log_metrics, and the file-store branch · from the Complete End to End Deep Learning Project With MLFLOW,DVC And Deployment · 2:33:40 to 2:35:44

The clip logs the deep-learning project's settings and its accuracy and loss, and branches on the tracking store to register the model on a hosted server; this run logs the forest's settings and its three regression metrics against the local file store.

Recording one run's settings and scores

fit is the student-performance trainer from the Experiment tracking lesson: it returns the fitted forest and a dictionary of its three scores. Those scores go straight into log_metrics, so no number is retyped on the way into the store.

python
model, scores = fit(max_depth=12)
ExampleParameters, metrics and a tag in one run
# the settings go in as params, the measurements as metrics, the label as a tag
with mlflow.start_run(run_name="depth-12"):
    mlflow.log_params({"n_estimators": 100, "max_depth": 12, "random_state": 42})
    mlflow.log_metrics(scores)
    mlflow.set_tag("stage", "experiment")

row = mlflow.search_runs(experiment_names=["student-performance"]).iloc[0]
print("params :", row["params.n_estimators"], row["params.max_depth"], row["params.random_state"])
print("metrics:", row["metrics.r2"], row["metrics.mae"], row["metrics.rmse"])
print("tag    :", row["tags.stage"])

Reading the row back

  • The parameters came back as strings, 100, 12 and 42, because MLflow stores a parameter as text: it is a label for the configuration, not a quantity.
  • The metrics came back as floats, r2 0.8485, MAE 4.6996 and RMSE 6.0721, which is what lets a query sort by r2 or threshold on it.
  • The tag came back under tags.stage, in its own namespace, so a tag never collides with a parameter of the same name.
  • One row holds the whole run. The configuration and its result are read together, which is the point of logging them together.
One tracked run drawn as a record with four compartments. The parameters compartment holds n_estimators 100, max_depth 12 and random_state 42, the settings you chose, stored as text. The metrics compartment holds r2 0.8485, MAE 4.6996 and RMSE 6.0721, the numbers the run measured, stored as floats you can sort. The tags compartment holds stage equals experiment, a label you filter by. The artifacts compartment holds the logged model and any file the run produced. All four are written under one experiment named student-performance, and a closing note says search_runs returns one row per run with a column for each parameter, metric and tag.

Params against metrics against tags

ParameterMetricTag
HoldsA setting you choseA number you measuredA label
Example heremax_depth 12r2 0.8485stage experiment
Stored asTextA float, with a history per stepText
Logged withlog_param, log_paramslog_metric, log_metricsset_tag

Where the three calls earn their keep

  • A sweep, where every run logs the same parameter names so the rows line up in one table.
  • A long training job, where log_metric is called once per epoch and MLflow keeps the whole curve rather than the last value.
  • A filtered search, where a tag such as the owner or the data version narrows thousands of runs to the ones you care about.
Watch out. Logging a measured number as a parameter is the common mistake. It is stored as text, so it will not sort, chart or threshold, and a query for the best run compares "0.8485" with "0.8374" as strings. If the run produced it, it is a metric.
Try it yourself
  • Move random_state out of log_params and into set_tag, then find it under a different column name.
  • Log r2 with log_param instead and watch the value come back as a string.
  • Add a second tag naming the dataset, then filter with mlflow.search_runs(filter_string="tags.stage = 'experiment'").

Slow is fine. Stopping is the only problem.