В январе 2018 года я принял участие в конкурсе Kaggle под названием Ценовое предложение Mercari. Конкурс длился три месяца и закончился несколько недель назад. Задача заключалась в том, чтобы создать алгоритм, который автоматически предлагает цены на продукты для онлайн-продавцов на основе текстовых описаний, категорий продуктов и некоторых других атрибутов.

Я присоединился к конкурсу за месяц до его окончания, желая изучить, как использовать методы глубокой обработки естественного языка (NLP) для решения этой проблемы. Эти четыре недели соревнований были чередой любопытства, заинтересованности и азарта. В итоге я получил единую сквозную модель глубокого обучения (без ансамблей), реализованную в Tensorflow, которая поместила меня на 35 позицию (из 2384 команд) в частной таблице лидеров. Потом был обман. И я расскажу, как я потерял серебряную медаль на том соревновании.

Соревнование

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

Это было соревнование ядра, что означает, что полное время выполнения конвейера машинного обучения (ML) было ограничено всего 60 минутами, а вычислительные ресурсы были ограничены ядром с 4 ядрами ЦП и 16 ГБ ОЗУ (без графических процессоров).

В обычных соревнованиях Kaggle участники скачивают данные и тренируют / оценивают свои модели в своей собственной инфраструктуре. Обычно победители создают конвейер, который работает часами или днями, обучая десятки моделей с разными алгоритмами, которые затем сумасшедшими способами объединяются. Для больших наборов данных конкуренты с большим количеством вычислительных ресурсов (в облаке или локально), такие как кластеры Spark или серверы с большими графическими процессорами, имеют очевидное преимущество перед другими.

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

Набор данных

Структура данных конкурса была довольно простой - файл TSV со списком объявлений о продуктах, состоящий из следующих атрибутов: name, item_description, item_condition_id, category_name , brand_name, shipping (1, если стоимость доставки оплачивается продавцом, и 0, если покупателем). Целевое поле - цена в долларах США.

В наборе поезда было выставлено более 1,4 миллиона продуктов, а в тестовом наборе для Фазы 1 было около 700 000 продуктов (цена не указана). На этапе 2 размер тестового набора был увеличен в 5 раз, поэтому важно подготовить конвейеры машинного обучения для работы в рамках временных и аппаратных ограничений.

Исследовательский анализ данных

Первоначальный анализ показал, что цена соответствует распределению с длинным хвостом, когда цены на большинство товаров ниже. Средняя цена составляла 17 долларов, а 75% продуктов (III квартал) стоили менее 29 долларов. С другой стороны, были продукты по цене выше 2000 долларов.

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

Вот список из 10 основных комбинаций категорий, брендов и доставки, для которых наблюдались большие различия в ценах (высокий коэффициент вариации)

Если мы углубимся в исследование верхней линии (продукты для макияжа лица Mary Kay), мы увидим, что, хотя средняя цена составляет 10 долларов, а средняя цена составляет 17,60 долларов, существуют продукты стоимостью более 100 долларов, которые на самом деле входят в комплект многих продуктов, как в примерах. ниже. Такая информация доступна только в текстовых атрибутах, что сделало эту проблему НЛП очень интересной для решения.

Вот великолепное интерактивное ядро исследовательского анализа данных (EDA), предоставленное одним из конкурентов, исследующее основные ключевые слова описаний продуктов (кластеризация TF-IDF) и темы (LDA).

Конвейер машинного обучения

Я сделал свой код решения для конкурса общедоступным как ядро ​​Kaggle. Это сквозная модель глубокого обучения, реализованная в Tensorflow, со слоями сверточных нейронных сетей (CNN) для обработки текстовых данных и слоями прямого распространения для объединения с другими функциями.

Функциональная инженерия

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

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

