Всё началось с того, что на «Хабре» мне попалась статья о модели TAPe — Theory of Active Perception, или «теории активного восприятия». В статье были громкие заявления и интересные описания, но попробовать модель на практике не удалось.
Я собрал ключевые тезисы о TAPe и обратился к ChatGPT с запросом:
«Я студент и хочу в рамках дипломной работы реализовать модель, использующую подходы Theory of Active Perception. Она должна определять, где находятся игроки и какие действия они выполняют: подачу, приём, передачу или атаку».
Я также уточнил, что у меня есть видеозаписи, а для передачи временного контекста я хочу использовать клипы из девяти кадров.
Почему именно девять кадров? Ранее я уже модифицировал модель TrackNet для трекинга волейбольного мяча по девяти последовательным кадрам и решил придерживаться похожего подхода, чтобы сократить вычислительные затраты.
Первые итерации
Первая версия модели использовала query-based-подход для одновременного поиска игроков и мяча в видеоклипе.
Принцип работы
На вход модели подаётся фиксированный клип из девяти кадров в формате
[B, T, C, H, W]. Каждый кадр приводится к разрешению 512 × 288 пикселей.TinyBackboneизвлекает признаки на трёх уровнях пирамиды FPN: 1/4, 1/8 и 1/16 от исходного разрешения.Для поиска игроков используются 20 обучаемых query-слотов с начальными пространственными якорями. Декодер последовательно применяет:
self-attention между query;
cross-attention к визуальным признакам;
temporal attention между кадрами.
Для каждого query модель предсказывает:
класс объекта —
playerилиbackground;ограничивающий прямоугольник — bounding box;
embedding для сопоставления одного и того же игрока между кадрами.
Мяч обнаруживается отдельной grid-head. Для каждого кадра модель предсказывает карту уверенности и смещение объекта внутри наиболее вероятной ячейки.
После инференса применяются порог уверенности и NMS для боксов игроков. Для мяча выбирается ячейка с максимальным значением уверенности.
На словах всё выглядело красиво. Оставалось найти данные, обучить модель и проверить, насколько хорошо теория работает на практике.
На первом этапе я решил уменьшить аппетиты и сосредоточиться только на детекции игроков. План был следующим: сначала научиться надёжно находить игроков, затем расширить временной контекст и добавить распознавание действий.
Главная проблема обучения подобных моделей — наличие качественных размеченных датасетов.
Первые эксперименты я проводил на датасете по паделу, состоящем из двух двадцатиминутных видео с покадровой разметкой.
После первых запусков модель действительно научилась находить игроков в паделе, но точность оставалась недостаточной. Она часто предсказывала боксы в пустых областях кадра, где в текущий момент игрока не было. При этом с точки зрения статистики такие предсказания выглядели объяснимо: в этих областях игроки часто находились на соседних кадрах или в других эпизодах.

Я довольно долго обсуждал с ChatGPT возможные причины ложных детекций. И только спустя некоторое время случайно заметил, что на визуализации валидации рамки вообще не совпадают с положением игроков.
Причина оказалась в разметке. В одном из матчей был эпизод с крупным планом, но соответствующие кадры при аннотации пропустили. Из-за этого вся последующая разметка сдвинулась относительно видео. В результате почти половина датасета оказалась некорректной.
Датасет для пляжного волейбола
Почему именно пляжный волейбол? Сейчас лето, и мы регулярно играем на песке. Я записываю матчи, чтобы потом посмотреть на свою игру со стороны.
На площадке всего четыре человека, поэтому такие видео проще размечать. Кроме того, в будущем мне было бы интересно автоматически получать статистику собственных матчей: нарезку игровых эпизодов без пауз, количество касаний, длительность розыгрышей и другие показатели.
Для первичной разметки датасета я использовал крупную модель YOLO11l. Однако быстро выяснилось, что авторазметка моих видео требует ручной корректировки.
Особенно заметны ошибки во время атак и блоков. Когда игрок прыгает, модель иногда обрезает bounding box по голове или не включает в него поднятые руки атакующего и блокирующего.
В процессе работы над датасетом я также понял, что мои записи слишком однообразны: одна и та же площадка, похожее освещение и практически неизменная позиция камеры. Чтобы увеличить разнообразие данных, я добавил видео с играми профессиональных спортсменов.
Обратная связь от LLM
Итак, у нас есть датасет для экспериментов и сгенерированная архитектура модели, которую можно обучать.
В идеальном мире после этого мы получили бы работающий детектор. В реальности модель действительно что-то находит, но часто не там, где нужно. А иногда, наоборот, не находит игроков там, где они явно присутствуют.
Тогда мы берём результаты и пишем:
«Дорогой ChatGPT, посмотри, что за ерунда получилась…»
LLM отвечает:
«Ты совершенно прав: это предсказуемая ошибка, которая заложена в архитектуре твоей сети…»
После этого обычно следует уверенное объяснение того, какой именно блок модели работает неправильно, почему ошибка была неизбежна и что необходимо изменить в архитектуре.

