◂ База знанийRYA.AEPre-Edit Word Precision — пословный транскрипт максимальной точности (forced alignment)обновлено 12 июл 2026
Монтаж интервью режется по словам: границы клипов, вставок, мультикам-переключений берутся из пословных таймкодов транскрипта. Whisper выдаёт слова через DTW по кросс-аттеншену — приблизительно, часто ставит слово раньше реального звука (особенно после пауз — в начале фраз, ровно там, где режем). Forced alignment (wav2vec2 CTC) сажает каждое слово на реальный акустический онсет; сетка модели 20 мс (= ½ кадра при 29.97p). На нашем материале (YTCH05) границы сдвинулись от whisper на медиану ~174 мс (до ~1 с в начале фраз) и стали точнее ложиться на энергетический онсет (проверено). Это преэдит-слой: между транскрибацией и сборкой.
📌 Когда применять. Любой проект, который режется по транскрипту: пре-эдит по «как надо», мультикам-word-sync (2.7), авто-подборки, VO Было→Стало. Вход: готовый {stem}.words.json (mlx/wordrole, текст+диаризация) + аудио клипа. Не нужно, если режете на глаз/по маркерам. Требует изолированный venv .venv_whisperx с torch·torchaudio·transformers (см. грабли — установку через VPN делает Winston, чтобы не трогать venv работающего конвейера).
📄 транскрипт words.json 🎧 16 kHz mono 🧠 wav2vec2 CTC emission 🎯 forced_align по словам 🗣 speaker по диаризации .aligned.words.json (мс)

Зачем: почему whisper-таймкоды «плывут»

Whisper (в т.ч. mlx-whisper large-v3) не слышит границы слов напрямую — он восстанавливает их из кросс-аттеншена декодера методом DTW. Это даёт приблизительный тайминг: сетка ~20 мс, но реальная ошибка границы слова ±50–100 мс, а на паузах/дыхании/числах слово «прилипает» к соседнему. Для чтения транскрипта норм, для покадрового реза — нет: рез по такому таймкоду садится в середину предыдущего слова или в паузу.

Forced alignment решает обратную задачу: текст уже известен (из whisper), надо лишь выровнять его по акустике. Акустическая CTC-модель (wav2vec2) выдаёт для каждого 20-мс кадра распределение по фонемам/буквам; алгоритм forced-align прокладывает единственный путь через эти кадры, совпадающий с текстом → каждая буква и слово садятся на реальный звук. Именно так работает WhisperX (whisper для текста + wav2vec2 для таймингов). Точность границы падает до ±10–20 мс — суб-кадрово.
whisper DTW — раннее размещениеwav2vec2 CTC — сетка 20 мсграницы на акустическом онсете
Проверено на YTCH05 (клип 0511, 133 слова). Forced-align сдвинул границы слов от whisper на медиану 174 мс (mean 398, p90 998 мс) — сдвиг систематически положительный: whisper ставит слова раньше звука, сильнее всего в начале фраз после пауз (напр. первое слово «вот»: whisper 0.00 с → онсет 0.66 с). Контроль по энергии аудио: доля попадания старта на энергетический рост у forced-align 1.37× против 1.20× у whisper — ложится точнее, особенно на границах фраз = точках реза. Не серебряная пуля: остаточная точность ~1 кадр (20 мс сетка + CTC-лаг), на отдельных словах внутри фразы выигрыш мал. Максимальная польза — на паузах/началах реплик.

Точность, нужная Premiere

Premiere покадровый: таймкод осмыслен до длительности кадра, всё мельче округляется к ближайшему кадру. Значит целевая точность = длительность кадра. Стандарт хранения — миллисекунда (как в SRT ЧЧ:ММ:СС,ммм): она мельче любого fps.
fps1 кадрвывод
23.976 / 2441.7 / 41.67 мсms-точность с большим запасом
25 (PAL)40 мс
29.97 (наш)33.37 мсforced-align ±20 мс = суб-кадр
50 / 59.94 / 6020 / 16.68 / 16.67 мстут whisper ±50–100 мс уже даёт промах на 1–3 кадра — forced-align обязателен
Итог: храним мс (3 знака, 0.001 с). Whisper на нашем материале мажет медиану ~170 мс (≈5 кадров), особенно в начале фраз; forced alignment садит слово на акустический онсет с сеткой 20 мс (≈½ кадра). Больше 3 знаков смысла нет — это ложная точность (акустика не даёт).

Как работает forced alignment (wav2vec2 CTC)

