Expand description
The seam for encoder-only (embedding) models, which is deliberately
not crate::engine::Engine.
§Why a second trait and not a variant of the first
crate::engine::Engine is forward_token(token_id, pos, &mut State) -> Vec<f32>: one token in, one logit vector out, carrying
per-layer state forward. Every part of that signature is an
autoregression assumption, and a BERT encoder violates all of them:
- There is no state to carry. Attention is bidirectional, so token 0’s output depends on token 7. Nothing can be computed until the whole sequence is present, and nothing computed for one sequence is reusable for the next. A KV cache is not merely unnecessary here, it is meaningless — there is no “next token” for a cached key to be attended to by.
- There are no logits. The result is
n_tokens × n_embdhidden states (llama.cpp’sres->t_embd); this checkpoint has no output head at all, andtoken_embd.weightis not tied to one. posis not a cursor, it is an index into a learned table. BERT addsposition_embd.weight[i]to the token embedding rather than rotating Q/K, which is why the sequence length is hard-capped byn_ctx_traininstead of merely degrading past it.
Forcing that through Engine would mean a State that is a
pretend-cache, a forward_token that can only be called with the
last position after a hidden batch call, and a vocab_size that has
no meaning. The Kimi comment on Engine already records the cost of
bending a trait around a model it does not fit; this is the same
judgement, made before rather than after.
So: TextEncoder is sequence-in, matrix-out, stateless. What the
two seams do share is pooling (crate::pooling), which is why
that lives in its own module and not in either of them.
Structs§
- Pair
Sequence - One two-segment encoder input: the token ids and, for each of them, which half of the pair it belongs to.
Enums§
Traits§
- Text
Encoder - A model that turns a whole token sequence into hidden states in one pass, with no carried state and no logits.