Привет, Хабр! Меня зовут Антон Рябых, работаю в Doubletapp и в этой статье расскажу про технические детали применения машинного обучения в проекте HitFactor.

Что такое hit factor? На соревнованиях по практической стрельбе спортсмены быстро перемещаются, меняют магазин и стреляют по разным, в том числе и подвижным мишеням. Hit factor — это результат соревнования, то есть количество набранных очков, деленное на время прохождения. 

Нам рассказали об этом чемпионы мира по практической стрельбе Алена Карелина и Роман Халитов, которым нужно было мобильное приложение для помощи в тренировках. Двигаться экономнее, стрелять быстрее — анализ записи тренировки поможет понять, как сократить время на прохождение упражнения и повысить эффективность.

В проекте требовалось очень точно определять время начала выстрела и время стартового сигнала. Каких-то готовых решений на момент разработки продукта (2019 год) не было. В статье расскажу:

Описание конечного продукта

Клиенты попросили нас разработать приложение для практической стрельбы, которое будет параллельно показывать 2 видео, синхронизировать их по стартовому сигналу, отмечать на таймлайне выстрелы из каждого видео. Это позволит спортсменам видеть, где они совершают лишние движения между выстрелами и тратят время. Также возле видео отображается время совершения каждого выстрела. Интерфейсно в приложении это выглядело так:

Здесь на таймлайне белой вертикальной линией отмечен стартовый сигнал, красными линиями — выстрелы.
Здесь на таймлайне белой вертикальной линией отмечен стартовый сигнал, красными линиями — выстрелы.
А здесь уже 2 видео: они синхронизированы по стартовому сигналу, красные линии снизу таймлайна указывают выстрелы левого видео, красные линии сверху таймлайна — выстрелы правого видео. Дополнительно сбоку есть таблица с моментами выстрелов. Это позволяет спортсменам сравнивать свое выполнение стрелкового упражнения с другими попытками, своими или чужими.
А здесь уже 2 видео: они синхронизированы по стартовому сигналу, красные линии снизу таймлайна указывают выстрелы левого видео, красные линии сверху таймлайна — выстрелы правого видео. Дополнительно сбоку есть таблица с моментами выстрелов. Это позволяет спортсменам сравнивать свое выполнение стрелкового упражнения с другими попытками, своими или чужими.

Подробнее именно с продуктовой точки зрения можно почитать (и посмотреть видео) тут.

Требования к распознаванию: должно работать офлайн на iOS-девайсе, требуется очень высокая точность распознавания времени выстрела (не более 50 ms ошибки).

Работа над решением

Задача решалась только через обработку звука. Решить ее через обработку видео не представлялось возможным, т.к. стрелок может быть вообще не виден (находиться за препятствием). Кроме того, на видео никак не видно стартовый сигнал, т.е. обрабатывать звук потребовалось бы в любом случае.

Для начала поймем, что есть выстрел и что есть стартовый сигнал.

  • Стартовый сигнал — это равномерный звуковой сигнал на частоте примерно 2 kHz, но приборы, дающие сигнал, бывают разные, поэтому и частота может меняться. Продолжительность сигнала — примерно секунда. На площадке звуков, похожих на него, почти нет.

  • Выстрел — громкий звук с некоторой продолжительностью, может быть секунда, может быть дольше, постепенно затухающий. Выстрелов может быть несколько подряд. Нам важно находить именно начало звука выстрела, т.е. момент, когда выстрел был совершен. В том числе, если было несколько выстрелов подряд, нужно найти начало каждого выстрела. Похожих на выстрел звуков на площадке может быть много (любой стук, лязг оружия).

Готовые решения

Как я уже говорил, в 2019 году, когда мы работали над проектом, готовых решений не нашлось — и для задачи распознавания выстрелов, и в целом чтобы находить точный момент начала какого-то конкретного звука на длинной аудиодорожке.

Данные, pt. 1

В начале работы данных было мало — несколько роликов мы получили от клиентов и скачали с YouTube, а также нашли датасеты со звуками выстрелов, но там их было немного и звуки были не изолированы, т.е. в одной дорожке могла быть тишина, потом звук выстрела, снова тишина, и иногда снова выстрел, иногда очередь.

Baseline, решение без ML

