Skip to content

Commit c004b47

Browse files
paper: Reproducible Machine Learning Workflows for Scientists with Pixi
1 parent 6d33953 commit c004b47

File tree

16 files changed

+658
-0
lines changed

16 files changed

+658
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
## Acknowledgements
2+
3+
Matthew Feickert is supported by the U.S. National Science Foundation (NSF) under Cooperative Agreement PHY-2323298 (IRIS-HEP) and by the US Research Software Sustainability Institute (URSSI) via grant G-2022-19347 from the Sloan Foundation.
4+
Ruben Arts is supported by prefix.dev GmbH.
5+
John Kirkham is supported by NVIDIA.
6+
The described work [@reproducible_machine_learning_scipy_2025_tutorial] was created in association with [@Feickert_Reproducible_Machine_Learning].

papers/matthew_feickert/banner.png

495 KB
Loading
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SCM syntax highlighting & preventing 3-way merges
2+
pixi.lock merge=binary linguist-language=YAML linguist-generated=true
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# pixi environments
2+
.pixi
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
ARG CUDA_VERSION="12"
2+
ARG ENVIRONMENT="gpu"
3+
4+
FROM ghcr.io/prefix-dev/pixi:noble AS build
5+
6+
# Redeclaring ARGS in a stage without a value inherits the global default
7+
ARG CUDA_VERSION
8+
ARG ENVIRONMENT
9+
10+
WORKDIR /app
11+
COPY . .
12+
ENV CONDA_OVERRIDE_CUDA=$CUDA_VERSION
13+
RUN pixi install --locked --environment $ENVIRONMENT
14+
RUN echo "#!/bin/bash" > /app/entrypoint.sh && \
15+
pixi shell-hook --environment $ENVIRONMENT -s bash >> /app/entrypoint.sh && \
16+
echo 'exec "$@"' >> /app/entrypoint.sh
17+
18+
FROM ghcr.io/prefix-dev/pixi:noble AS final
19+
20+
ARG ENVIRONMENT
21+
22+
WORKDIR /app
23+
COPY --from=build /app/.pixi/envs/$ENVIRONMENT /app/.pixi/envs/$ENVIRONMENT
24+
COPY --from=build /app/pixi.toml /app/pixi.toml
25+
COPY --from=build /app/pixi.lock /app/pixi.lock
26+
# The ignore files are needed for 'pixi run' to work in the container
27+
COPY --from=build /app/.pixi/.gitignore /app/.pixi/.gitignore
28+
COPY --from=build /app/.pixi/.condapackageignore /app/.pixi/.condapackageignore
29+
COPY --from=build --chmod=0755 /app/entrypoint.sh /app/entrypoint.sh
30+
31+
ENTRYPOINT [ "/app/entrypoint.sh" ]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[workspace]
2+
channels = ["conda-forge"]
3+
name = "ml-example"
4+
platforms = ["linux-64", "osx-arm64", "win-64"]
5+
version = "0.1.0"
6+
7+
[tasks]
8+
9+
[dependencies]
10+
python = ">=3.13.5,<3.14"
11+
12+
[feature.cpu.dependencies]
13+
pytorch-cpu = ">=2.7.1,<3"
14+
torchvision = ">=0.22.0,<0.23"
15+
16+
[feature.cpu.tasks.train-cpu]
17+
description = "Train MNIST on CPU"
18+
cmd = "python src/torch_MNIST.py --epochs 2 --save-model --data-dir data"
19+
20+
[feature.gpu.system-requirements]
21+
cuda = "12"
22+
23+
[feature.gpu.target.linux-64.dependencies]
24+
pytorch-gpu = ">=2.7.1,<3"
25+
torchvision = ">=0.22.0,<0.23"
26+
27+
[feature.gpu.tasks.train-gpu]
28+
description = "Train MNIST on GPU"
29+
cmd = "python src/torch_MNIST.py --epochs 14 --save-model --data-dir data"
30+
31+
[feature.inference.dependencies]
32+
matplotlib = ">=3.10.3,<4"
33+
34+
[environments]
35+
cpu = ["cpu"]
36+
gpu = ["gpu"]
37+
inference = ["gpu", "inference"]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Conda packages
2+
3+
Conda packages (`.conda` files) are language agnostic file archives that contain built code distributions and metadata.
4+
This is quite powerful, as it allows for arbitrary code to be built for any target platform and then packaged with its metadata.
5+
When a conda package is downloaded and then unpacked with a conda package management tool (e.g. Pixi, conda, mamba) it is then "installed" by copying the package's file directory tree to the base of the environment's directory tree.
6+
Package contents are also simple; they can only contain files and symbolic links.
7+
8+
### conda-forge
9+
10+
Conda packages can be distributed on package indexes that support the concept of "channels" which redirect URLs to directory trees of conda packages.
11+
Channel names serve as the base path for hosting packages.
12+
The most broadly used community channel for conda-packages is the `conda-forge` channel, which hosts the conda packages generated from builds on the global conda-forge community cyberinfrastructure.
13+
The conda-forge community operates in a GitHub organization that hosts Git repositions that contain conda package build recipes as well as automation infrastructure and continuous integration (CI) and continuous delivery (CD) workflows.
14+
This allows for conda-forge community members to submit and maintain recipes &mdash; instructions for conda package build systems &mdash; to build and distribute conda packages for multiple variants of computing platforms &mdash; combinations of operating systems and hardware architectures &mdash; for Linux, macOS, and Windows.

