cohere: stop degenerate repetition in the greedy decoder - #153
Open
alexxxcoelho wants to merge 1 commit into
Open
cohere: stop degenerate repetition in the greedy decoder#153alexxxcoelho wants to merge 1 commit into
alexxxcoelho wants to merge 1 commit into
Conversation
The Cohere decoder picks each token by plain argmax with no repetition penalty, no no_repeat_ngram and no coverage term, so nothing can break a self-reinforcing state: when the most likely continuation of a phrase is that same phrase, it is emitted until the token budget runs out. The transcript comes back with TRANSCRIBE_OK and no diagnostic beyond the "output truncated at N tokens" warning, which fires because of the loop rather than before it. Measured on 60 problem recordings from a French dictation corpus, Q8_0 and Q4_K_M loop token for token — 6 repetitions, 43 words, identical output — which rules out quantisation and points at the search. Adds a tail-repetition guard on both decode paths (static-graph and dynamic-graph). A block counts as a loop only if it recurs at least 3 times and spans at least 12 tokens in total, so a 1-token block needs 12 repeats while a 4-token block needs 3; that floor keeps an emphatic "no no no" from being mistaken for a loop. Every copy but the first is trimmed, a warning is logged, and decoding stops as if EOS had been reached. Only the tail is examined, so deliberate repetition earlier in an utterance survives. Result on the same 60 files: 1 -> 0 looping files, maximum repetition 6 -> 2, and only 4 of 60 outputs change at all. Verified independently on a second corpus of 7,724 speech units (28.9 h of French meeting audio): 36 loops stopped at the source, none residual. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
Thanks I'll take a look and likely pull in, I think the reference doesn't do this but this should be helpful in the general case so we should |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
cohere: stop degenerate repetition in the greedy decoder
The bug
src/arch/cohere/model.cppselects each token with a plain argmax over thelogits (three sites: the prompt pass at ~L1063 and both step loops). There is no
repetition penalty, no
no_repeat_ngram, and no coverage term. The only exitsare
eos_idand the token budget.That leaves nothing to break a self-reinforcing state: when the most likely
continuation of a phrase is that same phrase, the decoder emits it until the
budget runs out. The result is a transcript that repeats one sentence dozens of
times, returned with
TRANSCRIBE_OKand no diagnostic beyond theoutput truncated at N tokenswarning — which fires because of the loop, notbefore it.
Evidence that this is the search, not the weights
Reproduced on 60 problem recordings from a French dictation corpus, decoded
twice with the same audio and the same parameters:
Q8_0Q4_K_MOn the worst case the two quantisations loop token for token: 6 repetitions,
43 words, identical output. Quantisation is not the cause.
A related observation: every
output truncated at 512 tokenswarning in thatcorpus landed on a looping file. The cap is a symptom, not the disease.
The fix
A tail-repetition guard in the decode loop, on both the static-graph (GPU) and
dynamic-graph (CPU) paths.
After each token is appended,
looping_tail_block()reports the length of ablock that repeats at the end of the generated sequence. A block counts only if
it recurs at least
kMinLoopRepeat(3) times and spans at leastkMinLoopTokens(12) tokens in total — so a 1-token block needs 12 repeatswhile a 4-token block needs 3. That floor is what keeps an emphatic
"no no no"from being read as a loop.When a loop is found,
trim_looping_tail()drops every copy of the block butthe first, the decoder logs a warning, and decoding stops as if EOS had been
reached. Text preceding the loop is kept — it is usually correct; only the
runaway tail is discarded.
Only the tail is examined, so a deliberate repetition earlier in an utterance
survives untouched.
Results
On the same 60 problem files:
56 of 60 files are byte-identical, so the guard is not rewriting healthy output.
Independently verified on a second corpus of 7,724 speech units (28.9 h,
French meeting recordings): 36 loops stopped at the source, and a mechanical
scan of the outputs found no residual loop.
Notes
<algorithm>is added forstd::max.a field on
transcribe_run_params, and I am happy to rework it that way.canaryandcanary_qwendecoders, which carry the same
output truncated at %d tokenswarning. I havenot reproduced a loop there, so I have left them alone rather than change code
I could not test.