Мы разрабатываем Macroscop почти десять лет. И за это время в разработке интеллектуальных модулей сложился уклад очень основательного и серьезного подхода к созданию новых функций. С одной стороны, это очень хорошо. Серьезность намерений ходит близко с высоким качеством продукта. Но в то же время, основательность может граничить с неповоротливостью и неоперативностью процесса.
Еще пару лет назад, когда к нам приходили запросы от пользователей на разработку чего-то новенького (не входящего в генеральный план развития продукта), мы долго прогнозировали сроки, оценивали универсальность и востребованность функции у широкого круга пользователей. И часто либо отказывали, либо оценивали сроки реализации как очень долгие. Но однажды нам пришел запрос для крупного проекта. В случае успешной и быстрой реализации недостающей пользователю функции, перспективы и масштабы внедрения Macroscop были очень хорошими. И мы взялись попробовать! У нас были жесткие временные рамки, отзывчивый и готовый помогать пользователь и полная свобода действий.
И… все получилось!
Мы сделали новую функцию в короткий срок. К тому же она была точной и быстрой. Все остались довольны: пользователь получил заветный интеллектуальный модуль, разработчики получили крутой опыт, компания — продажи.
Эта практика положила начало новому подходу к разработке интеллектуальных функций в Macroscop: мы стали чаще и легче идти навстречу своим пользователям. И это дает свои результаты.
Самое важное – выявить реальную потребность пользователя и вместе с ним четко сформулировать задачу. Когда речь идет о быстрой разработке функций «на заказ» (дальше мы будем называть их быстрыми функциями), надо обязательно оговаривать требования: что пользователь хочет видеть в итоге, на что делать акценты. Потому что, условно, одним надо, чтобы в первую очередь точно работало, другим – чтобы круто выглядело. Когда мы беремся за быструю функцию, речь не идет о том, что в итоге она будет работать в любых условиях с 100%- точностью. Мы беремся проверить саму идею и пробуем создать для начала что-то адекватно работающее и приемлемое для использования на одном конкретном объекте. А уже потом в случае успеха дорабатываем и доводим до универсального продукта с хорошими показателями.
Когда постановка задачи и приоритеты ясны, мы беремся за разработку. В течение короткого времени разрабатываем прототип, который пользователь уже может оценить. И даем его в тест. Если то, что у нас получилось, коррелирует с тем, что надо пользователю, и в общих чертах ему нравится, а метод, который мы применили в разработке еще не исчерпал себя и имеет перспективы для улучшения функции, мы идем дальше. Если же получилось совсем не то и совсем не так, мы закрываем проект. И так как это происходит на ранней стадии, почти ничего не теряем.
При таком подходе разработчики и пользователи должны максимально пойти навстречу друг другу. От пользователя тоже требуется включение в процесс: надо тщательно протестировать прототип на разных камерах и в разных условиях, попробовать разные настройки и нажать разные кнопки, дать исчерпывающую обратную связь: что удобно, что не удобно, что работает не так, как он оценивает точность, насколько грузится сервер и т.п.
Изначально мы удовлетворяем потребность одного конкретного заказчика, но еще до начала работы прикидываем, насколько универсальной может стать эта функция в будущем, скольким людям она сможет помочь в решении их задач. И в дальнейшем адаптируем быструю функцию таким образом, чтобы она была полезна и применима в как можно большем количестве видеосистем.
Первой быстрой функцией для нас стал модуль подсчета людей в очереди. Вообще, он был у нас и раньше, но условия применимости были ограничены: модуль работал только в одной проекции, когда камера смотрела строго сверху вниз. Однажды к нам обратился пользователь, которому нужно было считать людей в очереди при принципиально других условиях — когда камера смотрит на очередь по диагонали (прямо и чуть сверху).
в таком ракурсе модуль Macroscop считать умел
а в таком — научился
Ему всем нравился Macroscop, но не хватало заветной функции. Проект был очень перспективным, а пользователь готов был всячески с нами сотрудничать, лишь бы такой модуль появился, и ПО можно было установить на объект. Мы решили не упускать возможность, и приступили к разработке.
В прошлой вариации модуля задача подсчета людей решалась классическими методами компьютерного зрения, что накладывало серьезные ограничения на условия применения. Но в рамках новой задачи модуль должен был научиться считать людей в принципиально других, значительно более сложных условиях.
Группа разработки интеллектуальных функций разделилась на 3 подгруппы, и каждая стала пробовать свой метод. Все они были основаны на применении нейросетей.
Первая — пробовала перенести в модуль для подсчета людей в очередях инфраструктору разработанного нами детектора отсутствия касок (см. Статью о том, как мы попробовали применить современные нейросетевые технологии, чтобы находить каски на головах людей). Этот подход казался очень логичным: детектор касок на определённом этапе работы решает схожую задачу.
Вторая группа пробовала применить регрессионную нейросеть. Она считает количество людей на изображении, но при этом не выделяет конкретные объекты, из-за чего её сложно контролировать. При обучении на регрессионную нейросеть подается картинка и указывается количество человек, которые на ней присутствуют, а нейросеть выдает одно число – сколько человек она нашла. Наполняя выборку новыми изображениями, мы стремились обучить ее считать правильно.
К сожалению, оба метода мы отбросили, так как точность созданного на их основе счетчика была низкой.
Третья группа тестировала один достаточно известный детектор общего назначения, который умеет детектировать разнообразные объекты в режиме реального времени. Он умеет искать тысячу видов разных объектов, но не решать нашу задачу со всеми ее особенностями. Мы доработали этот детектор, обучили его на собственноручно созданной обширной выборке и получили вполне неплохой результат – счетчик людей с приемлемой точностью. Новыми выборками улучшили еще, и в итоге получили прототип, который уже было нестыдно отдать пользователю на тест. И его оценка была… положительной! Он сказал, что в целом решение уже конкурентоспособное, однако точность еще не была высокой – всего 60-70%.
Первая версия счетчика людей в очереди была создана преимущественно с использованием роликов от этого пользователя. Мы решали задачу – чтобы заработало конкретно у него,- но понимали, если обучим нейросеть и сделаем модуль под один конкретный проект, ни о каком дальнейшем масштабировании не может быть речи. Поэтому далее обучение велось на более универсальной выборке, что привело к росту точности даже без глобальных внутренних доработок. Затем мы стали работать над упаковкой модуля – улучшили интерфейс, прикрутили разные настройки, обратили внимание на удобство использования и логику. Параллельно исправили ряд багов в нашем прототипе (кстати, один из них неожиданно ускорил работу модуля в 7 раз), придумали, как снизить потребление ресурсов процессора, подключили работу на видеокарте. В итоге получили объективно хорошо работающий и удобный в управлении модуль, который анализировал быстро, выдавал точные результаты, умел работать на видеокарте, не загружая при этом процессор.
Наш пользователь был просто счастлив! Он поехал ставить новую версию в свои магазины, и подтвердил, что на практике все отлично работает. Нам удалось достичь 85-90%-точности (для ситуаций, когда люди в очереди не перекрывают друг друга полностью, и их можно различить).
Конечно, в процессе разработки не все шло гладко, и, например, между первым прототипом и решением, которое сейчас установлено на объекте, была провальная версия, которая работала хуже предыдущей. Зато на её опыте мы поняли, на что обращать внимание при тестировании, узнали ряд особенностей используемых фреймворков. И учитывая это, сделали классный итоговый модуль, а затем на его основе — еще одну быструю функцию.
Сейчас применение модуля подсчета людей в очереди новой версии расширяется на другие магазины этого пользователя. А финальный вариант — ушел в продакшн и вошел в версию Macroscop, которая готовится к релизу. Кстати, пользователю настолько понравился результат и в целом способ работы, что поступил еще один запрос – сделать детектор пустых полок. И мы снова взялись, и снова сделали (но это уже совсем другая история).
Если подводить какой-то итог, то для сравнения: разработка и доработка старой версии модуля подсчета людей в очереди (4 года назад) заняла около 8 месяцев. Новый модуль мы сделали за 2 месяца (причем первый рабочий прототип передали пользователю уже через 2-3 недели).
Пока это только проба пера и только в рамках одного направления — разработки интеллектуальных функций. В целом же мы придерживаемся более строгого и основательного подхода к развитию продукта — с планированием, многочисленными валидациями идей, анализом востребованности, глубоким тестированием. Что остается неизменным — это практика создания Macroscop (будь то разработка ядра или модулей видеоанализа) в тесном сотрудничестве с пользователями.
Нет уверенности, что подход быстрых функций стоит применять на постоянной основе и в рамках всего отдела, но уже сейчас мы получаем реальный опыт быстрой разработки, а пользователи, для которых это делается, – реальную пользу от продукта.
В любом случае, для себя мы вывели несколько правил, соблюдение которых – половина успеха разработки быстрых функций:
Еще пару лет назад, когда к нам приходили запросы от пользователей на разработку чего-то новенького (не входящего в генеральный план развития продукта), мы долго прогнозировали сроки, оценивали универсальность и востребованность функции у широкого круга пользователей. И часто либо отказывали, либо оценивали сроки реализации как очень долгие. Но однажды нам пришел запрос для крупного проекта. В случае успешной и быстрой реализации недостающей пользователю функции, перспективы и масштабы внедрения Macroscop были очень хорошими. И мы взялись попробовать! У нас были жесткие временные рамки, отзывчивый и готовый помогать пользователь и полная свобода действий.
И… все получилось!
Мы сделали новую функцию в короткий срок. К тому же она была точной и быстрой. Все остались довольны: пользователь получил заветный интеллектуальный модуль, разработчики получили крутой опыт, компания — продажи.
Эта практика положила начало новому подходу к разработке интеллектуальных функций в Macroscop: мы стали чаще и легче идти навстречу своим пользователям. И это дает свои результаты.
Самое важное – выявить реальную потребность пользователя и вместе с ним четко сформулировать задачу. Когда речь идет о быстрой разработке функций «на заказ» (дальше мы будем называть их быстрыми функциями), надо обязательно оговаривать требования: что пользователь хочет видеть в итоге, на что делать акценты. Потому что, условно, одним надо, чтобы в первую очередь точно работало, другим – чтобы круто выглядело. Когда мы беремся за быструю функцию, речь не идет о том, что в итоге она будет работать в любых условиях с 100%- точностью. Мы беремся проверить саму идею и пробуем создать для начала что-то адекватно работающее и приемлемое для использования на одном конкретном объекте. А уже потом в случае успеха дорабатываем и доводим до универсального продукта с хорошими показателями.
Когда постановка задачи и приоритеты ясны, мы беремся за разработку. В течение короткого времени разрабатываем прототип, который пользователь уже может оценить. И даем его в тест. Если то, что у нас получилось, коррелирует с тем, что надо пользователю, и в общих чертах ему нравится, а метод, который мы применили в разработке еще не исчерпал себя и имеет перспективы для улучшения функции, мы идем дальше. Если же получилось совсем не то и совсем не так, мы закрываем проект. И так как это происходит на ранней стадии, почти ничего не теряем.
При таком подходе разработчики и пользователи должны максимально пойти навстречу друг другу. От пользователя тоже требуется включение в процесс: надо тщательно протестировать прототип на разных камерах и в разных условиях, попробовать разные настройки и нажать разные кнопки, дать исчерпывающую обратную связь: что удобно, что не удобно, что работает не так, как он оценивает точность, насколько грузится сервер и т.п.
Изначально мы удовлетворяем потребность одного конкретного заказчика, но еще до начала работы прикидываем, насколько универсальной может стать эта функция в будущем, скольким людям она сможет помочь в решении их задач. И в дальнейшем адаптируем быструю функцию таким образом, чтобы она была полезна и применима в как можно большем количестве видеосистем.
Ты помнишь, как все начиналось?.. (с)
Первой быстрой функцией для нас стал модуль подсчета людей в очереди. Вообще, он был у нас и раньше, но условия применимости были ограничены: модуль работал только в одной проекции, когда камера смотрела строго сверху вниз. Однажды к нам обратился пользователь, которому нужно было считать людей в очереди при принципиально других условиях — когда камера смотрит на очередь по диагонали (прямо и чуть сверху).
в таком ракурсе модуль Macroscop считать умел
а в таком — научился
Ему всем нравился Macroscop, но не хватало заветной функции. Проект был очень перспективным, а пользователь готов был всячески с нами сотрудничать, лишь бы такой модуль появился, и ПО можно было установить на объект. Мы решили не упускать возможность, и приступили к разработке.
В прошлой вариации модуля задача подсчета людей решалась классическими методами компьютерного зрения, что накладывало серьезные ограничения на условия применения. Но в рамках новой задачи модуль должен был научиться считать людей в принципиально других, значительно более сложных условиях.
Группа разработки интеллектуальных функций разделилась на 3 подгруппы, и каждая стала пробовать свой метод. Все они были основаны на применении нейросетей.
Первая — пробовала перенести в модуль для подсчета людей в очередях инфраструктору разработанного нами детектора отсутствия касок (см. Статью о том, как мы попробовали применить современные нейросетевые технологии, чтобы находить каски на головах людей). Этот подход казался очень логичным: детектор касок на определённом этапе работы решает схожую задачу.
Вторая группа пробовала применить регрессионную нейросеть. Она считает количество людей на изображении, но при этом не выделяет конкретные объекты, из-за чего её сложно контролировать. При обучении на регрессионную нейросеть подается картинка и указывается количество человек, которые на ней присутствуют, а нейросеть выдает одно число – сколько человек она нашла. Наполняя выборку новыми изображениями, мы стремились обучить ее считать правильно.
К сожалению, оба метода мы отбросили, так как точность созданного на их основе счетчика была низкой.
Третья группа тестировала один достаточно известный детектор общего назначения, который умеет детектировать разнообразные объекты в режиме реального времени. Он умеет искать тысячу видов разных объектов, но не решать нашу задачу со всеми ее особенностями. Мы доработали этот детектор, обучили его на собственноручно созданной обширной выборке и получили вполне неплохой результат – счетчик людей с приемлемой точностью. Новыми выборками улучшили еще, и в итоге получили прототип, который уже было нестыдно отдать пользователю на тест. И его оценка была… положительной! Он сказал, что в целом решение уже конкурентоспособное, однако точность еще не была высокой – всего 60-70%.
Первая версия счетчика людей в очереди была создана преимущественно с использованием роликов от этого пользователя. Мы решали задачу – чтобы заработало конкретно у него,- но понимали, если обучим нейросеть и сделаем модуль под один конкретный проект, ни о каком дальнейшем масштабировании не может быть речи. Поэтому далее обучение велось на более универсальной выборке, что привело к росту точности даже без глобальных внутренних доработок. Затем мы стали работать над упаковкой модуля – улучшили интерфейс, прикрутили разные настройки, обратили внимание на удобство использования и логику. Параллельно исправили ряд багов в нашем прототипе (кстати, один из них неожиданно ускорил работу модуля в 7 раз), придумали, как снизить потребление ресурсов процессора, подключили работу на видеокарте. В итоге получили объективно хорошо работающий и удобный в управлении модуль, который анализировал быстро, выдавал точные результаты, умел работать на видеокарте, не загружая при этом процессор.
Наш пользователь был просто счастлив! Он поехал ставить новую версию в свои магазины, и подтвердил, что на практике все отлично работает. Нам удалось достичь 85-90%-точности (для ситуаций, когда люди в очереди не перекрывают друг друга полностью, и их можно различить).
Конечно, в процессе разработки не все шло гладко, и, например, между первым прототипом и решением, которое сейчас установлено на объекте, была провальная версия, которая работала хуже предыдущей. Зато на её опыте мы поняли, на что обращать внимание при тестировании, узнали ряд особенностей используемых фреймворков. И учитывая это, сделали классный итоговый модуль, а затем на его основе — еще одну быструю функцию.
Хеппи энд
Сейчас применение модуля подсчета людей в очереди новой версии расширяется на другие магазины этого пользователя. А финальный вариант — ушел в продакшн и вошел в версию Macroscop, которая готовится к релизу. Кстати, пользователю настолько понравился результат и в целом способ работы, что поступил еще один запрос – сделать детектор пустых полок. И мы снова взялись, и снова сделали (но это уже совсем другая история).
Если подводить какой-то итог, то для сравнения: разработка и доработка старой версии модуля подсчета людей в очереди (4 года назад) заняла около 8 месяцев. Новый модуль мы сделали за 2 месяца (причем первый рабочий прототип передали пользователю уже через 2-3 недели).
Пока это только проба пера и только в рамках одного направления — разработки интеллектуальных функций. В целом же мы придерживаемся более строгого и основательного подхода к развитию продукта — с планированием, многочисленными валидациями идей, анализом востребованности, глубоким тестированием. Что остается неизменным — это практика создания Macroscop (будь то разработка ядра или модулей видеоанализа) в тесном сотрудничестве с пользователями.
Нет уверенности, что подход быстрых функций стоит применять на постоянной основе и в рамках всего отдела, но уже сейчас мы получаем реальный опыт быстрой разработки, а пользователи, для которых это делается, – реальную пользу от продукта.
В любом случае, для себя мы вывели несколько правил, соблюдение которых – половина успеха разработки быстрых функций:
- Попробовать пойти навстречу пользователю, но не забывать о собственных целях: браться за проекты, которые можно масштабировать, вкладывать силы в то, что будет полезно в долгосрочной перспективе.
- Докопаться до истинных задач и потребностей пользователя, выявить приоритеты.
- Заручиться поддержкой пользователя. Если он готов активно коммуницировать, тестировать, давать обратную связь и предоставлять необходимые данные (видео с реального объекта, например), то есть все шансы разработать хорошо и быстро.
- Не бояться провала и относиться к нему как к одному из возможных результатов.
- Не стремиться разработать что-то уникальное с нуля, а по возможности использовать существующий опыт: в нашем случае пытаться применять части алгоритмов из уже реализованных модулей. А уже если получившееся решение окажется жизнеспособным — уделять время исследованиям и кастомизации.