Естественный язык и «магические фичи»: как программисту победить в соревновании на Kaggle

Помимо работы над проектами клиентов, data science команда InData Labs часто принимает участие в различных соревнованиях. Об успешном опыте участия в соревновании на Kaggle рассказал data scientist компании Артём Фаразей.

Фото: InData Labs

Недавно мы показали хороший результат в Quora Question Pairs Challenge на Kaggle. Соревнование примечательно большим количеством неожиданных открытий и оживлённых дискуссий среди участников. Поэтому я решил детально описать особенности именно этого соревнования и поделиться рецептом победы.

Описание и цель соревнования

Quora — социальный сервис для обмена знаниями, где любой может задать интересующий вопрос. Аудитория Quora очень разнообразна. Ежемесячно ресурс посещает более 100 млн человек, поэтому неудивительно, что многие задают похожие вопросы. Дубликаты усложняют процесс поиска ответов и заставляют отвечающих тратить больше сил на то, чтобы охватить все похожие вопросы. Перед участниками соревнования поставили задачу предсказать, какие из предложенных пар вопросов являются дубликатами.

Участникам предложили тренировочный набор данных, который содержал больше 404 тысяч пар вопросов. Если присмотреться к примерам, можно понять, что задача очень сложна даже для человека:

Первые три вопроса были предварительно помечены Quora как дубликаты, а пары 4-6 считались уникальными. Как видно из примеров, словарное наполнение вопросов-дубликатов может совсем не совпадать, а вопросы, которые не являются дубликатами, могут отличаться всего одним словом. Это одна из главных особенностей датасета, которая делает задачу такой сложной для технологий обработки естественного языка (NLP).

Интересные особенности датасета

Практически сразу после начала Kaggle-соревнования участники начали делиться интересными наблюдениями о датасете. Примеры самых распространенных наблюдений:

«Шумная» разметка данных

Как признались сами организаторы, «Предварительная разметка необходима для того, чтобы проинформировать участников, но не является на 100 процентов правильной и может содержать ошибки. Мы считаем, что в разметке соблюдено равновесие, но в отдельных случаях возможны исключения». В реальности участники встретили множестве примеров, где разметка была неправильной либо двусмысленной, а некоторые сравниваемые вопросы были включены только частично. Вот несколько примеров:

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

Большое количество вопросов про Индию

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

Зависимость количества дубликатов от времени и различная доля дубликатов в обучающей и тестовой выборках

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

Иллюстрация: Артём Фаразей

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

Если учесть, что распространённой практикой является использование в качестве обучающей выборки более старых данных, а для тестовой — более свежих,  это наталкивает на мысль, что доля дубликатов в тестовой выборке, на самом деле ниже, чем в обучающей. Более того, для самых новых вопросов из обучающей выборки доля дубликатов находится в районе 15-20 процентов, что очень хорошо согласуется с предыдущими оценками доли дубликатов в public leaderboard, согласно которым в тестовой выборке всего 17,5 процентов дубликатов.

Вкупе с тем, что организаторы случайным образом делили тестовую выборку на public и private, мы вполне можем надеяться, что и в private-датасете доля дубликатов будет примерно такой же.

Очень важно знать, если распределение дубликатов в тестовой выборке существенно отличается, так как используемая в данной задаче метрика качества очень чувствительна к её изменению. Например, если модель будет сомневаться, к какому классу отнести какой-нибудь пример, то она скорее всего отметит его как не дубликат просто потому, что в выборке, на которой эта модель обучалась, не дубликатов было значительно больше, чем дубликатов.

Магические фичи

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

граф будет выглядеть следующим образом:

Теперь для каждой пары вопросов, которые соответствуют какой-либо записи из датасета, мы можем посчитать количество «общих соседей», т.е. вопросов, которые соседствуют в графе с обоими вопросами из данной записи. Например, для первой записи из нашего примера таких вопросов-соседей будет два:

Оказалось, что количество таких «общих соседей» — очень мощная фича. Это можно увидеть на следующем графике, где изображено соотношение дубликатов и не дубликатов в обучающей выборке для записей с определенным числом общих соседей.

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

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

