Как приготовить 10 000 меню за три недели, когда есть Data Science
В период перехода на удалёнку команда Data Science 2ГИС придумала решение, как быстро приготовить красивые меню для 10 000 ресторанов и кафе более чем в 100 городах.
Придумываем блюдо
В конце 2019 года мы добавили в 2ГИС товарный поиск по строительным рубрикам и шинам. Сделали глубокий рубрикатор строительных категорий, узнали про балясину и RunFlat и научились извлекать из названий и описаний необходимые для выбора атрибуты товаров.
Оценив востребованность, решили развивать поиск в новых рубриках.
Основной камень преткновения — полнота данных. Чтобы поиск давал исчерпывающие результаты, нужны цены очень многих компаний. И желательно в машиночитаемом формате. Но у одних компаний нет сайта — значит, они не могут дать ссылку на прайс-лист. У других не настроена автоматическая выгрузка из 1С. Решение с нашим FTP тоже не взлетело, так как многие срезались на стадии формата файлов.
Тогда мы решили увеличить полноту, снизив порог входа даже для самых неподготовленных фирм — предоставили им возможность самостоятельно вносить цены в Личном кабинете при помощи CRUD. А заодно — добавлять фотографии товаров.
Весной этого года мы готовились к релизу. Оставалось несколько недель работы, как пришёл COVID-19 и последующий за ним карантин. И исходя из новых условий, мы сфокусировались на самых востребованных категориях: «еда» и «лекарства».
Отправляем мини-паука
Чтобы ускорить наполнение витрин и помочь компаниям, решили собрать данные за них, оставив им только проверку и добавление единичных позиций.
В 2ГИС несколько десятков тысяч сайтов заведений общепита. И примерно треть — с ценами на блюда. И было очень логично взять цены с этих сайтов.
Разрабатывать парсер для каждого сайта — утопия. А вот один, не завязанный на структуру конкретного сайта и с применением ML, — вполне рабочее решение. По сути, мы сделали поискового мини-паука, который ходит по сайтам и собирает разрешённую для индексации информацию.
Классифицируем данные
Задачу сформулировали так: на странице с меню должно быть название блюда, а где-то визуально рядом с названием указана цена. Поэтому нам нужен классификатор текста на три класса:
- название блюда,
- описание или ингредиенты,
- другая строка.
Цену нужно отличать от граммовки, а фотография блюда всегда находится рядом с описанием.
При помощи headless-браузера мы скачали несколько сотен гигабайт страниц с сайтов общепита и сохранили computed style каждого html-элемента. Так мы получили возможность определять взаимное расположение элементов для необходимых строк.
Классификатор названий блюд сделали из двух моделей бинарных классификаторов. Первая модель определяла, является ли строка блюдом или каким-то другим текстом, а вторая разделяла блюда и описания.
Готовим датасет
Датасет собирали самостоятельно.
Класс «название»
Названия взяли из рецептов блюд в различных сборниках. Выучили простой классификатор, а на основе скачанных данных делали разметку.
Класс «описание»
С этим классом возились дольше, так как названия некоторых блюд совпадают с перечислением ингредиентов — например, «баклажаны с сыром».
И кроме описаний блюд на сайтах компаний встречаются отзывы об этих блюдах. Они нам явно не подходили, поэтому датасет описания формировали из шагов приготовления блюд и случайных ингредиентов в тех же рецептах.
Класс «остальное»
Собрать такие данные легче всего, так как до этого мы уже собирали данные с сайтов других категорий — к примеру, тех же СТО. Требовалось только итерационно очистить этот класс от названий и описаний.
Замешиваем обучение
Пробовали различные варианты моделей: от классических регрессий до претрейна Bert. Самым стабильным и предсказуемым вариантом на наших данных оказался vowpal wabbit на различных текстовых фичах и их комбинациях.
Когда модель начала давать приемлемое качество, сделали кластеризацию найденных строк по визуальному расположению и обязательному наличию цифрового токена, похожего на цену. Кому интересны значения, «приемлемое качество» — F1 0.90+.
Для классификации «цена»/«не цена» использовали набор эвристик: название html-класса, наличие символов, указывающих на валюту, и исключение токенов с явным указанием граммовки и калорий.
Так получили большой набор кандидатов на название блюда с указанием цены.
Убираем алкоголь
Выпускать в таком виде мы не могли из-за различного мусора и запрещённых товаров вроде алкоголя.
Потребовалось сделать ещё две модели. Для мусора — классификатор мусора, где мы улучшали бинарную классификацию «блюдо»/«не блюдо». А для алкоголя… классификатор алкоголя.
Датасет по алкоголю мы собрали ещё до этого по данным модерации товаров из «Чека». Но так как в некоторых ресторанах довольно редкие вина или виски, его оказалось недостаточно. И чтобы его расширить, взяли прайс-листы крупных оптовых поставщиков алкоголя.
Раскладываем на категории
Чтобы Витрина в ресторанах выглядела как настоящее меню с картинками, мы поставили себе ещё одну задачу — многоклассовую классификацию блюд. Самое сложное — понять, где заканчивается «мясо», а где начинается «горячее».
Как и с другими задачами — разметили данные и итеративно улучшали метрики и «внешний вид» меню.
Добавляем фото
Отдельная задача — выбор фотографии для блюда.
Сначала мы искали фотографию большого размера в визуальной окрестности названия блюда, а потом добивались уникальности этих фотографий, чтобы различные заглушки и фон сайта не назначались как фотография блюда.
Все выкаченные фотографии отфильтровали по размеру: слишком маленькие и слишком большие. А также профильтровали на количество цветов, чтобы убрать плейсхолдеры.
Подаём на бой
После этого мы залили красивые меню в Личные кабинеты и сделали рассылку, что всё уже готово — компаниям осталось только проверить цены и актуальность.
Подход сработал. За несколько дней компании загрузили ещё примерно такое же количество своих прайсов, сколько мы заранее подготовили. А предмет нашей отдельной гордости и показатель популярности этого раздела — владельцы киоска в спальном районе Новосибирска добавили прайс на беляши.
Команда, вы супер! Гениально, мощно, очень быстро. Лучшее MVP, что я знаю из такого обьема 🔥
Есть ли официально сам 2Gis на VC? Узнал о посте через ваш канал: https://www.instagram.com/p/CHxI2xgFs4c
Пока нет. Прощупываем )
Как вам идея добавить в 2 гис функционал аналога приложения Citizen, чтобы пользователи могли добавлять контент с мест событий?
Пока не планировали.
Планировали добавить функции трекинга, чтобы можно было увидеть пройденный маршрут?
В ближайших планах не планировали.