Chapter 12 of 60

Reproducible Notebooks

Concepts

CHAPTER 12 β€” REPRODUCIBLE NOTEBOOKS

PART III β€” Debugging Interactive and Numerical AI

PURPOSE

Converts the green-but-divergent notebook (0.91/0.84 author/colleague + 0.88 self-rerun, zero errors) into demonstrated agreement via the five-argument pinning function (code, lockfile, seeds, data, clock) applied one class per run β€” with the seed’s double role made explicit.

CENTRAL QUESTION

When the same notebook gives different answers without failing, what ordered discipline converts “runs” into “reproduces”?

UNIQUE CLAIM

A reproducible notebook is a pure function of (code hash, lockfile, seed set, data hash, pinned time), pinned in substrateβ†’seedsβ†’data/time order with per-class attribution β€” because most irreproducibility is administrative (unpinned kernel/packages, unseeded RNG, latest.csv moving targets, wall-clock/network reads), not numerical; and the seed serves two opposite goals: pinned for debugging isolation (bisectable determinism) but varied-and-aggregated for performance estimation, since a single-seed val_accuracy is one sample not an estimate (Bouthillier) β€” with reproducibility in the strict sense (different team, your artifacts, your number) demonstrated only by β‰₯3 cold Run Alls within tolerance, where ~63% independent ML-paper reproduction (Raff) is predicted exactly by these pinned items.

DEBUGGING OBJECT

State as reproducibility record β€” kernelspec/freeze hashes, full seed set + PYTHONHASHSEED, input sha256s, TZ/frozen-clock/network policy, and the cold-Run-All number table (min/max spread, never a single figure).

CONCEPTS INTRODUCED

Five-argument reproducibility function; ordered pinning (substrateβ†’seedsβ†’data/time, never multi-class jumps); seed-pinning illusion (pinned number = one draw); methods-vs-results reproducibility + repeatability/reproducibility/replicability trichotomy (Ch10–12 chase strict reproducibility; replicability is Part VI); cell-1 reproducibility header; tolerance-declared confirmation (spread ≀ tol).

CONCEPTS DEVELOPED / REUSED

Parity discipline from Ch9 (kernelspec + lockfile rebuild = H1 row); Run-All gating from Ch10–11 (cold runs as the only admissible measurement); seed discipline split (pin-to-isolate here, vary-to-estimate foreshadowed for Ch14–16); prevention/CI enforcement from Ch8 (green record β†’ per-change Run-All gate).

PREREQUISITES

Ch9 (parity), Ch10 (Run All), Ch11 (hidden state), Ch1 (single-run fallacy).

LOCAL INVARIANTS

Baseline the gap with two cold runs + declared tolerance first; pin one class per run (multi-variable jumps inconclusive); rerun twice minimum per stage, confirm Γ—3 cold at the end; report spread, never a point; no reproducibility verdict with any row UNKNOWN or trials < 3.

FAILURE MODES

Seed-only superstition (seed pinned, packages/data floating); latest.csv syndrome (fixed name, moving bytes); headline-version parity (“both 3.11”); single-pair green (agreement after a three-class jump, zero attribution); output-committed notebooks (fossils as proof); tolerance amnesia (0.885 without Β± or run count).

DIAGNOSTIC METHOD

  1. Two cold baselines + tolerance. 2. Pin substrate (kernelspec+lockfile, fresh-env rebuild, re-measure β†’ H1 row). 3. Pin seeds (full set + PYTHONHASHSEED + deterministic flags, repeats β†’ H2 row). 4. Lock data/time (hash/vendor/freeze-clock/stub-network β†’ H3 row). 5. Confirm Γ—3 cold, stamp green record with all five hashes, enforce in CI.

RESEARCH-DERIVED IDEAS

