6470f882f1cc26f505043d94583521ab2a2821c4
Обучение: * page_text/kinozal_text: текст kinozal = h1 + «Техданные» + «Релиз» из data/raw/kinozal/*.json (в pages/kz-*.txt нет заголовка и «Техданных», откуда половина меток kinozal; сервис видит обе вкладки). * label_page: позитив — raw или audio_raw; неразмеченная кодек-строка русской дорожки («Аудио 3: … Русский (MVO, X)», «Аудио Rus: … | X») получает -1 и в обучение не идёт: как негатив она учила отбрасывать кодек-строки. Сводка «Аудио: Русский, английский (…)» остаётся негативом. Инференс (release): * model_segments -> (основные, кодек); выбранные моделью кодек-строки идут второстепенным источником вместе с правилом codec_segments; иноязычные («Казахский (Dub, …)», «English Dubbing», «Original») отбрасываются. * merge_extra_tracks(adopt_type): кодек-дорожка с типом «неизвестно» получает тип единственной дорожки страницы без студии. e2e_segmenter: тот же текст kinozal, не больше E2E_MAX (400) страниц на трекер, train-страницы только с --train-pages (меню парсит только тест). Co-Authored-By: Claude Opus 5.5 <[email protected]>
grabber-ai — нейро-модель «тип озвучки + автор + 18+»
По тексту дорожки озвучки (или HTML-странице релиза с трекеров rutracker / kinozal / nnm-club) модель определяет:
- тип озвучки (
translation_type, 15 классов: проф. дубляж, проф. многоголосый, авт. одноголосый, …) — классификация; - автора (
studio: студия или переводчик) — извлечение имени прямо из текста (работает и для авторов, которых не было в обучении) + нормализация по словарю алиасов («HDRezka Studio» →HDrezka, «Ю. Сербин» → «Юрий Сербин»); - 18+ дорожка или нет — бинарная голова.
Обучено на датасете из 3286 разметанных дорожек + 458 аугментированных
морфологических вариантов (снимок labels.csv из deepseek-harness).
Multi-task RuBERT: сравниваются rubert-tiny2 и ruBert-base,
лучшая кладётся в models/best.
Квикстарт (5 минут)
# 1. окружение (Windows, Python 3.13; GPU требует колёс cu128 — см. docs/training-windows.md)
python -m venv .venv
.venv\Scripts\python -m pip install torch --index-url https://download.pytorch.org/whl/cu128
.venv\Scripts\python -m pip install -r requirements.txt
# 2. данные -> подготовка (data/labels.csv уже в репозитории)
.venv\Scripts\python -m src.prepare_data
# 3. обучение (обе модели, 5-fold CV; ~15-20 мин на RTX 5070 Ti)
.venv\Scripts\python -m src.train
# результат: models/best, reports/summary.md, reports/*.png
# 4. инференс
.venv\Scripts\python -m src.predict --text "Перевод: Профессиональный (многоголосый закадровый) | HDRezka Studio 18+"
.venv\Scripts\python -m src.predict --html data/samples/rt-topic.html
.venv\Scripts\python -m src.predict --file data/pages/6907007.txt --out result.json
Python API:
from src.predict import DubPredictor
model = DubPredictor() # models/best по умолчанию
model.predict_text("Авторский (одноголосый закадровый) - Ю. Сербин")
# {'translation_type': 'авт. одноголосый', 'studio': 'Юрий Сербин', 'is_18plus': False, ...}
model.predict_html(open("data/samples/rt-topic.html", encoding="utf-8").read())
# [ {...дорожка 1...}, {...дорожка 2...}, ... ]
HTTP-сервис и интеграция из Rust/TypeScript (ONNX без Python) — см. docs/integration.md.
Структура
data/ датасет (labels.csv — снимок) и HTML-дампы страниц
data/processed/ подготовленные данные (dataset.json, label_maps.json)
src/ код: подготовка, модель, обучение, инференс, сервис, ONNX-экспорт
models/ чекпоинты (best — итоговая модель)
reports/ метрики, аудит датасета, confusion matrix, ONNX-parity
tests/ юнит-тесты (tests/run_tests.py)
examples/ клиенты: rust (HTTP, ONNX), typescript (HTTP, ONNX)
docs/ документация
Документация
- docs/architecture.md — устройство модели и пайплайна
- docs/dataset.md — схема датасета, семантика классов, аудит
- docs/training-windows.md — обучение на Windows+GPU, troubleshooting
- docs/api-reference.md — все значения ответа сервиса (15 классов
translation_typeи др.) и принцип работы;labels.csvдля этого открывать не нужно - docs/inference.md — CLI/API, JSON-контракт
- docs/integration.md — Rust/TypeScript: HTTP-сервис и ONNX
Качество
Held-out test (645 строк, группировка по релизам и текстам; полная таблица и графики — reports/summary.md):
| модель | type acc | type macro-F1 | span exact | 18+ acc | joint exact |
|---|---|---|---|---|---|
| rubert-tiny2 | 0.991 | 0.970 | 0.918 | 1.000 | 0.915 |
| ruBert-base (models/best) | 0.994 | 0.966 | 0.950 | 1.000 | 0.946 |
- joint exact — строка распознана полностью (тип + автор + 18+).
- E2E на сохранённых страницах релизов: rutracker — precision 0.83 / recall 0.98 по парам (тип, автор); kinozal-текстовые дампы содержат только тех-вкладку без авторов (автор подгружался на сайте через JS), поэтому там автор недоступен в принципе.
- ONNX-экспорт эквивалентен PyTorch на 100% строк test — reports/onnx_parity.md.
- Smoke на формулировках, которых нет в датасете: тип 9/10, 18+ 10/10, автор 5/10 (полное извлечение автора на непривычных форматах записи — известное ограничение, см. ниже).
Ограничения
- Метки датасета сгенерированы регулярками (пайплайн deepseek-harness) — метрики измеряют согласование с этой разметкой; ценность модели — обобщение на новые формулировки без сопровождения регулярок.
- Источники несбалансированы (rutracker 1658 / nnm 1549 / kinozal 77 строк).
- Извлечение автора уверенно работает на форматах записи, представленных в датасете (имя после типа через разделитель, в скобках у AVO/MVO/DVO-дорожек, после тире); на непривычных форматах («Одноголосый закадровый (Фамилия)») модель может не выделить имя — span остаётся пустым, тип при этом верный.
- kinozal-страницы: авторы озвучки подгружаются на сайте JavaScript-ом; в статическом HTML и текстовых дампах их нет — модель их не увидит (это ограничение данных, не модели).
- Классы с 1–8 примерами почти не выучиваются (видно в macro-F1 и confusion matrix); морфологическая аугментация (458 вариантов: «многоголосое» и т.п.) частично закрывает этот пробел.
Languages
Python
55.4%
HTML
44.3%
JavaScript
0.2%
Dockerfile
0.1%