papers/matthew_feickert/cuda.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
## CUDA
2+
3+
CUDA (Compute Unified Device Architecture) is a parallel computing platform and programming model developed by NVIDIA for general computing on graphical processing units (GPUs). [@CUDA_paper; @CUDA_slides]
4+
The CUDA ecosystem provides Software Development Kits (SDKs) with APIs to CUDA that allow for software developers to write hardware accelerated programs with CUDA in various languages for NVIDIA GPUs.
5+
CUDA has official language support for C++, Fortran, and Python, with community support for Julia and other languages.
6+
While there are other types of hardware acceleration development platforms, as of 2025 CUDA is the most abundant platform for scientific computing that uses GPUs and effectively the default choice for major machine learning libraries and applications.
7+
8+
CUDA is closed source and proprietary to NVIDIA, which means that NVIDIA has historically limited the download access of the CUDA toolkits and drivers to registered NVIDIA developers (while keeping the software free (monetarily) to use).
9+
CUDA then required a multi-step installation process [@CUDA_install_guide] with manual steps and decisions based on the target platform and particular CUDA version.
10+
This meant that when CUDA enabled environments were setup on a particular machine they were powerful and optimized, but brittle to change and could easily be broken if system wide updates (like for security fixes) occurred.
11+
CUDA software environments were bespoke and not many scientists understood how to construct and curate them.
12+
13+
## CUDA packages on conda-forge
14+
15+
### Initial implementation
16+
17+
After discussion in late 2018 [@conda-forge_github_io_issue_687] to better support the scientific developer community, the CUDA packaging community agreed to use the Anaconda `defaults` channel's [@anaconda-defaults-channel] `cudatoolkit` package.
18+
Initially the `cudatoolkit` package was designed around Numba's CUDA needs [@conda-recipe-cudatoolkit], though it evolved to a bundle of redistributable CUDA libraries.
19+
In 2019, NVIDIA began packaging the `cudatoolkit` package in the [`nvidia` conda channel](https://anaconda.org/nvidia).
20+
With help from the broader community, the `cudatoolkit` package was added to `conda-forge` in 2020 [@staged-recipes-pr-12882].
21+
For the first time, this provided users the _ability to specify different versions of CUDA libraries_ and download them in newly created conda environments.
22+
23+
Supporting initial conda-forge CUDA builds required additional components:
24+
* [A conda-forge Docker image](https://github.com/conda-forge/docker-images/pull/93) using [the NVIDIA CUDA Docker images](https://hub.docker.com/r/nvidia/cuda/), which provided the NVIDIA build tools for compiling packages.
25+
* [A shim package](https://github.com/conda-forge/staged-recipes/pull/8229) to leverage the NVIDIA build tools within a conda package build.
26+
* [A CUDA build matrix in conda-forge's global pinnings](https://github.com/conda-forge/conda-forge-pinning-feedstock/pull/285), which tied these two pieces together.
27+
28+
These ideas were tied together in the first package build on September 20, 2019 [@ucx-split-feedstock-pr-14], and the initial implementation of this work was completed later in 2019.
29+
In 2020, support was expanded to [Windows CUDA builds](https://github.com/conda-forge/conda-forge-pinning-feedstock/pull/914).
30+
Lots of iteration on this work happened after, all using the same basic foundation.
31+
32+
### Revised implementation
33+
34+
After some time using these packages and build process, a few observations became clear.
35+
First, some packages used only a subset of the libraries, like the driver, the CUDA runtime library, or particular library components like cuBLAS.
36+
However, the `cudatoolkit` package shipped considerably more than that, so having finer specifications of dependencies would provide a better package maintainer and end-user experience.
37+
Second, some packages needed components that were not part of the `cudatoolkit` bundle like other libraries or parts of the build toolchain.
38+
Having some way to depend on these components would improve usability.
39+
Third, the infrastructure management overhead of custom Docker images and their integration into the conda-forge build matrix was cumbersome for conda-forge maintainers.
40+
Being able to install and use the build tools directly would simplify maintenance and benefit end-users wishing to use these build tools.
41+
42+
To address these issues, NVIDIA began working on a revised set of packages.
43+
These more closely matched packages in other distribution channels (like Linux distribution package managers) and were adapted to the conda user experience.
44+
For example, Linux distributions often install packages at the system level, which differs from the first-class userspace environment experience that conda package environments provides.
45+
As a result, some distinctions that a Linux distribution provides are unneeded in conda.
46+
There are additional differences around behavior in pinning versions of dependencies or how compilers are packaged and expected to work in their installed environments.
47+
Initial production of the packages were made on the `nvidia` channel, however, all of this work was being done internally in NVIDIA and published to a separate channel.
48+
This made the packages less visible and required additional knowledge to use.
49+
50+
In [2023](https://youtu.be/WgKwlGgVzYE?si=hfyAo6qLma8hnJ-N), NVIDIA began adding the releases of CUDA conda packages from the `nvidia` channel to conda-forge, making it easier to discover and allowing for community support.
51+
Given the new package structure, NVIDIA added the packages for CUDA `12.0` to indicate the breaking change.
52+
Also with significant advancements in system driver specification support, CUDA `12` became the first version of CUDA to be released as conda packages through conda-forge and included all CUDA libraries from the [CUDA compiler `nvcc`](https://github.com/conda-forge/cuda-nvcc-feedstock) to the [CUDA development libraries](https://github.com/conda-forge/cuda-libraries-dev-feedstock).
53+
[CUDA metapackages](https://github.com/conda-forge/cuda-feedstock/) were also released, which allow users to easily describe the version of CUDA they require (e.g. `cuda-version=12.5`) and the CUDA conda packages they want (e.g. `cuda`).
54+
This significantly improved the ability for researchers to easily create CUDA accelerated computing environments.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
version: 6
2+
environments:
3+
cpu:
4+
channels:
5+
- url: https://conda.anaconda.org/conda-forge/
6+
packages:
7+
linux-64:
8+
9+
...
10+
11+
- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda
12+
- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda
13+
- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.7.1-cpu_mkl_py313_h58dab0e_103.conda
14+
- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-cpu-2.7.1-cpu_mkl_hc60beec_103.conda
15+
16+
...
17+
18+
gpu:
19+
channels:
20+
- url: https://conda.anaconda.org/conda-forge/
21+
packages:
22+
linux-64:
23+
24+
...
25+
26+
- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvcc-tools-12.9.86-he02047a_2.conda
27+
- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvdisasm-12.9.88-hbd13f7d_0.conda
28+
- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvrtc-12.9.86-h5888daf_0.conda
29+
- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvtx-12.9.79-h5888daf_0.conda
30+
- conda: https://conda.anaconda.org/conda-forge/linux-64/cuda-nvvm-tools-12.9.86-h4bc722e_2.conda
31+
- conda: https://conda.anaconda.org/conda-forge/noarch/cuda-version-12.9-h4f385c5_3.conda
32+
- conda: https://conda.anaconda.org/conda-forge/linux-64/cudnn-9.10.1.4-hbcb9cd8_1.conda
33+
34+
...
35+
36+
- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda
37+
- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda
38+
- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.7.1-cuda129_mkl_py313_h1e53aa0_304.conda
39+
- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.7.1-cuda129_mkl_h43a4b0b_304.conda
40+
41+
...
42+
43+
packages:
44+
45+
...
46+
47+
- conda: https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.7.1-cuda129_mkl_h43a4b0b_304.conda
48+
sha256: af54e6535619f4e484d278d015df6ea67622e2194f78da2c0541958fc3d83d18
49+
md5: e374ee50f7d5171d82320bced8165e85
50+
depends:
51+
- pytorch 2.7.1 cuda*_mkl*304
52+
license: BSD-3-Clause
53+
license_family: BSD
54+
size: 48008
55+
timestamp: 1753886159800
56+
57+
...
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
## Introduction
2+
3+
A critical component of research software sustainability is the reproducibility of the software and computing environments software operates and "lives" in.
4+
Providing software as a "package" &mdash; a standardized distribution of all source or binary components of the software required for use along with identifying metadata &mdash; goes a long way to improved reproducibility of software libraries.
5+
However, more researchers are consumers of libraries than developers of them, but still need reproducible computing environments for research software applications that may be run across multiple computing platforms &mdash; e.g. scientific analyses, visualization tools, data transformation pipelines, and artificial intelligence (AI) and machine learning (ML) applications on hardware accelerator platforms (e.g. GPUs).
6+
While workflow engines and Linux containers offer a gold standard for scientific computing reproducibility, they require additional layers of training and software engineering knowledge.
7+
Modern open source multi-platform environment management tools, e.g. [@pixi], provide automatic multi-platform hash-level lock file support for all dependencies &mdash; down to the compiler level &mdash; of software on public package indexes (e.g. PyPI [@PyPI_website] and conda-forge [@conda-forge_community]) while still providing a high level interface well suited for researchers.
8+
Combined with the arrival of the full CUDA [@CUDA_paper] stack on conda-forge, it is now possible to declaratively specify a full CUDA accelerated software environment.
9+
We are now at a point where well supported, robust technological solutions exist, even for applications with highly complex software environments.
10+
What is currently lacking is the education and training by the broader scientific software community to adopt these technologies and build community standards of practice around them, as well as an understanding of what are the most actionably useful features of adopting computational reproducibility tools.
11+
12+
## Reproducibility
13+
14+
"Reproducible" research is a term that can mean multiple things across various fields.
15+
Some fields may view work as "reproducible" if the full process is documented, and other may view "reproducible" as meaning that all computations will give the same numerical outputs barring entropy variations.
16+
As there are multiple levels of reproducibility, we will restrict "reproducibility" to software environment reproducibility.
17+
We define this as be limited to the ability to define and programmatically create a software environment composed of packages that specifies all software, and its dependencies, with exact URLs and binary digests ("hashes").
18+
Reproducible environments need to be machine agnostic in that for a specified computing platform in the environment they must be installable without modification across multiple instances.
19+
20+
### Hardware accelerated environments
21+
22+
Software the involves hardware acceleration on computing resources like GPUs requires additional information to be provided for full computational reproducibility.
23+
In addition to the computer platform, information about the hardware acceleration device, its supported drivers, and compatible hardware accelerated versions of the software in the environment (GPU enabled builds) are required.
24+
Traditionally this has been very difficult to do, but multiple recent technological advancements (made possible by social agreements and collaborations) in the scientific open source world now provide solutions to these problems.

0 commit comments

Comments
 (0)