В качестве базового решения мы просто попробовали находить выстрелы по громкости и делать отсечку по затуханию. Решение показало себя очень ненадежным:

  • Непросто подобрать порог: иногда, если человек уходит подальше от камеры, звуки становятся сильно тише; разное оружие стреляет с разной громкостью, плюс по-разному звучит в помещении и на улице — единый порог заранее не подобрать.

  • Часто есть много посторонних громких звуков: речь человека с камерой, лязг оружия, выбрасывание обоймы — много ложных срабатываний.

  • Это не помогало искать стартовый сигнал.

Решение с ML

Почти все решения с распознаванием аудио работают поверх спектрограммы, так что и тут обработка аудио быстро перешла в формат работы через спектрограмму. Для справки: спектрограмма — это по сути 2д массив, показывающий, какая была громкость на какой частоте в каждый момент времени.

Пример спектрограммы. Синие области — области тишины, красные — области громкого звука, в данном случае как раз выстрелы. Видно, что они затухают некоторое время.
Пример спектрограммы. Синие области — области тишины, красные — области громкого звука, в данном случае как раз выстрелы. Видно, что они затухают некоторое время.

Спектрограмма получалась через библиотеку librosa.

Видео с выстрелами было немного, но в них самих выстрелов было немало. Плюс были аудиодорожки, где были выстрелы среди тишины.

Решили нарезать звуков выстрелов через программу Audacity, сделать синтетические дорожки, где есть выстрелы поверх, например, говорящего или бегущего человека или поверх музыки, а также есть стартовые сигналы, и на этом обучить сеть.

Первая версия сети

Изначально была опробована следующая архитектура: сеть получала на вход 2д массив размера 9х1025, где 9 — это количество бинов времени (т.е. горизонтальных элементов на графике спектрограммы; один бин — это, например, 0,2 секунды в зависимости от параметров преобразования звука в спектрограмму), а 1025 — количество бинов частоты (вертикальные на графике). Задача сети была предсказать, является ли центральный бин началом выстрела, стартовым сигналом или фоном.

Пример выстрела, короткий график на 15 бинов времени — более наглядный, чем на 9. Опробованы были разные параметры сети, и вход с 9 давал более точный результат.
Пример выстрела, короткий график на 15 бинов времени — более наглядный, чем на 9. Опробованы были разные параметры сети, и вход с 9 давал более точный результат.

Архитектура сети была сверточной, с постепенным снижением вертикальной размерности за счет MaxPooling (размерность по времени не снижалась).

В конце была классификация на три класса: фон, начало выстрела, стартовый сигнал.

Метрики

Для первой архитектуры метрики считались как для задачи классификации конкретного момента времени (т.е. является ли центр началом выстрела) и по цифрам выглядели неплохо: f-мера для выстрелов была примерно 92%.

Результат первой архитектуры 

Она была внедрена в мобильное приложение на iOS и работала в офлайне через CoreML. Каких-то сложностей с внедрением этой архитектуры не было.

Проблемы

  1. Много false positives на реальных роликах: обучение было на синтетических данных, и на них же считались метрики; разметки настоящих видео со стрельб не было. И тут возникли проблемы: звуки, похожие на выстрелы, вроде лязга оружия, воспринимались за ложноположительные результаты.

  2. Низкая точность нахождения начала выстрела: хотя сами выстрелы находились неплохо, время старта не определялось точно. Причина этого, скорее всего, была в том, что, вырезая выстрелы из Audacity, мы не суперточно обрезали их начало — просто потому что инструмент для этого неудобен. В результате сеть научилась детектировать в качестве старта выстрела не совсем старт, а что-то рядом.

  3. Долгое время работы на устройстве: так как для каждого момента времени нужно было прогнать сетку, обработка была долгой: около минуты на 20-секундный ролик.

Данные, pt. 2: инструмент для разметки

Чтобы разобраться с первыми двумя проблемами, было решено собрать больше роликов от клиента и разметить именно их, при этом разметить очень точно. Т.е. в реальной звуковой дорожке знать, где находятся выстрелы, где — стартовый сигнал. Это должно было снизить количество false positives за счет того, что реальные похожие на выстрелы звуки будут присутствовать на аудио и будут размечены как фон, а точность вырастет за счет точной разметки начала выстрела. Также решено было размечать не только начало выстрела, но и последующее затухание.

Клиент в этот момент передал жесткий диск, где была примерно тысяча роликов с тренировок — достаточно много.

Чтобы сделать такую разметку, потребовалось написать свой инструмент разметки. Он работал в браузере, сервер был написан на Flask.

