[Otimização | Retrospectiva] O que o módulo de tradução do YouDub fez durante o dia inteiro, quais direções funcionaram e quais já foram refutadas
O principal campo de batalha deste dia, na verdade, não foi o TTS, e sim o módulo de tradução.
O motivo é direto: mais adiante, o módulo de dublagem já pode ser trocado por uma solução mais próxima do timbre alvo, mas no vídeo final a deficiência mais óbvia ainda é a própria tradução das legendas, que não parece suficiente com um produto maduro do Bilibili (B 站), especialmente em:
- Em cenários de rolling-caption (legenda rolante), a frase anterior “rouba” informação da próxima.
- A legenda original em inglês é fragmentada; quando o chinês é traduzido rigidamente linha a linha, fica longo e estranho.
- Se buscar demais “completar as frases”, você vaza com antecedência a punchline, os substantivos e as ações que vêm depois.
- Se buscar demais “preservar os limites dos fragmentos”, o chinês fica fragmentado demais e não parece legenda de vídeo final de verdade.
Então, nesta rodada de otimização, mudei o foco de “consertar um vídeo específico” para “fazer um framework de avaliação e iteração de tradução que seja generalizável”.
O que foi feito especificamente neste dia
1. Primeiro, montar o sistema de baseline, sem ajustar no achismo
Eu não continuei preso em um único vídeo corrigindo linha por linha; em vez disso, peguei diretamente mais de 90 amostras de referência Bilibili/YouTube em C:\\Users\\1\\bili_yt_export\\bili_youtube_first100.csv para montar o baseline.
Foram duas coisas:
- Expandir
scripts/benchmark_translation.py - Adicionar
scripts/analyze_translation_artifact.py
O primeiro roda em lote a cadeia completa de tradução + divisão de frases + reescrita do texto para dublagem, e gera métricas e artefatos intermediários de cada case.
O segundo serve para abrir cada case e olhar em detalhe, especialmente estas camadas:
source_rowsprepared_source_rowstranslated_rows_pre_splitpredicted_rowsreference_rows
Este passo foi crítico, porque muitos problemas depois não são causados pelo pós-processamento; é o próprio LLM que, já na etapa translated_rows_pre_split, “puxa emprestado” conteúdo que vem depois.
2. O problema central ficou claramente localizado: rolling-caption “empresta” do texto seguinte
O maior ganho deste dia não foi algum indicador disparar de repente, mas localizar com precisão o problema principal:
Nas legendas automáticas/oficiais do YouTube, há muita estrutura de rolling-caption; muitas linhas já são meia frase, fragmentos, ou frases que continuam de uma linha para outra.
Se você simplesmente pede ao modelo para “traduzir naturalmente linha a linha”, ele tende fortemente a trazer informação de uma ou duas linhas futuras para a linha atual, deixando o chinês mais fluido, porém vazando no eixo do tempo (spoiler antecipado).
Esse problema aparece com mais força nesses dois hard cases: zwIqbrD6JX4 e o2V-JJpJH_I.
3. Promover fragment_guard a padrão da linha principal
Com base no achado acima, passei a deixar fragment_guard ativado por padrão.
A ideia central não é “forçar o chinês a ficar quebrado”, e sim impor limites explícitos ao modelo:
- O id atual só pode expressar a semântica que já apareceu na linha de origem atual.
- Se a linha de origem for claramente um fragmento inacabado de rolling-caption, é melhor o chinês ficar levemente aberto do que completar antecipadamente o conteúdo futuro.
Esta é, por enquanto, a única mudança já comprovadamente estável e eficaz, e que eu aceito colocar como padrão da linha principal.
Resultados atualmente confirmados como eficazes
Configuração da linha principal
A linha principal estável atual é 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- Demais flags experimentais desligadas por padrão
Ganhos já confirmados em dados
fragment_guard, de experimento local a amostra média, tem retorno positivo:
- Comparação em 4 cases:
52.432 -> 53.322 - Comparação em 8 cases:
55.958 -> 56.058
Relatório atual da linha principal em 8 cases:
- composite:
56.058 - chrF:
0.3707 - char F1:
0.7729 - density MAE ratio:
0.4272
Isso indica que, pelo menos na linha principal atual, ficou menos provável “puxar” texto futuro para antes, e o ritmo geral também ficou mais próximo da distribuição de legendas prontas do Bilibili.
Quais direções deste dia eu basicamente já refutei
1. Habilitar fragment_hints globalmente
Não é que seja totalmente inútil; pelo contrário, em alguns cases é muito forte.
Por exemplo:
zwIqbrD6JX4no hard2 foi de54.439para57.980VT6rLcVKhzgtambém teve ganho evidente
Mas o problema é que não é estável.
Levando para 8 cases, o geral cai de 56.058 para 55.296.
Ou seja, é mais como um “remédio forte para uma estrutura específica”, não uma estratégia de linha principal que dê para ligar por padrão.
2. auto_hybrid_v2
Eu fiz uma versão com lógica de seleção automática de profile mais agressiva, tentando fazer vídeos diferentes alternarem automaticamente entre literal_context / bilibili_dub / bilibili_pacing.
O resultado em 8 cases caiu direto para 54.375, pior do que a linha principal atual 56.058.
A conclusão é simples: a lógica de gating ainda não é precisa o suficiente; por enquanto não dá para ir para a linha principal.
3. Forçar a ampliação do escopo de tradução em contexto completo
Eu tentei duas direções:
- Aumentar o limiar de full-context, para mais vídeos serem traduzidos de uma vez só
- Aumentar o chunk diretamente de uma janela pequena para uma maior
À primeira vista parece mais próximo de “entender o texto inteiro antes de traduzir”, mas na prática não houve ganho estável.
O motivo é que, com mais contexto, o modelo também fica mais propenso a atravessar ids e “emprestar” conteúdo, e a linha do tempo pode ficar ainda mais bagunçada.
4. Reduzir o chunk para uma granularidade muito fina
Por exemplo, a ideia de chunk_max_items=2: intuitivamente parece reduzir mistura de frases, mas na prática o ganho foi ruim e a velocidade ficou claramente mais lenta.
O resultado no hard2 não trouxe melhoria de qualidade, mas a latência subiu muito, especialmente o2V-JJpJH_I, que fica arrastado.
5. Simplesmente “maximizar prompt de estilo Bilibili”
Eu testei:
literal_contextbilibili_dubbilibili_pacingauto_hybrid
No mixed4, auto_hybrid foi o melhor, literal_context em segundo; os outros dois profiles mais “fortes em estilo” foram piores.
Isso mostra que agora não é “quanto mais o prompt parecer Bilibili, melhor”; primeiro é preciso resolver limites de contexto, fragmentos e alinhamento com o timing, e só depois falar em estilização.
A mudança de entendimento mais importante deste dia
Eu achava que o maior problema era “a frase não está traduzida com naturalidade suficiente”, mas depois vi que não.
O problema mais fundamental é:
- A entrada a priori já é fragmentada
- E os fragmentos têm alta sobreposição entre si
- Para o chinês ficar natural, é necessário complementar moderadamente tom e estrutura
- Mas, ao complementar demais, você vaza conteúdo futuro antecipadamente
Então o ponto mais difícil do módulo de tradução não é “chinês → inglês” ou “inglês → chinês” em si, e sim:
Sob a premissa de não ultrapassar o limite temporal, organizar o inglês fragmentado em um ritmo de chinês que pareça legenda de vídeo final de verdade.
Isso não é o mesmo problema de tradução automática (MT) comum.
Pontos que ainda não foram resolvidos
Embora a linha principal esteja mais estável do que antes, ainda está muito longe do alvo que eu quero, e especialmente ainda não chega ao nível de acabamento dos exemplos do Bilibili que você me deu.
Pontos que ainda estão claramente mal resolvidos:
- Alguns hard cases ainda “emprestam” do texto seguinte
- Em alguns cases, o chinês ainda soa “traduzido” (tradução literal/engessada)
- A correspondência de comprimento após dividir frases ainda não é estável o suficiente
fragment_hintsainda não encontrou condições de gating estáveis- A quantidade de few-shot e a seleção das amostras ainda não foram ajustadas ao ótimo
Próximos passos que eu considero mais valiosos
O que mais vale perseguir agora não é adicionar mais prompts “místicos”, e sim estas três coisas:
1. Fazer gating por características para fragment_hints, em vez de um switch global
Já está claro que ele pode ser fortemente eficaz em alguns cases.
O próximo passo é fazer gating com base nestas características:
- fragmentary source ratio
- overlap ratio
- punctuated source ratio
- short/tiny line ratio
Ou seja, habilitar só em vídeos com “alto fragmento, alto rolling-caption”, em vez de cortar com faca única global.
2. Continuar validando a quantidade de few-shot
Um pequeno sinal no fim de hoje foi: fewshot=4 no hard2 apareceu pela primeira vez com um pequeno ganho líquido:
- baseline hard2:
50.454 - fewshot=4 hard2:
50.600
O ganho não é grande, mas a direção é positiva.
Se mixed4 e mid8 também mantiverem, isso sugere que os 8 few-shots atuais talvez estejam com ruído demais.
3. Continuar com chunk context de “dar contexto, sem dar permissão de traduzir”
Eu já adicionei uma versão de janela de contexto antes/depois do chunk, mas por enquanto está em modo experimental.
O valor desse caminho é:
- Dar ao modelo capacidade de entender o trecho inteiro
- E ao mesmo tempo exigir que ele só produza os ids do chunk alvo
Em teoria, isso é mais adequado do que simplesmente ampliar o chunk para “entender o texto todo sem ultrapassar limites”.
Conclusão do dia
Se eu tivesse que resumir em uma frase:
O maior resultado deste dia não foi terminar o módulo de tradução, mas sim esclarecer “por que esse problema é difícil, onde está o gargalo principal agora, quais direções funcionam e quais já não valem mais queimar tempo”, e estruturar esse entendimento.
Agora pelo menos já está claro:
- O problema de tradução deste projeto, na essência, não é MT comum
- O principal conflito é o limite de rolling-caption
fragment_guardé o único ganho positivo estável atualmentefragment_hintstem potencial, mas precisa de gating- few-shot e estratégia de contexto ainda valem ser aprofundados
Se depois quisermos realmente lapidar essa ferramenta rumo a “a melhor tradução e dublagem de vídeos em língua estrangeira do mundo”, o módulo de tradução não pode mais depender de ajustar prompt no palpite; é preciso continuar no caminho de baseline guiando, atribuição por case, e A/B incremental.
Hoje, pelo menos, esse caminho já foi pavimentado.