Machine learning experiments depend on more than source code. A model's performance can change when the training dataset, preprocessing logic, hyperparameters, or software environment changes. If a team cannot identify exactly which inputs produced a particular model, reproducing the experiment becomes difficult.

Git works well for versioning source code, configuration files, and small text-based assets. However, large datasets, model checkpoints, and other binary files can quickly make a Git repository difficult to manage.

Data Version Control (DVC) addresses this problem by connecting data and machine learning artifacts to a version-controlled workflow. It allows teams to track large files, define reproducible pipelines, and associate experiment results with the code and data used to produce them.

This article explains how DVC works, how to version a dataset, how to build a reproducible training pipeline, and how to compare machine learning experiments.

What Is DVC?

DVC is an open-source tool designed to manage data and machine learning workflows alongside Git. It tracks the relationship between source code, datasets, pipeline stages, and generated artifacts without requiring every large file to be stored directly in Git.

Instead of committing a large dataset to Git, DVC stores metadata in the repository and keeps the actual data in a configured storage location. The storage might be local, on a shared server, or in a supported cloud object store.

A typical project may look like this:

Code
ml-project/
├── data/
│   └── training.csv
├── src/
│   └── train.py
├── models/
├── metrics.json
├── params.yaml
├── dvc.yaml
├── dvc.lock
└── .git/

The files serve different purposes:

  • training.csv contains the dataset used by the model.

  • train.py contains the training code.

  • params.yaml stores configurable training parameters.

  • dvc.yaml defines the pipeline stages.

  • dvc.lock records the resolved dependencies and outputs of pipeline stages.

  • metrics.json contains evaluation results.

The exact files depend on the project. DVC generates and updates its metadata as you add data and execute pipelines.

Git and DVC work together: Git versions the code and DVC metadata, while DVC manages references to the larger data and model artifacts.

Install DVC and Initialize a Project

Before tracking data, install Git and Python. DVC can be installed into a virtual environment using Python's package manager.

Bash
python -m venv .venv

Activate the environment.

On Windows:

PowerShell
.venv\Scripts\Activate.ps1

On Linux or macOS:

Bash
source .venv/bin/activate

Install DVC:

Bash
python -m pip install dvc

For cloud storage integrations, install the appropriate DVC storage extra for your provider. For example, an S3-based remote generally requires the corresponding S3 dependencies.

Create the project directory and initialize Git and DVC:

Bash
mkdir ml-project
cd ml-project

git init
dvc init

DVC initialization creates the configuration and metadata files needed to manage data in the repository.

Commit the initial setup:

Bash
git add .dvc .gitignore
git commit -m "Initialize DVC project"

DVC versions can introduce changes to commands and configuration options. Use a compatible version across development and CI environments, and check the installed version when troubleshooting.

Track a Dataset Without Committing It to Git

Suppose the project uses a CSV file containing historical customer transactions.

Place the file in the data directory:

Code
data/training.csv

Track it with DVC:

Bash
dvc add data/training.csv

DVC creates a metadata file, typically data/training.csv.dvc, and updates the relevant Git ignore rules so the large data file is not accidentally committed to Git.

The metadata file contains information that allows DVC to identify and retrieve the tracked data. It does not contain the entire dataset.

Commit the metadata:

Bash
git add data/training.csv.dvc .gitignore
git commit -m "Track training dataset with DVC"

The dataset itself remains in the working directory. To make it available to other developers or CI workers, configure DVC storage and push the data there.

Configure Remote Storage

A shared remote allows team members to retrieve the same dataset instead of keeping independent copies.

For example, a local directory can be used as a simple remote:

Bash
dvc remote add -d storage /path/to/shared/dvc-storage

The path must refer to a suitable storage location accessible to the machines that need the data. A local path is useful for testing but is not automatically shared across different computers.

Commit the remote configuration:

Bash
git add .dvc/config
git commit -m "Configure DVC remote storage"

Push the tracked data:

Bash
dvc push

For a cloud remote, configure the provider-specific location and authentication according to DVC's supported storage options. Keep credentials outside version-controlled configuration files.

A developer who clones the repository can retrieve the tracked data using:

Bash
git clone <repository-address>
cd ml-project

dvc pull

Replace <repository-address> with the actual Git repository address.

This workflow separates source-code history from large data storage while keeping both connected through version control.

Build a Reproducible Training Pipeline

Tracking a dataset is useful, but a machine learning experiment also depends on the operations that transform the data and train the model.

