[Optimisation & rétrospective] Qu’a fait le module de traduction de YouDub pendant toute cette journée, quelles directions ont été efficaces, lesquelles ont déjà été réfutées
Le champ de bataille principal de la journée n’était en réalité pas le TTS, mais le module de traduction.
La raison est simple : pour le module de doublage, on pourra ensuite basculer vers une solution plus proche du timbre cible, mais dans le rendu final, le point faible le plus évident reste que la traduction des sous-titres ne ressemble pas assez à un produit Bilibili abouti, en particulier :
- Dans les scènes de rolling caption, la phrase précédente “mange” des informations de la phrase suivante.
- Les sous-titres anglais d’origine sont fragmentés ; une fois traduits en chinois phrase par phrase de manière rigide, ça devient long et maladroit.
- Si l’on cherche trop à “compléter les phrases”, on dévoile à l’avance la punchline, les noms propres, les actions qui arrivent ensuite.
- Si l’on cherche trop à “conserver les frontières des fragments”, le chinois devient trop haché, pas comme de vrais sous-titres de version finale.
Donc, pour cette optimisation, j’ai déplacé le focus de “corriger une vidéo en particulier” vers “mettre en place un cadre d’évaluation et d’itération de traduction généralisable”.
Concrètement, qu’est-ce qui a été fait pendant la journée
1. D’abord mettre en place le système de référence, ne plus régler au feeling
Je n’ai pas continué à corriger ligne par ligne une seule vidéo ; j’ai directement utilisé les plus de 90 échantillons de comparaison Bilibili/YouTube dans C:\\Users\\1\\bili_yt_export\\bili_youtube_first100.csv comme base de référence.
Deux choses ont été faites en conséquence :
- Extension de
scripts/benchmark_translation.py - Ajout de
scripts/analyze_translation_artifact.py
Le premier exécute en batch toute la chaîne : traduction + segmentation + réécriture du texte de doublage, et sort pour chaque cas des métriques et des artefacts intermédiaires.
Le second sert à “démonter” chaque cas pour l’examiner, surtout à ces niveaux :
source_rowsprepared_source_rowstranslated_rows_pre_splitpredicted_rowsreference_rows
Cette étape est cruciale, car beaucoup de problèmes ensuite ne viennent pas du post-traitement : dès l’étape translated_rows_pre_split, le LLM a déjà “emprunté” du contenu à ce qui suit.
2. Le problème central a été clairement localisé : le rolling-caption emprunte le texte à venir
Le plus gros gain de la journée n’est pas qu’une métrique ait soudain explosé, mais d’avoir correctement identifié le problème principal :
Les sous-titres automatiques/officiels de YouTube contiennent massivement des structures rolling-caption : beaucoup de lignes sont à la base des demi-phrases, des bribes, des phrases qui se prolongent sur plusieurs lignes.
Si on demande directement au modèle de “traduire naturellement ligne par ligne”, il aura une forte tendance à ramener dans la ligne courante des informations des une à deux lignes suivantes : le chinois paraît plus fluide, mais sur la timeline, ça spoil en avance.
Ce problème est le plus évident sur deux hard cases : zwIqbrD6JX4 et o2V-JJpJH_I.
3. Faire de fragment_guard la ligne principale par défaut
Sur la base de ce constat, j’ai fait en sorte que fragment_guard soit activé par défaut.
Son idée centrale n’est pas de “forcer le chinois à être incomplet”, mais d’imposer clairement au modèle :
- L’id courant ne peut exprimer que la sémantique déjà apparue dans la ligne source courante.
- Si la ligne source est manifestement un fragment non terminé de rolling-caption, mieux vaut que le chinois reste légèrement ouvert plutôt que de compléter à l’avance avec du contenu futur.
C’est, pour l’instant, la seule modification dont l’efficacité est stabilisée, prouvée, et que je suis prêt à mettre en valeur par défaut sur la branche principale.
Résultats actuellement confirmés comme efficaces
Configuration principale (mainline)
La mainline stable actuelle est grosso modo :
- provider:
openai_context - api base:
http://192.168.10.88:8317/v1 - model:
gpt-5.4-mini - prompt profile:
auto_hybrid - temperature:
0 - fewshot:
8 fragment_guard=on- Les autres switches expérimentaux désactivés par défaut
Gains chiffrés déjà confirmés
fragment_guard donne un gain positif, des expériences locales jusqu’aux échantillons moyens :
- Comparaison sur 4 cas :
52.432 -> 53.322 - Comparaison sur 8 cas :
55.958 -> 56.058
Rapport mainline actuel sur 8 cas :
- composite:
56.058 - chrF:
0.3707 - char F1:
0.7729 - density MAE ratio:
0.4272
Cela indique qu’au moins sur la mainline actuelle, il est moins probable qu’on ramène à l’avance du contenu futur, et le rythme global est plus proche de la distribution des sous-titres Bilibili finalisés.
Quelles directions ont été globalement réfutées pendant la journée
1. Activer fragment_hints globalement
Ce n’est pas totalement inutile ; au contraire, c’est très puissant sur certains cas.
Par exemple :
zwIqbrD6JX4dans hard2 monte de54.439à57.980VT6rLcVKhzga aussi une amélioration nette
Mais le problème, c’est l’instabilité.
Sur 8 cas, l’ensemble baisse au contraire de 56.058 à 55.296.
Autrement dit, ça ressemble davantage à un “médicament fort pour une structure spécifique” qu’à une stratégie mainline activable par défaut pour l’instant.
2. auto_hybrid_v2
J’ai fait une version plus agressive de la logique de sélection automatique de profile, voulant que chaque vidéo bascule automatiquement entre literal_context / bilibili_dub / bilibili_pacing.
Résultat : sur 8 cas, ça tombe directement à 54.375, inférieur à la mainline actuelle 56.058.
Conclusion simple : la logique de gating n’est pas assez précise, donc ça ne peut pas aller en mainline à ce stade.
3. Forcer l’augmentation de l’étendue de traduction full-context
J’ai testé deux directions :
- Augmenter le seuil full-context, pour que plus de vidéos soient traduites d’un coup sur l’ensemble
- Agrandir directement les chunks depuis une petite fenêtre vers une plus grande
À première vue, cela se rapproche de “comprendre l’ensemble puis traduire”, mais en pratique il n’y a pas de gain stable.
La raison : avec plus de contexte, le modèle emprunte aussi plus facilement du contenu à travers les ids ; la timeline peut devenir plus chaotique.
4. Réduire très finement la granularité des chunks
Par exemple une approche chunk_max_items=2 : intuitivement cela semble réduire les contaminations entre phrases, mais les gains sont en réalité très faibles, et la vitesse diminue nettement.
Les résultats sur hard2 n’ont pas amélioré la qualité, mais la latence a fortement augmenté, surtout o2V-JJpJH_I qui traîne beaucoup.
5. Mettre à fond les “prompts style Bilibili”
J’ai testé :
literal_contextbilibili_dubbilibili_pacingauto_hybrid
Sur mixed4, auto_hybrid est le meilleur, literal_context est deuxième ; les deux profiles plus “style fort” sont au contraire moins bons.
Cela montre qu’actuellement ce n’est pas “plus le prompt ressemble à Bilibili, mieux c’est”, mais qu’il faut d’abord résoudre les frontières de contexte, les fragments de phrase et l’alignement timing, puis seulement parler de stylisation.
Le changement de compréhension le plus important de la journée
Je pensais au départ que le plus gros problème était “la phrase n’est pas traduite de manière assez idiomatique”, puis j’ai réalisé que ce n’était pas ça.
Le problème plus fondamental est en fait :
- L’entrée a priori est elle-même fragmentée
- Les fragments se recouvrent fortement
- Pour que le chinois soit naturel, il faut compléter modérément la tonalité et la structure
- Mais dès qu’on complète trop, on révèle à l’avance le contenu futur
Donc l’endroit le plus difficile du module de traduction n’est pas la traduction “EN->ZH” ou “ZH->EN” en soi, mais :
Sous la contrainte de ne pas franchir les frontières temporelles, remettre en forme de l’anglais fragmenté pour obtenir un rythme en chinois qui ressemble à de vrais sous-titres finalisés.
Ce n’est pas le même problème qu’une traduction automatique classique.
Les points pas encore résolus
Même si la mainline est plus stable qu’avant, on est encore très loin de l’objectif que je vise, et surtout on n’atteint pas encore le niveau de finition des exemples Bilibili que tu as donnés.
Points qui restent clairement mal résolus :
- Certains hard cases continuent d’emprunter le texte à venir
- Sur certains cas, le chinois reste encore “traductionnel”
- Après segmentation, la correspondance des longueurs n’est pas assez stable
fragment_hintsn’a pas encore de conditions de gating stables- Le nombre de few-shot et la sélection des exemples ne sont pas encore optimisés
Les directions que je juge les plus valables à poursuivre ensuite
À ce stade, ce qu’il vaut le plus la peine de poursuivre n’est pas d’ajouter plus de prompts “ésotériques”, mais ces trois points :
1. Faire un gating par caractéristiques pour fragment_hints, plutôt qu’un switch global
On sait déjà que c’est fortement efficace dans certains cas.
La prochaine étape est de faire du gating à partir de ces caractéristiques :
- fragmentary source ratio
- overlap ratio
- punctuated source ratio
- short/tiny line ratio
Autrement dit, ne l’activer que sur les vidéos “très fragmentées / très rolling-caption”, plutôt que de l’activer partout indistinctement.
2. Continuer à valider le nombre de few-shot
Un petit signal apparu à la toute fin aujourd’hui : fewshot=4 a donné pour la première fois un léger gain net sur hard2 :
- baseline hard2:
50.454 - fewshot=4 hard2:
50.600
Le gain est faible, mais la direction est positive.
Si mixed4 et mid8 peuvent le conserver, cela indiquerait que les 8 few-shot actuels apportent peut-être au contraire trop de bruit.
3. Continuer sur le chunk context “donner le contexte, sans donner le droit de traduire”
J’ai déjà ajouté une version de fenêtre de contexte avant/après chunk, mais elle n’est encore qu’en mode expérimental.
La valeur de cette direction, c’est :
- Donner au modèle une capacité de compréhension sur un segment complet
- Tout en exigeant qu’il ne sorte que les ids du chunk cible
Théoriquement, c’est plus adapté que d’agrandir simplement les chunks pour faire une traduction “comprendre tout mais ne pas dépasser”.
Conclusion de la journée
Si je dois résumer en une phrase :
Le plus grand résultat de la journée n’est pas d’avoir “terminé” le module de traduction, mais d’avoir clarifié “pourquoi ce problème est difficile, où est le goulot d’étranglement actuel, quelles directions sont efficaces, lesquelles ne valent déjà plus la peine d’y brûler du temps”.
Désormais, au moins, c’est clair :
- Le problème de traduction de ce projet n’est pas de la MT classique
- La frontière rolling-caption est la contradiction principale
fragment_guardest actuellement le seul gain stablefragment_hintsa du potentiel, mais doit être sous gating- Les stratégies few-shot et de contexte valent encore la peine d’être creusées
Si par la suite on veut vraiment polir cet outil vers “le meilleur doublage/traduction de vidéos étrangères au monde”, le module de traduction ne peut plus être réglé au hasard avec des prompts ; il faut continuer sur cette voie : pilotage par benchmark, attribution par cas, puis petits A/B itératifs.
Aujourd’hui, au moins, cette route a été tracée.