Raff NeurIPS 2019 (255 papers 1984–2017 re-implemented codeless: ~63% reproducible; predictors = pseudocode detail, hyperparameter spec, narrow claims β€” one person’s broad older sample); Bouthillier/Laurent/Vincent ICML 2019 (methods vs results reproducibility; single-seed conclusions frequently reverse β€” flip rates for specific DL comparisons); Bouthillier et al. MLSys 2021 “Accounting for Variance” (5 DL tasks: data splits + weight init among largest variance sources; counter-intuitive β€” randomizing many sources at once estimates true generalization better than fix-all-but-one, ~50x cheaper β†’ un-pin broadly for Ch14–16, not just the RNG seed); Pineau et al. JMLR 2021 NeurIPS 2019 Reproducibility Program (checklist + code policy β€” precedent for this chapter’s instrument); Plesser 2018 (repeatability/reproducibility/replicability definitions).

EXPERIMENT / LAB

Lab 12 (PROPOSED): injected irreproducibility (seed removed / latest.csv / split package versions), H1/H2/H3 with distinct gap-collapse FORECASTs, staged pins one class per run with per-stage OBSERVATION. H-structure: independent var = pinned class; controls = file hash, Run-All procedure, seed values once set. Success = fully green checklist + three cold runs within tolerance + per-class attribution rows; multi-class-jump green pair is not completion.

COMPANION TOOL

Notebook Reproducibility Checklist β€” accepts: notebook hash + kernelspec/lockfile + seed set + data hashes + clock/network policy + β‰₯3 cold numbers. Can-establish: that the notebook reproduces within tolerance across examined substrates + which pin closed which gap. Cannot-establish: data correctness (Ch13), model/eval validity (Ch15–16), future stability (rots without CI); never one-run or headline-match verdicts.

PREVENTION ARTIFACT

Green record with all five hashes + CI enforcement (lockfile Run All per change, hashes asserted) + seed-header cell + vendored data + frozen clock.

READER OUTCOME

Reader can take a 0.91/0.84/0.88 divergence and attribute each gap to its pin class with staged measurements β€” testable via Lab 12’s red-to-green table.

DEPENDENCIES

Ch9, Ch10, Ch11.

FORWARD BRIDGE

Ch13 “Debug the Data Before the Model” β€” inherits the contents gap: a faithfully reproducing notebook can faithfully compute on corrupt inputs (deterministic garbage, delivered identically everywhere).

EVIDENCE / RESEARCH REQUIREMENTS

0.89/0.88β†’0.885Β±0.002 walkthrough constructed illustration; Raff ~63% single-implementer bounded; Bouthillier flips comparison-specific; nondeterminism claims need β‰₯3 cold runs.

ANTI-CLAIMS / LIMITS

Green record is necessary, never sufficient (certifies no correctness); one revision/substrates/tolerance only; rots with next release; human verification on high-stakes paths.

Explain this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Apply this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Part III β€” Debugging Interactive and Numerical AI

Green here, red there, same notebook

Chapters 10 and 11 closed with a notebook that runs clean: order verified, namespace explained, Run All green. The engineer sends it to a colleague. Same file, same Run All β€” different number:

# author laptop (passes)                  # colleague laptop (fails)
Cell 18: val_accuracy = 0.91              Cell 18: val_accuracy = 0.84
Run All: green                            Run All: green β€” but different answer
# and on the author's own machine, rerun: 0.88

No traceback. No ghost variable. Three executions, three answers, zero errors. Worse: re-running on the same machine drifts too.

OBSERVATION: identical .ipynb hash β†’ 0.91 / 0.84 / 0.88 across two machines and one rerun; all runs green. HYPOTHESIS H1 (unpinned substrate): kernel spec, package versions, or data file differ across runs β€” the program is deterministic but its inputs are not the same. HYPOTHESIS H2 (unpinned nondeterminism): unseeded randomness (shuffle, init, dropout, GPU ordering) makes the same program a different draw each run. HYPOTHESIS H3 (unlocked data/time): the notebook reads data/latest.csv, calls datetime.now(), or streams an API β€” the world moved between runs. INFERENCE: none yet β€” green runs with divergent numbers convict nothing by themselves. Only pinning one variable class per run separates H1/H2/H3.

This chapter’s question: when the same notebook gives different answers without failing, what ordered discipline converts “runs” into “reproduces”?

Why “it ran” is not “it reproduces”

