Чат-боты на все случаи жизни. 7 инструкций по созданию мини-приложений в Telegram

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

В подборке под катом — семь инструкций по созданию Telegram-ботов с разной функциональностью. Сохраняйте статью в закладки: пригодится как начинающим, так и опытным разработчикам.

Выберите интересующего вас бота:

Паттерны взаимодействия с ботами
Веб-приложение на Node.js и React
Платежная система с Payments
Карманный ChatGPT, которому можно отправлять голосовые сообщения
Бот для генерации сложных паролей
Бот для создания стикеров из сообщений
Бот для мониторинга погоды на Python

Паттерны взаимодействия с ботами


Вы можете усовершенствовать своего Telegram-бота, добавив паттерны взаимодействия с пользователями. Например, заменить командную строку на ключевое слово «Поехали!» или расположить под сообщением кнопку START. О том, как интегрировать эти паттерны в бот, подробно рассказываем в статье.

В процессе разработки мы использовали Python 3.9 и python-telegram-bot — фреймворк на основе обработчика. Ядро получает обновления (Update) от Telegram Bot API и вызывает соответствующий обработчик из списка зарегистрированных. Или игнорирует, если подходящего обработчика нет. В тексте вы найдете наглядную демонстрацию описанной работы.

Перейти к инструкции →

Кнопки InlineKeyboard.

Что вы узнаете из текста?

  • Какие существуют способы взаимодействия с Telegram-ботом.
  • Какие нюансы стоит учитывать в работе с ботами.
  • Как создать обработчик аргументов команды /start или кодовых слов — например, «Поехали!».
  • Как создать кнопки ReplyKeyboard, заменяющие клавиатуру, и InlineKeyboard, располагающиеся под сообщением.

Веб-приложение на Node.js и React


Год назад в Telegram Bot API добавили Web Apps — кастомные страницы, которые открываются внутри переписки с ботом. Сейчас эта функция востребована у большого количества пользователей — от создателей маленьких tg-ботов до коммерческих компаний.

Мы подготовили инструкцию по созданию такого веб-приложения. В ней — пошаговая разработка с JavaScript-кодом, который вы можете использовать в своем tg-боте. В качестве фронтенда использовали React, а для бэкенда — node.js. Готовое веб-приложение будет поддерживать авторизацию пользователя, встроенную оплату через платежные системы, кастомизацию под выбранную пользователем тему и многое другое.

Перейти к инструкции →

Форма обратной связи.

Что вы узнаете из текста?

  • Как сделать форму обратной связи.
  • Как добавить кнопку «купить» и корзину с товарами.
  • Как обработать полученные данные методом sendData.

Платежная система с Payments


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

В статье используем популярную библиотеку на Python — aiogram, предназначенную для создания ботов. И связываем ее с приложением через Telegram Bot API. Дополнительно вы можете выбрать платежную систему. Мессенджер поддерживает оплату более чем в 200 странах через такие шлюзы, как Stripe, YooMoney, Sberbank, LiqPay, PayMaster и другие.

Перейти к инструкции →

Что вы узнаете из текта?

  • Как сгенерировать invoice с деталями платежа.
  • Как подключить возможность оплаты в рублях и долларах.
  • Как настроить платежи с помощью аргумента Pre Checkout Query и Successful Payment.

Карманный ChatGPT, которому можно отправлять голосовые сообщения


Часто сервисы Open AI неудобно использовать на территории России. Приходится каждый раз подключать VPN и писать запросы вручную. Потратить несколько минут на формулировку и еще несколько — на набор текста. Теперь представьте: вы открываете tg-бот, записываете голосовое сообщение и получаете текстовый ответ из ChatGPT. В нашей инструкции рассказываем, как это сделать.

Для настройки рабочего окружения мы использовали Node.js и фреймворк Telegraf. После научили бота обрабатывать голосовые сообщения, получать на них ссылки в формате ogg и преобразовывать в текст. А чтобы бот всегда был на связи, задеплоили его на облачный сервер.

Перейти к инструкции →

Что вы узнаете из текста?

  • Как научить бота фильтровать голосовые сообщения и получать на них ссылки в формате ogg.
  • Как обрабатывать и конвертировать полученные данные из getFileLink в mp3.
  • Как сохранять сообщения и их контексты для каждого пользователя.

Бот для генерации сложных паролей


Чтобы придумать надежный, но запоминающийся пароль, можно взять несколько слов и объединить их в последовательность, а после — добавить специальные символы. А можно пойти другим путем. Написать Telegram-бота, который будет генерировать пароли прямо в мессенджере.

Для создания такого бота мы использовали сразу несколько инструментов. Среди них — PyCharm, aiogram и Redis. Далее настроили параметры конфигурации из готового репозитория на GitHub. По умолчанию вы можете выбрать любой из трех пресетов для генерации пароля. Если в какой-то момент их станет недостаточно, вы всегда можете добавить новые или изменить существующие в XKCD-password-generator.

Перейти к инструкции →

Что вы узнаете из текста?

  • Как создать новую конфигурацию бота.
  • Как изменить готовые пресеты в XKCD.
  • Как сделать бота, если у вас не PyCharm.

Бот для создания стикеров из сообщений


Было ли у вас такое, что какое-то сообщение друга в Telegram хотелось отправлять впоследствии в виде стикера? С этой инструкции вы можете создать с друзьями или коллегами свой личный «фонд золотых цитат». Концепт идеи прост: пользователь пересылает сообщение в диалог с ботом и получает готовый стикер. Без лишних действий и стороннего софта.

Для разработки мы выбрали Python версии 3.8. Нарисовали шаблон стикера с помощью библиотеки Pillow и продумали уникальные имена для набора. С последним оказалось не все так просто. Если использовать имя бота в уникальном имени набора, то любой может получить к нему доступ. Поэтому в качестве уникального имени выбрали скрытый UUID в Telegram.

Перейти к инструкции →

Что вы узнаете из текста?

  • Как исправить «особенность» с уникальным именем набора.
  • Какие ограничение мессенджер накладывает на стикеры.
  • Как разработать бота без БД и промежуточных файлов.

Бот для мониторинга погоды на Python


Классика бот-разработки! Задача отлично подойдет для знакомства с Telegram-ботами и первого пет-проекта. В результате получите карманного синоптика, который будет отправлять вам данные о погоде в любом городе.

В статье детально рассмотрим работу с API с помощью токенов для OpenWeather и Telegram-бота. Приступим к парсингу ответов в формате JSON: заберем данные о городе и температуре, влажности, давлении и скорости ветра. А после — задеплоим на облачный сервер. И все это на асинхронной библиотеке aiogram.

Перейти к инструкции →

Что вы узнаете из текста?

  • Как добавить разные эмодзи в зависимости от погоды.
  • Какие форматы данных поддерживает «карманный синоптик».
  • Как научить бота обрабатывать города через API и возвращать данные о погоде.

Какой бот вам показался наиболее полезным? Или, может, вы хотите новый текст про бота с определенной функциональностью? Пишите в комментариях!