В инструменте одно видео было в двух плеерах, был таймлайн с размеченными выстрелами, звуковая волна, тоже с размеченными выстрелами, и спектрограмма. На спектрограмме или звуковой волне можно было мышкой обвести область и нажать на нужную кнопку на клавиатуре — и тогда она сохранялась как выстрел, стартовый сигнал или похожий на выстрел звук. Из видеоплееров один просто показывал видео, и оно синхронизировалось с таймлайнами, а второй показывал только выделенные области — это позволяло не сбрасывать основной таймлайн при просмотре области.

Результатом разметки был как файл для аудиодорожки с отметками конкретных мест (тут выстрел, тут стартовый сигнал), так и отдельные нарезанные звуки, чтобы впоследствии их можно было использовать для генерации дорожек.

Инструмент был удобен и позволил быстро разметить множество видео.

Разметили мы 30 видео и придумали, как модифицировать архитектуру так, чтобы работа сети была больше похожа на ручную работу по разметке. То есть идея архитектуры возникла, когда мы размечали области на спектрограммах.

Вторая версия сети

Новая архитектура состояла из 1д сверток, за которыми следовала BiLSTM. Один из вариантов архитектуры показан на скриншоте:

1д свертки понижали частотную размерность, учитывая соседние по времени столбики. Временная размерность не менялась до самого конца.

В конце была классификация на 4 класса: 

  • фон;

  • стартовый сигнал;

  • начало выстрела;

  • остальная часть выстрела (затухание).

Начало выстрела помечалось не одним, а пятью подряд идущими элементами. Это должно было чуть упростить для сети сильную несбалансированность классов, не сбивая при этом точность нахождения начала, плюс помочь понимать, что если что-то похоже на начало, то и соседние элементы должны быть началом.

Остальная часть выстрела функционально была не нужна, но логика за ней была в том, что сети проще определить, где начало выстрела, когда она явно умеет находить его продолжение. При этом продолжение выстрела было долгим и такого сильного дисбаланса классов с ним не было.

Первая версия такой сети была обучена на небольшой части размеченных данных — около 30 роликов. После этого она была интегрирована в инструмент для разметки и стала подсказывать и показывать, что считает выстрелом. Это позволило:

  • понимать, насколько хорошо работает сеть на глаз — видеть, что она считает выстрелом, началом выстрела, стартовым сигналом;

  • ускорить разметку — если сеть правильно находила выстрел, достаточно было нажать на него и после нажать на клавишу, сохранявшую выстрел. Если сеть ошибалась, то ее ответ можно было удалить.

Таким образом было в итоге размечено примерно 120 роликов.

Еще столько же было сгенерировано: брались нарезанные звуки выстрелов и стартовых сигналов, вставлялись поверх фоновой дорожки (бег, музыка, подкасты).

На таком датасете была обучена итоговая сеть.

С таким подходом мы решили проблемы предыдущей версии:

  • стало мало false positives, так как сложные звуки из оригинальных дорожек были сохранены и показаны сети;

  • точность детекции начала выстрела стала высокой, так как инструмент позволил очень точно размечать начало выстрела на спектрограмме — его легко увидеть;

  • после портирования на девайс эта сеть работала намного быстрее.

Итоговые метрики

Метрики распознавания выстрелов считались с учётом того, что начало выстрела, определённое сетью, не должно отличаться от реального начала выстрела больше чем на 50 миллисекунд.

  • Точность распознавания составила 99,1%.

  • Полнота распознавания 97,8%.

Точность — вероятность того, что найденный выстрел — действительно выстрел.

Полнота — вероятность того, что существующий выстрел будет найден.

Портирование на iOS

Сеть была портирована на iOS и работала в офлайне. В отличие от предыдущей версии, эта версия работала гораздо быстрее и точнее. Была сложность с тем, что в CoreML было ограничение на длину последовательности в LSTM — если запись была примерно с минуту, появлялась ошибка. Ее решили простым разбиением аудиозаписи на n частей и склейкой ответа.

Также после портирования выяснилось, что модель плохо работает на данных, полученных с отличным от тех, на которых она обучена, sample rate (44 kHz vs 22 kHz), и это несмотря на то, что после получения спектрограммы разницы по идее быть не должно. Поэтому итоговая модель была обучена на разных вариантах sample rate (22 kHz, 44 kHz, 11 kHz). Это позволило ей обобщиться и нормально воспринимать данные с девайса.

Итог 

Мы сделали приложение, которое очень точно находит стартовые сигналы и время выстрелов, и попутно разработали удобный инструмент для разметки звуков в сочетании с видео.

