[Optimización y retrospectiva] ¿Qué hizo el módulo de traducción de YouDub durante todo el día? Qué enfoques funcionaron y cuáles ya quedaron refutados
El principal campo de batalla de este día no fue el TTS, sino el módulo de traducción.
La razón es directa: más adelante el módulo de doblaje ya puede cambiarse a una solución más cercana al timbre objetivo, pero en el producto final la carencia más evidente sigue siendo que la traducción de subtítulos en sí no se parece lo suficiente a un producto maduro de B 站, especialmente:
- En escenarios de rolling caption, la frase anterior se “come” información de la siguiente.
- Los subtítulos originales en inglés están fragmentados; en cuanto el chino se traduce rígidamente línea por línea, queda largo e incómodo.
- Si se persigue en exceso “completar las oraciones”, se filtran por adelantado el punchline, los sustantivos y las acciones de más adelante.
- Si se persigue en exceso “mantener los límites del fragmento”, el chino se vuelve demasiado fragmentado y no parece un subtítulo real ya montado.
Así que en esta ronda de optimización, cambié el foco de “arreglar un video” a “hacer un marco de evaluación e iteración de traducción generalizable”.
Qué se hizo específicamente este día
1. Primero montar el sistema de referencia, dejar de ajustar por intuición
No seguí fijándome en un solo video para corregir línea por línea, sino que directamente tomé más de noventa muestras de referencia B 站/YouTube del C:\\Users\\1\\bili_yt_export\\bili_youtube_first100.csv como baseline.
En consecuencia hice dos cosas:
- Amplié
scripts/benchmark_translation.py - Añadí
scripts/analyze_translation_artifact.py
El primero se encarga de ejecutar en lote toda la cadena de traducción + segmentación + reescritura de texto para doblaje, y sacar para cada case métricas e intermedios.
El segundo se encarga de desarmar cada case para mirarlo por capas, especialmente estas:
source_rowsprepared_source_rowstranslated_rows_pre_splitpredicted_rowsreference_rows
Este paso es crucial, porque muchos problemas posteriores no los causa el posprocesado, sino que el LLM ya en translated_rows_pre_split “toma prestado” contenido que viene después.
2. Se ubicó con claridad el problema central: en rolling-caption se “toma prestado” del texto posterior
La mayor ganancia de este día no fue que alguna métrica se disparara de golpe, sino haber clavado el problema principal:
En los subtítulos automáticos/oficiales de YouTube abundan estructuras rolling-caption; muchas líneas en realidad son medias frases, frases incompletas o continuaciones que atraviesan varias líneas.
Si al modelo se le pide simplemente “traducir naturalmente línea por línea”, tenderá mucho a traer información de una o dos líneas futuras a la línea actual, de modo que el chino se vea más fluido, pero en la línea de tiempo se produce spoiler adelantado.
Este problema se ve más claro en estos dos hard case: zwIqbrD6JX4 y o2V-JJpJH_I.
3. Elevar fragment_guard a la línea principal por defecto
Con base en lo anterior, dejé fragment_guard activado por defecto.
Su idea central no es “forzar a que el chino quede mutilado”, sino imponer explícitamente límites al modelo:
- El id actual solo puede expresar la semántica que ya apareció en la línea fuente actual.
- Si la línea fuente es claramente un fragmento incompleto de rolling-caption, es preferible que el chino sea ligeramente abierto antes que completar por adelantado contenido futuro.
Por ahora, este es el único cambio que ya demostró de forma estable ser efectivo, y que estoy dispuesto a meter como valor por defecto en la línea principal.
Resultados que ya se confirmaron como efectivos
Configuración de la línea principal
La línea principal estable actual es aproximadamente:
- 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- El resto de interruptores experimentales desactivados por defecto
Mejoras de datos ya confirmadas
fragment_guard, desde pruebas locales hasta muestras medianas, da rendimiento positivo:
- Comparativa 4 case:
52.432 -> 53.322 - Comparativa 8 case:
55.958 -> 56.058
Reporte actual de la línea principal en 8 case:
- composite:
56.058 - chrF:
0.3707 - char F1:
0.7729 - density MAE ratio:
0.4272
Esto indica que al menos la línea principal actual, frente a versiones más tempranas, ya es menos propensa a traer contenido futuro por adelantado, y el ritmo general también se parece más a la distribución de subtítulos terminados de B 站.
Qué direcciones de este día ya quedaron básicamente refutadas
1. Activar fragment_hints globalmente
No es que no sirva; al contrario, en algunos case es muy fuerte.
Por ejemplo:
zwIqbrD6JX4en hard2 sube de54.439a57.980VT6rLcVKhzgtambién mejora de forma notable
Pero el problema es que no es estable.
En 8 case, el total cae de 56.058 a 55.296.
Es decir, se parece más a un “medicamento fuerte para estructuras específicas” que a una estrategia de línea principal que pueda activarse por defecto.
2. auto_hybrid_v2
Hice una versión con una lógica de selección automática de profile más agresiva, buscando que distintos videos cambien automáticamente entre literal_context / bilibili_dub / bilibili_pacing.
El resultado: en 8 case cayó directamente a 54.375, peor que la línea principal actual 56.058.
La conclusión es simple: la lógica de compuertas aún no es lo bastante precisa; en esta etapa no puede ir a la línea principal.
3. Forzar a ampliar el alcance de traducción de contexto completo
Probé dos direcciones:
- Subir el umbral de full-context para que más videos se traduzcan de una sola pasada
- Ampliar el chunk desde una ventana pequeña a una mucho más grande
A primera vista parece más cercano a “entender el texto completo y luego traducir”, pero en la práctica no hay ganancia estable.
La razón es que con más contexto el modelo también tiene más facilidad para cruzar ids y “tomar prestado” contenido; la línea de tiempo incluso puede volverse más caótica.
4. Bajar demasiado la granularidad del chunk
Por ejemplo, chunk_max_items=2: por intuición parece reducir el encadenamiento de frases, pero en realidad el rendimiento es malo y la velocidad se vuelve claramente más lenta.
Los resultados en hard2 no trajeron mejora de calidad, pero la latencia subió mucho, sobre todo o2V-JJpJH_I se arrastra.
5. Llevar al máximo directamente el “prompt de estilo B 站”
Probé:
literal_contextbilibili_dubbilibili_pacingauto_hybrid
En mixed4, auto_hybrid fue el mejor, literal_context el segundo; los otros dos profiles más “de estilo fuerte” fueron peor.
Esto muestra que ahora no es “cuanto más parecido a B 站 sea el prompt, mejor”, sino que primero hay que resolver límites de contexto, frases fragmentadas y alineación con el timing, y recién después hablar de estilización.
El cambio de entendimiento más importante de este día
Antes pensaba que el mayor problema era “las frases no se traducen con suficiente naturalidad”; luego vi que no era así.
El problema más de base en realidad es:
- La entrada previa ya viene fragmentada
- Entre fragmentos hay un solapamiento muy alto
- Para que el chino sea natural, hay que añadir moderadamente tono y estructura
- Pero en cuanto se añade de más, se filtra contenido futuro por adelantado
Así que lo más difícil del módulo de traducción no es la “traducción” en sí (de EN a ZH o de ZH a EN), sino:
Bajo la premisa de no cruzar los límites temporales, reorganizar el inglés fragmentado en un ritmo en chino que parezca subtítulos realmente terminados.
Esto no es el mismo problema que la traducción automática común.
Lo que aún no está resuelto
Aunque la línea principal está más estable que antes, todavía está muy lejos del objetivo que quiero, especialmente aún no alcanza el nivel de acabado de los casos de B 站 que compartiste.
Puntos que claramente siguen sin resolverse bien:
- En algunos hard case todavía se toma prestado del texto posterior
- En algunos case el chino sigue sonando a “chino de traducción”
- Tras segmentar, el ajuste de longitudes aún no es lo bastante estable
fragment_hintstodavía no encontró condiciones de compuerta estables- La cantidad y la selección de muestras de few-shot aún no se ajustaron por completo a lo óptimo
Lo que considero que vale más la pena seguir en el siguiente paso
Ahora lo más valioso de perseguir no es añadir más prompts “místicos”, sino estas tres cosas:
1. Hacer compuertas por características para fragment_hints, no un interruptor global
Ya se sabe que en algunos case es fuertemente efectivo.
El siguiente paso debería ser hacer compuertas según estas características:
- fragmentary source ratio
- overlap ratio
- punctuated source ratio
- short/tiny line ratio
Es decir, habilitarlo solo en videos con “alta fragmentación y mucho rolling-caption”, no aplicarlo globalmente de un solo tajo.
2. Seguir validando la cantidad de few-shot
Una señal pequeña que apareció al final de hoy fue: fewshot=4 en hard2, por primera vez, mostró una pequeña ganancia neta:
- baseline hard2:
50.454 - fewshot=4 hard2:
50.600
La ganancia no es grande, pero la dirección es positiva.
Si en mixed4 y mid8 también se mantiene, significa que los 8 few-shot actuales quizá estén metiendo demasiado ruido.
3. Seguir con el chunk context de “dar contexto, pero no permiso de traducir”
Ya añadí una versión de ventana de contexto antes y después del chunk, pero por ahora está en modo experimental.
El valor de esta dirección es:
- Darle al modelo capacidad de comprensión de un tramo completo
- Y al mismo tiempo exigir que solo genere los ids del chunk objetivo
Esto, en teoría, es más adecuado que simplemente ampliar el chunk para lograr “entender todo sin salirse de los límites”.
Conclusión de este día
Si hubiera que resumirlo en una frase:
El mayor logro de este día no fue “terminar” el módulo de traducción, sino aclarar por completo el entendimiento de “por qué este problema es difícil, dónde está el cuello de botella principal ahora, qué direcciones funcionan y cuáles ya no vale la pena seguir quemando tiempo”.
Ahora al menos ya está claro:
- El problema de traducción de este proyecto, en esencia, no es MT común
- El límite de rolling-caption es la contradicción principal
fragment_guardes el único rendimiento positivo estable por ahorafragment_hintstiene potencial, pero debe ir con compuertas- few-shot y la estrategia de contexto todavía valen la pena explorar
Si luego se quiere pulir de verdad este conjunto de herramientas hacia “la mejor traducción y doblaje de videos en idiomas extranjeros del mundo”, el módulo de traducción ya no puede depender de ajustar prompts a ojo; tiene que seguir por la ruta de benchmark guiado, atribución por case y A/B en pasos pequeños.
Hoy, al menos, ya quedó pavimentada esa ruta.