Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Reliable and Reproducible Outputs

Authors
Affiliations
Johns Hopkins Bloomberg School of Public Health
Johns Hopkins Bloomberg School of Public Health
import numpy as np
rng = np.random.default_rng(4)

# Stand-in for a decoder: softmax over 5 candidate answers at varying temperature.
logits = np.array([2.0, 1.6, 0.4, 0.1, -0.5])
answers = list("ABCDE")

def probs(T):
    z = logits / T
    z -= z.max()                      # log-sum-exp: T near 0 overflows otherwise
    p = np.exp(z)
    return p / p.sum()

print(f"{'T':>5} {'modal':>6} {'P(modal)':>9} {'distinct/4k':>12} {'entropy':>8}")
for T in [0.01, 0.3, 0.7, 1.0, 1.5]:
    s = rng.choice(len(logits), size=4000, p=probs(T))
    c = np.bincount(s, minlength=5) / 4000
    ent = abs(-(c[c > 0] * np.log2(c[c > 0])).sum())   # entropy >= 0; abs kills the -0.0
    print(f"{T:>5.2f} {answers[c.argmax()]:>6} {c.max():>9.3f} "
          f"{int((c > 0).sum()):>12d} {ent:>8.2f}")
print("\nT->0 is deterministic (entropy 0); T=1.5 spreads mass over all five.")
    T  modal  P(modal)  distinct/4k  entropy
 0.01      A     1.000            1     0.00
 0.30      A     0.787            4     0.78
 0.70      A     0.564            5     1.51
 1.00      A     0.484            5     1.78
 1.50      A     0.392            5     2.06

T->0 is deterministic (entropy 0); T=1.5 spreads mass over all five.
import numpy as np
rng = np.random.default_rng(5)

# A model that is right with probability p on each independent attempt.
def majority_correct(p, m, trials=20000):
    draws = rng.random((trials, m)) < p
    return (draws.sum(axis=1) > m / 2).mean()

print(f"{'p':>5} " + " ".join(f"m={m:<6}" for m in [1, 3, 5, 9]))
for p in [0.4, 0.5, 0.6, 0.7, 0.8]:
    row = " ".join(f"{majority_correct(p, m):<8.3f}" for m in [1, 3, 5, 9])
    print(f"{p:>5.1f} {row}")
print("\nBelow p=0.5 voting makes it worse; above, it amplifies. Condorcet, not magic.")
    p m=1      m=3      m=5      m=9     
  0.4 0.403    0.346    0.313    0.267   
  0.5 0.499    0.497    0.504    0.500   
  0.6 0.595    0.651    0.682    0.730   
  0.7 0.701    0.785    0.835    0.904   
  0.8 0.801    0.897    0.941    0.982   

Below p=0.5 voting makes it worse; above, it amplifies. Condorcet, not magic.

0.1Reliable and Reproducible Outputs

Run the same prompt twice and you will often get two different answers. For a statistician this should provoke curiosity rather than alarm: a language model with nonzero sampling temperature is a random number generator, and a single response is a draw, not an estimate. The reproducibility question is therefore two questions wearing one coat. The first is stochastic --- can I obtain the same output again, and if not, how much does it vary? The second is substantive --- does the conclusion I drew survive that variation? A pp-value computed by a script whose input was one lucky draw from a chat window is not reproducible in any sense a journal should accept, and the fix is not to hope for determinism but to characterise the sampling distribution the way you would for any other Monte Carlo procedure. This section takes that stance seriously. We treat repeated model calls as replicates, define run-to-run variance as an estimable quantity, examine what aggregation across draws does and does not buy, explain why fixing the seed is neither sufficient nor always possible, and lay out the pinning and logging discipline that makes an LLM-containing analysis rerunnable a year later.

Reliability of model outputs, translated: run-to-run variability is a variance-components problem and aggregation across draws is Monte Carlo estimation with correlated replicates.

