Переразбор всех страниц data/raw: legacy-страницы импорта, форматы префиксов ([*]Перевод (1):, Озвучка:, №2, диапазоны), дорожки только в строках «Аудио» (в т.ч. «(Русский, X)», «| Rus | Звук: …»), «Язык озвучки», несколько студий в одной строке, студия без типа. Кодек-строки: «Аудио #N Русский:», «Аудио pic RUS:», «Звук N:», «Аудиодорожка 01:», кириллические АVO/МVO. «Авторский» без числа голосов -> авт. одноголосый, «двугоголосый». Чистка студий: языки, кодеки, «по заказу», «в составе контейнера», отказы от озвучки. Итог: страниц с метками 30 260 (85.5 %), страниц с дорожкой без меток 1 259 -> 223; data/pages 30 444; после prepare_data 60 944 строки (train 54 686 / test 6 258). Новый tools/coverage_audit.py + reports/coverage_audit.md, docs/dataset-coverage.md. Переобучен models/best (base): на общем тесте type acc 0.976 против 0.949 у прежней, span 0.674 против 0.643; e2e kinozal 0.805/0.867, rt 0.771/0.740, nnm 0.752/0.693.
12 KiB
Покрытие страниц data/raw датасетом: аудит сборщика
Отчёт о том, как сборщик (tools/collect.py → tools/harvest/harvest_core.py →
src/prepare_data.py) превращает сохранённые страницы в обучающий датасет, какие
форматы строк он терял и что исправлено. Цифры получены на всех страницах
data/raw (35 385 шт.), а не на выборке.
Инструмент замера — tools/coverage_audit.py (пишет reports/coverage_audit.md,
data/raw/_coverage_pages.csv, data/raw/_coverage_lines.csv). Строка считается
«дорожкой» независимым от парсеров детектором (префикс «Перевод/Озвучка/Аудио…»,
слово типа озвучки или блок |студия|), а «покрытой» — если для неё есть строка
labels.csv этого релиза (raw / audio_raw / role_raw).
Как устроен сборщик и где терялись данные
Пять стадий, у каждой свой кэш:
| стадия | вход → выход | кэш / версия |
|---|---|---|
collect |
браузер → data/raw/<трекер>/<id>.json + .html |
_state.json |
build |
data/raw → data/pages/<release_id>.txt + строки data/labels.csv |
_built.json, PARSE_VERSION |
meta |
страницы → data/meta.csv |
_meta_cache.json, META_VERSION |
prepare_data |
labels.csv → data/processed/dataset.json |
— |
train |
dataset.json → models/best, reports/ |
— |
До этого аудита датасет собирался из 28 155 релизов (56 131 строка), хотя страниц
в data/raw было 35 385: 7 371 страница не дала ни одной строки. Часть из них
законно не содержит озвучки (русский оригинал, «Перевод: не требуется», немое
кино, музыка/сканы), но не менее 1 259 страниц с настоящими дорожками в
датасет не попадали. Причины — в порядке убывания потерь:
- legacy-страницы не переразбирались. Импорт старого harvest дал 2 633
страницы только с текстом поста (
.legacy.json);buildбрал лишь парыjson+html, поэтому их метки остались от старых регулярок и больше не обновлялись. - Форматы строк, которых не понимали префиксы.
[*]Перевод (1):,►Перевод:,Перевод №2:,Перевод 01-02:,Аудио RUS 1-3:,Озвучка:,Озвучивание:(nnm/rutracker) не совпадали сRT_RE_FIELD/NNM_RE_PEREVOD, потому что те ждали ровно «Перевод:» или «Перевод 2:». - Дорожки, названные только строкой «Аудио» (nnm/rutracker, аниме-раздачи):
Аудио 4: AC3 … (внешние) (Русский, Anilibria),| Rus | Звук: Zendos & Eladiel,Аудио RUS: … Озвучка: двухголосая (… ). Студия была в скобках после языка или в хвосте «Звук:/Озвучка:», а разметка такие строки не читала: у nnm требовался «(MVO, Студия)», а язык — только «Русский/Английский/Украинский». - Студия без типа озвучки («Перевод: Dreamers Team», «Перевод: CrunchyRoll»,
«Озвучка: Persona99») выбрасывалась:
translation_typeдавал «неизвестно», и строка отбрасывалась вместе с автором. Для головы автора это потеря: на странице автора назвали. - Несколько студий в одной строке («Профессиональный (многоголосый) — Jaskier, TVShows, HDrezka Studio, LostFilm», «| ОРТ, KidsCo»): в метки шла одна студия или склеенное «Jaskier, TVShows».
- Строка «Язык озвучки: Русский (Berofu, Takera, Euji [AniMaunt])» (nnm, аниме) вообще не считалась дорожкой.
- kinozal читал только одну вкладку. Если в «Релизе» была строка «Перевод:», «Техданные» игнорировались (и наоборот) — терялись типы или студии второй вкладки.
- Кодек-строки мимо правил сервиса (
segmenter.RE_AUDIO_TRACK,release.RE_CODEC_TRACK): «Аудио pic RUS:», «Аудио #1 Русский:», «Аудио N Rus:», «Звук N:», «Аудиодорожка 01:», «Audio #3:», кириллические «АVO/МVO». Это дыра не только в метках сегментатора, но и в запасном пути инференса.
Что исправлено
tools/harvest/harvest_core.py (PARSE_VERSION 4 → 6), src/html_extract.py,
src/release.py, src/segmenter.py, src/trackinfo.py, tools/collect.py,
src/prepare_data.py, src/studio_norm.py:
- общий разбор префикса дорожки: декоративные
[*] ► •, номер(1),№2, диапазоны01-02, «Озвучка/Озвучивание/Дубляж» — для rutracker, nnm и kinozal; buildпереразбирает legacy-страницы (метки обновляются, страница не перезаписывается);- nnm: студии из
(Русский, Anilibria),| Rus | Звук: …, языкRUS/Rus/RU; строка «Язык озвучки»; несколько студий в «Перевод:» → по строке на студию; студия без типа сохраняется с типом «неизвестно»; - rutracker: разбор
(Русский, Студия)и[студия], хвостов «Озвучка:»/«Звук:», списков студий; те же правила для «Перевод/Озвучка/Озвучивание»; - kinozal: обе вкладки, несколько студий в строке, «Отсутствует/Без перевода» не считаются автором;
- кодек-строки: префиксы «Аудио #N Русский:», «Аудио pic RUS:», «Звук N:», «Аудиодорожка 01:», «Audio #3:», «Track #2:», «Title :»; тип-слова с кириллическими двойниками (АVO/МVO/DVO); если у релиза нет ни одной строки, из кодек-строки берётся дорожка без имени (тип без студии);
- чистка имён: «Русский, - AlexFilm» → AlexFilm, «X по заказу Y» → X,
«озвучил - N» → N; мусор (коды/каналы/языки/«по умолчанию»/«в составе
контейнера»/«Отсутствует»/эпизоды) отбрасывается; в
prepare_dataстроки-отказы от озвучки удаляются отдельным счётчиком.
Результат
| было | стало | |
|---|---|---|
страниц в data/raw |
35 385 | 35 385 |
релизов в labels.csv |
28 155 | 30 401 |
строк в labels.csv |
56 131 | 71 339 |
| страниц с метками | 28 014 | 30 260 (85.5 %) |
| страниц без меток, но с дорожкой (по детектору) | 1 259 | 223 |
data/pages (страниц для сегментатора) |
28 198 | 30 444 |
строк после prepare_data |
46 894 | 60 944 (train 54 686 / test 6 258) |
| строк со студией (спан найден) | 38 583 (82.3 %) | 50 477 (82.8 %) |
| уникальных текстов | 25 614 | 30 761 |
| дорожек 18+ | 1 006 | 1 156 |
По трекерам (строки labels.csv → строки после подготовки, включая аугментацию):
- kinozal: 21 773 → 23 969 (15 188 после подготовки);
- nnm: 18 384 → 27 092 (24 453);
- rutracker: 15 974 → 20 278 (21 303).
Остаток 223 страниц — почти весь музыка/OST-раздачи, где «дорожкой» оказался
трек-лист, плюс единичные нестандартные строки («Аудио 6: двухголосая закадровая
Flowers Media» без кодек-параметров, «Аудио ShinkaDan: …», «Аудио #1: Russian …
| Original | Censor»). Они перечислены в data/raw/_coverage_lines.csv и годятся
для следующей итерации правил.
Непокрытые строки на страницах с метками (12 908 из 56 921) — в основном не
потерянные дорожки, а дубли: строка «Аудио N:» той же дорожки, что уже размечена
из «Перевод:», дорожки оригинала/другого языка и техданные. Их доля видна в
reports/coverage_audit.md.
Как воспроизвести
# замер покрытия (кэш текстов ускоряет повтор)
.venv\Scripts\python tools\coverage_audit.py --cache data\raw\_coverage.pkl
# переиндексация: страницы + метки + meta (после правки парсеров поднять PARSE_VERSION)
.venv\Scripts\python tools\collect.py build --rewrite --relabel
# подготовка датасета и аудит
.venv\Scripts\python -m src.prepare_data
Переиндексация всех 35 тыс. страниц одним процессом занимает ~30 мин (основное
время — запись новых страниц и meta.csv); на нескольких процессах — в разы
быстрее (Runner(workers)), но в песочнице DSH процессы с именованными каналами
недоступны.
Что это дало модели
Переобучение на расширенном датасете (held-out test 6 258 строк, обе модели оценены на одном тесте):
| модель | type acc | type macro-F1 | span exact | joint exact |
|---|---|---|---|---|
прежняя models/best (02.11) |
0.949 | 0.880 | 0.643 | 0.615 |
новая models/best (09.11) |
0.976 | 0.906 | 0.674 | 0.658 |
E2E по релизам (пары тип + студия, precision / recall; те же 400 страниц на
трекер): прежняя модель — kinozal 0.752 / 0.830, rutracker 0.729 / 0.700,
nnm 0.669 / 0.604; новая — 0.805 / 0.867, 0.771 / 0.740, 0.752 / 0.693.
Абсолютный span exact на новом тесте ниже прежних 0.922 из-за того, что в тест
попали форматы, которых раньше в датасете не было; сравнение «на одном тесте»
показывает рост по всем головам.