Один бот против тысяч мемов, или как я создал бота-полицейского для мем-чата
Привет, я Дима Абакумов, разработчик в диджитал-агентстве ДАЛЕЕ. Расскажу, как я написал бота на Python, который находит дубли мемов в нашем мем-чате, и какие методы сравнения изображений для этого использовал.
Как появился кейс?
Есть у нас классный чат с мемами в компании, который используется для поддержания командного духа. Это самая популярная активность в компании: у нас работает 300 человек, а в мем-чате сидит 179 из них. Это уже отдельная субкультура в рамках агентства. Чат появился стихийно в прошлом году, но за короткое время завоевал сердца сотрудников.
Наш HR-менеджер Аня Евсеева объясняет его популярность так:
Секрет один — все любят мемы! Тут каждый может найти единомышленника и скидывать картинки на любимую тему. Любишь мемы с котиками? — круто! Сделал самодельный мем со смешной фотографией с корпоратива? — ещё круче!
Возможно, вы и кликнули на эту статью, потому что в названии было слово «мем».
При этом в чате есть несколько правил:
Кидать можно мемы из интернета.
Можно присылать самодельные, про компанию и команду.
- Но главное — нельзя повторяться, а то ждут репрессии от бан-полиции.
Раньше повторы отслеживались вручную: кто-то скинул мем, ему отвечают, что такое уже было. Но это ещё надо доказать, поэтому ребятам приходилось листать бесконечную историю сообщений, чтобы найти пруфы. «Непорядок», — подумал я, и решил этот процесс автоматизировать. Вот что у меня получилось.
Первая версия — EfficientNet
Писать свою модель и обучать я не стал, это слишком затратно для задачи «напиши бота как можно быстрее», поэтому в первом варианте алгоритма использовал EfficientNet-Lite. Эта свёрточная нейросеть анализирует признаки изображения и задаёт для каждого из них вектор. Для сопоставления изображений используется принцип косинусного сходства.
Как это работает?
Я делаю ресайз изображения до 512×512 пикселей.
Модель вытягивает около двух тысяч признаков изображения и сохраняет их как векторы.
Каждый пиксель нормализуется до 1.
Перевожу изображение в оттенки серого.
- Затем для каждого вектора исходного изображения и изображения из базы вычисляется косинус.
Получившееся значение — это показатель от нуля до единицы, где 0 означает, что изображения вообще не похожи, а 1 — максимально схожи.
Бот определяет сходство новой картинки и мемов из собственной базы и выносит свой вердикт. Оптимальный порог идентичности — 86%. При >86% бот не видел сходства там, где они были, а ниже — считал похожими абсолютно разные картинки.
У первой версии бота было много ложных срабатываний, которые сами стали источником мемов. Когда я только выкатил бота в чат, он вообще все картинки прогонял по базе и выдавал «Такого мема ещё не было», и так на каждую уникальную картинку, сообщений по 40 в день. В чате даже устраивали голосование, стоит ли удалить бота. Я всё починил за полчаса, и над ним смилостивились. Но мемы в истории остались.
Где бот лажал
После того, как бот несколько месяцев проработал в мем-чате, появились ложноположительные и ложноотрицательные срабатывания.
Во-первых, бот плохо распознавал текстовые мемы. На разные скриншоты твитов бот реагировал как на одинаковые картинки, даже если текст не совпадал совершенно. За это доставалось от пользователей — мемами, подколами и даже посылами куда больше.
Во-вторых, бот мог определить идентичные изображения с разным размером или объёмом фона как непохожие. Некоторые участники пытались пользоваться этим лайфхаком: обрезать или уменьшать изображение, чтобы обойти бота. Но в большинстве случаев бан-комитет мем-чата все равно их разоблачал. А ещё упрекал бота в том, что он халатно относится к своим обязанностям.
В-третьих, были визуалы, которые отличались парой деталей. Мемная стража также считает это баном.
Вторая версия — CLIP
Короче, все эти проблемы побудили меня начать работать над второй версией бота. Я провёл небольшое исследование, чтобы определить лучший метод сравнения изображений. В русскоязычном сегменте интернета, как водится, мало свежего контента по теме. Даже на Хабре последние релевантные статьи, если верить поиску, выходили в 2014 году. В тот момент мне очень помогла вот эта статья с описанием подходов.
Вот какие методы я рассматривал.
Цветовые гистограммы
Гистограммы отражают распределение значений пикселей в изображении. Схожесть определяется на основе сравнения двух гистограмм. Для этого используются метрики пересечения и корреляции.
Индекс структурной схожести (SSIM)
SSIM — метрика, оценивающая структурную схожесть между двумя изображениями. Она учитывает яркость, контраст и структуру, проставляя оценку между -1 (не схожи) и 1 (идентичны).
Подход на основе глубокого обучения (deep learning)
Для второй версии бота я отказался от свёрточной нейронной сети и выбрал CLIP, нейросеть, обученную на парах «картинка-текст». К слову, алгоритм CLIP встроен в генеративные нейронные сети, такие как MidJourney или DALLE-3, для связи векторных представлений текста и изображений, что позволяет создавать новые изображения на их основе. Этот подход также помогает оптимизировать хранение медиаконтента, предотвращая загрузку дубликатов, а также реализовать рекомендации в интернет-магазинах на основе сходства модели или цвета. В общем, штука крайне разносторонняя и полезная.
В случае с моей задачей, CLIP полезен тем, что трансформирует изображения в векторную репрезентацию и делает это с более высокой точностью.
Тестирование методов и выбор лучшего
Я тестировал методы на трёх разных типах пар картинок.
#1. Изображения структурно похожи, но суть разная, например, разный текст на белом фоне. В таком случае чем ниже процент идентичности, который получен при том или ином методе, тем лучше.
#2. Изображения одинаковые, разница только в размере или в объёме фона. Идеальный результат, который должен выдать метод, — 1.
#3. Изображения почти идентичны — отличаются только небольшие участки. Здесь чем ниже процент схожести, тем лучше.
Что получилось
Для каждой пары картинок я вычислил процент идентичности по трём методам сравнения изображений.
Код тестирования
Для каждой пары я пытался найти оптимальный порог, для референса брал 86%. Несмотря на то, что в некоторых случаях метод гистограмм тоже давал приемлемые значения, в среднем, CLIP лучше справлялся с задачей. А вот SSIM подкачал по результатам тестирования.
Кроме алгоритма бот сменил и библиотеку для предварительной обработки изображений: я отказался от PIL и выбрал OpenCV. Большая часть методов сравнения использовали именно её, к тому же у меня раньше не было опыта работы с ней. Хотелось это исправить.
Чтобы сравнивать новые картинки с уже присланными, нужно было придумать, как обойти техническую особенность ботов в Telegram. Получить доступ к истории чата можно через бота, который туда добавлен, — тогда будут видны только новые сообщения. Есть еще вариант через аккаунт пользователя. Но тут опасность в том, что ключ доступа от аккаунта (не пароль) хранится в коде, и если кто-то обратится к исходникам, то он сможет действовать от моего имени: это и переписки, и сообщения, и звонки. Поэтому в первой версии я просто выгрузил историю чата и скормил её боту, чтобы сформировать библиотеку в формате «картинка — дата отправки». Бот, когда видел повторяющийся мем, присылал не ссылку на конкретное сообщение, а изображение из базы.
Сейчас алгоритм немного другой. Я дописал дополнительный модуль, который получает доступ к истории сообщений от моего аккаунта, собирает всю нужную информацию и передает в основную часть бота, который уже распознает изображение и отправляет ссылку на сообщение в истории чата.
Что впереди
Мемы в чате существуют не просто так: есть целая схема конкурсов за самые смешные картинки. Раз в неделю пост с самым большим количеством реакций становится мемом недели; из них выбирается мем месяца, а потом и мем года. Победители номинаций получают славу и почёт мем-чата, сертификат на онлайн-покупки или лимитированный мерч ДАЛЕЕ и диплом с уникальным дизайном.
Сейчас HR считает количество реакций на каждом посте вручную, поэтому в планах автоматизировать и эту активность за счёт бота — пока отложилось из-за загрузки на проектах (мы вообще делом занимаемся, а не только мемы считаем).
Так что на этом история мемной стражи не заканчивается. Такая вот история. Спасибо, что дочитали до конца и узнали, как простой разработчик и его бот защищают юзеров чата от повтора мемов.
Я для своей компании сделал бот, который считает статистику каждую неделю по лайкам, сообщениям, и ищет мем недели.
Использовал telegram core api, обычный bot api не предоставляет информацию по лайкам
О, у вас тоже есть мем-чат, где вы проводите конкурсы?
Можно к вам в мем-чат?
Ахаха, ну если устроитесь к нам на работу, то велком)