ML / AI termStatistical analogueWhat is the sameWhat is different / the catch
One model responseA single Monte Carlo drawBoth are realisations whose value is only interpretable through their distributionPeople report the draw as if it were the estimate, and there is no convention requiring otherwise
Greedy decoding (T=0T=0)Reporting the posterior modeBoth remove sampling variability by taking the argmaxDeterminism is not reproducibility: batching, hardware and kernel changes still perturb floating-point sums, so ties break differently across runs
Self-consistency / majority voteMajority rule over correlated replicatesBoth aggregate repeated readings into one answer Wang et al., 2022Draws from one model are positively correlated, so accuracy plateaus far below the independent-voting benchmark
Run-to-run varianceA variance component in a random-effects decompositionBoth quantify the spread attributable to one source of randomnessSeed, prompt phrasing, model version and provider routing are separate components, and only the first is under your control
Ensembling several modelsCombining estimators; model averagingDiversity of members is what makes the average better than a memberMembers share pretraining corpora and architectures, so the effective number of independent members is much smaller than the count Lakshminarayanan et al., 2016
Temperature 0 “determinism”Fixing a random seedBoth eliminate one identified source of randomnessThe provider may change the model behind an unchanged name, so yesterday’s deterministic call is not today’s
Structured output constraintRestricting the sample spaceBoth make impossible outputs impossibleRemoves format variance only; the answer inside the format still varies
Confidence / logprob of an answerA predicted probability needing calibrationBoth are only meaningful if calibrated against observed correctnessSelf-reported confidence is not a posterior over correctness and is systematically overconfident Guo et al., 2017
Prompt sensitivitySensitivity of a conclusion to an analysis choiceBoth are specification uncertainty, not sampling uncertaintyPrompt space is unbounded and unenumerable, so you cannot integrate over it --- you can only sample from a pre-specified set
Pinning a model versionRecording software versions in a reproducibility statementBoth make the computational environment part of the methodHosted models are withdrawn on the provider’s schedule, so a pinned version may simply cease to exist Pineau et al., 2021Sculley et al., 2015

0.1.1Decompose the variance before you try to remove it

Let YijkY_{ijk} be the outcome of interest --- an accuracy indicator, an extracted number, a graded score --- for item ii, prompt variant jj, and repeated call kk. A first-pass decomposition treats items, prompts and calls as crossed and nested random effects,

Yijk  =  μ  +  ai  +  bj  +  (ab)ij  +  εijk,Var(Y)=σitem2+σprompt2+σint2+σcall2,Y_{ijk} \;=\; \mu \;+\; a_i \;+\; b_j \;+\; (ab)_{ij} \;+\; \varepsilon_{ijk}, \qquad \mathrm{Var}(Y) = \sigma^2_{\text{item}} + \sigma^2_{\text{prompt}} + \sigma^2_{\text{int}} + \sigma^2_{\text{call}},

which is an ordinary mixed model your readers can fit before lunch. The value of writing it down is that it names the enemy. If σcall2\sigma^2_{\text{call}} dominates, average more draws. If σprompt2\sigma^2_{\text{prompt}} dominates, no amount of averaging within a prompt helps, and you must either pre-specify the prompt or report across a pre-registered family of them. If σitem2\sigma^2_{\text{item}} dominates --- the usual case --- your evaluation is item-limited, and the fix is more items, which is the sample-size argument made in the evaluation section.

0.1.2What majority voting actually buys

Sampling BB answers and taking the plurality --- “self-consistency” --- is the standard reliability trick Wang et al., 2022. Under the fiction of independent draws each correct with probability p>1/2p > 1/2, the vote is correct with probability Pr(Bin(B,p)>B/2)1\Pr(\mathrm{Bin}(B,p) > B/2) \to 1, and the gain looks free. Draws from one model are not independent: they share a prompt, a parameter vector and a systematic misunderstanding. A convenient way to see the consequence is to let the per-call success probability itself be random, PBeta(α,β)P \sim \mathrm{Beta}(\alpha,\beta) with mean pp and intra-item correlation ρ=1/(α+β+1)\rho = 1/(\alpha+\beta+1), so that the vote count is beta-binomial. Then as BB grows

Pr(majority correct)    Pr ⁣(P>12)  =  1I1/2(α,β)  <  1,\Pr(\text{majority correct}) \;\longrightarrow\; \Pr\!\left(P > \tfrac12\right) \;=\; 1 - I_{1/2}(\alpha, \beta) \;<\; 1 ,

a hard ceiling strictly below one that no amount of extra sampling can breach. The interpretation is the one every meta-analyst knows: correlated replicates carry less information than their count suggests, and here the shortfall is not gradual but asymptotic.