The obvious explanation β€” “floating point / GPUs are just nondeterministic, accept the noise” β€” fails because most notebook irreproducibility is administrative, not numerical. Four unpinned inputs do the damage before randomness matters:

  1. Unpinned kernel and packages. The notebook metadata names python3 β€” which resolves to 3.10 with sklearn 1.2 on one machine and 3.11 with sklearn 1.4 on the other. Chapter 9’s freeze diff applies verbatim: the environment is an input, and >= specifiers make every install a new experiment.
  2. Unseeded randomness. train_test_split(...) without random_state, numpy/random/torch seeds unset, PYTHONHASHSEED drifting (set-order iteration changes), GPU kernels nondeterministic by default. Each rerun is a fresh draw presented as a replication.
  3. Unlocked data. pd.read_csv("data/latest.csv") resolves to different bytes per machine and per week. No hash, no version, no chance of reproduction β€” the input file is a moving target wearing a fixed name.
  4. Time and network. datetime.now() in a feature, requests.get(...) in a loader, random.sample over a changing directory listing. Wall-clock and network reads import the outside world into a supposedly closed computation.

OPINION: reproducibility is not a virtue of careful people; it is a checklist of pinned things. Unpinned items are not minor gaps β€” each one is a license for the result to differ.

The mental model: a reproducible notebook is a pure function of (code hash, lockfile, seed set, data hash, pinned time). Change any argument, change the result β€” legitimately. The discipline is making every argument explicit, then demonstrating green Run All from a cold start on a second substrate with all five pinned.

The method: the reproducibility checklist run

Ordered probe β€” substrate before stochasticity before world, cheapest pin first, one variable class per run:

  1. Pin the kernel and environment. Record kernelspec name, pip freeze hash, and re-run from a fresh env built from the lockfile. Prediction if H1: the cross-machine gap collapses once both sides build from the same lock (remaining drift is H2/H3).
  2. Pin every seed. random.seed, numpy, framework seeds, PYTHONHASHSEED=0, deterministic flags where affordable; log all seed values in cell 1. Rerun twice on the same machine. Prediction if H2: rerun variance collapses (repeated trials agree); persistent variance implicates GPU nondeterminism or H3. One caveat for the model chapters ahead: pinned seeds buy isolation, not bitwise replay β€” batch-non-invariant serving can still vary outputs at temperature 0 (Ch21). Collapsed variance means the seeds were the mechanism, not that replay is now exact.
  3. Lock data and time. Hash every input file (sha256sum), vendor the bytes or pin the versioned URI, freeze TZ=UTC and inject a fixed timestamp instead of now(). Prediction if H3: the rerun and cross-machine numbers converge once data hashes match.
  4. Cold-start Run All on two substrates. Fresh kernel, cleared outputs, Run All on machine A and machine B (or container vs. host). Reproducibility is demonstrated agreement, not asserted hygiene β€” record both numbers.
    flowchart TD
    B["baseline: two cold Run Alls, record both numbers, declare tolerance"] --> S1["pin substrate: kernelspec + lockfile, rebuild a fresh env"]
    S1 --> C1{"cross-machine gap collapses?"}
    C1 -->|yes| H1["H1: unpinned substrate"]
    C1 -->|"narrows, drift remains"| S2["pin seeds: random / numpy / framework / PYTHONHASHSEED=0"]
    H1 --> S2
    S2 --> C2{"rerun variance collapses?"}
    C2 -->|yes| H2["H2: unseeded nondeterminism"]
    C2 -->|"persists"| S3["lock data + time: sha256 inputs, vendor bytes, TZ=UTC, injected timestamp"]
    H2 --> S3
    S3 --> C3{"numbers converge across runs and machines?"}
    C3 -->|yes| H3["H3: unlocked data / time"]
    C3 -->|no| GPU["residual GPU-kernel nondeterminism: document it, bound it with repeated trials"]
    H3 --> CF["confirm with 3 cold runs, report the spread, enforce in CI"]
    GPU --> CF
  
# cell 1: the reproducibility header (every reproducible notebook opens with this)
import random, os
SEED = 42
random.seed(SEED)
os.environ["PYTHONHASHSEED"] = "0"
import numpy as np
np.random.seed(SEED)
# torch.manual_seed(SEED)  # uncomment per stack; log versions below
print("seed =", SEED, "| data_sha =", open("evidence/data.sha256").read().strip())
# MEASUREMENT: seed set + data hash printed into the run record itself