ссылка на оригинал статьи https://habr.com/ru/companies/selectel/articles/751930/

Тед Лассо — идеальная модель поведения менеджера по тестированию? Или же плохой пример для подражания?

Постер с сайта https://www.apple.com/

Постер с сайта https://www.apple.com/

Привет, Хабр! Меня зовут Глеб Боос, уже много лет работаю в области QA и прошел путь от начинающего тестировщика до руководителя на разных уровнях. За время работы я попробовал себя в различных ролях, включая тим-лида, тест-менеджера, руководителя проектов, ресурсного менеджера, руководителя отдела тестирования и руководителя центра компетенций. Благодаря этому опыту у меня была возможность тесно взаимодействовать со смежными отделами, такими как HR, маркетинг, продажи, закупки и финансы.

В своем стремлении внедрять эффективные процессы и улучшать коммуникацию как внутри команд, так и между проектами/отделами, я всегда активно изучал разнообразные источники информации. Книги PMBOK и P2M, статьи на Habr, видеозаписи конференций на YouTube, практические занятия и тренинги, а также общение с профессионалами помогали мне формировать представление о том, как создать эффективную рабочую среду для проектов и команд. Однако недавно я открыл для себя необычный исходный материал для анализа и обучения — сериал «Тед Лассо» от Джейсона Судейкиса и Билла Лоуренса, создателей известного сериала «Клиника».

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

Внимание! Далее пойдут спойлеры, которые будут касаться первых двух сезонов сериала.

Давайте немного поговорим вообще о том, о чем этот сериал. Комедия рассказывает историю американского футбольного тренера по имени Тед Лассо, который получает предложение возглавить футбольную команду в Англии, не имея опыта в этой области. В ходе фильма мы наблюдаем за его необычными методами тренировок и подходами к управлению командой. Главный герой демонстрирует позитивный настрой, развитый эмоциональный интеллект и способность находить общий язык с игроками, что помогает ему преодолевать трудности. Помимо футбола, в сериале затрагиваются вопросы личного роста, межличностных отношений и значимость коллективной работы. «Тед Лассо» представляет смешение комедийных и драматических элементов, создавая вдохновляющую и проникновенную историю.

Кто такой Тэд Лассо?