Majority voting over repeated model calls saturates when the calls are correlated: with a single-sample accuracy of 0.60, independent draws would reach 0.87 by B=31 votes, but an intra-item correlation of \rho=0.30 caps the vote at about 0.64 no matter how many samples are bought. Curves are beta-binomial majority probabilities; \rho=0 is the binomial benchmark.
:width: 90%

Figure 1:Majority voting over repeated model calls saturates when the calls are correlated: with a single-sample accuracy of 0.60, independent draws would reach 0.87 by B=31B=31 votes, but an intra-item correlation of ρ=0.30\rho=0.30 caps the vote at about 0.64 no matter how many samples are bought. Curves are beta-binomial majority probabilities; ρ=0\rho=0 is the binomial benchmark. :width: 90%

0.1.3Seeds, determinism, and why T=0T=0 is not enough

Setting temperature to zero makes decoding a deterministic argmax, and a seed makes any residual sampling reproducible on your machine. Neither delivers reproducibility across time or across machines. Floating-point addition is not associative, so a change in batch size, tensor-parallel layout, GPU model, or kernel library changes the summation order and hence the logits in the last decimal places; where two candidate tokens are nearly tied, that noise flips the argmax and the two continuations diverge irreversibly. Mixed-precision arithmetic makes this worse by design Micikevicius et al., 2018, and distributed execution reorders reductions as a matter of course Goyal et al., 2017Rajbhandari et al., 2020. This is not an abstract hazard even for a locally served model: high-throughput inference servers such as vLLM group incoming requests into batches whose composition depends on what else arrived at that moment, so the same prompt run twice on the same weights and the same hardware can be reduced in a different order and, at a near-tie, decode to a different token. The lesson generalises well beyond language models: a single training run is a draw from a distribution too, which is why seed-to-seed variance belongs in every reported comparison. :::{note} Author note Write the two-paragraph version of the floating-point argument with a tiny worked example --- sum three numbers in two orders and show the last-bit difference --- because readers will not believe it otherwise. Then give the operational recommendation: report results as a mean over 5\ge 5 seeds with a standard error, never as a single run, and treat exact bitwise reproduction as a nice-to-have rather than the goal. :::

0.1.4A pinning and logging discipline

Reproducing an analysis that contains a model call requires recording the things that determine the output. Figure the figure lists them by layer. The rule of thumb is that anything the provider can change without telling you must be captured on your side: the exact model identifier including its dated snapshot, the decoding parameters, the full prompt text, any retrieved context, and the raw response including token log-probabilities where available. For work that must be reproducible over years rather than months, the only robust option is a locally hosted open-weights model whose checkpoint you archive, since a hosted endpoint can be retired at the provider’s convenience. (Mechanically this means pinning by content rather than by name --- the Hugging Face Hub exposes an immutable commit hash per repository that transformers and vllm will both accept in place of a tag, and runtimes such as llama.cpp and Ollama address a quantised checkpoint by its file digest --- so that a re-run either loads the same bytes or fails loudly rather than loading something newer under an unchanged label.)

Reproducibility of a model call degrades layer by layer: the top two layers are fully under your control and must be logged verbatim, while the bottom two are controlled by whoever serves the model, which is the argument for archiving open weights when an analysis must be rerunnable in five years.
:width: 90%

Figure 2:Reproducibility of a model call degrades layer by layer: the top two layers are fully under your control and must be logged verbatim, while the bottom two are controlled by whoever serves the model, which is the argument for archiving open weights when an analysis must be rerunnable in five years. :width: 90%

0.1.5Reporting standards for an LLM-in-the-loop analysis

The book should state a standard plainly, because none is yet settled. Report the model and snapshot, the decoding settings, the number of repeated calls, and the aggregation rule. Report a measure of run-to-run variability, not just a point estimate. Pre-specify the prompt, or pre-specify the family of prompts over which results are averaged, and report both. Archive raw responses. This is exactly the checklist culture that Pineau et al. (2021) argued for in machine learning and that Gebru et al. (2021) and Mitchell et al. (2019) argued for data and models respectively; the novelty here is only that the analysis instrument is now stochastic. One class of output resists this discipline almost completely: a report produced by an agent that searched the live web depends on what was retrievable at that moment, so the retrieved material must be archived alongside the response or the result is unrepeatable in principle rather than merely in practice (Section that section). :::{note} Author note Close with the framing that lands with this audience: an LLM call is a measurement instrument with a drift problem. Chemists calibrate instruments and record lot numbers; we should do the same. Cross-reference the technical-debt argument of Sculley et al. (2015). :::