После каждого изменения архитектуры модель нужно заново обучать на моём датасете из 21 000 кадров. Одна эпоха занимает 6–7 минут, а полный цикл обучения на RTX 3060 с 12 ГБ видеопамяти — около 8–10 часов.
После более чем двадцати итераций мы получили модель, которая почти работает. Она уже достаточно уверенно находит игроков, но остаются проблемы с «прыгающими» боксами и периодической потерей детекций на отдельных кадрах.

После каждой доработки или изменения архитектуры важно не забывать сохранять код в Git. Не все улучшения действительно делают модель лучше, поэтому всегда должна оставаться возможность откатиться к последней удачной версии.
Смена архитектуры
После множества «правильных исправлений», предложенных LLM, в модели постепенно накопилось большое количество костылей, дополнительных параметров и механизмов, подогнанных под детекцию игроков. При этом качество предсказаний почти не улучшалось.
В какой-то момент я решил не продолжать бесконечно дорабатывать текущую реализацию, а попросил LLM полностью переработать архитектуру и устранить обнаруженные узкие места.
Главный вывод из этого этапа: важно вовремя распознать тупиковое направление и перейти к новому эксперименту, вместо того чтобы продолжать усложнять неудачное решение.
Компонент | Нулевая версия |
|
|---|---|---|
Входные данные | Grayscale, 1 канал | RGB, 3 канала по умолчанию |
Backbone |
|
|
Поиск игроков | 20 фиксированных обучаемых queries и пространственные anchors | Динамические proposals из карты |
Работа с признаками | Cross-attention между queries и всей evidence memory |
|
Временная связь | Temporal self-attention внутри decoder | Явный |
Уточнение боксов | Общий query decoder и единая box head | Несколько |
Дополнительные выходы | Класс, bounding box и embedding | Класс, bounding box, embedding, точки головы и ног, visibility, court points и association |
Версия архитектуры | 18 | 19 |
Первая архитектура уже работала на удовлетворительном уровне, но дальнейшие улучшения давались всё тяжелее и почти не влияли на качество. Поэтому новая версия стала не просто очередной правкой, а отдельным архитектурным экспериментом.

Схема рабочей модели REVEL‑VB

