Нейросортировщик видео
Локальная нейросетевая программа для оценки и фильтрации видеороликов по
«эмоциональной» составляющей. Пользователь оценивает ролики от 1 до 5,
программа извлекает высококачественные мультимодальные эмбеддинги
(видео + аудио), обучает компактный регрессор на GPU, и затем фильтрует
новые ролики по обученной модели — копируя «понравившиеся» в отдельный
каталог.
Полностью портативная: Python, виртуальное окружение, все зависимости и
загружаемые модели хранятся внутри папки проекта. Никаких системных
установок, кроме наличия Python (или WinPython).
Содержание
- Возможности
- Быстрый старт
- Архитектура
- Модели и эмбеддинги
- Модули программы (GUI)
- Настройки (
config.ini) - Портативность и кеши
- Производительность
- Решённые проблемы («грабли»)
- Устранение неполадок
- Структура проекта
1. Возможности
- GUI с тёмной темой (PySide6 + python-mpv), три вкладки.
- Оценка роликов встроенным мини-плеером (пауза по клику, прогресс,
громкость, шкала 1–5, навигация назад/вперёд). - Мультимодальные эмбеддинги на базе SOTA открытых моделей:
Qwen3-VL-Embedding-8B (видео) + CLAP (настроение музыки) + MERT
(музыкальные признаки). - Обучение локального MLP-регрессора на GPU с ранним стопом и
стратифицированным валидационным сплитом. - Фильтрация новых роликов с тремя режимами: мягкий (сигмоида +
температура), строгий (порог), рассортировка поrating_1..rating_5. - Управление процессом: пауза/отмена фильтрации, батч-извлечение с
настраиваемым размером. - Полное GPU-ускорение (RTX 5090 / sm_120 / CUDA 12.8).
- Кеширование эмбеддингов в
.npy— повторное обучение мгновенно. - Портативность: всё внутри папки проекта, никаких изменений системы.
2. Быстрый старт
Требования
- Windows 10/11 x64 (тестировалось на 10.0.19045).
- Python 3.10–3.13 в системе (через
pylauncher или вPATH), либо
распакованный WinPython вpython/. - NVIDIA GPU с поддержкой CUDA 12.8 (тестировалось на RTX 5090, sm_120).
- 7-Zip в
C:\Program Files\7-Zip\7z.exe(для распаковки libmpv). - ≈ 20 ГБ свободного места (для моделей и виртуального окружения).
Установка
- Скачайте проект в любую папку, например
C:\neuro_sort_video2. - Запустите
setup.bat. Скрипт:- найдёт Python (системный
py/pythonили портативный WinPython); - создаст виртуальное окружение
.venv/; - установит PyTorch с CUDA 12.8 для Blackwell (RTX 5090, sm_120);
- установит остальные зависимости из
requirements.txt; - скачает и распакует
libmpv-2.dll(для видео-плеера); - при наличии
ttt\ffmpeg-master-latest-win64-gpl-shared.zip— установит
ffmpeg.exe+ DLL из него, иначе попробует скачать с gyan.dev; - проверит, что CUDA доступна и определена правильно.
- найдёт Python (системный
- Дождитесь сообщения
SETUP COMPLETEи строки
Device: NVIDIA GeForce RTX 5090/Capability: (12, 0).
Запуск
Откроется окно «Нейросортировщик видео». При первом обучении будет
скачана модель Qwen3-VL-Embedding-8B (~16 ГБ) в model/ — нужен стабильный
интернет.
3. Архитектура
Ключевая идея: тяжёлые предобученные модели (Qwen3-VL, CLAP, MERT)
работают как замороженные feature-extractor'ы — извлекают
высококачественные эмбеддинги, которые кешируются. Обучается только
компактный MLP-регрессор (~1–20 МБ), что делает обучение быстрым и
устойчивым даже на малых выборках (от 10–20 роликов).
4. Модели и эмбеддинги
Двухбашенная архитектура
Открытых моделей, одинаково хорошо понимающих и видео, и музыку, не
существует: единые video-LLM (InternVL3, VideoLLaMA3) воспринимают аудио
только как речевые/событийные токены. Поэтому применяется двухбашенный
подход с независимыми оптимальными энкодерами для каждой модальности.
| Башня | Модель (HF ID) | Размерность | Назначение |
|---|---|---|---|
| Видео | Qwen/Qwen3-VL-Embedding-8B |
4096 | SOTA видеоэмбеддинги (рисовка, монтаж, персонажи, динамика) |
| Аудио-настроение | laion/larger_clap_music |
512 | Настроение/вайб музыки (эпик, грусть, энергия, чилл) |
| Аудио-музыка (опц.) | m-a-p/MERT-v1-330M |
1024 | Музыкальные признаки (темп, тональность, жанр, бит) |
Итоговая размерность: 4096 + 512 + 1024 = 5632 (с MERT) или
4096 + 512 = 4608 (без MERT).
Особенности извлечения
- Видео: N равномерно распределённых кадров извлекаются через PyAV с
container.seek()(10–50× быстрее линейного декодирования), подаются
модели по одному, эмбеддинги mean-pool'ятся и L2-нормализуются. - Аудио: ffmpeg извлекает wav 48 кГц моно. CLAP работает на 48 кГц
напрямую, MERT даунсемплируется до 24 кГц. Берётся центральный фрагмент
длинойaudio_sample_sec. - Финальная нормализация: весь склеенный вектор
[vid | clap | mert]
L2-нормализуется целиком — это критично для стабильности downstream-MLP.
Регрессор
Компактный MLP на PyTorch:
- Без активации на выходе (raw-рейтинг). Раньше использовался
3 + 2·tanh(x)— но tanh насыщается, градиенты умирают, и модель
застревала на константном прогнозе «все = 5». head.weight = 0,head.bias = 3.0— инициализация в центре диапазона.clamp(1, 5)применяется только на inference.- Loss = MSE, оптимизатор AdamW, early stop по val_MAE.
5. Модули программы (GUI)
Вкладка 1 — «Оценка роликов»
- Выбор папки с роликами, рекурсивное сканирование (
.mp4 .mkv .webm .avi .mov .m4v .mpg .mpeg .ts .wmv .flv). - mpv-плеер (
python-mpv+libmpv-2.dll): прогресс, громкость,
play/pause по клику. - Шкала 1–5 (звёзды), навигация «◀ Назад» / авто-переход вперёд.
- Сохранение оценок в SQLite (
data.db). - Кнопка «Обучить нейромодель» — фоновый поток (
QThread):
извлечение эмбеддингов батчами + обучение регрессора →
END_Model/regressor.pt.
Вкладка 2 — «Настройка обучения»
Все параметры с подробными русскими тултипами и рекомендациями:
| Группа | Параметр | Диапазон | По умолч. |
|---|---|---|---|
| Эмбеддинги | n_video_frames |
4–32 | 16 |
frame_resolution |
224 / 336 / 448 | 448 | |
audio_sample_sec |
5–60 сек | 20 | |
extract_batch_size |
1–16 | 4 | |
audio_use_mert |
вкл/выкл | вкл | |
| Нейросеть | hidden_dim |
128–4096 | 1024 |
n_layers |
1–4 | 2 | |
dropout |
0.0–0.6 | 0.2 | |
| Оптимизация | learning_rate |
1e-5–1e-1 | 1e-3 |
epochs |
10–2000 | 100 | |
batch_size (обучения) |
4–256 | 32 | |
weight_decay |
0–0.5 | 1e-4 | |
val_split |
0.0–0.5 | 0.15 |
Живой расчёт размера модели (в Б/КБ/МБ) и числа параметров.
Вкладка 3 — «Фильтрация»
- Выбор обученной модели, входного и выходного каталогов.
- Температура (0.00–1.00): ползунок + поле, синхронизированы.
- Порог рейтинга (1.0–5.0).
- Чекбоксы:
- «Строгий режим» — простой отбор
rating ≥ threshold. - «Рассортировать по рейтингу» — все ролики копируются в подпапки
rating_1..rating_5(порог/температура игнорируются).
- «Строгий режим» — простой отбор
- Кнопки «▶ Старт» / «⏸ Пауза» / «✕ Отмена».
- Прогресс-бар и лог с прогнозами по каждому ролику.
Логика «понравилось» (мягкий режим):
prob = 1 / (1 + exp(-(rating - threshold) / temperature)),
копируется при prob ≥ 0.5. Меньшая температура → жёстче отбор.
6. Настройки (config.ini)
Все настройки сохраняются в config.ini (создаётся при первом запуске).
Разделы:
Полный список ключей с умолчаниями — в src/config.py в словаре DEFAULTS.
Важно: при наличии
config.iniзначения берутся из него, а не из
DEFAULTS. Если после обновления кода параметры ведут себя странно —
удалитеconfig.ini, он пересоздастся с актуальными умолчаниями.
7. Портативность и кеши
Все данные хранятся внутри папки проекта — никаких системных изменений:
| Путь | Содержимое |
|---|---|
python/ |
Портативный WinPython (если используется) |
.venv/ |
Виртуальное окружение со всеми pip-зависимостями |
lib/ |
libmpv-2.dll, ffmpeg.exe, ffprobe.exe, av*.dll |
model/ |
Кеш HuggingFace (HF_HOME, TORCH_HOME) — загруженные модели (~16 ГБ) |
cache/ |
XDG_CACHE_HOME + cache/embeddings/*.npy — кеш эмбеддингов |
END_Model/ |
regressor.pt + meta.json — результат обучения |
data.db |
SQLite с оценками и метаданными эмбеддингов |
config.ini |
Настройки |
Переменные окружения (устанавливаются в start.bat перед запуском Python):
Благодаря этому проект можно перенести на другой компьютер простым
копированием папки (при совпадении версии GPU-драйвера).
8. Производительность
Измерения на RTX 5090 (32 ГБ VRAM, sm_120), видеоклипы ~60 сек:
| Операция | Время |
|---|---|
| Загрузка моделей (Qwen3-VL + CLAP + MERT) | ~17 сек |
_sample_frames 60-сек ролика, 8 кадров (с seek) |
0.09 сек |
| Полный encode одного видео (видео + аудио) | ~0.76 сек |
Батч из 4 видео (с extract_batch_size=4) |
~1.9 сек |
| Обучение MLP на 60 роликах (100 эпох) | несколько секунд |
| Прогноз одного ролика | <10 мс |
Ускорения, внедрённые в код:
- seek вместо линейного декодирования — основное (раньше сэмплинг
длинного ролика занимал 10–40 сек, теперь <0.1 сек). - Батч-извлечение (
extract_batch) — один вызовmodel.encode()на
все кадры всех видео батча → ~2× ускорение, GPU не простаивает. - Кеширование эмбеддингов — повторное обучение идёт мгновенно.
- bf16 на инференсе — вдвое меньше VRAM, без потери качества.
9. Решённые проблемы («грабли»)
Ниже — реальные проблемы, с которыми столкнулись в ходе разработки, и их
решения. Полезно для будущей отладки и расширения.
9.1. Видео-энкодер: нельзя использовать прямой forward()
- Симптом: Эмбеддинги получались мусорными (все нули или шум).
- Причина: Qwen3-VL-Embedding — это LLM-архитектура. Качественный
эмбеддинг получается только черезSentenceTransformer.encode(), который
внутри применяет корректный pooling и projection-head. Ручной
model(**inputs).last_hidden_stateдаёт скрытое состояние LLM, а не
embedding. - Решение: Использовать
SentenceTransformerAPI, кадры подавать как
PIL-изображения.
9.2. На Windows нельзя подавать путь к видео напрямую в модель
- Симптом:
torchvision.io has no attribute 'read_video'или
libtorchcodec_core4.dll not found. - Причина: В torchvision ≥0.26
read_videoудалён, а замена (torchcodec)
имеет проблемы с DLL на Windows. - Решение: Кадры сэмплируются сами через PyAV (
av.open+seek),
подаются модели по одному, эмбеддинги mean-pool'ятся.
9.3. Сэмплинг seek'ом вместо линейного декодирования
- Симптом: Между батчами было ~40 секунд простоя (GPU 0%, CPU 13%).
- Причина:
_sample_framesдекодировал видео линейно от начала, чтобы
дойти до целевых индексов — тысячи кадров чистого CPU-декодирования в
одном потоке. - Решение:
container.seek(timestamp)для каждого целевого кадра.
Ускорение ~70×. Есть fallback на линейное декодирование при сбое seek.
9.4. CLAP: ClapProcessor падает с HTTP 401
- Симптом:
ClapProcessor.from_pretrained→RepositoryNotFoundError 401. - Причина: В
transformers ≥5.xClapProcessorпытается листать
additional_chat_templatesна сервере, и на некоторых репозиториях это
падает. - Решение: Использовать
ClapFeatureExtractorнапрямую (минует Processor).
Параметр —raw_speech=(неaudios=).
9.5. CLAP на 48 кГц, MERT на 24 кГц
- Симптом: Предупреждение «trained using sampling rate of 48000...» и
мусорные эмбеддинги. - Причина: CLAP обучен на 48 кГц, MERT — на 24 кГц. Подача одного wav
в обе модели искажает результат. - Решение: ffmpeg извлекает wav сразу на 48 кГц; CLAP работает напрямую,
MERT даунсемплируется до 24 кГц (_resample).
9.6. MERT требует nnAudio
- Симптом: Кастомный код MERT (
trust_remote_code=True) падал на Windows. - Причина: MERT использует
nnAudioдля CQT-спектрограмм. - Решение:
nnAudio>=0.3добавлен вrequirements.txt.
9.7. Регрессор застревал на константном прогнозе («все = 5»)
- Симптом: После обучения MAE ≈ 1.17, все прогнозы ≈ 5, фильтрация
копировала низкооценённые ролики. - Причина: Активация
3 + 2·tanh(raw)насыщается у краёв диапазона,
градиенты через tanh умирают, модель не может из них выбраться. - Решение: Убрать tanh. Финальный слой —
Linearбез активации (raw).
head.bias = 3.0для старта в центре диапазона.clamp(1,5)только на
inference. После правки: MAE 0.000 на синтетике, Pearson r = 1.000.
9.8. Нужна финальная L2-нормализация всего вектора
- Симптом: Обучение шло нестабильно, изредка нулевые векторы ломали
датасет. - Причина: Подвекторы из разных башен имеют разные шкалы; конкатенация
без общей нормировки путает downstream-MLP. - Решение: L2-нормализация всего склеенного вектора перед кешированием.
Если итоговая норма < 1e-6 — кеш НЕ сохраняется, файл помечаетсяNone.
9.9. Версия кеша EMB_VER
- Симптом: После изменения логики extractor'а старый кеш отдавал
некорректные эмбеддинги. - Решение: Введён
EMB_VER(сейчас= 2). Включается в хэш-строку —
при бампе версии старый кеш автоматически становится невалидным.
v1 — без общей L2-нормализации; v2 — с ней.
9.10. Случайный сплит ломал обучение на малых данных
- Симптом: На 15–20 роликах валидация бессмысленна или один из классов
целиком уходил в val. - Решение: (а) Stratified split по рейтингам — каждый класс
представлен пропорционально; (б) приn < 30принудительноval_split = 0
(обучение на всём датасете).
9.11. Чёрные окна ffmpeg перехватывали фокус
- Симптом: При пакетной обработке постоянно мигали консольные окна,
мешая работать в других программах. - Решение:
subprocess.run(..., creationflags=CREATE_NO_WINDOW)на
Windows (флаг0x08000000).
9.12. setup.bat: SourceForge отдаёт HTML вместо архива
- Симптом: Автоскачивание
libmpv-2.dllломалось — приходил HTML-редирект. - Решение: Скачать через GitHub API
(api.github.com/repos/zhongfly/mpv-winbuild/releases/latest), распаковать
локальным 7-Zip.
9.13. setup.bat: ffmpeg shared-сборка требует DLL рядом
- Симптом: После копирования одного
ffmpeg.exeон падал с «avcodec-63.dll
не найден». - Решение: Копировать не только
ffmpeg.exe, но и весь набор DLL из
bin/(avcodec,avformat,avutil,swscale,swresampleи т.д.).
9.14. PyTorch не видел RTX 5090
- Симптом:
torch.cuda.is_available()=False, или
«no kernel image available for executingopon sm_120». - Причина: Обычный
pip install torchтянет старый CUDA 12.1 без поддержки
Blackwell (sm_120). - Решение: Устанавливать torch из индекса CUDA 12.8:
pip install torch --index-url https://download.pytorch.org/whl/cu128.
Требуется torch ≥ 2.7.
9.15. bat-файлы в UTF-8 ломали cmd
- Симптом: При запуске
setup.bat— кракозябры и
'слово' is not recognized as a command. - Причина: Windows cmd читает
.batв OEM-кодировке (CP866); кириллица и
умные кавычки в UTF-8 ломают парсер.chcp 65001помогает только для
вывода, не для разбора самого bat-файла. - Решение: Оба
.bat-файла написаны на чистом ASCII (без кириллицы,
без«», без—), без BOM. Кириллица — только в Python-коде.
9.16. Скобки в echo внутри if (...) закрывали блок
- Симптом:
'from' is not recognized as a command. - Причина: Строка
echo Download (64-bit) from ...внутриif (...)
блока: скобки(64-bit)закрывали блок раньше времени. - Решение: Сложную логику вынести в подпрограммы (
:label+call),
убрать скобки изecho-строк.
10. Устранение неполадок
«CUDA available: False»
- Обновите драйвер NVIDIA до последней версии (требуется поддержка CUDA 12.8).
- Проверьте установку:
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_capability())". Должно бытьTrue (12, 0)для RTX 5090. - Если ставили torch не из
cu128— переустановите:
pip install torch --index-url https://download.pytorch.org/whl/cu128 --force-reinstall.
«Плеер не инициализируется / нет модуля mpv»
- Проверьте, что
lib/libmpv-2.dllсуществует (см.setup.bat, шаг 4). - Проверьте, что
python-mpvустановлен:pip show python-mpv.
«ffmpeg не найден»
- Проверьте
lib/ffmpeg.exeи DLL рядом (avcodec-63.dll,avformat-63.dll,
avutil-61.dllи т.д. — нужны все, т.к. используется shared-сборка). - При отсутствии — перезапустите
setup.bat(он докачает).
Обучение даёт плохой результат (MAE > 1, Pearson < 0.3)
- Мало данных: нужно минимум 10–20 оценённых роликов, лучше 50+.
- Однородные оценки: если все ролики оценены на «5», модели нечему
учиться — нужны контрастные оценки (1–2 и 4–5). - Удалите старый кеш:
del /Q cache\embeddings\*(после правок формата). - Удалите старую модель:
del END_Model\regressor.pt.
«401 Unauthorized» при загрузке CLAP/MERT
- Это известная проблема
ClapProcessorв transformers ≥5.x. Программа
используетClapFeatureExtractorнапрямую. Если ошибка повторяется —
проверьте, что вconfig.iniстоит
clap_model_id = laion/larger_clap_music(а неlaion/clap_htsat_music).
Фильтрация копирует низкооценённые ролики
- Модель обучена плохо (см. выше про MAE).
- Проверьте порог: он должен быть выше среднего прогноза для «не
понравившихся». - Попробуйте строгий режим или уменьшите температуру.
Медленное извлечение эмбеддингов (простой GPU)
- Проверьте, что
_sample_framesиспользует seek (новая версия кода). - Увеличьте
extract_batch_sizeво вкладке 2 (4–8 для RTX 5090). - Убедитесь, что ролики не на медленном сетевом диске.
11. Структура проекта
Технические заметки
Используемый стек
- GUI: PySide6 6.11 + python-mpv 1.0.8
- ML: PyTorch 2.11+cu128, transformers 5.14, sentence-transformers 5.6
- Видео: PyAV 17.1 (декодирование), ffmpeg (извлечение аудио)
- Аудио: soundfile 0.14, nnAudio 0.3
- Хранение: SQLite (встроенный),
.npy(numpy) - Портативность: venv, переменные окружения для кешей HF/Torch
Версии моделей (зафиксированы в config.ini)
| Модель | HF ID | Размер |
|---|---|---|
| Видео | Qwen/Qwen3-VL-Embedding-8B |
~16 ГБ |
| CLAP | laion/larger_clap_music |
~600 МБ |
| MERT | m-a-p/MERT-v1-330M |
~1.5 ГБ |
Лицензии
- Код проекта — собственный.
- Зависимости: PySide6 (LGPL), python-mpv (GPLv2+), PyTorch (BSD),
transformers (Apache 2.0), модели — согласно их карточкам на HuggingFace.