fix(voxtral): Repetition-Loop bremsen ('vergiss das'-Schleife)
Stefans Repro: eine echte Nachricht endete mit "...vergiss das, das ist nur..."
und Voxtral hat den Satz ~15x geloopt, bis zur Brain durch. Klassische
Repetition-Halluzination bei Stille/Rauschen am Ende.
Zwei Ebenen: (1) Generation bekommt no_repeat_ngram_size=4 + repetition_penalty
=1.15 → erste echte Nennung bleibt, exakte 4-Gramm-Wiederholung verboten, Loop
bricht an der Quelle ab. (2) Post-Detektor _collapse_repetitions kassiert einen
durchgerutschten Loop auf eine Kopie ein. Gegen den echten Loop + legitime
Doppelungen ('ja ja ja', 'sehr sehr') verifiziert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+38
-1
@@ -94,6 +94,24 @@ _HALLUCINATION_RE = re.compile(
|
|||||||
re.IGNORECASE,
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Kollabiert unmittelbar wiederholte Phrasen (Voxtral-Repetition-Loop) auf EINE
|
||||||
|
# Kopie. Zweites Netz hinter no_repeat_ngram in der Generation. Phrase 5-80 Zeichen,
|
||||||
|
# 3+ mal hintereinander → eine. Kurze legitime Doppelungen ('ja ja', 'sehr sehr')
|
||||||
|
# bleiben (Unit < 5 Zeichen bzw. < 3 Wiederholungen).
|
||||||
|
_REPEAT_RE = re.compile(r"(.{5,80}?)(?:\s*\1){2,}", re.IGNORECASE | re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
def _collapse_repetitions(text: str) -> str:
|
||||||
|
if not text:
|
||||||
|
return text
|
||||||
|
out = text
|
||||||
|
for _ in range(3): # mehrfach fuer verschachtelte/ungleiche Loops
|
||||||
|
new = _REPEAT_RE.sub(r"\1", out)
|
||||||
|
if new == out:
|
||||||
|
break
|
||||||
|
out = new
|
||||||
|
return out.strip()
|
||||||
|
|
||||||
# Speaker-ID Gating global an/aus. DEFAULT AUS (fail-open) — die "nur meine Stimme"-
|
# Speaker-ID Gating global an/aus. DEFAULT AUS (fail-open) — die "nur meine Stimme"-
|
||||||
# Pruefung ist ein BEWUSSTER Schalter, kein Automatismus: ein einziger schlechter
|
# Pruefung ist ein BEWUSSTER Schalter, kein Automatismus: ein einziger schlechter
|
||||||
# Enroll darf nie die ganze STT lahmlegen (genau das ist passiert). Wird per config-
|
# Enroll darf nie die ganze STT lahmlegen (genau das ist passiert). Wird per config-
|
||||||
@@ -162,7 +180,20 @@ class VoxtralRunner:
|
|||||||
with torch.no_grad():
|
with torch.no_grad():
|
||||||
# hoch genug fuer lange Diktate (stoppt eh am EOS); 512 hat
|
# hoch genug fuer lange Diktate (stoppt eh am EOS); 512 hat
|
||||||
# mehrminutige Aufnahmen abgeschnitten.
|
# mehrminutige Aufnahmen abgeschnitten.
|
||||||
outputs = model.generate(**inputs, max_new_tokens=4096)
|
# Repetition-Bremse: Voxtral kippt bei Stille/Rauschen am Ende
|
||||||
|
# gern in eine Schleife und wiederholt einen Satz zig-mal
|
||||||
|
# ("Vergiss das, das ist nur... Vergiss das, das ist nur..."
|
||||||
|
# x15). no_repeat_ngram_size=4 laesst die ERSTE echte Nennung
|
||||||
|
# durch, verbietet aber die exakte 4-Gramm-Wiederholung → Loop
|
||||||
|
# bricht ab; repetition_penalty daempft zusaetzlich. Beides mild,
|
||||||
|
# damit normale Sprache (auch mal ein doppeltes Wort) unberuehrt
|
||||||
|
# bleibt.
|
||||||
|
outputs = model.generate(
|
||||||
|
**inputs,
|
||||||
|
max_new_tokens=4096,
|
||||||
|
no_repeat_ngram_size=4,
|
||||||
|
repetition_penalty=1.15,
|
||||||
|
)
|
||||||
trimmed = outputs[:, inputs.input_ids.shape[1]:]
|
trimmed = outputs[:, inputs.input_ids.shape[1]:]
|
||||||
text = proc.batch_decode(trimmed, skip_special_tokens=True)
|
text = proc.batch_decode(trimmed, skip_special_tokens=True)
|
||||||
return (text[0] if text else "").strip()
|
return (text[0] if text else "").strip()
|
||||||
@@ -485,6 +516,12 @@ class SessionManager:
|
|||||||
final_text = sess.last_partial
|
final_text = sess.last_partial
|
||||||
stt_ms = int((time.time() - t0) * 1000)
|
stt_ms = int((time.time() - t0) * 1000)
|
||||||
duration_s = audio.size / 16000.0
|
duration_s = audio.size / 16000.0
|
||||||
|
# Repetition-Loop einkassieren, falls trotz no_repeat_ngram was durchkam.
|
||||||
|
_collapsed = _collapse_repetitions(final_text)
|
||||||
|
if _collapsed != final_text:
|
||||||
|
logger.info("Stream %s: Repetition-Loop kollabiert (%d→%d Zeichen)",
|
||||||
|
sess.request_id[:8], len(final_text), len(_collapsed))
|
||||||
|
final_text = _collapsed
|
||||||
logger.info("Stream %s: FINAL (reason=%s, %.1fs, %dms): %r",
|
logger.info("Stream %s: FINAL (reason=%s, %.1fs, %dms): %r",
|
||||||
sess.request_id[:8], reason, duration_s, stt_ms, final_text[:120])
|
sess.request_id[:8], reason, duration_s, stt_ms, final_text[:120])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user