DVC pipelines describe these operations as stages. Each stage declares its command, dependencies, outputs, and optionally parameters and metrics.

Consider a training script that reads a CSV file, trains a model, and writes the resulting artifact.

Create the Training Script

Create src/train.py:

Python
import json
from pathlib import Path

import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
import joblib

data_path = Path("data/training.csv")
model_path = Path("models/model.joblib")
metrics_path = Path("metrics.json")

df = pd.read_csv(data_path)

# Example dataset requirements:
# The CSV contains a target column named "target".
if "target" not in df.columns:
    raise ValueError("The dataset must contain a 'target' column.")

X = df.drop(columns=["target"])
y = df["target"]

if X.empty or len(df) < 2:
    raise ValueError("The dataset does not contain enough training data.")

X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.2,
    random_state=42,
    stratify=y if y.nunique() > 1 else None
)

model = make_pipeline(
    StandardScaler(),
    LogisticRegression(max_iter=1000, random_state=42)
)

model.fit(X_train, y_train)
predictions = model.predict(X_test)

accuracy = accuracy_score(y_test, predictions)

model_path.parent.mkdir(parents=True, exist_ok=True)

joblib.dump(model, model_path)

metrics_path.write_text(
    json.dumps({"accuracy": accuracy}, indent=2),
    encoding="utf-8"
)

print(f"Model accuracy: {accuracy:.4f}")

This example assumes that the CSV contains numeric features suitable for the chosen preprocessing and model. Categorical data, missing values, class imbalance, and other dataset-specific requirements may require additional preprocessing.

The fixed random seed makes the split more consistent, but it does not guarantee identical results across all library versions, platforms, or hardware configurations.

Install the required dependencies:

Bash
python -m pip install pandas scikit-learn joblib

Record the project's dependency versions using an appropriate dependency-management workflow. For example, a pinned requirements file can help CI and other developers recreate a compatible environment.

Define the Pipeline

Create dvc.yaml in the project root:

Code
stages:
  train:
    cmd: python src/train.py
    deps:
      - src/train.py
      - data/training.csv
    outs:
      - models/model.joblib
    metrics:
      - metrics.json

This stage tells DVC to execute the training script when its relevant dependencies change.

The deps section identifies inputs that affect the training stage. The outs section identifies the generated model artifact. The metrics section identifies evaluation results that DVC can track and compare.

Run the pipeline:

Bash
dvc repro

DVC evaluates the stage dependencies and determines whether the stage needs to run. If the relevant inputs have not changed and the existing outputs are available, DVC can reuse the existing results instead of repeating the work.

After a successful run, commit the pipeline definition and generated lock metadata:

Bash
git add dvc.yaml dvc.lock
git commit -m "Add reproducible training pipeline"

If the pipeline produces changes to other tracked metadata or configuration files, include those as appropriate.

Push the model artifact to remote storage:

Bash
dvc push

Now the Git commit identifies the pipeline configuration, while DVC tracks the data and generated model artifact associated with that state.

Track Parameters Instead of Hardcoding Them

Machine learning experiments often differ only in a few settings, such as regularization strength, learning rate, batch size, or number of training iterations.

Keeping these values inside the training script makes it harder to compare experiments systematically.

Create a params.yaml file:

Code
train:
  test_size: 0.2
  random_state: 42
  max_iter: 1000
  regularization: 1.0

The training script can load these values and use them to configure the model. For example, regularization can be passed to the C parameter of LogisticRegression, while max_iter controls the maximum number of solver iterations.

Add the parameter file to the training stage:

Code
stages:
  train:
    cmd: python src/train.py
    deps:
      - src/train.py
      - data/training.csv
    params:
      - train
    outs:
      - models/model.joblib
    metrics:
      - metrics.json

The parameter section tells DVC that the train section of params.yaml is an input to the stage. The script must actually read and use these parameters; declaring them in dvc.yaml alone does not change the training behavior.

When you change a tracked parameter, DVC can detect that the stage's inputs have changed and rerun the relevant work.

Compare Machine Learning Experiments

A reproducible workflow should make it possible to compare experiments rather than merely rerun them.

For example, you might train one model with a regularization value of 1.0 and another with 0.1. If the script reads this value from params.yaml, you can use DVC experiments to evaluate the alternative configuration without permanently changing the main project state.

Run an experiment with a modified parameter:

Bash
dvc exp run --set-param train.regularization=0.1

DVC runs the pipeline with the specified parameter override and records the experiment state.