Кадр из сериала Ted Lasso (https://www.apple.com)

Кадр из сериала Ted Lasso (https://www.apple.com)

В сериале, созданном Джейсоном Судейкисом и Биллом Лоуренсом, Тед становится не только главным героем, но и примером для подражания. Он привлекает внимание своим неподражаемым стилем и светлым характером. Этот тренер, не имеющий опыта в футболе, принимает вызов возглавить футбольную команду в Англии. Вместе с американским оптимизмом и улыбкой на лице, Тед Лассо принимает каждый день как новое приключение. Он сталкивается с трудностями, но никогда не теряет веры в свою команду и в себя.

Тед Лассо  умеет находить общий язык с игроками, создавая дружескую и поддерживающую атмосферу. Он не стремится быть типичным тренером-диктатором, а ставит на первое место взаимоотношения и личностный рост своих игроков.

Качества характера Тэда Лассо, которые говорят о нем, как о хорошем менеджере

Давайте рассмотрим основные менеджерские качества Тэда Лассо и попробуем экстраполировать их на роль менеджера по тестированию.

  1. Позитивный настрой: Тед Лассо всегда поддерживает здоровую атмосферу и вдохновляет свою команду на достижение высоких результатов. В реалиях жизни команды тестирования стресс, вечная гонкка за сроками и попытками уложить регрессионное тестирование в последние полчаса до релиза риск повышения токсичности и раздражительности  взлетает до небес. Учитывая вышесказанное, умение QA-менеджера поддерживать позитивный настрой даже в самые непростые и сложные для команды времена ценится превыше всего.

  2. Эмоциональный интеллект: главный герой обладает умением понимать и учитывать эмоции и потребности своих игроков. Он строит доверительные отношения и находит подход к каждому члену команды. В управлении тестированием ПО эмоциональный интеллект позволяет менеджеру понимать потребности и проблемы сотрудников, адаптировать подходы и создавать поддерживающую и продуктивную рабочую среду. У каждого тестировщика в команде может быть свой мотиватор, у каждого свои сильные/слабые стороны, у каждого есть свой талант. При индивидуальной проработке с каждым сотрудником всех этих моментов итоговый результат может получиться вполне себе хорошим.

  3. Коммуникационные навыки: Тед Лассо владеет искусством эффективной коммуникации. Он умеет слушать и проявляет гибкость в общении, а также ясно и понятно выражает свои идеи и ожидания. В менеджменте тестирования ПО хорошие коммуникационные навыки помогают менеджеру эффективно передавать требования, ожидания и инструкции, а также обеспечивать открытое и результативное взаимодействие с командой и со смежными подразделениями. Найти правильный ключик к бизнесу, который часто меняет требования или к команде разработки, которые затягивают сроки своего этапа (а дата дедлайна при этом никудане двигается) — это тот самый скилл, который является жизненно необходимым.

  4. Гибкость и адаптивность: Тед способен быстро адаптироваться к изменяющимся обстоятельствам и принимать нестандартные решения. В менеджменте тестирования ПО гибкость и адаптивность позволяют  эффективно реагировать на изменения в проекте, распределять ресурсы и принимать решения, обеспечивая выполнение задач в срок и с высоким качеством.

  5. Толерантность и уважение: Тед Лассо уважительно относится к каждому члену своей команды, создавая атмосферу взаимного доверия и поддержки. В менеджменте тестирования ПО толерантность и уважение позволяют создать команду, где каждый сотрудник чувствует себя ценным и вносит свой вклад в успех проекта. На самом деле тут и добавить больше нечего, так как даже в самых конфликтных ситуациях взаимоуважение всегда ценится больше, чем что либо.

  6. Доверие и делегирование: тренер доверяет своей команде и умеет делегировать ответственность. Он позволяет сотрудникам проявить свои сильные стороны и развиваться, создавая атмосферу самостоятельности и ответственности. В менеджменте тестирования ПО доверие и делегирование помогают менеджеру распределить задачи и ответственность в команде, развивая у сотрудников навыки и повышая эффективность работы. Здесь можно выделить еще два отдельных пункта: умение не вмешиваться там, где ситуация того не требует, а также умение делегировать компетенции и обязанности так, чтобы закрыть свои слабые стороны. В первом случае очень важно не пытаться разрешить (напрямую) абсолютно все конфликты испорные ситуации, которые могут возникнуть в работе, ведь многие из членов команды могут быть вполне самостоятельными, чтобы разрешить эту проблему без участия менеджера. Во втором случае нужно понимать, что чем больше у менеджера становится управленческих задач, то тем сложнее ему держать в тонусе свою техническую квалификацию. Именно для этого необходимо уметь распределять компетенции и не стараться угнаться за всем подряд, ведь рано или поздно это может привести к перегрузке и потере контроля.

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

  8. Человечность: Тед Лассо не является каким-то особенным, он ровно такой же человек, как и все мы. Порой это особенно важно в осознании того, как правильно вести коммуникации не только по отношению к своим подчиненным, ведь это работает в обе стороны. Если вы работаете в роли менеджера QA, то вспомните моменты, когда на вас может давить руководитель/заказчик/бизнес, когда задачу нужно выкатить на бой уже завтра утром, а тебе передали новый функционал на тестирование вот только что. А какие-нибудь кейсы из разряда того, что кто-то из твоих сотрудников пропустил баг на боевой контур? Или вдруг начинающий стажер снес всю тестовую базу, которую вы так бережно готовили для предстоящего регресса, а вернуть ее в полноценном виде уже не получится. Все это эмоциональным катком может проходить по менеджеру, который вполне может испытывать человеческие чувства/стресс/панику/раздражительность, которые могут повлиять на конечный результат.

Тед Лассо — менеджер мечты?

К сожалению или к счастью, это не совсем так. Стоит делать скидку на то, что сериал не является документальным, а, значит, возведение наивности и абсурдности в абсолют в той или иной ситуации сопровождает фильм на протяжении всех серий. Поэтому те или иные «скиллы» Тэда, которые открыто показывают его некомпетентность, далеко не всегда отражаются на развитии сюжета. Но ведь все мы знаем, что идеальных менеджеров не существует, верно? Давайте попробуем рассмотреть квалификационные провалы Тэда Лассо и аналогичным образом отразить это через призму способностей менеджера по тестированию.

  1. Отсутствие стратегии: Тренер Тед Лассо не придерживается четкой стратегии и плана действий, что приводит к хаотичности и неопределенности. В менеджменте по обеспечению качества ПО отсутствие стратегии может привести к неорганизованности в процессе тестирования, неясности в задачах и сроках выполнения. В самом лучшем случае это может привести к тому, что команда допустит на продуктив Major или Critical баги, а в худшем… Дисбаланс в распределении обязанностей, отсутствие четкого понимания объемов и сроков, некачественный тест-дизайн, отсутствие четкой преоритизации могут привести к катастрофе, выраженной в колоссальных убытках для бизнеса.

  2. Низкая компетентность: Тед Лассо не обладает достаточными знаниями и навыками в тренерской деятельности (по европейскому футболу), что затрудняет его способность эффективно руководить командой. В менеджменте тестирования ПО низкая компетентность менеджера может привести к неправильному планированию ресурсов, неэффективному распределению задач и недостаточной поддержке сотрудников. Однако, стоит учесть, что менеджеру QA не обязательно совмещать свои обязанности с ролью тех-лида команды, эту задачу может решить делегирование обязанностей. Но можете ли вы представить себе менеджера по тестированию, который не имеет представление о том, что такое регрессионное тестирование? Или о том, что такое тест-план? Или не знать основной инструментарий, который может использоваться для тестирования ПО?

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

  4. Дисбаланс в развитии команды: и хотя Тед Лассо довольно много времени уделяет моральному и личностному развитию команды, однако, практически не занимается ее техническим развитием. Здесь снова можно сделать отсылку к «делегированию», о котором шла речь раньше, но все же сложно не заметить тот факт, что главный герой не занимается ни своим развитием в «тех.части», ни развитием команды в этом направлении. Перенося это в реалии тестирования, представьте себе такой кейс:  для того, чтобы протестировать какой-то новый функционал команде необходимо изучить новый стек технологий/СУБД/архитектуру, а ваш менеджер вместо этого больше концентрируется на том, что вы большой молодец и обязательно справитесь с этой задачей.

  5. Чрезмерная мягкость: Тед Лассо представляет собой очень мягкого и тактичного менеджера, крайне редко раскрывая другую свою человечную сторону, которая способна на какой-то прессинг и агрессию. В условиях сериала это работает успешно, но, к сожалению, в реальной жизни не все вопросы можно решить тактичным подходом. Лично я не являюсь сторонником жестких и тоталитарных подходов, но умение в нужный момент жестко отстоять свою точку зрения и, при необходимости, сбавить свой привычный уровень лояльности порой может привести к более высоким результатам. Излишняя токсичность сотрудника, нежелание им выполнять свои обязанности, отсутствие взаимопонимания со стороны смежных отделов и другие конфликтные ситуации в реальной жизни далеко не всегда можно решить тактичными переговорами.

Какой итог?

Если мы хотим построить красивый и удобный европейский город, мы берем примеры с Амстердама, Барселоны, Парижа, Лондона. Если мы хотим снять хороший фильм, то мы берем пример с культовых Кубрика, Тарковского или Уэса Андесона. Мы стараемся брать лучшее и ориентироваться на это в построении наших проектов, процессов или команд. Ровно также, опираясь на этот анализ, можно сделать вывод, что лучшие качества Теда Лассо являются отличным ориентиром для менеджера, который хочет выстроить эффективные коммуникации, поддерживать высокий уровень мотивации сотрудников, развивать команду и быть источником вдохновения для них. Также стоит обратить внимание на его способность преодолевать трудности, сохранять позитивный настрой и создавать дружескую и поддерживающую атмосферу. Применение этих качеств в работе менеджера по тестированию ПО поможет улучшить командную динамику, повысить качество работы и достичь успешных результатов в проектах.

Важно помнить, что каждый менеджер должен адаптировать и применять эти качества в соответствии с конкретными потребностями и особенностями своей команды и проектов. Ну и, само собой, необходимо делать анализ «мало-профессиональных» качеств персонажа, чтобы брать на вооружение потенциальные проблемы, которые могут всплыть в процессе управления, и находить подходы для их проработки и минимизации.

Эту статью я хочу закончить цитатой одного из главных героев: «Хороший учитель ждёт, что ты пойдёшь вперёд. Отличный — знает, что так и будет.»

Кадр из сериала Ted Lasso (https://www.apple.com)

Кадр из сериала Ted Lasso (https://www.apple.com)


ссылка на оригинал статьи https://habr.com/ru/companies/inpglobal/articles/751974/

PostgreSQL: обходим подводные камни при миграции

Привет! Меня зовут Антон Казачков, я специалист вендорской поддержки СУБД Platform V Pangolin. Это реляционная СУБД уровня enterprise, которая разработана в СберТехе на основе PostgreSQL и доработана до корпоративного уровня надёжности и производительности. Сегодня Pangolin — целевая СУБД Сбера, основа для миграции всех существующих приложений и разработки новых.

Расскажу о ситуациях, с которыми сталкиваются команды на последних этапах миграции СУБД и в начале производственного цикла. На Хабре часто обсуждаются нюансы переезда СУБД из разных проприетарных версий на PostgreSQL. А вот информации о том, как вводить базы в эксплуатацию и выявлять производственные «болячки», на мой взгляд, не так много.

Миграция на финишной прямой — что может пойти не так?

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

Предположим, миграция данных состоялась, система запущена в рабочем профиле нагрузки, но что-то идёт не так. Как понять, что именно? С одной стороны, есть мониторинг, по которому можно собрать анамнез: время и симптомы проблемы. С другой стороны, в системах мониторинга, как правило, отсутствуют запросы к БД и затраты на их исполнение. Поэтому одного мониторинга для решения проблемы недостаточно.

Какие варианты решения существуют:

  • оперативный анализ статистических представлений через, например, pgcenter Алексея Лесовского;

  • включение auto_explain и анализ логов;

  • инструментарий ОС: perf record, atop, strace, ltrace и пр.

Как видите, большинство инструментов либо представляют собой средства оперативного вмешательства, либо требуют специальных знаний. Исключением являются логи БД, но в них может находиться чувствительная информация. Кроме того, включение логирования запросов — не бесплатная операция, она приводит к перерасходу места в ОС и некоторым затратам СУБД на формирование лога. А анализ статистических представлений и вовсе зависит от работы СУБД, не имеет временных отметок и не применим для postmortem.

Решение: создаём performance insight и pg_profile, инструменты для выявления проблем производительности

До последнего времени анализ проблем производительности у нас основывался на изучении логов СУБД, применении explain к подозрительным запросам и подборе параметров для explain, при которых проявлялась проблема. Трудности начинались при вычислении затрат на исполнение запроса и конкурентных блокировок.

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

  1. Семплировать статистические метрики из pg_stat_activity и pg_locks c заданной периодичностью по всем БД экземпляра.

  2. Сохранять определённое количество семплов в памяти, периодически сбрасывая их на диск. Глубина хранения на диске должна быть ограничена.

  3. При завершении работы постмастера, в том числе аварийной, сбрасывать несохранённые данные на диск.

  4. При старте постмастера считывать определённое количество последних семплов в память.

Что ещё было важно:

  1. Прозрачная работа для СУБД с минимальным влиянием на систему.

  2. Способность работать на всех нодах кластера, включая ведомые при физической репликации.

  3. Работа инструмента не должна менять профиль нагрузки БД (включая блокировки).

  4. Независимость от сетевой доступности.

  5. Пользовательские интерфейсы — SQL (пользователи могут не иметь доступа к консоли ОС).

  6. Умение строить красивые отчеты в HTML 😊

Уже существующие аналогичные инструменты не подошли по ряду причин, среди которых — несовместимость лицензий, или, скажем, отсутствие сохранения состояния на момент аварийного завершения сервиса в том же pgsentinel.

Инструмент решили назвать performance insight. Мы сделали его в формате фонового процесса, с хранением данных в отдельном каталоге $PGDATA.

Интерфейсы:

Объект

Описание

FUNCTION pg_stat_get_activity_history

Снимок pg_stat_activity с отметками времени семплирования

FUNCTION pg_stat_get_activity_history_last

Последний снятый снимок pg_stat_activity

FUNCTION pg_lock_status_history

Снимок pg_locks с отметками времени семплирования

FUNCTION pg_stat_get_activity_and_lock_status_history

Снимок pg_stat_activity и pg_locks с отметками времени семплирования

FUNCTION pg_stat_activity_history_reset

Сброс статистики

FUNCTION pg_stat_activity_and_lock_status_history_report

Отчёт в HTML за заданный период

В отчёте можно увидеть:

  • топ ожидаемых событий;

  • топ ожидаемых блокировок;

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

  • топ выполняемых запросов, находящихся в процессе ожидания событий, с топом ожидаемых событий;

  • тексты запросов;

  • топ сессий, ожидающих завершения некоторого события;

  • топ сессий, ожидающих снятия блокировки;

  • топ событий ожидания и блокировок, с разбивкой по интервалам времени.

Пример отчёта (заголовок и топ ожидаемых событий):

Инструмент получился весьма требовательным к памяти, поэтому идентификаторы объектов из представлений pg_locks и pg_stat_activity мы решили не расшифровывать. Пессимистичный бюджет RAM на один семпл занимает около 9Мб.

В чём это помогло:

  1. Поиск взаимоблокировок.

  2. Оценка блокировок по временным интервалам.

  3. Суммарные блокировки, вызванные запросом.

  4. Оценка длительности операций обслуживания СУБД в периоды минимальной нагрузки.

В чём это не помогло:

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

Не блокировками едиными

При анализе работы БД хорошо бы ещё понять, как используются объекты в базе данных, и выявить слабые места. Это особенно актуально при миграции с проприетарных БД с другим диалектом SQL, типами данных и инструментами сопровождения на требовательный к качеству запросов PostgreSQL. После такой миграции надо пройти функциональное, интеграционное и нагрузочное тестирование, а для последнего — рассчитать профиль нагрузки, написать генератор и собрать метрики.

К сожалению, часто бывает, что при нагрузочном тестировании графики выявляют проблему, а причина её непонятна. К нам нередко обращаются за помощью в проведении нагрузочного тестирования, чтобы выявить узкие места или настроить параметры СУБД. Обычно в такой ситуации мы запрашивали:

  • логи СУБД с включённым auto_explain;

  • параметры СУБД и роли (\drds);

  • DDL БД;

  • выгрузки статистики из pg_stat_%.

Включить auto_explain, собрать гигабайты ценной телеметрии и, подхватив ещё двоих коллег, несколько часов смотреть в htop и pgcenter — всё это отнимало время и ресурсы. И мы стали искать решение, чтобы упростить этот процесс.

Хорошим выходом из ситуации могла бы стать передача инструментов профилирования в руки владельцев сервиса. Но к этой работе пришлось бы привлечь не только администраторов ОС или DBA, но и разработчиков, которые, в нашем случае, не имеют удалённого доступа к сервису.

Задача решилась довольно просто. Мы взяли за основу pg_profile Андрея Зубкова и доработали решение:

  1. Послойно ввели свои метрики расчёта размера отношений. Вакуум не всегда успевал обновлять статистику, тем более по отношениям, перестраиваемых pg_repack и pg_squeeze.

  2. Связали pg_stat_statements и pg_stat_kcache. Доработка связана с тем, что pg_profile сбрасывает pg_stat_statements, но не pg_stat_kcache.

  3. Дополнили dblink собственными методами аутентификации без хранения пароля в открытом виде.

В обозримом будущем также планируем убедить pg_profile не сбрасывать pg_stat_statements, так как это плохо влияет на графики.

Итоги из нашей практики

  1. Не забывайте про интеграционное тестирование. Часть выявленных нами проблем касалась работы сторонних сервисов — систем резервного копирования, мониторинга. Это приводило к изменению профиля нагрузки системы.

  2. Если у вас есть хранимая логика в БД — проводите тестирование более тщательно. Существующие инструменты не разворачивают планы выполнения внутри процедур и функций на plpgsql. Мы отлаживали процедуры и функции, используя самые простые методы, RAISE NOTICE и clock_timestamp()-current_timestamp.

  3. Хорошо продумайте партицирование, особенно PARTITION BY HASH. Обосновывайте эффективность партицирования.

  4. Настраивайте max_wal_size. Недостаточное значение может приводить к ненужным контрольным точкам.

  5. Статистика для планировщика может собраться очень неудачно. Ещё недавно эта группа запросов могла работать отлично, а буквально сейчас она «устала» и не показывает объективную картину.

Автор: Антон Казачков (@ilway)


ссылка на оригинал статьи https://habr.com/ru/companies/sberbank/articles/751938/

Оптимизация анимации на Compose: крутим лоадеры, ищем неочевидные функции

Android-разработчик red_mad_robot Серёжа Чумиков рассказывает о том, как сделать классную анимацию, не перегрузив смартфон, почему ей не нужна рекомпозиция и как её избежать.


Некоторые особенности анимации

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

Рекомпозиция UI-дерева может быть затратной операцией и приводить к увеличению потребления и уменьшению заряда батареи.

Composable и Suspend: две группы функций

Есть две группы анимации: более универсальные и более специфичные.

Нужно иметь в виду, что Composable-функции реализованы с помощью Suspend-функций. Чтобы разобраться, в чём разница между этими двумя группами, нужно понять, за что отвечают функции:

  1. animate*AsState — базовая анимации (числа, цвета, рамки, и т. д.). Её используют, когда нужно анимировать переход от одного состояния к другому — например, от синего цвета к красному или от 0 до 100.

  2. AnimatedVisibility — анимация появления/скрытия.

  3. AnimatedContent и Crossfade — анимации перехода от одного контента к другому.

  4. updateTransition — позволяет запускать несколько анимаций одновременно.

А вот функции группы Suspend:

  1. Animatable — расширенная анимация перехода от одного значения к другому (содержит в себе AnimationState), обеспечивает согласованность при отмене и начале новой анимации. У этой функции более богатое API, чем у AnimationState.

  2. AnimationState — простая анимация от одного значения к другому (использует animate) с сохранением промежуточного состояния.

  3. animate — базовая Suspend-функция анимации, выдающая поток готовых анимированных чисел через колбэк без хранения промежуточного состояния. Использует TargetBasedAnimation, DecayAnimation.

  4. TargetBasedAnimation, DecayAnimation — низкоуровневые классы, позволяющие контролировать время выполнения анимации.

Чаще разработчики используют для анимации Composable-функции, которые нельзя назвать универсальными. Их использование без учёта специфики увеличивает число рекомпозиций и повышает энергопотребление.

Composable-функции подходят для простых случаев, когда опорное значение (то, на основе которого строится анимация) меняется достаточно редко. Например, задано первоначальное значение (число, цвет, прозрачность и т. д.) и конечное, до которого идёт анимация. Анимация прошла — все дальнейшие изменения, например нажатие кнопки, будут нескоро или не будут вообще.

Suspend-функции находятся за пределами обновления разметки и прорисовки, их остановка происходит явно, и в конечном итоге это просто функции, выдающие поток чисел. Благодаря этому они оказывают меньшую нагрузку на процессор и аккумулятор. Но для передачи конечных значений анимации в Compose нужно совершить дополнительные действия.

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

Composable-анимация. animateAsState и аналоги

Базовая проблема с Composable-функциями

Функция animateFloatAsState выдаёт последовательный список чисел, а точнее возвращает объект State<Float>.

@Composable fun Header(show: Boolean) {     val myAlpha by animateFloatAsState(target = if (show) 1f else 0f)     Item(modifier = Modifier.alpha(myAlpha)) // До 100 рекомпозиций. Стадия композиции. }

Здесь меняется прозрачность (alpha) элемента Item от 0 до 1 (по умолчанию шаг 0,01). 0 — абсолютная прозрачность, 1 — полная видимость.

animateFloatAsState возвращает State<Float>. При чтении из делегата, который находится внутри Compose-функции, происходит рекомпозиция Compose-функции (Header) с новым вещественным числом, а уже затем число передаётся модификатору элемента Item. То есть передача значения модификатору происходит на стадии рекомпозиции.

В качестве альтернативы можно не использовать модификатор alpha. Он нужен для инициализации прозрачности элемента и его редкого изменения на стадии композиции. Вместо этого можно изменять прозрачность на стадии прорисовки:

@Composable fun Header(show: Boolean) {     val myAlpha = animateFloatAsState(target = if (show) 1f else 0f)     Item(                                                   // 1 композиция.         modifier = Modifier.graphicsLayer {             alpha = myAlpha.value                  // Стадия рисования         }     ) }

Если для конечной анимации ошибка в использовании Composable-функций не так критична (она ведь всё равно закончится), то в случае с бесконечной анимацией и рекомпозиция будет идти бесконечно.

@Composable fun Header() {     val transition = rememberInfiniteTransition()     val rotation = transition.animateValue(         initialValue = 0,         targetValue = 360,         typeConverter = Int.VectorConverter,         animationSpec = infiniteRepeatable(tween(DURATION))     )      DrawClock(rotation.value) // Другая функция, которая рисует круг с вращающейся часовой стрелкой. 360 рекомпозиций за поворот. }  private const val DURATION = 2000 // Полный оборот за две секунды

Небольшие хитрости

Иногда можно схитрить и передать State<T> через все Composable-функции до стадии рисования и только после этого использовать результат без опасения. Если попутно нужно преобразовать значение, можно использовать derivedStateOf {}, который преобразует один State в другой.

Внутри лямбды derivedStateOf {} мы задаём алгоритм преобразования значения State<T> в другое значение, после чего результат оборачивается в новый стейт типа State<R>. По смыслу это похоже на функцию map {}.

В примере ниже — из State<Int> в State<Float>.

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

@Composable fun AnimatedProgress(progress: State<Int>) { // 2 рекомпозиции.     // Вместо 100 значений Int только два состояния Float!     val targetAlpha = remember {          derivedStateOf {             if (progress.value > 0) 1f else 0f)         }      }      // Две рекомпозии.     val alpha = animateFloatAsState(target = targetAlpha.value)      // А вот так было бы 100 рекомпозий.     // val alpha = animateFloatAsState(target = if (progress.value > 0))      Canvas {          // Стадия рисования. Получаем множество значений alpha (Float) от 0f до 1f.         doSomething(alpha.value, progress.value)     } }

Иногда ничего не помогает

Например, у нас есть линейный индикатор, отображающий прогресс. Поскольку прогресс может увеличиваться случайно и скачкообразно (допустим, 1, 13, 15, 22, 60, 90, 100), то, чтобы сгладить его, мы принимаем решение — значения прогресса будут пропускаться через функции анимации, которые сами заполнят промежуточные значения между скачками прогресса.

// Рекомпозируется столько раз, сколько изменилось значение progress: вплоть до 100 для Int, или до 10000 для Float, Double (если точность до сотых — от 0.00 до 100.00) fun AnimatedProgress(progress: Int) {     val animatedValue = animateFloatAsState(target = progress)     Canvas {         drawProgress(animatedValue.value)     } }

В результате мы получим рекомпозиции на каждое изменения прогресса, а их может быть очень много. И если индикаторов несколько, это перерастает в серьёзную проблему. Замена progress на State<Int> не поможет — проблема просто перейдёт на строку ниже.

fun AnimatedProgress(progress: State<Int>) {       // Читаем progress.value внутри Compose-функции. Очень много рекомпозиций.     val animatedValue = animateFloatAsState(target = progress.value)     Canvas {         drawProgress(animatedValue.value)     } }

В этом случае выручит Suspend-анимация.

Suspend-анимации. Animatable и AnimationState

Чем меньше рекомпозиции, тем энергоэффективнее будет анимация. А стационарной анимации рекомпозиция не нужна вообще. Но что такое стационарная анимация?

Рекомпозиция в стационарной анимации

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

  1. Анимация показывается всегда или почти всегда, входящие переменные стабильны (State<T>), композиция остаётся без изменений.

  2. Область, в которой происходит прорисовка, находится на одном и том же месте. Размер и местоположение не меняются. Анимация не влияет на «родителей».

  3. Такая анимация не имеет «детей». Её состояние (композиция, размер, местоположение) не влияет ни на кого.

  4. У неё нестандартная прорисовка.

Такую анимацию можно назвать стационарной, или независимой.

В результате оказывается, что если все изменения происходят только на стадии прорисовки, то рекомпозиция должна быть равна нулю. Если такую анимацию делать только с помощью Composable-функций, мы неизбежно получим рекомпозиции, за исключением совсем простых случаев. Благодаря Suspend-функции этого можно избежать.

Как вообще избежать рекомпозиций

Возвращаемся к анимации линейного индикатора прогресса:

@Composable fun AnimatedProgress(progress: State<Float>) {     // При создании Animatable мы нигде не читаем progress.value, поэтому рекомпозиции нет.     val animatedProgress = remember {         Animatable(                 initialValue = 0f,                 typeConverter = Float.VectorConverter,                 visibilityThreshold = 0.01f,                 label = "MyAnimation"         )     }      LaunchedEffect(Unit) {                    // Вне рекомпозиции         snapshotFlow { progress.value } // Чтение (подписка) — вне рекомпозиции             .collect { newValue ->                 animatedProgress.animateTo(newValue, tween(DURATION)) // suspend             }     }      Canvas {         // Здесь уже стадия отрисовки, поэтому можно безопасно читать animatedProgress.value         drawProgress(animatedProgress.value)     } }  private const val DURATION = 100

При том же результате — анимации изменения прогресса Composable-функции нигде не использованы, а подписка на progress.value происходит за пределами стадии рекомпозиции. И если в примере с использованием Composable-функций в зависимости от реализации может быть до 100 или даже 10 000 рекомпозиций, то для примера выше — 0.

Почему об этом почти никто не задумывается? Дело в том, что до определённого момента ясные метрики Compose просто отсутствовали. Сейчас они существуют:

  1. Счётчик рекомпозиций в LayoutInspector из AndroidStudio.

  2. Compose-метрики стабильности, выдаваемые компилятором.

У применяемого выше класса Animatable есть три главных метода:

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

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

  • stop() — остановка анимации на текущем значении.

Есть также свойство value. На самом деле это (by) делегат от State<T> для текущего значения анимации. Поэтому стоит быть осторожнее и не допускать его чтения в Compose-функции, иначе теряется весь смысл использования Suspend-функций и State<T>.

Решение проблем с приостановкой бесконечной анимации

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

@Composable fun AnimatedLoader() {     // Цепляюсь к скоупу функции, чтобы анимация прекратилась при декомпозиции.     val scope = rememberCoroutineScope()       val rotation = remember { Animatable(0, Int.VectorConverter) }     val infiniteSpec = remember { infiniteSpec(tween(DURATION)) }     Column {         Button(onClick = { scope.launch { rotation.animateTo(MAX_ANGLE, infiniteSpec) } })         Button(onClick = { scope.launch { rotation.stop() } })     }      Canvas { drawRoation(smoothProgress) } }  private const val MAX_ANGLE = 360 private const val DURATION = 2000  //  один оборот за две секунды.

Анимацию можно запустить и приостановить по кнопке. Всё будет хорошо до тех пор, пока вы не запустите анимацию снова. Лоадер будет крутиться только часть оборота. Если раньше он полностью проходил путь от 0° до 360°, то теперь — только от части круга до 360°, например от 45° до 360° или от 286° до 360°. То есть от того места, где вы остановили анимацию.

Дело в том, что в момент остановки сдвигается начальная точка анимации: с 0° на тот момент, где вы её остановили.

Простое, но неполное решение — перескок в начало при повторном запуске анимации.

То есть этот вариант:

Button(onClick = { scope.launch { rotation.animateTo(MAX_ANGLE, infiniteSpec) } })

Заменить на этот:

Button(     onClick = {         scope.launch {             rotation.snapTo(0) // Мгновенный переход в начало.             rotation.animateTo(MAX_ANGLE, infiniteSpec)         }     } )

Это даст полный круг, но при остановке и повторном запуске картинка резко перескочит в начало (0), что создаст неприятное ощущение от анимации.

Неприятно, не правда ли?

Неприятно, не правда ли?

Правильное решение — «смещение во времени» в настройках перезапуска анимации. Поскольку результат функции infiniteSpec иммутабельный, мы не будем создавать спецификацию анимации в начале, а перенесём её на момент запуска анимации.

@Compose fun RotationLayout() {     val scope = rememberCoroutineScope()     val rotation = remember { Animatable(0, Int.VectorConverter) }     // Здесь мы сразу не определяем настройки бесконечной анимации (infiniteSpec), они будут ниже     val tweenSpec = remember { tween(DURATION) }     Column {         Button(             onClick = {                 scope.launch {                     // Вычисление прошедшего времени от начала и до момента остановки.                     val millisOffset = rotation.value / MAX_ANGLE * DURATION                     // FastForward — Мгновенное смещение во времени.                     val initialOffset = StartOffset(millisOffset, StartOffsetType.FastForward)                     // Мгновенный переход в начало.                     rotation.snapTo(0)                     // Анимация после мгновенного смещения во времени.                     rotation.animateTo(MAX_ANGLE, infiniteSpec(tweenSpec, initialOffset))                 }             }         )         Button(onClick = { scope.launch { rotation.stop() } })     }      Canvas { drawRoation(rotation.value) } }  private const val MAX_ANGLE = 360 private const val DURATION = 1000

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

Другое дело!

Другое дело!

И как теперь с этим жить

Любой инструмент удобен и эффективен, если применять его в подходящих ситуациях. Это не призыв полностью отказываться от Composable-функций в угоду Suspend-функциям. Следует понимать природу и область применения первых — это компактные простые анимации без частого изменения входящих параметров. Но за это удобство и компактность можно заплатить большую цену, если не смотреть на метрики. Suspend-функции дают больше свободы, но требуют больше труда, поэтому они незаменимы в сложных случаях. Как говорится, выбирай мудро.


Кстати, у нас открыта вакансия старшего android-разработчика.

Над материалом работали:

  • текст — Серёжа Чумиков, Алина Ладыгина,

  • редактура — Виталик Балашов,

  • иллюстрации и анимации — Марина Черникова.

Чтобы ничего не пропустить, следи за развитием цифры вместе с нами:

Да пребудет с тобой сила роботов! 🤖


ссылка на оригинал статьи https://habr.com/ru/companies/redmadrobot/articles/751976/

MiVOLO: новая State-of-the-Art нейросеть с открытым исходным кодом для определения пола и возраста по фотографии

Привет, Хабр!

Хочу рассказать вам нашу историю о том, как изначально рутинная рабочая задача закончилась созданием открытой Пример конечного результата работы нашего движка

Пример конечного результата работы нашего движка

Критическим этапом пайплайна является определение пола и возраста человека на фото. Не обязательно, правда, что он там есть. Например, мы работаем со СберМегаМаркет, где для рекомендаций пользователю ищем визуально похожие предметы одежды, а так же с другими маркетплейсами, и там далеко не во всех карточках одежда находится на модели. Но, чаще всего и в большинстве проектов, человек на фото присутствует, хотя бы частично:

Пример того, как может выглядеть вход в нашей задаче

Пример того, как может выглядеть вход в нашей задаче

И очень нехорошо, если на фото ребёнок, а мы предложили купить взрослую одежду, или, например, перепутали пол. А по самой одежде это очень тяжело понять (хотя мы и так тоже пытаемся, но точность невелика). Кроме того, возраст может в принципе давать ещё какую-то дополнительную информацию, помогающую делать рекомендации качественнее.

Вообще, open source решений для этой задачи довольно много. До создания своего, мы использовали открытую модель FairFace, выбрав её за простоту и близость к нашей задаче. Но, процент ошибок в проме удручал — в разных проектах он составлял от 1 до 10% и более. Соотвественно, это потенциальные 10% случаев, когда рекомендация вообще никак не вкатит пользователю и прибыль будет упущена. Звучит плохо. С другой стороны, мы никогда не занимались лицами профессионально, поэтому старались поступать мудро и надеялись на чужие решения (внутри компании или внешние open source).

Но со временем стало ясно, что терпеть дальше нельзя, а главное, что ничего для нашей задачи радикально более удачного уже и не появится. Дело в том, что визуальный домен у нас слишком широкий: от мыльного селфи до профессиональной студийной съёмки. Условия, фильтры, качество, цвета, размеры, всё совершенно произвольное и без каких-либо допущений. Системы распознавания лиц к таким условиям не адаптированы и не могут быть, а их основе, зачастую, и создают методы определения пола и возраста. Да и в целом, немногие на рынке эту задачу решают. Поэтому, 3 месяца назад мы начали разрабатывать свой подход.

Изначально, мы просто хотели как-то нивелировать существующие ошибки. Амбициозных целей мы себе в этой задаче не ставили, считая её не профильной и вспомогательной.

Ход работ: Создание baseline

Решение от FairFace имеет ряд серьёзных недостатков. Наибольшим изначально виделся их классификационный подход. Во-первых диапазоны разбиты неудачно для нас, во-вторых постоянно всплывали проблемы пограничных случаев, в-третьих, алгоритм не делает различия в ошибке между классами, например, 0-5 лет или 60-70, при тренировке, что никак не могло сказаться благоприятно на способности к генерализации. Тренировочного кода к модели тоже нет.

Задача для timm, огромного репозитория классификационных моделей от Hugging Face, претренированных на больших открытых датасетах.

Мы заменили классификацию на регрессию, с соответствующими изменениями в нужных частях кода. Этого нет в статье, поскольку она посвящена уже конечному решению на трансформере, но на этом этапе нашей основной моделью для экспериментов была чисто свёрточная нейросеть resnext50_32x4d. Она быстрая и хорошо себя зарекомендовала. Кроме того, мы прикрутили к сети Deep Imbalance Regression. Не вдаваясь в детали, эти подходы позволяют существенно компенсировать естественный дисбаланс возраста в данных. Особенно полезной оказалась первая техника, позволяющая посчитать веса для примеров в соответствии с их распределением, которые затем применяют в целевой функции MSE (Mean Squared Error).

Визуализация распределения возрастов в датасете IMDB-clean. Примерно такая же картина в большинстве открытых данных.

Визуализация распределения возрастов в датасете IMDB-clean. Примерно такая же картина в большинстве открытых данных.

Первые же эксперименты показали перспективность подхода, например, мы с полутыка получили CaiT и XCiT, показали себя очень хорошо, решив проблему недостающей мощности. Но, с ними есть уже другая проблема — они не блещут скоростью. А самый первый (исторически из удачных), весьма быстрый и популярный визуальный трансформер ViT не очень бодро сходился. Скорее всего из-за недостатка данных, этот трансформер очень прожорлив и требует огромных объёмов.

Через какое-то количество экспериментов, мы наконец нащупали идеальный вариант. Им стала VOLO. Она сочетает в себе преимущества свёрточных и трансформерных нейросетей. В этой модели, вместо простого нарезания изображения на Актриса Марсия Кросс на 30th Annual Film Independent Spirit Awards (2015), на тот момент ей было 53 года. Это контрольное изображение вызвало у разметчиков большие трудности: средняя ошибка ушла выше 16 лет.

Актриса Марсия Кросс на 30th Annual Film Independent Spirit Awards (2015), на тот момент ей было 53 года. Это контрольное изображение вызвало у разметчиков большие трудности: средняя ошибка ушла выше 16 лет.

Поэтому, мы отправились размечать с нуля на IMDB-clean датасета и знали для них точные ответы. А вот исполнители, напротив, не знали об этом ничего.

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

Суммарно мы собрали около 500 000 изображений из нашего прома и из Open Images Dataset. Часть данных из последнего было решено выложить, чтобы у сообщества исследователей наконец появился по-настоящему сбалансированный регрессионный бенчмарк. Мы сбалансировали его не только в общем, но и по полу внутри 5-летних диапазонов возрастов:

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

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

Что же касается агрегации голосов, то в итоге были перепробованы почти все существующие методы:

Методы агрегации голосов и их точность.

Методы агрегации голосов и их точность.

Мы не указали это в статье, но на самом деле экспериментов было сильно больше, чем в таблице, вплоть до методов машинного обучения. В итоге, с огромным отрывом победило среднее взвешенное. Весами в нём выступали индивидуальные ошибки пользователей:

Тут А - итоговое предсказание от вектора голосов v, а MAE(ui) - личная ошибка i-того пользователя.

Тут А — итоговое предсказание от вектора голосов v, а MAE(ui) — личная ошибка i-того пользователя.

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

MiVOLO: успех выходит из под контроля

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

И тут встал вопрос, как учить сеть делать две задачи (ещё и с мульти-таск выходом) и не растерять всю точность? А желательно бы ещё её и улучшить.

Размер входа у сети весьма скромный — 224х224. Если просто подавать туда весь образ человека — тело с лицом, то нейросеть гарантированно круто просядет в работе по лицам, а это самый надёжный вариант. Увеличивать разрешение? Очень дорого, скорость упадёт катастрофически.

Значит, правильно было бы подавать эти изображения независимо, как два входа в два свёрточных стебля (conv-stem), а потом объединять.

Как выглядят входные изображения тел

Как выглядят входные изображения тел

Большой вопрос как и когда это делать. Если использовать BottleneckAttention, но, в итоге, спроектировали свой собственный модуль.

На изображении можно увидеть основную суть, как мы это решаем. Мы берём сжатые представления от лиц и тел (по сути это уже патчи), выполняем cross-attention сначала в одну сторону, затем в другую, после чего признаки объединяем и сжимаем количество каналов через Общая схема и схема модуля. Можно наблюдать два стебля для двух входов.

Общая схема и схема модуля. Можно наблюдать два стебля для двух входов.

Таким образом, модуль решает три проблемы проблемы разом:

  1. С помощью механизма внимания признаки обогощаются и становятся качественнее

  2. Достигается целевая размерность признаков

  3. Эффективно обрабатываются примеры, когда полезная информация на изображении занимает UTKFace взяли первое место без доп. данных и почти без изменений в тренировке. Больше Adience и на свежем FairFace. Больше побеждать особо и некого — остальные датасеты или тяжело достать (нужно заполнять формы, авторы не очень уже и отвечают и пр.) или слишком маленькие и не интересные.

    Второй сюрприз заключался в точности работы системы на примерах без лиц. Для замера, мы закрыли чёрным квадратом лица в тестовых примерах и были сильно удивлены. Получился MAE 6.66, что лучше, чем человеческая точность с видимым лицом (см. ниже)!

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

    Но итоговая точность не тянет на «хотя бы как-то», она скорее «огого»:

    Результат работы на случайном примере из интернета.

    Результат работы на случайном примере из интернета.
    Результат на примере из начала статьи

    Результат на примере из начала статьи

    Это говорит о том, что модель научилась генерализировать по-настоящему, её понимание задачи выходит за рамки обучающей выборки.

    Все итоговые результаты и бенчмарки можно посмотреть на Papers With Code.

    Кстати, я мало тут говорю про точность пола, т.к. это чуть менее интересная и чуть менее сложная задача, чем возраст, но с ним мы так же взяли первое место на Adience, причём, с совсем уже каким-то космическим отрывом:

    Точность человека в задаче возраста

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

    Распределение точности людей. По оси абсцисс количество пользоваталей со средней ошибкой по оси ординат.

    Распределение точности людей. По оси абсцисс количество пользоваталей со средней ошибкой по оси ординат.

    Получается, что средняя точность 7.22, медиана отличается не сильно из-за формы распределения, близкой к симметричной. Это очень большая ошибка.

    Кстати, лучший индивидуальный результат в левом хвосте — 4.54.

    Интересно, а как оценивают свою точность люди? Я провёл опрос в своих соц. сетях и собрал 105 голосов, попросив оценить, какую среднюю ошибку респонденты ожидают у людей в такой задаче. Вот результат:

    Количество голосов и предсказанная средняя ошибка. Из респондентов 43% ответили абсолютно правильно. Ещё 24% в человечество верят слабо, а 38% точность людей переоценивают :)

    Количество голосов и предсказанная средняя ошибка. Из респондентов 43% ответили абсолютно правильно. Ещё 24% в человечество верят слабо, а 38% точность людей переоценивают 🙂

    Так что самокалибровка у нас довольно неплохая.

    Самый важный вопрос — насколько модель точнее человека? Намного:

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

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

    А самый популярный вопрос, который мне задают про точность людей, это есть ли связь с возрастом разметчика? Это интересный момент. Но оказалось, что Толока не отдаёт эту информацию по API. При этом, в веб версии она есть. Ну, чтож, пришлось расчехлять Сглаженный график средней ошибки к возрасту разметчиков.

    Сглаженный график средней ошибки к возрасту разметчиков.
    Процент ответов с ошибкой менее 6 - условно хороших ответов к возрасту разметчиков.

    Процент ответов с ошибкой менее 6 — условно хороших ответов к возрасту разметчиков.

    Как видно из графиков, связи практически нет. Для разметчиков старше 60 — 65 лет я не стал строить график — слишком мало данных и результаты там «скачут». Единственное, что обращает на себя внимание, это заметно пониженная точность в диапазоне 20 лет. Что это: юношеская нетерпеливость при разметке или особенности восприятия людей старше себя в этом возрасте? Я оставляю предполагать читателю. Особенно с учётом, что пользователи сервиса указывают возраст самостоятельно и его никто не проверяет.

    Ещё, интересным может быть такой вопрос — улучшалась ли точность людей в процессе разметки? Поскольку никакой обратной связи, кроме перманентного бана, не было, крайне маловероятно. Но всё-таки я проверил, вдруг у нас в мозге в таких случаях работает какой-то механизм unsupervised обучения. Однако, чуда не произошло. И даже наоборот — точность, в отсутствие каких-то санкций за небрежную разметку, падала (и в итоге разметчик попадал в бан). Если из этого и следует какой-то вывод, так тот, что в идеале очень желательно давать источник обратной связи. С другой стороны, это довольно трудозатратно реализовать и это всегда вопрос желаемых затрат и профита. В нашем случае овчинка выделки не стоила.

    Послесловие

    Есть много вещей, которые могут серьёзно улучшить модель. Главное, что я бы подчеркнул — это работа с кропом тела. Использованный нами метод достаточно топорный, основанный на простой обрезке через классический Image Processing. Если прикрутить к пайплайну вышедший на днях Fast SAM, позволяющий получать точные маски объектов, то можно добиться серьёзного улучшения. Кроме того, остаются проблемы с предсказанием возраста после ~70, но это решается через данные.

    Напоследок, я хочу отдельно подчеркнуть текущие лимиты модели. Мы создавали её для решения, в первую очередь, проблем нашего бизнеса. Поэтому на некоторых, безусловно интересных, но выходящих за пределы наших целей, задачах она может не отрабатывать. В частности, это игры с гримом и, кхм, прочими вещами. Модель не создана определять условно «реальный» биологический пол человека. Если на человеке, скажем аккуратно так, грим и одежда, «изменяющие» пол, правильным ответом для нас, как бизнеса, будет пол, который задаёт грим, а не «реальный». Философские вопросы мы оставляем философам.

    Та же история с некоторыми фильтрами, например, командный Телеграм канал. Мы там публикуем дополнительные материалы, мнения, новости, детали нашей работы. Если он вам понравится, пожалуйста, подпишитесь! Для нас это очень важно.

    Материалы из этого текста:


ссылка на оригинал статьи https://habr.com/ru/companies/sberdevices/articles/749810/