You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
str_tokens is a single joined string (not per-token) on transformers 5.x — token highlighting silently broken in explainer and classifier scorers #176
The sampler and constructor set example.str_tokens = tokenizer.batch_decode(example.tokens), where example.tokens is a 1-D tensor of shape (ctx_len,). On transformers ≥ 5, batch_decode forwards to decode(), which treats 1-D input as a single sequence — so it returns a length-1 list containing one joined string instead of the per-token list delphi assumes. On transformers 4.x, batch_decode was [self.decode(seq) for seq in sequences], and iterating a 1-D tensor yielded per-token strings.
Since pyproject.toml declares transformers unpinned, fresh installs get 5.x and hit this.
Consequences (mostly silent — no error)
Explainer: _highlight / _join_activations (explainers/explainer.py:75-116) iterate str_toks against per-token activation lists (length ctx_len). With len(str_tokens) == 1 they emit no <<>> marks and an empty Activations: line, so the explainer never sees which tokens activate.
Detection/fuzz scorers: scorers/classifier/sample.py:99 reads example.str_tokens the same way, so fuzzing prompts lose per-token highlighting (the fuzz scorer's premise is per-token marks) → silently degraded scores.
Simulation scorer: scorers/simulator/simulation/oai_simulator.py:103 builds ActivationRecord(self.tokenizer.batch_decode(example.tokens), activations) — a length-1 token list zipped against per-token activations.
Intruder scorer (loud symptom): scorers/classifier/intruder.py:122 indexes str_tokens[index] with token positions, which raises IndexError for any index > 0.
(latents/constructors.py:428 and :465 use the same pattern but immediately "".join(...) the result, so they still produce the intended text and are not functionally affected.)
Suggested fix
Replace batch_decode(example.tokens) at the affected sites with a per-token decode, which exactly restores the 4.x behavior:
example.str_tokens= [tokenizer.decode(t) fortinexample.tokens]
# or equivalently: tokenizer.batch_decode(example.tokens.unsqueeze(-1))
Note that tokenizer.convert_ids_to_tokens(...) is not a drop-in fix: it returns raw vocab strings, which for BPE tokenizers (GPT-2/Llama-style) injects encoding artifacts into prompts — e.g. ['Hello', 'Ġworld', ',', 'Ġtesting'] instead of ['Hello', ' world', ',', ' testing'] — and would also corrupt the scorers that "".join(str_tokens) (embedding, surprisal). For WordPiece tokenizers the two happen to be identical, but per-token decode is correct for all tokenizer types.
Alternatively/additionally, pinning transformers<5 in pyproject.toml would prevent the silent failure until the call sites are migrated.
Found while debugging unhighlighted explainer prompts in a downstream pipeline; behavior verified against both the 4.x and 5.x batch_decode implementations. Investigation was AI-assisted (Claude Code).
Activity