Во время соревнования эти фичи окрестили «магическими», так как они были очень мощными, и для многих было неожиданно, что можно извлечь информацию не только из текста. Организаторам соревнования также не было понятно, будут ли подобные фичи полезны в реальной жизни. К тому же, некоторые NLP модели (например TF-IDF) неявно используют частоту вопроса, а значит они могут давать прирост качества только потому, что эксплуатируют особенность датасета.

Наше решение Quora Question Pairs Competition на Kaggle

Deep learning

Учитывая то, какая перед нами стояла задача, можно справедливо отметить, что перед началом соревнования многие (в том числе и организаторы соревнования) возлагали большие надежды на deep learning. DL модели зачастую оказывались намного лучше, чем модели с использованием сотни ручных «фичей» (а именно такая модель использовалась на тот момент в Quora). Именно поэтому мы начали именно с них.

Word vectors (Embeddings)

Под DL-моделями в настоящее время понимают глубокие нейронные сети, которые принимают на вход сырые данные (в нашем случае это тексты вопросов) и сами извлекают из них необходимые фичи. Однако есть проблема — нейронные сети (да и вообще компьютеры в целом) предпочитают работать с наборами чисел (векторами) и совершенно не умеют работать с сырыми текстами. Например слова «dog» и «puppy» имеют довольно похожий смысл, но для компьютера это просто строки, причем не очень похожие. Они имеют разную длину, у них встречаются разные буквы, и компьютер просто по этим двум строкам не сможет сказать, что они имеют одинаковый смысл. А для данной задачи нам жизненно необходимо понимать, похожи слова или нет.

Для решения этой проблемы используют подход, который называется word2vec. Его смысл можно описать известной цитатой: «You shall know a word by the company it keeps» (Firth, J. R. 1957). Word2vec преобразует слова в векторы так, что слова, которые встречаются в схожих контекстах, имеют схожие векторы. Используя word2vec, мы можем преобразовать сырой текст в набор векторов, которые можно с лёгкостью скормить нейронной сети.

Стоит отметить, что word2vec (или другие embeddings) очень тяжело обучать, т.к. для этого требуется корпус текста размером с Википедию. Поэтому почти все участники используют заранее обученные модели.

«Капучино» и «эспрессо» почти всегда используются в одном контексте, поэтому неудивительно, что для робота-официанта это почти одно и то же.

Нейронные сети

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

В этой связи хочется отметить несколько вещей:

  1. Помимо двух входов для сравниваемых вопросов, присутствует третий вход для ручных фичей, что не очень характерно для DL-моделей. Это сделано из-за «магических фичей», которые хорошо работают на данном датасете;
  2. Мы использовали две нейронный сети: в первом случае для извлечения информации из вопросов использовался LSTM, а во втором — несколько свёрточных слоев с последующим Global Max Pooling;
  3. Архитектура, на которой мы остановились, не очень «глубокая». Чтобы глубокие нейронные сети работали хорошо, нужно очень много данных. К сожалению, наш датасет не такой большой, и довольно «шумно» размечен. При добавлении новых слоёв мы рискуем сильно переобучиться (особенно при использовании LSTM);
  4. Есть ещё много идей, с которыми мы бы хотели поэкспериментировать, например:
  • LSTM with attention
  • Character-Aware Neural Network
  • Triplet Neural Network
  • Target encoding

После экспериментов с нейронными сетями стало понятно, что только ими нам не обойтись. Чтобы DL модели показали на нём всю свою силу, есть проблемы с представлением текста в виде векторов: в датасете часто встречаются вопросы, которые содержат математические формулы, нераспространённые сокращения, опечатки и т.д. Из-за этого теряется часть информации о вопросе, что существенно усложняет задачу нейронным сетям.

Градиентный бустинг

Пришло время для излюбленного инструмента участников data science соревнований — градиентного бустинга, который зарекомендовал себя как мощная и устойчивая к «шуму» модель.