Комментарии (12)


  1. Serge78rus
    00.00.0000 00:00
    +2

    Как я уже говорил, в 2019 году, когда мы работали над проектом, готовых решений не нашлось — и для задачи распознавания выстрелов, и в целом чтобы находить точный момент начала какого-то конкретного звука на длинной аудиодорожке.
    Это же классическая задача оптимального обнаружения сигналов, которой посвящен отдельный раздел радиотехники. Человечество этим занимается уже много десятков лет, как минимум с момента появления радиолокации.


    1. Crash13
      00.00.0000 00:00

      Полезный комментарий.
      Благодарю!

      P.S.: "ушел гуглить". Будет с чем скоротать этот вечер.


  1. pvsur
    00.00.0000 00:00
    +1

    Шпионы там, шпионы здесь..

    Без них ни встать, без них ни сесть.

    Вздохнет француз - извееестно Кардиналу....

    (С) какой-то старый фильм

    Скоро на всех столбах страны. Это про "шепот" и интеграцию с "умным городом". Он тоже умеет определять выстрелы, драки и прочие неспокойные звуки. Щаз, говорят, проходит обкатку в зоне СВО как детектор присутствия дронов.


  1. shadrap
    00.00.0000 00:00
    +1

    как сложно! почему нельзя в оружие встроить блютузно интерфейсный детектор выстрела, у которого не будет ни задержки ни ошибки. Это может быть просто NFC наклейка с акселерометром на приклад, цевье, рукоять и тп и тд ... ну и со стартовым сигналом еще проще... я никогда не видел подобных соревнований, но подозреваю что статистика вполне легитимна для них..


    1. sentimentaltrooper
      00.00.0000 00:00

      Можно. Лучше не блютус конечно. Стрелку надо второй модуль, тогда можно идентифицировать и стрелка и оружие (оружие лучше не трогать). Плюс с помощью микрофонов расположенных в основании мишени можно, теоретически, рассчитать точку попадания по мишени. У нас такой проект был доведен до уровня лабораторных испытаний но потом заказчик свернул проект по экономическим соображениям: стендовая спортивная стрельба происходит относительно медленно и на относительно короткой дистанции - проще налепить камер, и есть альтернативные варианты на пьезо (от израильтян, но там пластина разрушается за несколько сотен выстрелов). Снайперы на 300м и далее - этим в самый раз, но 15к на 1 железку и год разработки прототипа который может быть заработает и дешевле 5к за мишень не будет (нужны хитрые микрофоны) клиент не был готов выложить. Без мишени чисто на акселерометре и носимом модуле интересно для shooting house но им надо full auto, все калибры и т.д. а тогда задержка на обработку начинает набегать. Короче говоря проект мне очень нравился, технически он выполним, но рядовой с биноклем дешевле нескольких лет разработки.


      1. K0styan
        00.00.0000 00:00

        Практическая стрельба как раз посередине между стендовой и совсем уж shooting house-ами. Но там часть мишеней стальные, а часть - картон: не уверен, что на последних от микрофона хоть какой толк будет...


        1. sentimentaltrooper
          00.00.0000 00:00
          +1

          Микрофоны располагаются под мишенью двумя ромбами и по сути рассчитывают траекторию по времени прибытия ударной волны. Материал мишени не важен и может меняться. Потому что обычную стальную дюймовую мишень убивают за 1 день стрельб в этом основная претензия у канадцев к израильскому решению.


  1. hellamps
    00.00.0000 00:00

    есть же 100500 этих таймеров, они тупожелезки, врядли там есть какие-то ML, небось очень задавленный микрофон и триггер по уровню сигнала


    1. K0styan
      00.00.0000 00:00

      Это если к микрофону прямой доступ есть. На смартфоне, подозреваю, можно получить поток только после автоматической регулировки уровня.

      Если же на уже готовой видеозаписи выделять - то того хуже.


  1. SensDj
    00.00.0000 00:00
    +3

    А название-то какое у статьи... Я думал вы научили робота по звуку определять откуда выстрелил снайпер и сразу же туда отвечать массированным автоматическим огнём из миномётов


    1. Radisto
      00.00.0000 00:00

      Где-то видел, что по звуку пролетевшей пули (при условии, что она еще сверхзвуковая) точнее получается. Снайпер обычно далеко и звук выстрела приходит с переотражениями


    1. Oleg_P_7
      00.00.0000 00:00

      Один мой знакомый участвовал в разработке такой системы по заказу оборонки, чтобы оборудовать блокпосты. Даже в поле испытывали. Но война в Чечне закончилась.