0.1.6Tools in practice

The tools that matter for reproducibility are not the ones that make a model answer better; they are the ones that put a boundary around what can change underneath you. Three durable distinctions organise the field. First, local weights versus a hosted endpoint: only the former can be archived, and archiving the checkpoint is the difference between an analysis that reruns in five years and one that cannot. Second, a provider-specific client versus a routing layer that presents one interface over many backends: the routing layer is what lets the same script be re-executed against a locally archived model when the hosted one is withdrawn, and it is also where a cache belongs. Third, ad hoc logging versus a tracing store: if the raw request and response are not written somewhere durable at call time, they do not exist, because nothing in the stack will reconstruct them for you. Capabilities and interfaces in this area move quickly enough that any specific claim here would be stale before the book is read; check current documentation, and evaluate a candidate tool by asking which of these three boundaries it draws.

The cache the reporting standard asks for is a few lines, and writing it out makes clear what "the same call" has to mean. The key is a hash of everything that determines the response --- model identity, snapshot, decoding parameters and the exact prompt bytes --- so that a cache hit is a guarantee rather than a hope.

def call_key(model, snapshot, prompt, **decode): “”“Content hash of everything that determines the response.”“” spec = {“model”: model, “snapshot”: snapshot, “prompt”: prompt, “decode”: decode} blob = json.dumps(spec, sort_keys=True, separators=(“,”, “:”)) return hashlib.sha256(blob.encode(“utf-8”)).hexdigest()

base = dict(model=“local-instruct-model”, snapshot=“”, prompt=“Extract the smoking status.”, temperature=0.0, top_p=1.0)

