✎ Edit content·DAY 066 · POST 4 OF 5 · Code Example

Temperature, Top-p, Top-k

NLP & LLMs · 11 slides
DAY 066 · POST 4 OF 5
(REMINDER)
DAY 066
The Dials In Real Code
@saurav_dnj_24github.com/SauravDnj · linkedin.com/in/sauravdnj
1 / 11

Theme

Palette

Download

4K — sharpest, slowest
🎬 Video options
Preparing preview…
Live preview · loops the “none” effect
All rendering runs in your browser. No server, no cost, no upload. MP4/WebM = full motion + effects · GIF = lightweight loop · PNG/PDF = static for the Instagram & LinkedIn carousel.

Caption (tap to copy)

📖 Deep dive (full written explanation)

The slides stay clean and scannable. Here's the in-depth explanation behind each one — great for the blog version, show notes, or studying the topic properly.
Slide 1 · The Dials In Real Code

This is the hands-on post, and its job is to convert the previous post's math into things you can run and feel. Every snippet uses gpt2, a small open model that loads on a laptop, so there's no excuse not to actually execute them. We isolate one dial at a time, then reimplement the sampler by hand so the API arguments stop being opaque.

The deliberate teaching arc is baseline first, then one variable at a time. If you change temperature, top-k, and top-p all at once you can't attribute the effect to any of them — so we hold the others fixed and move one knob per experiment.

Slide 2 · 1. Setup + greedy baseline

The first snippet establishes a deterministic baseline with greedy decoding. The critical detail is do_sample=False: when sampling is off, Hugging Face's generate ignores temperature, top_k, and top_p entirely and simply takes the highest-probability token at every step. Run it twice and you get byte-for-byte identical output.

This baseline matters for two reasons. It's the reference point against which every sampled variation is compared, and it's a common gotcha — beginners set temperature but forget do_sample=True, then wonder why nothing changes. Greedy is also the correct choice whenever you need full determinism.

Slide 3 · 2. Turn ONE dial: temperature

Here we turn exactly one dial: temperature, with do_sample=True and the other filters disabled (top_k=0, top_p=1.0) so nothing else interferes. Looping over 0.3, 0.7, and 1.3 lets you watch the same prompt produce increasingly varied continuations. At 0.3 the text is tight and predictable; at 1.3 it wanders and takes risks.

The manual_seed call before the loop is what makes the comparison fair — it fixes the random draw so the only thing changing between runs is T itself. Slicing out[0][ids.shape[1]:] prints just the newly generated tokens, not the echoed prompt, which makes the differences easier to read.

Slide 4 · 3. Add top-k and top-p

Now we add the truncation filters on top of a fixed temperature. top_k=50 first restricts the pool to the fifty most likely tokens; top_p=0.92 then keeps only enough of those to cover 92% of the remaining mass. The inline comment captures the ordering that the previous post established: top_k runs before top_p, so top-p operates on the already-clipped set.

This is the realistic, production-style configuration — a moderate temperature paired with both filters acting as guardrails against the incoherent tail. Seeing all three set together, with a comment on their interaction, ties the whole topic into one concrete call.

Slide 5 · 4. The sampler, by hand

This is the heart of the post: the sampler reimplemented in PyTorch so nothing is hidden. Logits are divided by T for temperature. For top-k, torch.topk finds the k-th largest value and everything below it is set to negative infinity, so softmax sends those tokens to zero. After softmax, top-p sorts the probabilities, uses a cumulative sum to mask tokens beyond the mass threshold, scatters the kept values back to their original positions, and multinomial draws one token from the renormalized result.

The mask expression cumsum(s) - s > top_p is the subtle part: subtracting the token's own probability ensures the token that crosses the threshold is itself kept, matching standard nucleus behavior. Tracing this function line by line is the surest way to truly own the concept.

Slide 6 · What the code does each step

The pipeline diagram mirrors the hand-written sampler step for step, so the code and the picture reinforce each other. Logits enter, get divided by T, the top-k mask sends rejects to negative infinity, softmax produces probabilities, the top-p mask zeros the tail, and multinomial draws a single token.

Mapping each code line to a visual stage closes the loop between the math post and this one. When you next pass these arguments to an API, you can picture exactly which stage each one controls and in what order they fire.

Slide 7 · 5. Reproducible sampling

Reproducibility gets its own demonstration because it's where sampling surprises people in production. The run function seeds the RNG, then generates with sampling on. Calling run(42) twice yields identical text — same seed, same params, same output — while run(42) versus run(7) diverges. This proves that the randomness is controllable, not magical.

The practical lesson is that 'non-deterministic' doesn't mean 'uncontrollable.' For tests and reproducible bug reports you pin the seed and the parameters; for production variety you let the seed float. The same machinery serves both needs depending on whether you fix the seed.

Slide 8 · 6. Peek at the probabilities

The final inspection snippet pulls back the curtain on the distribution itself. We run a forward pass, grab the logits at the last position, apply temperature via softmax, and print the top five tokens with their probabilities. Suddenly the abstract 'distribution over the vocabulary' is a concrete, readable table.

This is also a debugging superpower. When output looks wrong, printing the top candidates and their probabilities tells you whether the model genuinely favored a bad token or whether your sampling settings let an unlikely one through. Seeing the numbers turns guesswork into diagnosis.

Slide 9 · Gotchas you just saw

These gotchas are the ones you just witnessed in the snippets, collected so they stick. do_sample must be True or temperature, top_k, and top_p are silently ignored. Setting top_k=0 and top_p=1.0 disables those filters so you can isolate temperature. top_k is applied before top_p when both are present. And setting a seed is what makes any sampled run reproducible.

These four account for the overwhelming majority of 'why didn't my parameter do anything' confusion. Internalizing them saves hours of bewildered debugging the first time you wire sampling into real code.

Slide 10 · 7. Same idea via a hosted API

The hosted-API snippet closes the gap between local experimentation and production. The same conceptual dials — temperature and top_p — map directly onto the OpenAI chat call, and a seed argument provides best-effort reproducibility. The 'best-effort' caveat is honest: hosted providers can't always guarantee determinism across infrastructure changes the way a local model with a fixed seed can.

Note what's absent: there's no top_k here, because OpenAI's chat API doesn't expose it. That reinforces the earlier point that you should know which knobs your specific platform actually offers before you try to tune them.

Slide 11 · Save this. Follow for Day 67.

The closing slide hands off to the mistakes post. Having seen the dials work correctly, the natural next step is to study how they fail — stacking them carelessly, applying high temperature to structured output, and trusting inherited defaults. Seeing the correct behavior first makes the failure modes legible.

The progression across the five posts is intentional: concept, stakes, math, working code, and finally the field guide to misuse. By this point you can run the dials; next you learn how not to break them.

🎨 AI image prompt (matches this theme + palette)

Paste into Midjourney, DALL·E, Ideogram, etc. to generate an on-brand image, then upload it on the Edit content page. The prompt updates automatically with the selected theme + palette.