Обычно метрика среднеквадратичной ошибки (RMSE) используется для задач регрессии. Но поскольку цена следовала распределению с длинным хвостом (50% продуктов были менее 10 долларов США), чтобы сделать ошибки в отношении продуктов с низкой ценой более актуальными, чем для продуктов с более высокой ценой, метрикой, выбранной для оценки конкуренции, была среднеквадратичная логарифмическая ошибка. (RMSLE). Таким образом, я применил преобразование журнала к целевой переменной цены, чтобы сделать это предположение доступным для обучения модели.

Работа с недостающей информацией обычно важна для получения максимальных результатов в машинном обучении. В этом наборе обучающих данных более 632 000 продуктов не имеют информации о торговой марке. Но для многих из них название бренда можно найти в названии или описании продукта. Таким образом, был разработан эвристический алгоритм, чтобы угадать «нулевые» названия торговых марок из описания названий продуктов, фильтруя общие категории найденных брендов по фактической категории продукта. С помощью этого алгоритма было найдено 87 000 торговых марок!

Категории продуктов были проанализированы как инкрементальные. Например, для категории «Женщины / Ювелирные изделия / Кольца» были созданы три категориальных признака: «Женщины», «Женщины / Ювелирные изделия», и «Женщины / Ювелирные изделия / Кольца». С этими уровнями категорий модель могла бы научиться обобщать закономерности из высших категорий и применять ее к очень конкретным подкатегориям с меньшим количеством примеров.

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

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

Обработка текста

Текстовые атрибуты name и item_description были важными предикторами цен на продукты. Может быть трудно найти шаблоны в атрибутах произвольного текста, поскольку у пользователей разные стили письма и они могут по-разному выделять определенные характеристики своих продуктов, используя разные последовательности слов, слова в верхнем регистре и знаки препинания. Объявления об упаковках или наборах продуктов, как было показано выше, также создают дополнительные проблемы.

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

Первый шаг в любом решении НЛП для машинного обучения (ML) - решить, как структурировать текст.

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

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

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

Другой альтернативой является использование архитектуры Deep NLP, такой как сверточная нейронная сеть (CNN) или рекуррентная нейронная сеть (RNN), например, LSTM или GRU, чтобы узнать, как лучше всего представить текст в плотном векторе фиксированной длины.

Слова, естественно, представляют собой разреженное представление, так как в текстовом документе вы обычно найдете только небольшую часть слов, доступных в данном языковом словаре. Для его использования нейронными сетями важно иметь плотное представление слов, называемое вложениями слов. Вложения Word можно предварительно обучить на миллионах документов с помощью обучения без учителя. Такие методы, как Word2Vec и GloVE, использовались для переноса обучения предварительно обученным вложениям слов для моделей меньшего набора данных. Такие вложения несут семантику слов, так что похожие слова имеют похожие представления (сгруппированные вместе в модели векторного пространства вложений).

Модельная архитектура и обучение

Из-за ограничений времени выполнения для этого соревнования (и отсутствия графических процессоров) я выбрал архитектуру CNN для ее более быстрого обучения по сравнению с RNN. Фреймворки глубокого обучения, такие как Tensorflow (и более низкий уровень NVIDIA cuDNN), имеют эффективные распараллеленные реализации для CNN. Здесь я пропущу объяснение того, как CNN работают для NLP, указав на этот очень поучительный пост, который вдохновил мою архитектуру CNN (а также это ядро).

Поскольку длина текста является важным фактором для времени обработки архитектуры CNN, название продукта было зафиксировано на первых 20 словах, а описание элемента - не более чем на 70 словах. Для более коротких текстов последовательности были дополнены специальным маркером (‹PAD›). Редкие слова (с менее чем 30 вхождениями в корпусе) были заменены специальным маркером ‹UNK›, чтобы уменьшить размер словарного запаса.

Набор данных GloVE с 400 000 50-мерных встраиваний слов, предварительно обученный в Википедии, был использован для инициализации встраивания моделей, как описано в этом посте. Для слов вне словарного запаса (OOV), недоступных в этом наборе данных GloVE, я выбрал стратегию случайной инициализации вложений слов, следуя распределению векторов GloVE для совместимости.