Три шага под капотом whisperx.align / torchaudio.functional.forced_align:
# 1. EMISSION — акустическая модель → лог-вероятности по кадрам
emission = wav2vec2(audio_16k)   # [T кадров × V символов], кадр = 20 мс
# 2. TRELLIS + BACKTRACK — единственный путь через кадры, дающий ИМЕННО наш текст
tokens = tokenize("мне было двенадцать лет")      # текст из whisper
path   = forced_align(emission, tokens)          # CTC: буква↔кадр
# 3. FRAME→SEC — индекс кадра × (длит.аудио / число кадров)
word_spans = group_tokens_to_words(path)         # {word, s, e, score}
Ключевое отличие от whisper: тайминг берётся из акустики напрямую (где реально звучит буква), а не из внимания декодера. Модель для русского — wav2vec2-large-xlsr-53-russian (WhisperX подтягивает её сам по language_code="ru"). Мы переиспользуем готовый текст и диаризацию из words.json — гоняется только выравнивание, не повторная транскрибация.
Токенам, которые wav2vec2 не берёт (цифры «12», латиница, пунктуация), forced-align не даёт границу — им ставим fallback на whisper-таймкод, чтобы не терять слово.

Пайплайн по этапам

1
Взять готовый транскрипт

Из конвейера транскрибации — {stem}.words.json: segments[] с текстом + speaker (диаризация pyannote). Это источник ТЕКСТА и РОЛЕЙ; тайминги перепишем.

2
Аудио → 16 kHz моно

whisperx.load_audio(media) — извлекает и ресемплит (ffmpeg внутри). wav2vec2 работает на 16 кГц.

3
Загрузить align-модель

whisperx.load_align_model(language_code="ru", device="cpu") → wav2vec2-CTC для русского (скачивается с HuggingFace один раз, ~1–2 ГБ; качается по IPv4 — см. грабли).

4
Forced alignment

whisperx.align(segments, model, meta, audio, device) → каждый сегмент выровнен по акустике, на выходе words[] с {word, start, end, score}. score = уверенность выравнивания (0–1).

5
Вернуть роли

Каждому выровненному слову — speaker по перекрытию его середины с исходным диаризационным турном. Слова без выравнивания (числа/пунктуация) — fallback на whisper-таймкод.

6
Записать выход + отчёт точности

{stem}.aligned.words.json (мс) + .aligned.words.srt + .aligned.words.csv. В JSON — блок alignment: медианный/средний/p90 сдвиг границ vs whisper (доказательство, насколько подвинулось).

Запуск инструмента

~/YTAI/scripts/999_extra/forced_align/forced_align_words.py — принимает медиа + готовый транскрипт, пишет высокоточную версию рядом.
PY=~/YTAI/environment/.venv_whisperx/bin/python
$PY ~/YTAI/scripts/999_extra/forced_align/forced_align_words.py \
    --media clip.mp4 \
    --transcript clip.words.json \
    --language ru --out-dir DIR --device cpu
Идемпотентно по {stem}. В консоль — отчёт точности: медианный сдвиг Nмс, mean Mмс, p90 Pмс относительно whisper. Модель кэшируется после первого прогона (следующие клипы — без докачки).
Первый прогон качает align-модель с HuggingFace. venv .venv_whisperxотдельный от .venv_transcribe (там работает конвейер транскрибации): forced-align не должен трогать его зависимости.

Формат выхода и импорт в Premiere

{
 "words": [ {"w":"мне", "s":5.560, "e":5.740, "score":0.98, "speaker":"Speaker 1"}, … ],
 "segments": [ … ], "diarization": { … },
 "alignment": { "median_shift_vs_whisper_ms": …, "model":"wav2vec2-CTC" }
}
s/e — секунды, 3 знака (мс). Импорт в Premiere: .aligned.words.srt → как транскрипт/caption-трек (Premiere ест SRT с мс), либо words.json читает наш UXP/скрипт сборки и ставит маркеры/резы прямо по w.s/w.e. Пословный .csv (word,start,end,score,speaker) — для проверки и таблиц.

Грабли

Изоляция venv. WhisperX/torch ставим ТОЛЬКО в .venv_whisperx. pip install в .venv_transcribe апнет numpy/tokenizers и сломает mlx/pyannote работающего конвейера.
IPv6/DPI на pip. pip install (PyPI/files.pythonhosted.org) виснет по той же причине, что rclone/yt-dlp — режется IPv6/DPI. При этом модели с HuggingFace качаются по IPv4 (curl --ipv4 = HTTP 200). Установку пакетов проводит Winston через VPN/IPv4 (см. Метод), модели тянутся сами.
Русский = не латиница. WhisperX-модель для ru обучена на кириллице напрямую (xlsr-russian) — романизация не нужна. Для torchaudio-MMS пути потребовался бы uroman.
Числа/пунктуация/латиница wav2vec2 не выравнивает — этим словам fallback на whisper-таймкод (иначе слово выпадет). В score у них 0.
Сетка 20 мс. wav2vec2 — кадр 20 мс, интерполяция даёт мс, но физический предел точности ~20 мс (≈½ кадра @30p). Больше не выжать — и не нужно.
Мы НЕ транскрибируем заново. Текст и диаризация берутся из готового words.json; forced-align только двигает границы. Ошибку РАСПОЗНАВАНИЯ он не чинит — только тайминг.
Ch.2 · Editing — 2.9 Pre-Edit Word Precision · yt.rya.ae/kb