![](https://habrastorage.org/getpro/habr/upload_files/200/b95/f43/200b95f43f32c68436cdf55df7ae0e97.jpeg)
Всем привет! Это продолжение рассказа о том, как наша команда Ling Bizkit победила в двух хакатонах. В первый раз это было соревнование Северо-Западного региона в рамках Цифрового Прорыва, а во второй — уже всероссийский этап.
Пролог
Сложности начались опять с команды: двое из нас не смогли участвовать, поэтому нужно было найти кого-то на замену. В нашем окружении подходящих специалистов не было, поэтому мы решили поискать в соцсетях. Написали в чате внутреннего сообщества ML/DS, и нам ответил только один — Завен Мартиросян, ML-инженер в команде цифрового ассистента в VK tech. Второго человека, Олега Лашилина, мы нашли по рекомендации.
Встретились, обсудили задачи, которые должны быть на хакатоне. К тому времени многие из них уже были заняты. В результате решили делать универсальную рекомендательную систему, которую можно использовать в любой отрасли. Это сегодня востребовано, и у нас был подходящий опыт. Рекомендательная система должна была на основе содержания кассовых чеков синтезировать в реальном времени рекомендации, дополняющие текущий набор товаров. Решать эту задачу было проще, чем на предыдущем хакатоне, ведь у нас уже была придумана концепция. Но всё же работы по улучшению моделей предстояло много. Я отвечал за организацию команды, и когда ребята придумывали новые крутые идеи, старался направлять их внимание на то, что может быть полезно для нашего решения. Завен занимался языковыми моделями для генерирования текстов рекомендаций, Егор отвечал за фронтенд и бэкенд, Валентин занимался обработкой данных, тестированием гипотез и разработкой дополнительных признаков для модели, а Олег готовил сам рекомендательный движок и проводил эксперименты по повышению качества модели. В этот раз уже не тратили время на поиск инструментов: для MVP выбрали фреймворк Streamlit, для бэкенда — FastAPI. Основное внимание мы уделяли анализу данных и проверке гипотез.
Данные
В качестве исходных данных у нас был набор анонимизированных кассовых чеков за несколько лет. На каждом чеке есть QR-код, по которому можно перейти на сайт и посмотреть список купленных товаров. Главная сложность была в обилии групп товаров: рекомендации для одних группах совершенно не годились для других. Нужен был универсальный алгоритм, хорошо работающий во всех товарных категориях.
В первую очередь мы заметили большую сезонность спроса. Например, в супермаркетах в пятницу пиво было одним из самых популярных товаром. А летом очень востребовано мороженое. Все выявленные сезонные колебания мы постарались учесть в нашем алгоритме, чтобы они анализировал не только текущий набор товаров, но и день недели, и месяц. Хотя, забегая вперёд, это не дало сильного улучшения метрик. Вероятно, причина была в сильной разреженности исходных данных.
Также мы глубоко погрузились в работу онлайн-касс. Оказывается, уже есть очень удобные, современные аппараты, где можно просто на сенсорном экране выбирать товары из каталога и подключать системы лояльности.
Метрики
Очень странно, что организаторы оценивали решения участников по метрике Accuracy. Но такова была их установка: модели в первую очередь должны давать очень точные рекомендации. Бизнес-функции и дизайн интерфейса системы — на втором и третьем месте.
Модель
Сначала мы пробовали рекомендовать просто самые популярные или усредненные товары. Результат совсем не порадовал. Тогда мы попробовали модель SASRec (Self-Attention). Она показала результаты гораздо лучше, но недостаточно хорошие для нашей задачи.
![](https://habrastorage.org/getpro/habr/upload_files/475/a82/822/475a8282205ba08345a5d8b9d7234321.png)
В результате мы взяли линейный автоэнкодер, довольно известную в сообществе разработчиков рексистем модель Embarrassingly Shallow Autoencoder (EASE). Она довольно простая, гибко настраивается и очень легко обучается. Особенность модели в том, что она не слишком хорошо масштабируется: её лучше всего использовать для рекомендаций в каталоге из нескольких тысяч объектов. Поэтому обычно её применяют для академических исследований, а не коммерческих продуктов. Но её пытаются улучшить.
Модель обучалась на наших исторических данных «на лету», чуть меньше минуты. Производительность достигала 2000 запросов/сек. Во многом благодаря ей мы и победили. По точности прогнозирования её не могут превзойти даже ансамбли из моделей многих других видов.
Мы продолжили развивать свою систему. Идея была такая: нужно не просто выдать наиболее подходящую конкретному покупателю рекомендацию, а ещё и красиво ее подать, чтобы человек захотел купить. Для этого мы попробовали разные генеративные лингвистические модели.
Лучше всего себя показала Saiga — open-source-модель на основе Llama 2, обученная на русскоязычном корпусе текстов. Мы передавали ей содержимое заказа покупателя и рекомендуемый товар, а на выходе получали текст с рекомендацией. Например, если человек покупает торт, шоколадку и еще что-нибудь сладкое, и мы хотим предложить сок, то вторая модель скажет что-то вроде: «Вашу корзину хорошо дополнит сладкий нектар из яблок. Возьмите «Добрый сад» или сок «Добрый», чтобы устроить себе сладкий вечер». Ну, или что-нибудь полусладкое.
Основная сложность была в том, что для быстрой работы генеративной модели нужно мощное железо. Поэтому мы использовали квантизированную модель — уменьшенный вариант, которому достаточно центрального процессора. Из-за квантизации весов часть знаний теряется, модель начинает работать чуть хуже, но быстродействие сильно возрастает.
Еще один недостаток доступных в open source моделей в том, что они ограничены с точки зрения контекста. Мы попробовали решить это итеративным образом: сначала берём какой-то объем информации, прогоняем его через модель, потом снова прогоняем его с добавлением ещё одной порции данных, и так по кругу, чтобы уменьшить общий объем данных и получить удобоваримый результат.
Фронтенд и бэкенд
Поскольку по условиям задачи решение должно быть коробочным — заказчик ставит ее, подключает к своим данным и сразу получает рекомендательную систему, — мы сделали на FastAPI небольшой HTTP-сервер и добавили возможность загрузки в нашу систему TSV-файлов. Достаточно прописать, какие колонки должны быть в БД, и после загрузки система начинает работать.
Наша система работает итерационно: сначала подбирает вариант по первому отсканированному товару, потом по двум, потом по трем, и так до тех пор, пока кассир продолжает сканировать товары. Всё собрали в Docker-контейнеры и запустили на хостинге.
К слову, в последние шесть часов хакатона мы из последних сил делали красивую демо-версию, подняли для неё сервер, всё настроили, и вставили в презентацию QR-код со ссылкой. И никто из жюри так туда и не зашел: они просто взяли наш контейнер с Git и тестировали у себя локально.
![](https://habrastorage.org/getpro/habr/upload_files/da4/318/93b/da431893b9ec2ac555adcbfda687a731.jpeg)
Результаты и планы
Наша рекомендательная модель обучается меньше минуты. Причём мы хотели сделать так, чтобы кассиру ничего не нужно было вводить: он просто сканирует на кассе штрих-коды, а в конце ему на экран просто выводится текст рекомендации. Но из-за нехватки времени не успели реализовать весь этот замысел именно на хакатоне. Среди финалистов ни у кого не было такой бизнес-фичи, однако мы заняли второе место, потому что у конкурентов точность прогноза оказалась выше на 0,03 %. К тому же потом мы выяснили, что на защите нужно было сделать акцент на второй модели, генерирующей тексты рекомендаций — жюри просто не заметило эту фичу в презентации. Мы много рассказали про техническую сторону решения, и недостаточно — про бизнес-преимущества.
Как можно улучшить нашу разработку:
Увеличить количество параметров модели. Кроме артикулов и идентификатора кассы использовать категории и названия товаров, даты покупок и т. д.
Использовать более совершенную генеративную модель для улучшения текстов рекомендаций.
Хранить данные в БД, а не в TSV-файлах, которые получили от организаторов.
Интегрировать с системой лояльности «Эватор Бонус» для повышения персонализации рекомендаций.
Подстраивать рекомендации не под всю корзину, а под отдельные товары.
Добавлять в рекомендации партнёрские товары для повышения маржинальности.
Увы, как и после первого хакатона, бизнес-применения нашей разработки пока не предвидится. Организаторам понравились наши идеи, но для их воплощения нам поставили условие: переезд в другой регион с полной занятостью в офисе. Никакой удаленки и частичной занятости. Как вы понимаете, мы на такое не согласились.
Попробовать наше решение можно здесь.