variants = {"baseline ": base, "trailing space ": {**base, “prompt”: base[“prompt”] + " “}, “temperature changed”: {**base, “temperature”: 0.7}, “identical call “: dict(base)} keys = {name: call_key(**spec) for name, spec in variants.items()} for name, k in keys.items(): print(f”{name} {k[:16]} same as baseline: " f”{k == keys['baseline ']}”)

cache, network = {}, 0 other = {**base, “prompt”: “Extract the biopsy status.”} workload = [base, other, base, base] for spec in workload: k = call_key(**spec) if k not in cache: network += 1 cache[k] = “” print(f"\ncalls requested {len(workload)} calls actually issued {network}" f" distinct keys cached {len(cache)}")

calls requested 4 calls actually issued 2 distinct keys cached 2

The trailing space is the point of the example. A one-character edit that no reader would notice produces an entirely different key, which is the correct behaviour: it is also a different request, and a cache that treated the two as interchangeable would silently serve one prompt’s answer for another. The same strictness is what makes the cache a reproducibility device rather than a cost optimisation. Store the raw response body under this key, commit the store alongside the analysis, and every downstream figure regenerates with the network disabled --- which is the concrete form of the archival recommendation above, and the same argument made for caching scraped web data.

Pinning tells you what you asked for; it does not tell you whether what you got has changed. A canary suite closes that gap: fix a small set of probe prompts, run them at temperature zero, hash the responses, and re-run the suite whenever the analysis is re-executed. The interesting part is not the comparison but the sample-size question underneath it, since a suite too small to detect a change is a suite that certifies stability it never tested for.

def digest(text): return hashlib.sha256(text.encode(“utf-8”)).hexdigest()[:12]

1A canary suite: fixed probes run at T = 0, responses hashed and stored.

probes = [f"probe-{i:02d}" for i in range(12)] baseline = {p: digest(f"stored-response-{p}") for p in probes}

today = dict(baseline) # replayed from a later run today[“probe-03”] = digest(“stored-response-probe-03 (reworded)”) today[“probe-09”] = digest(“stored-response-probe-09 (reworded)”)

changed = [p for p in probes if today[p] != baseline[p]] n, x = len(probes), len(changed) ci = binomtest(x, n).proportion_ci(confidence_level=0.95, method=“exact”) print(f"changed {x}/{n}: {changed}“) print(f"drift rate {x / n:.3f} 95% CI ({ci.low:.3f}, {ci.high:.3f})”)

for d in (0.20, 0.10, 0.05, 0.01): n_needed = math.ceil(math.log(0.10) / math.log(1 - d)) print(f"90% chance of catching drift that touches {d:5.0%} of prompts:" f" {n_needed:3d} probes")

Two readings. The drift estimate itself is nearly uninformative --- twelve probes give an interval running from two percent to nearly half --- which is the same lesson the evaluation section makes about underpowered comparisons, arriving here in a monitoring rather than a benchmarking guise. The second block is the design calculation: to have a 90%90\% chance of catching a change that affects a fraction dd of prompts you need about log(0.10)/log(1d)\log(0.10)/\log(1-d) probes, which is 22 at d=0.10d=0.10 and 230 at d=0.01d=0.01. A suite of five probes, which is what people build, detects only changes so pervasive that you would have noticed them anyway. Note also what the canary cannot see: it certifies that these prompts return the same bytes, and generalising from that to your workload assumes the probes are exchangeable with it.

1.1Exercises

  1. Write the equation as a mixed model and give the expression for the intraclass correlation of two calls on the same item with the same prompt. Explain which variance component more items reduces and which more calls reduces.

  2. Derive the limit the equation for the beta-binomial voting model and show it is strictly less than one for any ρ>0\rho > 0. Find the value of ρ\rho at which B=31B = 31 votes achieve less than half the accuracy gain that independence would give.

  3. Suppose each call costs cc and the analyst values accuracy linearly. Using the beta-binomial ceiling, derive the optimal number of votes BB^\ast and show it is finite whenever ρ>0\rho > 0 but unbounded when ρ=0\rho = 0.

  4. Explain why greedy decoding does not guarantee identical output across two machines, using the non-associativity of floating-point addition. Then argue whether bitwise reproducibility is the right target for a scientific analysis, or whether a reported variance is.

  5. (Computational) Choose a task with 100 items. For each item, run 10 calls at T=0.7T=0.7 under each of 4 prompt paraphrases. Fit the equation, report all four variance components with intervals, and state which one your evaluation budget should attack.

  6. (Computational) Estimate ρ\rho from the data collected above, then plot observed majority-vote accuracy against BB alongside the binomial and beta-binomial predictions, reproducing Figure the figure on real output.

  7. (Computational) Build a caching wrapper that hashes the prompt and decoding settings, stores every raw response, and replays from cache on re-execution. Demonstrate that a downstream analysis script produces byte-identical results on a second run with the network disabled.

References
  1. Wang, X., Wei, J., Schuurmans, D., Le, Q., Chi, E., Narang, S., Chowdhery, A., & Zhou, D. (2022). Self-Consistency Improves Chain of Thought Reasoning in Language Models.
  2. Lakshminarayanan, B., Pritzel, A., & Blundell, C. (2016). Simple and Scalable Predictive Uncertainty Estimation using Deep Ensembles.
  3. Guo, C., Pleiss, G., Sun, Y., & Weinberger, K. Q. (2017). On Calibration of Modern Neural Networks. Proceedings of the 34th International Conference on Machine Learning (ICML), 1321–1330.
  4. Pineau, J., Vincent-Lamarre, P., Sinha, K., & others. (2021). Improving Reproducibility in Machine Learning Research.
  5. Sculley, D., Holt, G., Golovin, D., Davydov, E., Phillips, T., Ebner, D., Chaudhary, V., Young, M., Crespo, J.-F., & Dennison, D. (2015). Hidden Technical Debt in Machine Learning Systems. Advances in Neural Information Processing Systems (NeurIPS).
  6. Breiman, L. (1996). Bagging Predictors. Machine Learning, 24, 123–140. 10.1007/BF00058655
  7. Micikevicius, P., Narang, S., Alben, J., & others. (2018). Mixed Precision Training.
  8. Goyal, P., Dollár, P., Girshick, R., & others. (2017). Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour.
  9. Rajbhandari, S., Rasley, J., Ruwase, O., & He, Y. (2020). ZeRO: Memory Optimizations Toward Training Trillion Parameter Models.
  10. Gebru, T., Morgenstern, J., Vecchione, B., Vaughan, J. W., Wallach, H., III, H. D., & Crawford, K. (2021). Datasheets for Datasets.
  11. Mitchell, M., Wu, S., Zaldivar, A., Barnes, P., Vasserman, L., Hutchinson, B., Spitzer, E., Raji, I. D., & Gebru, T. (2019). Model Cards for Model Reporting.