To inspect recorded experiments, use:

Bash
dvc exp show

The command displays available experiment information, including tracked metrics when configured.

For a more useful comparison, track metrics that match the actual objective. Accuracy alone may not be sufficient for imbalanced classification problems. Precision, recall, F1 score, ROC-AUC, or a business-specific metric may provide a better evaluation.

Also keep the test dataset and evaluation procedure consistent across experiments. If each experiment uses a different test split, apparent improvements may result from differences in the data rather than a better model.

Restore an Earlier Dataset or Model

One of DVC's main benefits is the ability to retrieve artifacts associated with an earlier version-controlled state.

Suppose a dataset update causes model performance to decline. If the earlier Git commit tracks the previous DVC metadata, you can inspect that commit and restore the corresponding files.

For example:

Bash
git log --oneline
git checkout <previous-commit>
dvc pull

Replace <previous-commit> with the desired Git commit identifier.

The first command lists the repository history. The second checks out the selected version of the repository, including the DVC metadata. The third retrieves the data and artifacts associated with that state from the configured remote, when they are available.

Checking out an older commit can affect your working tree. Commit or safely preserve any uncommitted changes before switching versions.

For routine collaboration, prefer a dedicated branch or another isolated working environment when investigating historical experiments.

DVC vs. Git for Machine Learning Projects

Capability

Git

DVC

Source-code versioning

Core capability

Works alongside Git

Large dataset management

Can track files, but large binaries can make repositories cumbersome

Tracks data through metadata and external storage

Pipeline definition

Does not provide native ML pipeline reproduction

Supports dependency-based pipeline stages

Experiment tracking

Tracks commits and code changes

Supports experiment workflows and metric comparisons

Artifact storage

Stores committed files in repository history

Can use configured local or remote storage

Reproducibility

Captures source-code history

Connects code, data, parameters, and pipeline outputs

DVC does not replace Git. The two tools solve related but different problems and are most useful when used together.

Common Mistakes to Avoid

Forgetting to Push Data to Remote Storage

Committing a .dvc metadata file does not automatically make the dataset available to teammates.

Run dvc push after tracking or generating the required artifacts, and verify that the remote contains the expected data.

Changing Data Outside the Versioned Workflow

If a dataset changes without the corresponding DVC metadata being updated, the repository may no longer describe the actual working data correctly.

After modifying tracked data, run the appropriate DVC commands, review the resulting metadata changes, and commit them with the related code changes.

Assuming a Fixed Random Seed Guarantees Reproducibility

A fixed seed controls some sources of randomness, but library versions, preprocessing behavior, hardware, parallel execution, and nondeterministic algorithms can still affect results.

Record dependencies, parameters, data versions, and relevant environment details. Test reproducibility by running the same pipeline in a clean environment.

Tracking Metrics Without Defining Their Meaning

A metric is useful only when its calculation is consistent and appropriate for the task.

Document the evaluation method, maintain consistent data splits, and avoid selecting models based on metrics that do not reflect the actual problem.

Storing Secrets in the Repository

Cloud credentials, API keys, and other secrets should not be committed to Git or DVC configuration files.

Use a secure secret-management mechanism and grant only the permissions required by the pipeline.

Advantages and Limitations

DVC helps teams manage large datasets without bloating Git history, reproduce pipeline stages, compare experiments, and restore artifacts associated with previous project versions. Its Git-based workflow also makes it easier to review changes to pipeline definitions and parameters.

However, DVC introduces additional configuration and operational responsibilities. Teams must manage remote storage, access permissions, dependency versions, and artifact retention. Reproducibility is also not automatic: the pipeline must explicitly declare its dependencies, and the code must account for nondeterministic behavior and environment differences.

DVC records and retrieves the artifacts you track. It cannot compensate for missing data, undocumented external dependencies, or training code that relies on uncontrolled inputs.

Summary

DVC helps make machine learning experiments reproducible by connecting datasets, model artifacts, parameters, and pipeline stages to version-controlled project history.

A practical workflow starts by tracking large files with DVC, storing them in a shared remote, defining pipeline dependencies in dvc.yaml, and committing the corresponding metadata through Git. Parameter tracking and experiment comparison then help teams understand why one model performs differently from another.

For reliable results, combine DVC with pinned dependencies, consistent evaluation datasets, secure credentials, and documented training procedures. This makes it easier to investigate regressions, reproduce previous experiments, and collaborate without relying on manually maintained copies of datasets and models.