87 lines
6.1 KiB
Markdown
87 lines
6.1 KiB
Markdown
# Архитектура
|
|
|
|
## Пайплайн
|
|
|
|
```
|
|
labels.csv (снимок датасета) HTML/текст страницы (инференс)
|
|
│ │
|
|
▼ ▼
|
|
src/prepare_data.py src/html_extract.py
|
|
• снять префикс «Перевод/Аудио N:» • HTML -> текст (BeautifulSoup:
|
|
• BIO-разметка автора (спан в тексте) .post_body / #tabs / весь текст)
|
|
• фильтры: мусорный studio, • сегментация на дорожки
|
|
«оригинал/субтитры», противоречия (строки «Перевод/Озвучка/Аудио N:»,
|
|
• 18+ флаг (age_18 / note / «18+» в raw) нумерация, «+ ; /», запятые-типы)
|
|
• сплиты: test 20% + 5-fold CV │
|
|
(группы = релиз ∪ одинаковый текст) ▼
|
|
│ src/predict.py (DubPredictor)
|
|
▼ • токенизация (max_len=128,
|
|
src/train.py offsets)
|
|
• multi-task обучение (CUDA, bf16) • модель -> тип, спан, 18+
|
|
• 5-fold CV + финальная модель • нормализация автора
|
|
• метрики, reports/, models/best • JSON
|
|
```
|
|
|
|
## Модель (src/model.py)
|
|
|
|
Один BERT-подобный энкодер и три головы (общий `[CLS]`/токенные состояния):
|
|
|
|
| Голова | Тип | Выход | Задача |
|
|
|---|---|---|---|
|
|
| `type_head` | Linear(hidden, 14) | `[CLS]` → класс | тип озвучки |
|
|
| `bio_head` | Linear(hidden, 3) | по токенам → O/B/I | позиция имени автора в тексте |
|
|
| `age_head` | Linear(hidden, 2) | `[CLS]` → 0/1 | дорожка 18+ |
|
|
|
|
Loss = CE(тип) + 1.0·CE(BIO, ignore_index=-100) + 0.5·CE(18+).
|
|
|
|
BIO-разметка строится по символьным спанам через `offsets_mapping`
|
|
токенизатора: токены, пересекающие спан, получают `B` (первый) и `I`
|
|
(остальные); все реальные токены вне спана — `O`; паддинг — `-100`.
|
|
Спецтокены ([CLS]/[SEP]) размечены как `O` — иначе их необученные логиты
|
|
при инференсе дают ложный «B» (эта ошибка была найдена при обучении и
|
|
исправлена; см. `decode_span` — он дополнительно пропускает токены с
|
|
пустыми offsets).
|
|
|
|
Декодирование спана: первый `B`-токен, затем цепочка `I` → символьный
|
|
диапазон через offsets → текст спана → каноническое имя автора через
|
|
словарь алиасов (`src/studio_norm.py`, порт из генератора датасета).
|
|
|
|
## Почему автор — извлечение, а не классификация
|
|
|
|
В датасете 253+ уникальных авторов, 177 из них встречаются ровно 1 раз.
|
|
Классификацией такие классы не выучить. При этом имя автора почти всегда
|
|
дословно присутствует в тексте дорожки (95.8% + варианты написания),
|
|
поэтому BIO-извлечение решает задачу и автоматически работает для авторов,
|
|
которых в обучении не было вовсе.
|
|
|
|
## Сравнение конфигураций
|
|
|
|
Обе модели учатся одинаково (5-fold CV + финальный запуск, ранний stopping
|
|
по среднему type-F1/span/age-F1):
|
|
|
|
* `cointegrated/rubert-tiny2` — 30M, lr 1e-4, batch 64, до 20 эпох;
|
|
* `ai-forever/ruBert-base` — 110M, lr 3e-5, batch 32, до 10 эпох.
|
|
|
|
Лучшая по joint exact на held-out test копируется в `models/best`.
|
|
Итог — в `reports/summary.md`.
|
|
|
|
## Сегментатор страниц (src/segmenter.py) — главный путь
|
|
|
|
Какие строки страницы описывают дорожки, решает обученная модель
|
|
(rubert-tiny2, бинарная классификация строки с контекстом «пред. || след.»),
|
|
а не регулярки. Правила (`html_extract.extract_segments`) — запасной путь:
|
|
используются, если `models/segmenter` нет или модель не нашла ни одной строки;
|
|
режим можно форсировать: `predict_release(..., segmenter="rules")`.
|
|
|
|
* Обучение: `python -m src.segmenter --train` (~1 мин). Разметка строк — по
|
|
`raw` из `labels.csv` (362 страницы в `data/pages`); синтетика: `raw` всех
|
|
train-строк (в т.ч. nnm/kinozal без страниц) вставляются в контекст реальных
|
|
строк страниц. Страницы делятся по хешу `release_id` (≈20% в тест).
|
|
* Метрики: строки (test) P 0.936 / R 0.994; e2e на непросмотренных страницах
|
|
rutracker (n=64) — precision 0.904 → 0.946 против правил, recall 0.975 → 0.975
|
|
(`tests/e2e_segmenter.py`; выборка мала, разброс большой).
|
|
* Нарезка ВНУТРИ строки («Дублированный, многоголосый») в данных не размечена
|
|
(91 группа из 6735) — остаётся на правилах `_split_entries`, но только на
|
|
строках, выбранных моделью.
|
|
* ONNX-путь (Rust/TS) сегментатор пока не включает: там по-прежнему правила.
|