DVC metrics and plots
dvc metrics is a pair of commands that read the metrics files a pipeline declares and print them, either as a table of the current numbers or as the change since the last commit.
Last updated: 19 Oct, 2026 · MLOps
The reruns in dvc repro and the cache each ended with a line of metrics scrolling past in the log. Reading a result off a log is fine once. What a project needs is the numbers for the commit you are on, and the difference between them and the numbers of the commit before.
Declaring a metrics file
The train stage already names one. cache: false is the part that matters here: DVC leaves the file alone instead of moving it into the cache, so git tracks it and every commit carries the numbers that commit's model scored.
metrics:
- artifacts/model_trainer/metrics.json:
cache: falseThe file is the plain JSON the trainer writes, a flat map of name to number, which is the shape DVC reads.
{
"r2": 0.8485,
"mae": 4.6996,
"rmse": 6.0721
}Printing the current numbers
dvc metrics showPath mae r2 rmse artifacts/model_trainer/metrics.json 4.6996 0.8485 6.0721
One row per metrics file, one column per metric, in alphabetical order rather than the order the file lists them. With several stages reporting, each file gets its own row, which is the quickest view of a pipeline's results.
Comparing with the last commit
dvc metrics diff compares what is in the working folder with the latest committed version, which gives it one requirement, and meeting that requirement is the point of cache: false. Where the metrics file has never been committed, there is nothing to compare against:
dvc metrics diffDVC failed to load some metrics for following revisions: 'HEAD'. Path Metric HEAD workspace Change artifacts/model_trainer/metrics.json mae - 4.8467 - artifacts/model_trainer/metrics.json r2 - 0.8374 - artifacts/model_trainer/metrics.json rmse - 6.2904 -
The HEAD column is empty and the change is a dash, because the file is not in that commit. Commit artifacts/model_trainer/metrics.json alongside the code and dvc.lock, and the same command, after the depth change of the previous lesson, has both sides to work with:
dvc metrics diffPath Metric HEAD workspace Change artifacts/model_trainer/metrics.json mae 4.6996 4.8467 0.1471 artifacts/model_trainer/metrics.json r2 0.8485 0.8374 -0.0111 artifacts/model_trainer/metrics.json rmse 6.0721 6.2904 0.2183
- HEAD is the committed run, at depth 12, and workspace is the one in front of you, at depth 4.
- Change is workspace minus HEAD. MAE rose by 0.1471 and RMSE by 0.2183, while r2 fell by 0.0111.
- Read the sign, not the size. An increase in MAE or RMSE is worse, an increase in r2 is better, and DVC has no idea which of your metrics are which.
- Only changed metrics are listed unless you pass
--all.
Seeing which parameter moved
A metric that moved is half the story. The matching command over the parameter files says what you changed to cause it.
dvc params diffPath Param HEAD workspace params.yaml MAX_DEPTH 12 4
The two commands together are a record of one experiment: this parameter went from 12 to 4, and these three numbers moved this much.
Plotting a series with dvc plots
A metric is one number per run. A plot is a series inside one run: a loss curve over epochs, a prediction against its true value. DVC reads those from a CSV or JSON file that a stage writes, named under a plots key with the columns to put on each axis.
plots:
- logs.csv:
x: epoch
y: accuracydvc plots showfile:///Users/usr/src/dvc_plots/index.html
- The command writes a page, not a picture. It renders the series into
dvc_plots/index.htmland prints the path for you to open. - dvc plots diff is the counterpart of
dvc metrics diff: it draws the same series from two commits on one set of axes. - The student-performance pipeline has no series to draw, since one fit produces one set of numbers rather than a curve. The command and the definition above come from the DVC plots documentation linked below, and nothing on this page was run against them.
Metrics vs plots
| metrics | plots | |
|---|---|---|
| Holds | One number per name | A series of rows |
| File | A flat JSON or YAML map | A CSV or JSON of records |
| Shown by | dvc metrics show, dvc metrics diff | dvc plots show, dvc plots diff |
| Output | A table in the terminal | An HTML page you open |
| Good for | Comparing runs at a glance | Watching something move within a run |
Where the metric commands fit
- Before a pull request. Run the diff and paste it in: the review then shows the code change and what it did to the score.
- In a retraining job. The job can read the diff and refuse to promote a model whose r2 fell.
- Through a tuning session. Edit a parameter, rerun the pipeline, read the diff, and repeat without touching a spreadsheet.
cache: false, or let a .gitignore rule cover the artifacts folder, and the file never reaches a commit, so every diff prints the empty HEAD column above. The numbers are small text and belong in git.Related
- Previous: dvc repro and the cache
- Next: Serving the model with FastAPI
- Reference: dvc metrics diff
- Reference: dvc plots show
- Run
dvc metrics diff --allto see the metrics that did not move listed too. - Commit the depth-4 run, change
MAX_DEPTHto 8, rerun the pipeline and read the new diff against it. - Pass an older commit to the command, as
dvc metrics diff HEAD~1, to compare with a run further back.
Slow is fine. Stopping is the only problem.