Цифровизация тренировок
Итак, у меня есть ученица. В свободное от творчества и просвещения время я — тренер. Создаю из собственной дочери теннисистку вот уже несколько лет.
Вообще-то по жизни я — латентный аналитик. Любую сложную проблему стараюсь вогнать в Excel. Я даже книгу написал для бизнес-аналитиков «Охота на данные».
Поэтому идея оцифровки тренировок пришла в мою восполенную бошечку одновременно с идеей самих тренировок.
Давным-давно, ещё когда сам занимался с тренером, я предпринял первую попытку всю вариативность теннисных ударов переложить в таблицу.
Не смог.
Потом познакомился с родственной душой — другим тренером, который в прошлом был аналитиком коммерческого банка. Сейчас он с помощью Excel тренирует кучу народу в разных точках Москвы. Мы с ним попытались вместе описать такую модель данных.
Чуть не разругались, разбежались.
Сами посудите. Вот открываете вы Excel. Вот форхенд, вот бэкхенд. Задняя линия, half-court, сетка, middle — это между задней линией и half-court. Ваш удар идёт по линии, кроссом или обратным кроссом. Удар плоский, с топспином или слайсом. Скорость, траектория, движение соперника, удар на ходу или из подготовленной позиции... Бр-р-р...
| Измерение | Примеры |
|---|---|
| Удар | FH, BH, slice, drive volley, smash, serve |
| Позиция игрока | baseline, middle, half-court, net |
| Направление | line, cross, reverse cross |
| Вращение | flat, topspin, backspin |
| Источник мяча | ball machine, coach, sparring partner |
| Положение подающего | baseline → net |
| Траектория | обычная, высокая, низкая, короткая |
| Предсказуемость | игрок знает направление → не знает |
| Движение | статично, в сторону, вперёд, назад |
| Вариативность | одинаковый мяч → постоянно меняющийся |
| Контекст | drill, rally, point, match |
Возникает аналогия с древней притчей: кто-то попросил положить на первую клетку шахматной доски одно зёрнышко риса, а на каждую следующую — в два раза больше, чем на предыдущую. До последней клеточки не дошли, потому что всё вокруг было в рисе.
Дело не в том, что мы забыли добавить ещё три поля. По идее, компьютер всё должен стерпеть. Проблема в самой попытке заранее описать пространство всех возможных теннисных ситуаций.
Трекер, который построил я
Когда появился доступный AI, я возрадовался. Ну теперь-то мы этого монстра распланируем и размоделируем. AI поможет придумать правильную модель данных, а ИИ-агенты — быстренько собрать приложение «какое хош».
На каждой тренировке я буду записывать, что именно мы делали, сколько времени длилось упражнение и какой получился результат. Потом из всего этого вырастут графики. Мы будем с интересом смотреть на них вместе с ученицей и принимать какие-то решения.
При любой непонятной ситуации делай PWA. Прекрасный девиз!
Тип удара — отдельная кнопка. Позиция соперника. Позиция играющего. Позиция ученика. Скорость и траектория мяча. Пушка или живой ввод мяча. Всё это было с успехом запрограммировано.
И вот мы выходим на тренировку.
Ученица отлично играет форхенд с задней линии с пушкой. Но когда вместо пушки мячи начинаю вводить я, вроде бы тот же форхенд, та же задняя линия — у неё внезапно всё может развалиться. Потом я сам начинаю двигаться к сетке, и снова меняются вариативность, глубина, скорость, траектория, предсказуемость.
Начинаю что-то подозревать: блин, а как это всё записать в нашем прекрасном, только что навайбкоженном трекере?
Дальше — хлеще. Чтобы имитировать свечу под заднюю линию, нужно задать пушке высокую траекторию, небольшой темп и сильный backspin.
А упражнения?! Два удара форхенд, два — бэкхенд, выход к сетке. Два воллея и, мать его, смеш!
Сегодня я могу добавить поле feeder_position. Завтра выяснится, что важно, знает ли ученица заранее направление следующего мяча. Послезавтра понадобится степень вариативности. Потом — качество предыдущего удара, потому что один и тот же forehand после удобного и после выбивающего мяча — это не один и тот же forehand.
Я тогда назвал это «бегом в мутную неизвестность». Автоматизировать всякие «газпромы» с «лукойлами» было гораздо легче, знаете ли.
Страшно то, что тренировка практически остановилась. Приходилось думать не об ученице, а о классификации упражнений.
Здесь есть ещё одна проблема: «с пушки получается» и «в живой игре получается» — не одно и то же. В спортивной науке существует понятие Representative Learning Design: тренировочная задача должна в достаточной степени воспроизводить информационные и двигательные требования настоящей игры. В небольшом исследовании теннисистов различались даже время начала движения и длительность замаха при приёме мяча от пушки и от реального подающего. А отдельная работа о переносе навыка в теннисе показала, что упражнения разной репрезентативности приводят к разным изменениям поведения игрока.
То есть контекст упражнения — не украшение карточки. Без него мы можем записать «форхенд получился» и потерять главное: где именно он получился и переносится ли этот навык в живую игру.
Хорошо. Тогда долой все кнопки
Дома я принял кардинальное решение: в приложении по конкретной тренировке останутся только текстовые записи.
При совершенно вольной диктовке можно что-то упустить. Есть, например, нюансы вроде разножки, о которой все забывают, хотя она должна отрабатываться фоном.
Но десятки кнопок на корте мне точно не нужны.
Записи могут выглядеть так:
Ученица на задней линии. Я сам ввожу мячи. Сначала стою у задней линии, потом постепенно двигаюсь к сетке. С пушки forehand шёл стабильно, от живого ввода качество резко упало.
Или так:
Ученица на задней линии. Сначала два мяча под forehand, потом два под backhand. Затем она выходит на half-court: два drive volley и один smash.
В этих описаниях будет время, результат и мои оценочные суждения. А структуру из них извлечёт AI.
Раньше цепочка выглядела так:
human → structured form → database → AI
Теперь гипотеза другая:
human → raw observation → AI → structured interpretation → database / analytics
Первичными данными становится человеческое наблюдение. ИИ превращает его в одну из возможных JSON-интерпретаций, которую при желании можно строить задним числом снова и снова.
Понимаете, о чём я? Когда мы шли через форму и забыли предусмотреть обязательное поле — например, величину backspin, — его просто нет.
А если мы просто пишем, что было сделано, свободным текстом, то через полгода можем сказать: слушай, я вспомнил, весь октябрь у нас вообще не было backspin. Возможно, поэтому сейчас нет навыка принимать резаные мячи. И новую информацию можно извлечь из старых наблюдений.
Если же в базе за эти полгода копилось только FH / BH / baseline / скорость 85% / рейтинг 4, недостающий контекст уже умер.
Логика похожа на schema-on-read из инженерии данных: сначала сохранить богатый исходный материал, а структуру накладывать при последующей обработке. Это, конечно, аналогия: Tennis Tracker — не data lake. Microsoft Azure Architecture Center использует schema-on-read именно для описания данных, которые сохраняются ближе к исходному виду и преобразуются позднее.
Задача языковой модели здесь не в том, чтобы поговорить со мной о теннисе. Она должна извлечь структурированные признаки из человеческого рассказа. Такой класс задач действительно существует: например, в работе Learning to Extract Structured Entities Using Language Models языковые модели используются для превращения свободного текста в структурированные сущности и свойства.
И что получается — формы ввода умерли? Да здравствует свободный и беспредельный ИИ?!
Нет. Структура всё равно нужна. Только создаёт её машина после «словоблудий» человека.
От «вертолётного кокпита» к «просто рассказу»
Несколько тренировок подряд я тщетно пытался надиктовывать в приложение всякие истории, в которых фигурировали уровни мощности бол-машины, количество мячей в корзине, соотношение попыток и результатов плюс всяческие импровизации...
Чтобы не отвлекаться сильно на телефон, я болтал в него «как придётся» — без редактирования и шлифовки.
То есть ничего не тегировал. Не выбирал из dropdown. Не заполнял карточку упражнения. Просто «что видел, то и пел».
Моей голубой мечтой было начать получать примерно вот такой джейсончик:
{
"feed_source": "ball_machine",
"ball_machine_power_percent": 60,
"estimated_ball_count": 70,
"strokes": ["forehand", "backhand"],
"target": "soft topspin to half-court",
"backhand_success_rate": 0.3,
"follow_up": ["short angles", "live coach feed", "slice serve preparation"]
}
Точность не нужна. Приблизительные значения могут оставаться приблизительными. Неизвестные — неизвестными. Качество зависит от схемы извлечения и возможности вернуться к первоисточнику.
Но тут пришла Apple и всё испортила
Следующая версия Tennis Tracker должна была стать почти идеальной. Минимум полей. Свободный текст. Диктовка непосредственно во время тренировки.
Но встроенное распознавание iPhone так обработало теннисную терминологию, английские слова и мою разговорную речь, что местами транскрипт перестал быть исходными данными.
Вот реальные примеры:
«поиграли косы и мячи»
«резана высоким реза NOM»
И особенно:
«ну конечно же не лучше получается чем в начале тренировки прогноз непонятных людей на нервничал не получив ударов даже дома нет особо пакет рве и Надя»
Последнюю тарабарщину даже я сам не разобрал. И... кто такая Надя?))
В общем, если использовать нативную диктовку iPhone, вместо тренировки происходит постоянное производство ошибок и их лихорадочное истребление.
До чего мы в итоге докатились
Мы с моими ИИшками дошли до версии Tennis Tracker, в которой на корте вообще не надо транскрибировать речь. Приложение стало «тупым» диктофоном: нажал кнопку → сказал наблюдение → сохранился оригинальный аудиофайл.
Это решило важную проблему предыдущей версии: ошибка распознавания больше не уничтожает первичные данные. Не нравится сегодняшняя расшифровка — через год можно прогнать тот же .m4a другой моделью.
Ну океюшки, аудио мы накапливаем. Кто потом превратит его в нормальный текст?
Пока ехал за дочкой в школу, надиктовал мерзкий тестовый файл секунд на пятьдесят. Не «Здравствуйте, я ваш тренер», а сбивчивая речь с запинками, самоповторами, ээээ, немотивированным переключением с русского на английский и обратно, backhand, forehand, drive volley, с жаргоном и матюгами.
Сначала прогнали через установленный на Mac WhisperNotes. Общий смысл он ловил, переключение языков тоже, но на сложных местах начинал фантазировать. drive volley превратился в drive all the shoulders, а ахуительно хорошо — в толерантное опустительно хорошо.
Выслушав мой ор, Codex установил whisper.cpp и полную неквантованную OpenAI Whisper large-v3 — модель примерно на 3 ГБ — и прогнал тот же самый исходный файл. Эксперимент получился сравнительно честным.
И вот теперь всё стало намного лучше. Локальная модель корректно разобралась с моей белибердой.
Да, модель не справилась с drive volley: получилось drive более. Но смысл записи уже практически не разрушен.
Конечно, занятие теннисом — не заседание суда. Нам не нужна идеальная транскрипция. Всё равно следующим шагом пойдёт большая LLM, которой предстоит понять, что же в действительности происходило на корте.
Поэтому архитектура на данный момент получается простая, как рельса:
корт → оригинальное аудио → распознавание речи → LLM → структура → накопленная история → аналитика
Как только мы поймали оригинальное аудио, всё остальное можно адаптировать до посинения: заменить Whisper, поменять промпт, сменить LLM, придумать новую схему JSON, через год извлечь признаки, о которых сегодня вообще не задумывались.
Но как из разговоров всё-таки вызревает система
Система не будет «брать на веру» произнесённые мною слова. У неё есть уйма контекста.
Она знает даты, время и длительность тренировок. Может вычислять примерную продолжительность каждого упражнения. Знает габариты корта и позиции, которые я называю в диктовке. Не секрет для неё и возраст, и уровень игрока. Знает, сколько мячей помещается в корзину и сколько раз мы её собирали. Если я сказал, что пушка работала пять минут, система может оценить возможное количество подач — но не должна делать вид, что знает их точное число.
Она сможет затем оценивать даже количество шагов ученицы и пройденное ею расстояние.
Получается уже не просто расшифровка, а восстановление события из нескольких типов свидетельств:
| Источник | Что из него можно получить |
|---|---|
| Голос тренера | Цель упражнения, качество выполнения, неожиданные наблюдения |
| Время записи | Начало, конец и приблизительную длительность эпизода |
| История сессии | Последовательность упражнений и переходы между ними |
| Размеры корта | Нормализацию позиций и расстояний |
| Корзина с мячами | Верхнюю границу количества ударов между сборами |
| Параметры пушки | Возможный темп, направление, вращение и повторяемость |
| Данные об игроке | Возраст, уровень, предыдущие тренировки и индивидуальные ориентиры |
AI может сопоставить эти сведения и дать справочку: «Упражнение длилось около пяти минут. При заданном темпе пушки и одной корзине могло быть выполнено примерно 55–70 ударов. Тренер отдельно отметил около 30% удачных backhand. Оценка приблизительная: точного счётчика ударов не было».
Вот здесь и появляется свет в конце тоннеля. Я наконец-то не утыкаюсь в телефон, когда должен смотреть на ученицу.
Что получилось из четырёх подходов
| Подход | Что сохраняет | Что теряет | Работа человека при вводе |
|---|---|---|---|
| Excel | Сравнимые числа | Неизвестный заранее контекст | Высокая |
| Structured Tennis Tracker | Выбранные признаки и часть контекста | Всё, для чего не оказалось поля | Очень высокая на корте |
| Свободный AI-чат | Богатый и понятный контекст | Накопленную структуру без отдельной обработки | Низкая |
| Оригинальное аудио → AI | Богатый исходник и возможность поздней обработки | Требует отдельного распознавания и анализа | Минимальная на корте |
Контрольные работы
Нужно будет ещё протоколировать матчи. Я не говорю про буквальную запись самой игры через какой-нибудь SwingVision. Я снова про голос, который помогает делать заметки, не отрывая взгляда от игры.
Голос сохраняет богатство контекста. AI извлекает признаки. Код считает и интерпретирует результат.
Через условные двадцать четыре тренировки отдельный анализатор должен отвечать не на вопрос «какой средний балл у forehand?», а на гораздо более полезные:
- что мы практически не тренируем;
- какие навыки существуют только с пушкой и исчезают в живой игре;
- где слишком мало вариативности;
- где нужен спарринг, иначе ничего не прокатит;
- когда пора чаще играть на счёт;
- какие упражнения я, как тренер, почему-то постоянно повторяю;
- где остановился прогресс;
- что включить в следующие четыре недели.
Да и за мной присмотр нужен. Я могу увлечься одним навыком, упустив какой-то базовый.
И уж точно не обязательно такой анализатор пихать внутрь приложения на iPhone. Это будет отдельный локальный агент, которому периодически отдаётся накопленная история.
| Что искать | Главный вопрос |
|---|---|
| Дыры | Что мы систематически недотренировываем? |
| Перенос | Работает ли навык вне стерильного упражнения? |
| Чит-коды | Что даёт этому игроку необычно высокий результат? |
Чит-коды в теннисе
Часть моей «авторской методики» — чит-коды.
Однажды я неожиданно нашёл определённую хватку форхенда в комплекте с ощущением протаскивания мяча. Как будто я ловлю мяч в сачок и отправляю его обратно — внешне не сильно. Но происходит такое чудо, в результате которого даже мой тренер не всегда успевал за этим ударом.
Меня самого даже немного смущало, насколько легко мне это давалось и какие разрушительные результаты приносило.
Чит-код — элемент навыка, который конкретному человеку почему-то даётся непропорционально легко и при этом создаёт непропорционально большой эффект.
Я считаю, что при обучении любого игрока нужно найти его чит-коды. У кого-то убийственный backhand именно кроссом. Окей, его надо закрепить, периодически развивать, а если он уже развит — просто вспоминать.
Это нужно ещё и для нормальной психологической базы. Особенно после проигрышей. Все мы человеки и все мы психуем, переживаем, когда проигрываем. Иногда нужно поделать что-то достаточно лёгкое и дать команду собственному организму: ты молодец, ты не всё растерял, у тебя есть чит-коды, которые получаются вообще со свистом.
У разных игроков я видел разные чит-коды. У кого-то получается офигенный drop shot, у кого-то — убийственный drive volley. Кто-то играет slice так, что ты задолбаешься его ударчики выковыривать.
Поэтому будущий AI-анализатор должен искать две противоположности: дыры — чего не хватает для гармоничного развития, и чит-коды — где возникает необычно высокая эффективность.
И ещё одна задача — перенос навыка. То, что получается в упражнении, действительно должно работать против живого игрока.
То есть мы не только задалбываем ученика: вот это у тебя не получается, вот это у тебя не получается, вот это не получается. Нет. Просто строим нормальный процесс:
Закрываем дыры. Проверяем перенос. Выращиваем оружие.
Вот так я прошёл тернистый путь от системы, которая максимально загружала человека вводом странных структурированных данных, к системе, которая старается максимально ничего от человека не требовать.
Теперь на корте моя задача — снова с восхищением смотреть на ученицу, а не обслуживать базу данных.
Источники
- Microsoft Azure Architecture Center. What is a data lake?.
- Wu H. et al. Learning to Extract Structured Entities Using Language Models. EMNLP, 2024.
- Pinder R. A. et al. Representative Learning Design and Functionality of Research and Practice in Sport. Journal of Sport and Exercise Psychology, 2011.
- Krause L. et al. Enhancing skill transfer in tennis using representative learning design. Journal of Sports Sciences, 2019.
- Carboch J., Süss V., Kocib T. Ball machine usage in tennis. Journal of Sports Science & Medicine, 2014.