Для него мы использовали следующие фичи:

  • Длина вопросов, количество слов, количество слов без стоп-слов;
  • Количество заглавных букв, вопросительные знаки, скобки;
  • Индикаторы вопросов, такие как «Are», «Can», «How»;
  • Различные меры сходства, основанные на word embeddings (Word2Vec, FastText, Glove);
  • Word Mover's Distance;
  • Различные меры сходства, основанные на n-граммах символов (включая TF-IDF);
  • Jaccard, Canberra, Chebyshev similarities;
  • Фичи, предоставленные Abhishek и Mephistopheies;
  • PageRank.

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

Также мы добавили out of fold предсказания нейронных сетей как фичи для бустинга. Осталось только не забыть сбалансировать классы, подобрать параметры модели и аккуратно провалидировать результаты. Такой модели с лихвой хватит, чтобы получить серебряную медаль.

Рецепт победы

Что же нужно было сделать, чтобы победить в этом соревновании? Вот краткий «рецепт успеха»:

1. Чем больше, тем лучше

В то время как мы использовали около 70 ручных фич и 3 модели, победители соревнования оперировали 1000+ фичами и объединяли сотни (вплоть до 1000) моделей. В общем, как это часто бывает в соревнованиях на Kaggle, чем больше различных моделей объединяешь — тем лучше.

2. Продвинутые графовые фичи

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

3. Local rescaling

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

4. Постобработка предсказаний

Ещё один способ улучшить результат, которым пользовались участники — корректировка уже полученных предсказаний модели. Например, для этого можно использовать свойство транзитивности (т.е. если вопрос B — дубликат вопроса A, а вопрос C — дубликат вопроса B, то очевидно, что A и C — тоже дубликаты)

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

Соревнования на Kaggle это всегда отличное место, чтобы узнать что-то новое. Тем не менее, лучшее решение на Kaggle не гарантирует лучшее решение проблемы бизнеса. Пример этого соревнования показывает, что нужно быть очень внимательными и рассудительными во время подготовки данных для обучения моделей. Ведь если те особенности датасета, которые мы описали, являются искусственными и не характерны для всей базы Quora, то все решения, которые предоставили участники соревнования, не будут работать в реальной жизни. Это соревнование также показало, что важно сохранять широкий взгляд на проблему, видеть различные способы улучшения модели и оставаться открытыми к новым идеям и подходам.

*Редакция dev.by не несёт ответственности за содержание корпоративных блогов
Источник: dev.by
Нашли в тексте ошибку — выделите её и нажмите Ctrl+Enter.

Обсуждение

Missing-male

Можете немного пояснить, почему вы использовали сверточные сети для текста, что это дает и что конкретно сворачивали? (одномерные ядра использовали?)

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

Спасибо.

6fc9ae4f100c49819cdf03e74ea97d34?1531787011
+2

Добрый день! Сверточные сети - это уже довольно стандартный, эффективный и быстрый инструмент в NLP. Вкратце как это всё работает:

Мы конкатенируем векторные представления слов из текста в один большой вектор.

Проходимся по нему одномерной сверткой, размер и шаг которой кратен размерности слово-вектора (т.е. каждый раз мы сворачиваем векторные представления одного или нескольких подряд идущих слов).

Далее либо выбираем максимальный элемент из полученного в результате свертки вектора (т.е. используем Global Max Pooling), либо просто усредняем все его элементы (Global Average Pooling). Таким образом каждый фильтр в сети преобразует текст произвольной длины в единственное число.

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

Используя множество таких фильтров с различным размером и шагом получаем векторное представление исходного текста, которое затем используется в полносвязных слоях.

Более подробно о сверточных сетях для обработки текстов можно почитать http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp, а также посмотреть https://youtu.be/Lg6MZw_OOLI?list=PL3FW7Lu3i5Jsnh1rnUwq_TcylNr7EkRe6. А о применении сверточных сетей и их достоинствах и недостатках конкретно в этой задаче очень хорошо написано здесь https://www.kaggle.com/rethfro/1d-cnn-single-model-score-0-14-0-16-or-0-23.

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


Авторизуйтесь, чтобы оставлять комментарии

Использование материалов, размещенных на сайте, разрешается при условии прямой гиперссылки на dev.by. Ссылка должна быть размещена в подзаголовке или в первом абзаце публикации.
datahata — хостинг в Беларуси