# 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 минут) ```bash # 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: ```python 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](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/architecture.md) — устройство модели и пайплайна * [docs/dataset.md](docs/dataset.md) — схема датасета, семантика классов, аудит * [docs/training-windows.md](docs/training-windows.md) — обучение на Windows+GPU, troubleshooting * [docs/api-reference.md](docs/api-reference.md) — **все значения ответа сервиса (15 классов `translation_type` и др.) и принцип работы; `labels.csv` для этого открывать не нужно** * [docs/inference.md](docs/inference.md) — CLI/API, JSON-контракт * [docs/integration.md](docs/integration.md) — Rust/TypeScript: HTTP-сервис и ONNX ## Качество Held-out test (4 377 строк, группировка по релизам и текстам; модель `models/runs/20261102_190555_basecodec`, обучена 02.11.2026 на `labels.csv` со студиями из кодек-строк; сравнение моделей — `python -m src.runs`): | модель | type acc | type macro-F1 | span exact | 18+ acc | lang acc | joint exact | |---|---|---|---|---|---|---| | **ruBert-base (models/best)** | **0.978** | **0.903** | **0.922** | **1.000** | **0.997** | **0.906** | * joint exact — строка распознана полностью (тип + автор + 18+). * Тест-сплит пересобирается вместе с датасетом, поэтому цифры разных снимков напрямую не сравниваются (ранние 0.946 — на тесте из 645 строк). rubert-tiny2 на текущем датасете не переобучалась. * E2E по релизам (`tests/e2e_segmenter.py`, по 400 тестовых страниц на трекер, пары тип + студия, precision / recall): kinozal 0.766 / 0.879, rutracker 0.877 / 0.870, nnm 0.838 / 0.825. * ONNX-экспорт эквивалентен PyTorch на 100% строк test — [reports/onnx_parity.md](reports/onnx_parity.md). * Smoke на формулировках, которых нет в датасете: тип 9/10, 18+ 10/10, автор 5/10 (полное извлечение автора на непривычных форматах записи — известное ограничение, см. ниже). ## Ограничения * Метки датасета сгенерированы регулярками (пайплайн deepseek-harness) — метрики измеряют согласование с этой разметкой; ценность модели — обобщение на новые формулировки без сопровождения регулярок. * Датасет: 55 324 строки из 27 900 релизов (kinozal 21 766 / nnm 18 363 / rutracker 15 195). * Извлечение автора уверенно работает на форматах записи, представленных в датасете (имя после типа через разделитель, в скобках у AVO/MVO/DVO-дорожек, после тире); на непривычных форматах («Одноголосый закадровый (Фамилия)») модель может не выделить имя — span остаётся пустым, тип при этом верный. * kinozal-страницы: авторы озвучки лежат во вкладках, которые сайт подгружает JavaScript-ом. По `url` сервис скачивает их сам (`fetch_html`, `showtab`); в присланном `html`/`text` без этих вкладок студий может не быть. * Классы с 1–8 примерами почти не выучиваются (видно в macro-F1 и confusion matrix); морфологическая аугментация (458 вариантов: «многоголосое» и т.п.) частично закрывает этот пробел.