Как мы победили в двух хакатонах Цифрового Прорыва. История первая
Привет, Хабр. Мы — команда Ling Bizkit (я — Никита Лаврентьев, Валентин Ануфриков, Матвей Липилин, Егор Плужник и Павел Рыбаков). И это наш рассказ о том, как можно заработать 3 миллиона за два дня. А точнее, о победе в хакатоне на одном из этапов «Цифрового Прорыва».
Как всё закрутилось
Однажды я, Сергей Стаценко и Константин Котиков участвовали в хакатоне Тинькофф по искусственному интеллекту. Там я узнал, что есть ещё какой-то хакатон «Цифровой Прорыв» на ту же тему. Идея меня увлекла, захотелось поучаствовать. Тогда я пригласил в команду Валю как специалиста по работе с данными, и позднее познакомился с Егором, он бэкэнд-разработчик Но нам были нужны ещё люди, которые взяли бы на себя определённые задачи. Тогда я просто написал приглашение в корпоративных чатах, и на него откликнулись Павел и Матвей. Так сложилось ядро нашей команды.
Задумались, какую задачу решать. Выбрали ту, которая больше всего подходила нам с точки зрения интереса и пользы: нужно было помочь МФЦ Санкт-Петербурга оптимизировать их текущую базу знаний и сделать очень удобный поиск. И поскольку этот опыт они могут легко распространить на все другие МФЦ, можно сказать, что мы таким образом помогли всем жителям страны. (Но это, конечно же, никому в МФЦ не нужно.)
Хакатон длился 48 часов, начался вечером в пятницу. По условиям задачи решение должно было из коробки работать на любых системах. Мы ожидали, что организаторы предоставят некую базу знаний МФЦ и набор данных в виде вопросов пользователей с ответами из базы знаний. Но получили только простой Excel-файл с парами вопрос-ответ, всего около 3 тыс. пар. Ответы представляли собой огромные выдержки из формальных документов МФЦ, написанные тяжёлым канцеляритом. Даже вопросы были не такие, как люди обычно пишут в поисковиках, а словно взятые из той же документации. Забегая вперёд, мы большую часть хакатона надеялись, что нам ещё дадут нормальную базу знаний. И дождались! Базу нам всё-таки отгрузили, но… это оказалось то же самое, что в Excel-файле, только необработанное. «Заказчик» просто спарсил свой сайт и все это вывалил в один файл, прямо с HTML-тегами. Ничего путного извлечь из этого мы не смогли, и в дальнейшем работали с первым файлом. Этим вся помощь от организаторов и ограничилась.
Эластик и машинное обучение
Сначала попробовали применить Elasticsearch. Не сработало: он хорошо искал только в том случае, если запрос практически совпадал с вопросом в базе знаний. Никакой вариативности не допускал. На всякий случай потыкали ещё аналог Elasticsearch, MeiliSearch, но чуда не случилось.
Тогда перешли к машинному обучению, для этого использовали фреймворк Haystack. Сделали семантический поиск: базу знаний и запросы преобразовывали в векторное представление, а затем по списку векторов из базы искали самые близкие к вектору запроса. Реализация всего в несколько строчек, и большая предварительно обученная модель для векторизации текста из сети. Причём обученная уже на русском языке. В теории, ее можно дообучить в нужной тематической области, но на хакатоне у нас не было ни времени, ни данных, поэтому пришлось пользоваться готовой.
Мы сделали так: выгрузили всю базу знаний, нормализовали её и вычислили отдельные векторы для каждого вопроса в парах вопрос-ответ. А потом начали сравнивать с ними векторы пользовательских запросов. Ведь люди обычно пишут в поисковиках не канцелярско-бюрократическим языком, как было в базе знаний МФЦ, а по-простому, так, словно задают вопрос человеку. Например: «Что нужно для такой-то услуги». Поэтому мы искали совпадения с вопросами, и найдя самые близкие, показывали соответствующие им ответы.
Семантический поиск работает быстро, можно добавлять новые параметры. Но есть у него и недостаток: нельзя выделить в найденном ответе короткий фрагмент, непосредственно отвечающий на вопрос. Приходилось выдавать целиком отрывки из бюрократических инструкций МФЦ. Тогда мы попробовали добавить в цепочку генеративную модель, которая перефразировала бы найденные первой моделью ответы в более короткие и удобочитаемые. Получилось вполне неплохо.
Ещё мы предусмотрели, чтобы наша поисковая система не выдавала пользователю «плохих» ответов. Если схожесть векторов запроса и ответов в базе был на ниже определённого порога, то мы просто не показывали ответы, чтобы не путать человека выдачей нерелевантных ответов.
Поиск в реальном времени
И как вишенка на торте, мы добавили поиск в реальном времени. Как в Яндексе или Google, когда с каждой новой буквой обновляется список предлагаемых поисковиком вариантов запроса. Очень удобно, ведь человек может уже на полпути увидеть то, что ему нужно, и сразу кликнуть. А вот второе преимущество поиска в реальном времени не так очевидно: пользователь может написать такой невнятный вопрос, может так криво сформулировать мысль, что даже машинное обучение не найдет в базе совпадений. И пока он будет писать, он сразу увидит, что ему предлагается что-то не то, и, возможно, подумает: «Наверное, я пишу фигню, надо по-другому». Это стало киллер-фичей нашего решения на хакатоне.
Пока пользователь вводит запрос, мы нормализуем его, ищем в базе и постоянно обновляем поисковые предложения. Они еще и уникальные для каждого IP. Этот цикл продолжается до тех пор, пока пользователь не выберет какой-то из предложенных вопросов или не нажмет Enter. Тогда выбранный или введённый запрос попадает в базу данных, помещается в иерархию релевантности, и мы выдаем ответ и одновременно собираем метрики по нему.
И для улучшения качества поиска мы прибегли ещё к такой хитрости: заранее сильно перемешали слова в каждом предложении. Повторимся, люди не будут писать запросы так же, как в базе знаний, они могут формулировать мысли с разным порядком слов. Это очень помогло повысить релевантность поиска.
Помощь в обучении сотрудников
В МФЦ разные сотрудники отвечают за разные виды услуг, и осваивают разные наборы нормативов и законов. Мы добавили отслеживание самых частых запросов с агрегированием по услугам. Потом эти данные можно выгрузить и проанализировать наставникам, чтобы они в дальнейшем могли при обучении сделать акцент на каких-то темах, которыми пользователи интересуются чаще всего.
Масштабирование
Сразу ответим тем, кто сомневается в масштабируемости такой схемы. Во-первых, у МФЦ по всей стране нет единой базы знаний, каждый собирает свою собственную базу, соответствующую нормативам конкретной области и федерального округа. Поэтому нет нужды проектировать поисковик, способный обслуживать запросы всех граждан страны, нагрузка на каждую из локальных баз знаний будет ограничена пользователями соответствующего региона.
Во-вторых, наша хакатоновская разработка пока что масштабируется только вертикально с помощью новых серверов, но её можно доработать до горизонтально масштабируемой: как вариант, объединить ElasticSearch или иной инструмент с нашей векторизацией текста.
В-третьих, есть еще мысли, как можно снизить нагрузку:
Отправлять запросы к базе не после каждого введённого символа, а дожидаться ввода очередного пробела. Но это тема для отдельных экспериментов.
Развернуть векторную базу данных.
В-четвертых, если грамотно хранить всю информацию, до 99% пользовательских вопросов можно просто кешировать.
Стек
Фронтенд мы написали с помощью Python-фреймворка Streamlit. Бэкенд сначала хотели писать на Scala, и даже начали накидывать код. Но потом сообразили, что для хакатона это избыточно, и перешли на Python. Модели работают через FastAPI, обращение к ним выполняется через конечные точки HTTP. Всё это собрано в Docker-контейнере.
Репозиторий нашего решения
Как можно улучшить наше решение
Добавить рейтинги пользователей: проверять пользовательские оценки качества «вопросов».
Индивидуальные рекомендации: ранжировать вопросы из топ-5 по конкретному МФЦ и пользователю.
Дообучить модели на ответах пользователей.
Советы тем, кто задумывается о хакатонах
Вы ещё не бывали на хакатонах по машинному обучению, но идея привлекает? Позвольте дать несколько советов.
Внимательно слушайте команду и помогайте преодолевать трудности. Вы будете трудиться очень интенсивно, а если хакатон долгий, то во второй день уже не будет времени на сон, и придётся доделывать из последних сил. Поэтому помощь каждому участнику будет очень важна. Например, мы 18 часов бились над одной из задач. Перепробовали кучу решений, ни одно не работало. Едва не опустили руки и не сдались. Лишь благодаря взаимной поддержке продолжили поиск, и у одного из нас возникло озарение, которое помогло нам победить.
Внимательно слушайте, чего от вас хотят «заказчики». Иначе из-за недопонимания можно сделать совсем не то, что нужно.
Делайте демо для жюри. У организаторов просто нет времени подробно вникать во все решения. Они на скорую руку проверят и поставят оценку. Поэтому важно очень быстро показать им, что ваше решение действительно работает.
Позаботьтесь о вычислительных мощностях. Наша модель была не самой сложной на хакатоне, но даже она занимала около гигабайта. К счастью, у нас был ноутбук с видеокартой GeForce 3090 TI c 24 Гб памяти. Это оказалось очень важным подспорьем, потому что у многих наших конкурентов более слабые модели работали медленнее, и это несколько портило их презентации для жюри.