Files
wehub-resource-sync a8262fc01e
docs / build (push) Has been cancelled
docs / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:22 +08:00

4.1 KiB

Generation & Sampling

Training predicts the next token for known text. Generation uses the model's own sampled token as the next input.

That feedback loop is why small probability differences can produce very different completions.

Autoregressive loop

flowchart LR
    P["prompt ids"] --> CROP["keep last context_length ids"]
    CROP --> M["Transformer"]
    M --> LOGITS["last-position logits"]
    LOGITS --> PROBS["softmax / sampling"]
    PROBS --> NEXT["next token id"]
    NEXT --> APPEND["append to sequence"]
    APPEND --> CROP

The simple implementation in src/models/transformer.py:

for _ in range(max_new_tokens):
    idx_cond = idx[:, -self.context_length:]
    logits, _ = self(idx_cond)
    logits = logits[:, -1, :]
    probs = F.softmax(logits, dim=-1)
    idx_next = torch.multinomial(probs, num_samples=1)
    idx = torch.cat((idx, idx_next), dim=1)

Only the final position is used because that position has attended to the whole current context.

Greedy decoding vs sampling

Greedy decoding chooses:

[ \arg\max_i p_i ]

Sampling draws:

[ x \sim \text{Categorical}(p) ]

Greedy decoding is deterministic and often repetitive. Sampling is stochastic and can produce more varied text.

This repo's simple base generate method samples directly from the full softmax distribution. The post-training inference utilities add more controls.

Temperature

Temperature rescales logits before softmax:

[ p_i = \frac{\exp(z_i / \tau)} {\sum_j \exp(z_j / \tau)} ]

Effects:

  • (\tau < 1): sharper distribution, safer but less diverse;
  • (\tau = 1): unchanged;
  • (\tau > 1): flatter distribution, more diverse but more error-prone.

Temperature operates on logits, not probabilities.

Top-k and top-p

Many generation systems restrict the candidate set before sampling.

Top-k keeps only the (k) highest-probability tokens.

Top-p, also called nucleus sampling, keeps the smallest set of tokens whose cumulative probability is at least (p).

These controls are not part of the base model architecture. They are decoding policies layered on top of the model's logits.

Context cropping

The model has a fixed maximum context length:

idx_cond = idx[:, -self.context_length:]

If the conversation grows beyond that, the oldest tokens are dropped. The model cannot attend to text outside the retained context window.

This is why context length is a product constraint, not just a training hyperparameter.

Stop tokens

The tokenizer's EOT token is:

EOT_ID = 50256

During training, EOT appears between documents and after assistant messages. During inference, a chat loop can stop when EOT appears or when a formatted answer is complete.

If the model was never trained to emit a clear stop token or answer delimiter, decoding has to guess when to stop.

Why generated text can drift

During teacher-forced training, every input prefix comes from the dataset. During generation, prefixes come from the model itself. If the model samples a poor token early, future predictions condition on that poor token.

This distribution shift is one reason post-training matters:

  • SFT teaches the answer format;
  • reward/preference methods push the model toward preferred completions;
  • RLVR/GRPO can reward final answers that satisfy an external verifier.

Useful generation diagnostics

Symptom Likely cause Check
Repeats forever distribution too sharp or no learned stop behavior lower max tokens, check EOT handling, adjust sampling
Ignores instruction base model not SFT-trained enough test an SFT checkpoint
Wrong answer format SFT data format mismatch inspect chat template and masks
Random-looking text undertrained model or high temperature compare train/dev loss and lower temperature
Crashes on long prompts prompt exceeds context or device memory crop context and inspect context_length

Next

You now have the foundation for the full pipeline. Continue to: