Temperature: The One Number That Decides How Confident a Model Sounds

Copy a text-generation tutorial and you'll almost always find this line, unexplained:

outputs = model.generate(
    **inputs,
    max_new_tokens=200,
    temperature=1.0,
)

That's the standard Hugging Face transformers library convention for generating text — max_new_tokens just caps output length and isn't covered here; temperature is. Left at its default, tweaked without explanation, or blamed vaguely for a model being "too random" or "too repetitive" — most people setting this number couldn't say exactly what it does to the math underneath. Temperature doesn't pick a word. It reshapes the entire probability distribution the model is choosing from, before anything downstream ever runs. This post builds a tiny, fully verifiable version of that reshaping from scratch, so every number below is something you can rerun yourself, not a claim to take on faith.

What the Model Actually Outputs

At every step, a language model (LLM, short for large language model) doesn't output a word directly. It outputs a logit — a raw, unbounded score — for every entry in its vocabulary (the fixed list of words/tokens it's allowed to output), which can run into tens of thousands of candidates. A function called softmax turns those raw scores into a proper probability distribution: every value between 0 and 1, all of them summing to exactly 1.

To keep the numbers small enough to check by hand, this post uses a 12-token toy vocabulary (the full list of candidates the model is choosing between here) standing in for a model answering "How many days do I have to recover a deleted file?" — the correct answer is 30. A few distractors (wrong answers designed to look plausible) are mixed in: a wrong-but-plausible number belonging to a different rule entirely (14 — a refund window, not a recovery window), a wrong number tied to a different, enterprise-specific extension window (90), and three ungrounded words a model could drift toward if it stopped trusting the numbers at all (immediately, never, unsure). A real model's vocabulary works the same way, just with tens of thousands of tokens instead of 12. The logits themselves are made up by hand for this demo — but every probability and percentage computed from them below is real, run code, not an estimate.

How Temperature Reshapes the Distribution

Temperature is applied before softmax runs, dividing every logit by T:

softmax(logits / T)
Bar chart comparing token probabilities at T=0.5, T=1.0, and T=2.0 on the same logits — the correct answer '30' drops from 76.7% to 50.6% to 28.8% as temperature rises

Run the same logits through three temperatures and the correct answer's own probability moves from 76.69% at T=0.5, to 50.61% at T=1.0 (the untouched distribution), to 28.83% at T=2.0. The three ungrounded words combined tell the sharper story: their share of the total probability goes from 0.0052% at T=0.5, to 0.6733% at T=1.0, to 5.6448% at T=2.0 — over a thousand-fold increase (roughly 1,079 times), on identical logits.

Doesn't raising temperature just mean "more random," in some vague sense? Concretely, it means every candidate word that wasn't already the clear favorite gets a larger, calculable share of the total probability — including the ones you'd least want a factual system trusting. Divide by a number below 1 (T < 1) and the distribution sharpens: the leading token gets more likely, everything else shrinks. Divide by a number above 1 (T > 1) and it flattens: the tail gets a real, measurable chance it didn't have before. Temperature doesn't pick a word. It decides how much of the distribution's original confidence survives before the model picks one.

Get the Code

The full softmax and temperature code behind every number above is under 40 lines. Save it as temperature.py and run it with python temperature.py. It needs only numpy.

import numpy as np

VOCAB = ["30", "14", "90", "60", "45", "7", "10", "365", "0",
         "immediately", "never", "unsure"]

# Raw logits for: "How many days do I have to recover a deleted file?"
LOGITS = np.array([4.0, 3.2, 2.8, 1.5, 1.0, 0.6, 0.4, -0.5, -0.8, -1.0, -1.5, -2.0])
WRONG_TOKENS = ["immediately", "never", "unsure"]

def softmax(logits):
    shifted = logits - np.max(logits)  # subtract max for numerical stability
    exp = np.exp(shifted)
    return exp / exp.sum()

def apply_temperature(logits, T):
    return softmax(logits / T)

wrong_idx = [VOCAB.index(t) for t in WRONG_TOKENS]

probs = {T: apply_temperature(LOGITS, T) for T in (0.5, 1.0, 2.0)}
for T, p in probs.items():
    print(f"T={T}: P('30') = {p[0]:.4%}, ungrounded mass = {p[wrong_idx].sum():.4%}")

ratio = probs[2.0][wrong_idx].sum() / probs[0.5][wrong_idx].sum()
print(f"Ungrounded mass, T=2.0 vs T=0.5: {ratio:,.0f}x")

Running it prints:

T=0.5: P('30') = 76.6900%, ungrounded mass = 0.0052%
T=1.0: P('30') = 50.6141%, ungrounded mass = 0.6733%
T=2.0: P('30') = 28.8295%, ungrounded mass = 5.6448%
Ungrounded mass, T=2.0 vs T=0.5: 1,079x

Summary

Temperature answers one question — how much of a probability distribution's original confidence survives before a model turns it into one chosen word. On identical logits, T=0.5 dropped the combined probability of three ungrounded words to 0.0052% while the correct answer held 76.69%; T=2.0 raised that same ungrounded share to 5.6448% while the correct answer's own probability fell to 28.83%, a roughly 1,079x increase in how likely those ungrounded words become. It isn't a vague "randomness" dial; it's a specific, calculable reshaping of the distribution itself, applied before the model picks anything.

Comments

Popular posts from this blog

Retrieve list of Spooled files on System from SQL - IBM i

All about READ in RPGLE & Why we use it with SETLL/SETGT?

Greedy vs. Sampling: Why the Same Prompt Can Give You a Different Answer Every Time