OBSERVATION (constructed illustration, not a measured run): freeze diff showed sklearn 1.2 vs 1.4 (H1 live); after lockfile rebuild the gap narrowed 0.91/0.84 β†’ 0.89/0.88; after seeding + vendored sales_v3.parquet (sha256:9f2c…) two cold Run Alls agreed at 0.885 Β± 0.002 over three repeated trials. UPDATED BELIEF: H1 + H2 + H3 all contributed β€” no single cause. The checklist converted three red rows to green one variable class at a time. INFERENCE: “accept the noise” would have hidden two administrative defects behind one stochastic excuse. Pinning order matters: substrate first, or seed experiments measure the wrong distribution.

Because nondeterminism is intrinsic here, every claim in this chapter requires repeated trials: no convergence statement on fewer than three cold runs; single-run agreement is UNKNOWN, not green.

Two goals, two opposite uses of the seed. Pinning the seed is the right move for debugging: it makes the run a deterministic function you can bisect, so a change in output means a change in code, not a change in draw. It is the wrong move for trusting the number. Bouthillier and colleagues show that a conclusion drawn from a single seed β€” “model A beats model B” β€” frequently reverses under a different seed, so a pinned-seed val_accuracy is one sample, not an estimate (Bouthillier, Laurent & Vincent, 2019). Their quantitative follow-up went further: across five deep-learning tasks, data splitting and weight initialization were among the largest variance sources, and β€” counter-intuitively β€” randomizing many sources at once (splits, init, data order, augmentation, dropout) estimated true generalization performance better than the “ideal” fix-everything-but-one procedure, at roughly fifty times less compute (Bouthillier et al., 2021). This chapter pins the seed to isolate the administrative defects; Chapters 14–16 deliberately un-pin it β€” and the lesson from the follow-up is to un-pin broadly, varying every stochastic source, not just the RNG seed, because that is what the number actually is.

Research lineage: the checklist has a track record

Independent reproduction succeeds about two times in three, and the checklist items are what predict it. Raff manually re-implemented 255 machine-learning papers spanning 1984–2017 without looking at any released code, and found roughly 63% independently reproducible. The features that correlated with success were the ones this chapter pins: detailed pseudocode, fully specified hyperparameters, and narrowly scoped empirical claims; sheer mathematical density did not help (Raff, 2019).

The “reproducibility checklist” is a real instrument. The NeurIPS 2019 Reproducibility Program introduced a machine-learning reproducibility checklist and a code-submission policy; the follow-up report describes what changed and what did not (Pineau et al., 2021). This chapter’s Notebook Reproducibility Checklist is a narrowed, notebook-scoped version of that instrument.

The words matter. The literature distinguishes repeatability (same team, same setup, same result), reproducibility (a different team runs the original artifacts and gets the same result), and replicability (a different team, new artifacts, same finding). Chapters 10–12 are chasing reproducibility in that strict sense: a colleague, your artifacts, your number. Replicability β€” does the finding survive a fresh implementation β€” is a Part-VI concern.

Lab 12: checklist run, one variable class at a time

PROPOSED, not executed: no author-measured results are reported. The evidence this chapter requires is the reader’s own red-to-green checklist record.

Setup. Take the Chapter 10–11 notebook (or inject irreproducibility: remove one seed, point the loader at an unversioned latest.csv, install two package versions across two envs). You need two runtimes or two cold runs minimum.

Task.

  1. Write H1/H2/H3 with distinct predictions before pinning anything: which pin collapses which gap, and by roughly how much (FORECAST with tolerance, e.g., “lockfile rebuild halves the cross-machine gap”).
  2. Baseline: two cold Run Alls, record both numbers. Independent variable: pinned class (substrate β†’ seeds β†’ data/time); controlled variables: file hash, Run-All procedure, seed values once set.
  3. Apply pins one class per run, recording OBSERVATION after each (numbers verbatim) and UPDATED BELIEF. Never pin two classes between measurements β€” multi-variable jumps are inconclusive by Chapter 4’s rule.
  4. Close with three confirmatory cold runs; report spread (min/max), not a single figure.
