Decoder-only autoregressive sampling for causal LM heads.
The generation counterpart of MaskedLMMixin (the two canonical LM
objectives: causal vs masked). This drives autoregressive token-by-token
text generation — distinct from DiffusionMixin, which runs an
iterative denoising loop for image-generation models.
Provides generate — greedy or stochastic next-token sampling
with temperature, top-k, top-p (nucleus), and repetition-penalty
knobs. Per-sequence stopping at eos_token_id is honoured.
Concrete subclasses must:
- Inherit from
lucid.models.PretrainedModel. - Define
forward(input_ids, ...)returning alucid.models.CausalLMOutputwithlogitsof shape(B, T, vocab_size). - Expose a
configattribute carryingvocab_size/pad_token_id/bos_token_id/eos_token_id(fieldslucid.models.text.LanguageModelConfigdefines).
Notes
The first implementation does not use the model's KV cache —
every step re-runs the full prefix. This is correct but O(T²) in
the prefix length. Cache support is planned once GPT-2 lands and a
concrete past_key_values shape is available to plumb through.
Examples
>>> model = AutoModelForCausalLM.from_pretrained("gpt2_small")
>>> prompt = lucid.tensor([[1, 2, 3]]).long()
>>> out = model.generate(prompt, max_new_tokens=10, do_sample=True, top_p=0.9)
>>> out.shape
(1, 13)Used by 3
Instance methods
1generate(input_ids: Tensor, max_length: int = 20, max_new_tokens: int | None = None, do_sample: bool = False, temperature: float = 1.0, top_k: int | None = None, top_p: float | None = None, repetition_penalty: float = 1.0, pad_token_id: int | None = None, eos_token_id: int | None = None, use_cache: bool = True, cache_implementation: str = 'dynamic', max_cache_len: int | None = None)Autoregressively extend input_ids until a stop condition.
Parameters
input_idsTensor(B, T_prompt) int prompt tokens.max_length(int, optional, keyword - only)= 20max_new_tokens is supplied.max_new_tokens(int or None, optional, keyword - only)= Nonemax_length.do_sample(bool, optional, keyword - only)= FalseFalse → greedy argmax decoding; True → stochastic
sampling honouring temperature / top_k / top_p.temperature(float, optional, keyword - only)= 1.0< 1 sharpens, > 1
flattens. Ignored under greedy decoding.top_k(int or None, optional, keyword - only)= NoneNone disables.top_p(float or None, optional, keyword - only)= Nonetop_p. None disables.repetition_penalty(float, optional, keyword - only)= 1.01 / penalty (and by penalty if the logit is
negative). 1.0 → no effect; > 1 discourages
repetition.pad_token_id(int or None, optional, keyword - only)= Noneeos_token_id. Defaults to config.pad_token_id, then
0 if unset.eos_token_id(int or None, optional, keyword - only)= Noneconfig.eos_token_id.use_cache(bool, optional, keyword - only)= Truecache_implementation(str, optional, keyword - only)= "dynamic""dynamic" → lucid.utils.cache.DynamicCache (grows by
concatenation); "static" →
lucid.utils.cache.StaticCache (fixed pre-allocated buffer
written in place at cache_position, attending over only the filled
prefix). "static" is faster on compute-bound batches (in-place
write + filled-prefix attention beat the growing concat — ~1.4× at
GPT-2-base / B=16) and tied at B=1; it needs max_cache_len.max_cache_len(int or None, optional, keyword - only)= Nonecache_implementation="static"; defaults to
the total target length (prompt + generated). An over-sized buffer is
free — attention narrows to the filled prefix regardless.Returns
Tensor(B, T_final) int tensor where T_final ≤ max_length
(or T_prompt + max_new_tokens). Sequences that hit
eos_token_id early are right-padded with pad_token_id.
Raises
ValueErrorinput_ids is not 2-D.Notes
Decorated with lucid.no_grad — generation never builds an
autograd graph. Each step:
- Materialise the current prefix as
(B, T_cur). - Run a forward pass; take logits at the last position.
- Apply repetition penalty.
- Greedy argmax, or apply temperature / top-k / top-p and inverse-CDF sample one token per row.
- Replace finished rows' tokens with
pad_token_id.
Loop breaks early when every row has emitted EOS.
Examples
>>> model = AutoModelForCausalLM.from_pretrained("gpt")
>>> tokens = lucid.tensor([[1, 2, 3]]).long()
>>> out = model.generate(tokens, max_new_tokens=5)
>>> out.shape
(1, 8)