Для категориальных функций с низкой мощностью (несколько уникальных значений), таких как item_condition и shipping, использовалось преобразование однократного кодирования, а для категориальных функций с высокой мощностью, например product category и brand_name, были сгенерированы случайные плотные векторы внедрения.

Наконец, для числовых функций, таких как статистика item_description и статистика price, было использовано масштабирование функции стандартизации, чтобы обеспечить лучшее представление для нейронных сетей.

Окончательная архитектура нейронной сети представлена ​​ниже.

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

Текстовые функции (имя и описание) имеют свои слова, представленные общим слоем поиска встраивания. Эта последовательность обучаемых встраиваний слов, инициализированная вложениями GloVE, обрабатывается одномерным сверточным слоем (размер окна 3, для моделирования триграмм) с максимальной операцией объединения и генерирует вектор фиксированного размера, размерность которого, если размер фильтра .

Слой среднего объединения слов embeddings был интересной находкой для этой архитектуры, которая уменьшила ошибку модели на 0,003! Этот подход был разработан в соответствии с гипотезой, что первые слова названия продукта и описания являются наиболее релевантными. Таким образом, этот средний слой объединения просто усредняет особенности встраивания слов первых 5 слов названия продукта и первых 20 слов его описания.

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

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

Нормализация уровня, выпадение и нормализация L2 экспериментировались на многих уровнях, пытаясь бороться с переобучением. Хотя я эмпирически заметил, что допущение переобучения модели дает наилучшие результаты оценки, поскольку я мог обучать модель только в течение трех эпох при 60-минутном ограничении времени выполнения, установленном в конкурсе.

Этот полный конвейер машинного обучения в экземпляре ядра Kaggle занимает 48 минут. Мне удалось сократить время выполнения до 38 минут, уменьшив максимальную длину описания (с 70 до 50) и размер фильтра свертки с (96 до 64) с очень небольшим снижением точности модели. Это дало бы немного свободного времени для использования более быстрого алгоритма машинного обучения, такого как Ridge Regression (см. Этот замечательный пример с scikit-learn), и, в конечном итоге, улучшения результатов. Но для этого конкурса я решил продолжить изучение возможностей единой сквозной модели глубокого обучения.

Полученные результаты

После 26 заявок я был рад получить единственную модель глубокого обучения, которая, согласно моей оценке перекрестной проверки, поместит меня в число 2% лучших конкурентов.
Пс. Фактически, моя модель фактически набрала 0,41117 (вычислено с учетом позднего представления) и поместила бы меня на 35-ю позицию (из 2384 команд) в частной таблице лидеров.

Неплохо для одиночного решения нейронной сети, без ансамбля!

Но в одночасье пришел конец ...

Несовместимые сроки

Поскольку это был первый конкурс ядра, у платформы Kaggle были некоторые проблемы, из-за которых крайние сроки конкурса не согласовывались на веб-сайте и в его сообщениях. Для Фазы 1 был крайний срок подачи заявок, к которому участники должны были выбрать свои лучшие ядра для Фазы 2 (с большим набором тестов). Многие другие конкуренты, в том числе и я, пропустили эту дату подачи из-за непоследовательного индикатора выполнения конкурса (см. Ниже), а также на основании электронного письма, отправленного Kaggle в последний день подачи, в котором сообщалось, что у нас еще есть 7 дней до конца. конкурса.

Вот так я потерял свою серебряную медаль после месяца упорной работы.

Извлеченный урок: обращайте внимание на сроки, особенно когда они не согласованы. :)

Заключение

Конкурс предложения цены Mercari предоставил массу знаний как по классическому, так и по глубокому НЛП для изучения представления текста. Я был взволнован, увидев, как Deep Learning может помочь этому решению преуспеть в конкурентной борьбе, даже с простейшим проектированием функций и без ансамбля модели.

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

Я с нетерпением жду следующего конкурса ядра. Я полагаю, что скоро Kaggle / Google предоставит ядру графические процессоры, поскольку они уже находятся в свободном доступе в Google Colaboratory.

Глубокое НЛП - это круто!