Stage Pin applied Run A Run B (repeat) UPDATED BELIEF
baseline none ___ ___ gap quantified
substrate lockfile + kernelspec ___ ___ H1 supported/exonerated
seeds seed set + PYTHONHASHSEED ___ ___ H2 supported/exonerated
data/time vendored hash + frozen clock ___ ___ H3 supported/exonerated
confirm Γ—3 all pinned ___, ___, ___ spread ___ reproducible iff spread ≀ tolerance

Success criterion. A fully green checklist (every row pinned with artifact hashes) plus three cold Run Alls agreeing within a pre-declared tolerance. A single green pair after multi-class pinning is explicitly not completion β€” the per-class attribution rows are the diagnosis.

Companion tool: Notebook Reproducibility Checklist

What it accepts: the notebook hash, kernelspec + lockfile/freeze artifacts, seed set + PYTHONHASHSEED, input data hashes, clock/network policy, and the cold-Run-All number table (β‰₯3 runs). What it performs: it enforces pin order (substrate β†’ seeds β†’ data/time), refuses a reproducibility verdict while any row is UNKNOWN, checks repeated-trial spread against the declared tolerance, and stamps the green record with all five hashes. What it can establish: that the notebook reproduces within tolerance across the examined substrates and reruns β€” and which pin closed which gap, per the staged table. What it cannot establish: data correctness (Chapter 13), model/eval validity (Chapters 15–16), or future stability β€” today’s green rots with the next dependency release unless CI re-runs the checklist. It never treats one green run or headline-version match as reproducibility. How its output changes your next action: a green record routes to CI enforcement (Run All from lockfile on every change, hashes asserted); any red row routes to its pin (lockfile, seed header, vendored data, frozen clock) with the staged table as the regression artifact.

Paper form, sufficient for this chapter:

Notebook hash: ___   Kernelspec: ___   Lockfile/freeze hash: ___
Seeds (random/numpy/torch/PYTHONHASHSEED): ___ / ___ / ___ / ___
Data hashes: ___   Clock (frozen UTC / injected ts): ___   Network reads: none / pinned (circle)
Cold Run Alls (β‰₯3): ___ ___ ___   spread ___ ≀ tol ___? Y/N
RED→GREEN per class: substrate ___ seeds ___ data/time ___ (evidence line each)

Where a software implementation does not yet exist in the reader’s stack, this checklist is the tool. The pinning discipline precedes any automation.

Reusable procedure: every notebook destined for sharing gets this

  1. Baseline the gap β€” two cold Run Alls, numbers recorded, tolerance declared.
  2. Pin substrate β€” kernelspec + lockfile, fresh-env rebuild, re-measure.
  3. Pin seeds β€” full seed set header, deterministic flags, re-measure with repeats.
  4. Lock data/time β€” hash inputs, vendor bytes, freeze clock, stub network, re-measure.
  5. Confirm Γ—3 cold and enforce in CI β€” green record with all hashes, re-run per change.

Failure modes

  • Seed-only superstition. Setting random.seed(42) while packages and data float. Seeds pin the draw, not the distribution β€” substrate first.
  • Latest.csv syndrome. Version-less data paths. A fixed name on moving bytes is the most common H3 and the least examined.
  • Headline-version parity. “Both run Python 3.11.” Chapter 9 already convicted this: the freeze file is the environment; headlines are rumors.
  • Single-pair green. Two agreeing runs after a three-class pin jump. Agreement without attribution teaches nothing and prevents nothing.
  • Output-committed notebooks. Committing executed outputs as proof of health. Outputs are fossils (Chapter 10); the checklist record is the proof.
  • Tolerance amnesia. Reporting 0.885 without Β± spread or run count. A number without its repeat distribution is a FORECAST wearing MEASUREMENT’s clothes.

Limits, per contract: one checklist record certifies the examined revision across the examined substrates within the declared tolerance; it does not certify correctness, does not freeze the future, and does not replace human verification where money, safety, or production traffic is at stake. UNKNOWN where any row is unmeasured or trials < 3.