Запустить модель самостоятельно можно из репозитория:
RAVEL-VB Beach Volleyball Tracking
Модель состоит из двух независимых ветвей:
первая отвечает за детекцию игроков;
вторая — за поиск мяча и построена на базе
vballnet_grid_v1a.
Замеры производительности
В качестве отправной точки я запустил YOLO11n через OpenVINO и получил производительность 19,16 FPS.
На этом фоне результаты собственной модели выглядели вполне обнадёживающе:
на CPU использовался OpenVINO;
на GPU — PyTorch с CUDA.
Backend | Модель | Устройство | Pipeline FPS |
|---|---|---|---|
OpenVINO |
| CPU | 23,557 |
OpenVINO |
| CPU | 12,745 |
PyTorch |
| CUDA | 84,71 |
PyTorch |
| CUDA | 84,40 |
Во время подготовки статьи я решил сравнить производительность и точность модели с дообученной YOLO26n. Результат оказался неожиданным: YOLO26n-VB в OpenVINO показала 43,87 FPS.
Кадры в моём датасете имеют разрешение 512 × 288 пикселей. Это позволило ускорить YOLO-модели по сравнению с запуском на стандартном входном разрешении 640 × 640.
Почти двукратное отставание заставило меня глубже разобраться в вопросе производительности. Модель, которая должна была стать «убийцей YOLO», сама оказалась заметно медленнее.
Профилирование модели
Этап | Доля времени |
|---|---|
Backbone | 51,4% |
Ball grid | 27,0% |
Proposal head | 15,3% |
New query sampler | 1,6% |
Persistent queries | 1,5% |
Temporal linker | 0,7% |
Остальные операции | ≈1,1% |
Профилирование показало, что больше половины времени выполнения занимает backbone. Ещё 27% приходится на ветку поиска мяча, а 15,3% — на proposal head.
Главный вывод: в первую очередь необходимо ускорять backbone. Именно он является основным узким местом всей архитектуры.
Архитектурные оптимизации RAVEL-VB-002/003 относительно RAVEL-VB-001
В качестве базовой рассматривается стандартная конфигурация модели: входное разрешение 512 × 288 пикселей, ширина признакового пространства 128 каналов, 32 запроса и плотная генерация предложений на карте P4.
1. Уменьшение ширины признакового пространства
В RAVEL-VB-001 размер скрытого представления составляет 128 каналов, тогда как RAVEL-VB-002 и RAVEL-VB-003 по умолчанию используют 64 канала.
Сокращение ширины признакового пространства уменьшает количество вычислений и объём промежуточных данных, передаваемых между слоями модели.
2. C3k2/CSP-backbone вместо полноканальных блоков
Backbone в оптимизированных версиях построен на блоках C3k2, использующих CSP-подобное разделение потока признаков:
входные признаки проецируются в два потока уменьшенной ширины;
вычислительно дорогие свёртки выполняются только над одним из потоков;
промежуточные признаки объединяются;
итоговая карта формируется с помощью компактной проекции.
При коэффициенте расширения 0,5 основные свёртки работают примерно с половиной исходного числа каналов. Остаточные связи внутри блоков C3k при этом помогают сохранить стабильность обучения.
Количество параметров backbone:
Модель | Количество параметров |
|---|---|
RAVEL-VB-001 | 1 209 328 |
RAVEL-VB-002 | 404 848 |
RAVEL-VB-003 | 375 792 |
Таким образом, backbone RAVEL-VB-002 содержит примерно в три раза меньше параметров, чем backbone RAVEL-VB-001. В RAVEL-VB-003 количество параметров уменьшено ещё сильнее.
RAVEL-VB-002 сохраняет более информативное представление уровня P4, тогда как RAVEL-VB-003 реализует более агрессивный подход «вычисления по требованию». Плотная карта высокого разрешения используется только для первичного поиска объектов, после чего основная обработка переносится на разреженные запросы и признаки более низкого разрешения.
FPS бывают разными
Производительность модели можно измерять по-разному.
В одном случае оценивается максимальное количество входов, которое модель способна обработать за секунду. Такой тест обычно проводится на заранее подготовленных тензорах и показывает чистую скорость инференса без учёта чтения и декодирования видео.
В другом случае измеряется скорость обработки конкретного видео целиком.
Для практического применения нас интересует именно скорость получения предсказаний из видеопотока. Поэтому в итоговый замер входят:
чтение и декодирование кадров;
предварительная обработка;
инференс модели;
постобработка предсказаний.
Такой показатель правильнее называть производительностью всего конвейера, или pipeline FPS.
Тестовое видео
Для тестирования использовалось видео со следующими характеристиками:
Duration: 00:00:40.01 Start: 0.000000 Bitrate: 1119 kb/s Codec: H.264 High Pixel format: yuv420p Color space: BT.709 Resolution: 1280 × 720 Video bitrate: 982 kb/s Frame rate: 29.97 FPS
Для обеспечения повторяемости тестов замеры проводились на VPS и выделенном GPU. Это позволяет сравнивать версии модели в одинаковых условиях и уменьшает влияние фоновой нагрузки, различий в процессорах, настройках системы и скорости накопителей.
Сравнение точности моделей
Model | Weights | Input | mAP50 | mAP50:95 | Player mAP50:95 | Ball mAP50:95 | Precision | Recall |
|---|---|---|---|---|---|---|---|---|
RAVEL-VB-001-9f | beach-trained | 18×288×512 | 0.5888 | 0.3114 | 0.4870 | 0.1359 | 0.9069 | 0.7327 |
RAVEL-VB-002-9f | beach-trained | 9×288×512 | 0.6428 | 0.3312 | 0.5094 | 0.1529 | 0.7830 | 0.8178 |
RAVEL-VB-003-9f | beach-trained | 9×288×512 | 0.5889 | 0.2719 | 0.4114 | 0.1324 | 0.8158 | 0.7503 |
YOLO26n | official COCO | 640 | 0.3820 | 0.2815 | 0.5396 | 0.0234 | 0.8458 | 0.6802 |
YOLO26n-VB | beach-trained | 512 | 0.5808 | 0.3950 | 0.4269 | 0.3631 | 0.8594 | 0.5985 |
YOLO11n | official COCO | 640 | 0.4262 | 0.3102 | 0.5829 | 0.0375 | 0.9676 | 0.7260 |
YOLO11n-VB | beach-trained | 512 | 0.6005 | 0.3974 | 0.4741 | 0.3208 | 0.7031 | 0.6837 |
Замер производительности на облачном провейдере для повторяемости

Модель | OpenVINO, FPS | CV OpenVINO | PyTorch (GPU), FPS | CV PyTorch |
|---|---|---|---|---|
| 27,69 | 1,56% | 101,99 | 4,15% |
| 14,39 | 0,69% | 58,53 | 1,55% |
| 32,12 | 0,99% | 73,10 | 1,55% |
| 38,93 | 1,51% | 79,84 | 2,33% |
| 39,48 | 0,84% | 91,45 | 3,17% |
| 55,09 | 1,33% | 89,09 | 4,24% |
| 36,83 | 0,76% | 97,71 | 0,92% |
| 51,47 | 2,60% | 94,62 | 2,64% |
CV — коэффициент вариации FPS между повторными запусками. Чем он ниже, тем стабильнее результаты замеров.