[Оптимизация: разбор] Что YouDub сделал за весь день в модуле перевода, какие направления сработали, а какие уже опровергнуты
В этот день главным полем боя был на самом деле не TTS, а модуль перевода.
Причина очень простая: для модуля озвучки дальше уже можно переключиться на решение, которое ближе к целевому тембру, но в готовом ролике самый заметный недостаток всё равно в том, что перевод субтитров сам по себе недостаточно похож на зрелый готовый продукт уровня B站, особенно:
- в сценах rolling caption предыдущая строка «подъедает» информацию из следующей.
- английские исходные субтитры фрагментированы; если по-скриптовому переводить построчно, китайский получается длинным и неуклюжим.
- если чрезмерно стремиться «дополнить предложение», то punchline, существительные, действия из следующих строк будут преждевременно раскрыты.
- если чрезмерно стремиться «сохранить границы фрагментов», китайский становится слишком рубленым и не похож на реальные субтитры готового видео.
Поэтому в этом цикле оптимизации я перенёс фокус с «починить одно конкретное видео» на «сделать обобщаемую рамку оценки и итераций для перевода».
Что конкретно было сделано за этот день
1. Сначала поднял систему бенчмарка — больше не крутить “на ощущениях”
Я не продолжал упираться в одно видео и править по строкам, а сразу взял более 90 парных B站/Youtube-образцов из C:\\Users\\1\\bili_yt_export\\bili_youtube_first100.csv и сделал из них базу.
Под это сделал две вещи:
- расширил
scripts/benchmark_translation.py - добавил
scripts/analyze_translation_artifact.py
Первый отвечает за массовый прогон полного пайплайна: перевод + разбиение строк + переписывание текста под озвучку, с выводом метрик и промежуточных артефактов по каждому кейсу.
Второй отвечает за разбор каждого кейса по слоям, особенно по этим:
source_rowsprepared_source_rowstranslated_rows_pre_splitpredicted_rowsreference_rows
Этот шаг критически важен, потому что многие проблемы дальше возникают не из-за постобработки, а потому что LLM уже на этапе translated_rows_pre_split «одалживает» контент из будущих строк.
2. Главная проблема стала ясна: rolling-caption “берёт” из будущего
Самое большое достижение дня — не резкий рост какой-то метрики, а точное попадание в главный корень:
В автосубтитрах/официальных субтитрах YouTube много структуры rolling-caption: многие строки по сути полпредложения, обрывки, продолжения через строки.
Если напрямую попросить модель «естественно перевести построчно», она очень склонна подтягивать информацию из одной-двух будущих строк в текущую строку — китайский выглядит более гладким, но по таймлайну получается преждевременный спойлер.
Это наиболее заметно на двух hard case: zwIqbrD6JX4 и o2V-JJpJH_I.
3. Поднял fragment_guard до дефолтной основной линии
Исходя из обнаруженного, я сделал fragment_guard включённым по умолчанию.
Его ключевая идея — не «насильно писать обрывочный китайский», а явно ограничить модель:
- текущий id может выражать только ту семантику, которая уже присутствует в текущей исходной строке.
- если исходная строка очевидно является незавершённым фрагментом rolling-caption, лучше пусть китайский будет немного более открытым, чем будет заранее «дополнять» будущим содержимым.
На данный момент это единственное изменение, которое стабильно доказало эффективность и которое я готов включить в дефолт основного пайплайна.
Подтверждённо работающие результаты на данный момент
Конфигурация основной линии
Текущая стабильная основная линия примерно такая:
- 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- остальные экспериментальные переключатели выключены по умолчанию
Подтверждённые улучшения по данным
fragment_guard даёт положительный эффект как в локальных экспериментах, так и на среднем наборе:
- сравнение на 4 кейсах:
52.432 -> 53.322 - сравнение на 8 кейсах:
55.958 -> 56.058
Текущий отчёт основной линии на 8 кейсах:
- composite:
56.058 - chrF:
0.3707 - char F1:
0.7729 - density MAE ratio:
0.4272
Это означает, что по крайней мере текущая основная линия лучше, чем более ранняя версия, избегает преждевременного «заимствования» будущего текста, а общий ритм уже ближе к распределению субтитров готового продукта B站.
Какие направления за этот день я в целом уже опроверг
1. Глобально включать fragment_hints
Это не полностью бесполезно — наоборот, на некоторых кейсах очень сильная штука.
Например:
zwIqbrD6JX4в hard2 поднимается с54.439до57.980VT6rLcVKhzgтоже заметно улучшается
Но проблема в нестабильности.
На 8 кейсах суммарно оно наоборот падает с 56.058 до 55.296.
То есть это скорее «сильнодействующее лекарство под конкретную структуру», а не стратегия, которую сейчас можно включать по умолчанию в основной линии.
2. auto_hybrid_v2
Я сделал более агрессивную логику автоподбора profile, чтобы разные видео автоматически переключались между literal_context / bilibili_dub / bilibili_pacing.
В итоге на 8 кейсах сразу упало до 54.375, хуже текущей основной линии 56.058.
Вывод простой: логика гейтинга ещё недостаточно точная, на основную линию сейчас нельзя.
3. Насильно расширять область перевода “на весь текст”
Я пробовал два направления:
- поднять порог full-context, чтобы больше видео переводилось целиком за один проход
- увеличить chunk с маленького окна до большого
На вид это ближе к «сначала понять весь текст, потом переводить», но стабильной прибавки не дало.
Причина: чем больше контекста, тем легче модели перескакивать через id и «заимствовать» контент, таймлайн в итоге может стать ещё более хаотичным.
4. Делать chunk слишком мелким
Например, идея вроде chunk_max_items=2: по интуиции кажется, что это уменьшит склейку строк, но фактическая отдача плохая, а скорость заметно падает.
Результаты hard2 не дали улучшения качества, а задержка сильно выросла, особенно тяжело тянется o2V-JJpJH_I.
5. Просто “вкрутить B站-стиль подсказками на максимум”
Я тестировал:
literal_contextbilibili_dubbilibili_pacingauto_hybrid
На mixed4 лучше всего auto_hybrid, затем literal_context, а два «сильно-стильных» profile наоборот хуже.
Это показывает, что сейчас вопрос не в том, что «чем больше подсказка похожа на B站, тем лучше», а в том, что сначала нужно решить границы контекста, фрагментность и выравнивание по таймингу — и только потом заниматься стилизацией.
Самое важное изменение в понимании за этот день
Раньше я думал, что главная проблема — «предложения переводятся недостаточно естественно», но выяснилось, что дело не в этом.
На более базовом уровне проблема такая:
- априорный вход сам по себе фрагментированный
- между фрагментами сильное перекрытие
- чтобы китайский звучал естественно, нужно умеренно добавлять модальность и структуру
- но стоит переборщить — и будущее содержимое начинает утекать раньше времени
Поэтому самая трудная часть модуля перевода — не «перевод как таковой», а:
при условии не пересекать временные границы привести фрагментированный английский к китайскому ритму, похожему на настоящие субтитры готового видео.
Это не та же задача, что обычный машинный перевод.
Что пока не решено
Хотя основная линия стала стабильнее, до моей целевой планки ещё очень далеко — особенно до уровня завершённости из B站-примеров, которые ты давал.
Сейчас явно не решены:
- некоторые hard case всё ещё заимствуют из будущего
- на некоторых кейсах китайский всё ещё «переводческий»
- после разбиения строк соответствие по длинам ещё недостаточно стабильное
- для
fragment_hintsпока не найдено стабильное условие гейтинга - количество и подбор few-shot ещё не доведены до оптимума
Следующие шаги, которые я считаю наиболее стоящими
Сейчас наиболее перспективно не добавлять больше «магических» prompt’ов, а продолжать в трёх направлениях:
1. Сделать фиче-гейтинг для fragment_hints, а не глобальный переключатель
Уже известно, что на некоторых кейсах это очень эффективно.
Следующий шаг — включать это по признакам:
- fragmentary source ratio
- overlap ratio
- punctuated source ratio
- short/tiny line ratio
То есть включать только на видео с «высокой фрагментностью и высоким rolling-caption», а не рубить глобально.
2. Продолжить валидацию количества few-shot
Небольшой сигнал в конце дня: fewshot=4 на hard2 впервые дал небольшой чистый плюс:
- baseline hard2:
50.454 - fewshot=4 hard2:
50.600
Прирост небольшой, но направление положительное.
Если mixed4 и mid8 тоже удержат плюс, это будет означать, что текущие 8 few-shot могут быть даже чуть шумными.
3. Продолжать делать chunk context “даём контекст, но не даём права переводить”
Я уже добавил вариант окна контекста до/после chunk, но пока это экспериментально.
Ценность направления в том, что:
- модель получает возможность понимать целиком отрывок
- при этом всё равно обязана выводить только id целевого chunk
Это, теоретически, лучше подходит для «понимать весь текст, но не выходить за границы», чем простое увеличение chunk.
Итог дня
Если в одной фразе:
Главный результат дня — не то, что я “доделал” модуль перевода, а то, что я прояснил картину: почему это сложно, где сейчас главный узкий горлышко, какие направления работают, а какие уже не стоит дальше сжигать временем.
Теперь по крайней мере ясно:
- переводческая задача этого проекта по сути не обычный MT
- границы rolling-caption — главное противоречие
fragment_guard— единственный стабильный положительный эффект на данный момент- у
fragment_hintsесть потенциал, но нужен гейтинг - few-shot и контекстные стратегии ещё стоит копать
Если дальше реально доводить этот инструмент в сторону «лучшего в мире перевода и дубляжа иностранных видео», модуль перевода уже нельзя будет настраивать “по наитию” через prompt’ы — нужно продолжать идти по пути бенчмарк-драйв, атрибуция по кейсам, затем маленькие A/B-шаги.
Сегодня как минимум этот путь удалось проложить.