References

  • Edward Raff. A Step Toward Quantifying Independently Reproducible Machine Learning Research. Advances in Neural Information Processing Systems 32 (NeurIPS), 2019. https://papers.nips.cc/paper/2019/hash/c429429bf1f2af051f2021dc92a8ebea-Abstract.html
  • Xavier Bouthillier, CΓ©sar Laurent, and Pascal Vincent. Unreproducible Research is Reproducible. Proceedings of the 36th International Conference on Machine Learning (ICML), 2019, pp. 725–734. https://proceedings.mlr.press/v97/bouthillier19a.html
  • Xavier Bouthillier, Pierre Delaunay, Mirko Bronzi, Assya Trofimov, Brennan Nichyporuk, Justin Szeto, Nazanin Mohammadi Sepahvand, Edward Raff, Kanika Madan, Vikram Voleti, Samira Ebrahimi Kahou, Vincent Michalski, Tal Arbel, Chris Pal, GaΓ«l Varoquaux, and Pascal Vincent. Accounting for Variance in Machine Learning Benchmarks. Proceedings of Machine Learning and Systems (MLSys), 2021. https://arxiv.org/abs/2103.03098
  • Joelle Pineau, Philippe Vincent-Lamarre, Koustuv Sinha, Vincent LariviΓ¨re, Alina Beygelzimer, Florence d’AlchΓ©-Buc, Emily Fox, and Hugo Larochelle. Improving Reproducibility in Machine Learning Research (A Report from the NeurIPS 2019 Reproducibility Program). Journal of Machine Learning Research 22(164), 2021, pp. 1–20. https://www.jmlr.org/papers/v22/20-303.html
  • Hans E. Plesser. Reproducibility vs. Replicability: A Brief History of a Confused Terminology. Frontiers in Neuroinformatics 11:76, 2018. https://doi.org/10.3389/fninf.2017.00076

Debugging Checklist

  • Two cold-Run-All baselines recorded with pre-declared tolerance?
  • Kernelspec + lockfile pinned, fresh-env rebuild measured (H1 row)?
  • Full seed set + PYTHONHASHSEED pinned, repeats measured (H2 row)?
  • Data hashed/vendored, clock frozen, network stubbed, re-measured (H3 row)?
  • Pins applied one class per run β€” no multi-class jumps?
  • Three confirmatory cold runs with spread ≀ tolerance?
  • Green record kept with all five hashes for CI enforcement?

What This Chapter Established

  • Reproducibility as a five-argument function (code, lockfile, seeds, data, clock) with ordered pinning: substrate β†’ seeds β†’ data/time, each attributed by its own measurement.
  • The checklist run converting the 0.91/0.84/0.88 divergence to attributed green rows β€” constructed illustration, no measured runs claimed; repeated-trials rule (β‰₯3 cold runs) for all nondeterminism claims.
  • Lab 12 as a proposed red-to-green record the reader executes; the Notebook Reproducibility Checklist contract (accepts/performs/can-establish/cannot-establish/next-action).
  • What was NOT proved: correctness of data, model, or evaluation under a green record β€” reproducibility is necessary, not sufficient.
  • Empirical grounding: independent reproduction of ML papers succeeds ~63% and is predicted by exactly these pinned items (Raff); the checklist is a real instrument (Pineau et al., NeurIPS 2019); pinning the seed serves debugging, not performance estimation β€” a single-seed number is one sample (Bouthillier et al. 2019), and a trustworthy estimate randomizes many stochastic sources (splits, init, order, augmentation), which is both more accurate and far cheaper than fixing all but one (Bouthillier et al. 2021).
  • Forward link: a notebook can now reproduce faithfully β€” and faithfully compute on corrupt inputs. Deterministic garbage, delivered identically everywhere. The next defense is upstream of all of this: the data.

Next

The notebook reproduces. Same numbers, every machine, every rerun β€” and the numbers may still be lies, because reproducibility never checked what the numbers mean. A leaked label, a corrupt split, a silently shifted schema: all reproduce perfectly and all train a model on fiction. Chapters 10–12 debugged the container of computation; the next chapter debugs its contents β€” the data, before the model.