Эмбеддинги признаков и повышение точности ML-моделей
Прим. Wunder Fund: короткая статья о том, как эмбеддинги могут помочь при работе с категориальными признаками и сетками. А если вы и так умеете в сетки — то мы скоро открываем набор рисерчеров и будем рады с вами пообщаться, stay tuned.
Создание эмбеддингов признаков (feature embeddings) — это один из важнейших этапов подготовки табличных данных, используемых для обучения нейросетевых моделей. Об этом подходе к подготовке данных, к сожалению, редко говорят в сферах, не связанных с обработкой естественных языков. И, как следствие, его почти полностью обходят стороной при работе со структурированными наборами данных. Но то, что его, при работе с такими данными, не применяют, ведёт к значительному ухудшению точности моделей. Это стало причиной появления заблуждения, которое заключается в том, что алгоритмы градиентного бустинга, вроде того, что реализован в библиотеке XGBoost, это всегда — наилучший выбор для решения задач, предусматривающих работу со структурированными наборами данных. Нейросетевые методы моделирования, улучшенные за счёт эмбеддингов, часто дают лучшие результаты, чем методы, основанные на градиентном бустинге. Более того — обе группы методов показывают серьёзные улучшения при использовании эмбеддингов, извлечённых из существующих моделей.
Эта статья направлена на поиск ответов на следующие вопросы:
Что такое эмбеддинги признаков?
Как они используются при работе со структурированными данными?
Если использование эмбеддингов — это столь мощная методика — почему она недостаточно широко распространена?
Как создавать эмбеддинги?
Как использовать существующие эмбеддинги для улучшения других моделей?
Эмбеддинги признаков
Нейросетевые модели испытывают сложности при необходимости работы с разреженными категориальными признаками. Эмбеддинги — это возможность уменьшения размерности таких признаков ради повышения производительности модели. Прежде чем говорить о структурированных наборах данных, полезно будет разобраться с тем, как обычно используются эмбеддинги. В сфере обработки естественных языков (Natural Language Processing, NLP) обычно работают со словарями, состоящими из тысяч слов. Эти словари обычно вводят в модель с использованием методики быстрого кодирования (One-Hot Encoding), что, в математическом смысле, равносильно наличию отдельного столбца для каждого из слов. Когда слово передаётся в модель, в соответствующем столбце оказывается единица, в то время как во всех остальных выводятся нули. Это ведёт к появлению очень сильно разреженных наборов данных. Решение этой проблемы заключается в создании эмбеддинга.

По сути дела, речь идёт о том, что эмбеддинг, на основе обучающего текста, группирует слова со сходным значением и возвращает их местоположение. Например — значение эмбеддинга слова «fun» может быть подобно значениям эмбеддингов слов «humor» и «dancing», или словосочетания «machine learning». На практике нейронные сети демонстрируют значительное улучшение производительности при применении подобных репрезентативных свойств.
Эмбеддинги и структурированные данные
Структурированные наборы данных тоже часто содержат разреженные категориальные признаки. В вышеприведённой таблице, содержащей данные о продажах, имеются столбцы с почтовым индексом и идентификатором магазина. Так как в этих столбцах могут присутствовать сотни или тысячи уникальных значений, использование этих столбцов приведёт к появлению тех же проблем с производительностью, о которых мы говорили ранее в применении к NLP-моделям. Почему бы тут, по вышеописанной схеме, не воспользоваться эмбеддингами?
Проблема заключается в том, что сейчас мы имеем дело более чем с одним признаком. В данном случае это два отдельных разреженных категориальных столбца ( zip_code и store_id ), а так же другие ценные признаки, вроде общего объёма продаж конкретному клиенту. Мы просто не можем заложить все эти признаки в эмбеддинг. Но мы, однако, можем подготовить эмбеддинги в первом слое модели и добавить в модель, вместе с эмбеддингами, и обычные признаки. Это не только приведёт к преобразованию почтового индекса и идентификатора магазина в ценные признаки, но и позволит не «размывать» другие ценные признаки тысячами столбцов.

Почему эмбеддингам не уделяют достаточно внимания?
В крупнейших компаниях, занимающихся машинным обучением (Machine Learning, ML), эта техника работы с данными, бесспорно, применяется. Проблема заключается в том, что подавляющее большинство дата-сайентистов, находящихся за пределами таких компаний, никогда даже и не слышали о таком способе использования эмбеддингов. Почему это так? Хотя я и не сказал бы, что эти методы чрезвычайно сложно реализовать, они сложнее того, чему учат на типичных онлайн-курсах. Большинство начинающих ML-практиков просто не научили тому, как использовать эмбеддинги вместе с другими некатегориальными признаками. В результате признаки вроде почтовых индексов и идентификаторов магазинов просто выбрасывают из модели. Но это — важные данные!
Некоторые значения признака можно включить в модель, воспользовавшись другими техниками, вроде метода среднего кодирования (Mean Encoding), но подобное даёт лишь небольшие улучшения. Это привело к тенденции, когда от нейросетевых моделей полностью отказываются, выбирая методы, задействующие алгоритмы градиентного бустинга, которые способны лучше работать с категориальными признаками. Но, как уже было сказано, эмбеддинги могут улучшить оба метода моделирования. Сейчас мы разовьём эту тему.
Создание эмбеддингов
Самое сложное при работе с эмбеддингами — разобраться с наборами данных TensorFlow. И хотя устроены они совсем не так понятно, как датафреймы Pandas, научиться работать с ними весьма полезно. В частности — полезно для того, кто хотя бы задумывается о том, чтобы его модели работали бы с огромными наборами данных, и для того, кто стремится к созданию более сложных нейросетевых моделей, чем те, которые он уже разрабатывает.
В этом примере мы воспользуемся гипотетической таблицей продаж, фрагмент которой приведён выше. Наша цель заключается в прогнозировании целевых месячных продаж. Мы, ради краткости и простоты, опустим то, что имеет отношение к дата-инжинирингу, и начнём с предварительно подготовленных датафреймов Pandas. При работе с более крупными наборами данных, скорее всего, работа начнётся не с датафреймов, но это — тема для отдельной статьи.
Первый шаг заключается в преобразовании датафреймов Pandas в наборы данных TensorFlow:
Здесь стоит обратить внимание на то, что наборы данных TensorFlow и результаты дальнейших преобразований данных не хранятся в памяти так же, как хранятся датафреймы Pandas. Они, по сути, представляют собой конвейер. Через него, пакет за пакетом, походят данные, что позволяет модели эффективно обучаться на наборах данных, которые слишком велики для размещения их в памяти. Именно поэтому мы преобразуем в наборы данных словари датафреймов, а не реальные данные. Обратите внимание на то, что мы, кроме прочего, задаём сейчас, а не во время обучения модели, размер пакета данных, поступая не так, как обычно поступают при использовании API Keras.
Теперь, пользуясь столбцами признаков TensorFlow, можно определить конвейеры для обработки данных. Существуют разные способы решения этой задачи, выбор конкретного варианта зависит от типов признаков, хранящихся в таблице. Подробности об этом можно узнать в документации TensorFlow по столбцам признаков.
Обратите внимание на то, что при создании эмбеддингов нам нужно указать число измерений. Это определяет то количество признаков, к которому будут сведены признаки из категориальных столбцов. Общее правило заключается в том, что результирующее количество признаков выбирается равным корню четвёртой степени из общего количества категорий (например — 1000 уникальных почтовых индексов сводятся к
6 столбцам эмбеддинга). Но это — один из тех параметров, которые можно настраивать в собственной модели.
Теперь создадим простую модель:
Поздравляю! Вы только что обучили модель, в которой используются эмбеддинги. А как извлечь эмбеддинги из модели для использования их в других моделях? Достаточно просто взять из модели веса:
Обратите внимание на то, что порядок слоёв эмбеддингов может меняться, поэтому проверьте соответствие длины слоя весов количеству уникальных значений, объявленных выше. Это позволит вам убедиться в том, что вы взяли данные из правильного слоя. Теперь веса можно сохранить в датафрейме:
Итоги
Теперь, когда эмбеддинги сохранены, их можно включить в состав исходного набора данных. Можно даже ввести их в другие наборы данных, используя те же категориальные признаки. Я пока не встречался с ситуациями, когда использование расширенных наборов данных не приводило бы к росту точности моделей. Попробуйте эмбеддинги — и я обещаю, что их применение станет частью вашей обычной работы в сфере машинного обучения.
О, а приходите к нам работать? 😏
Мы в wunderfund.io занимаемся высокочастотной алготорговлей с 2014 года. Высокочастотная торговля — это непрерывное соревнование лучших программистов и математиков всего мира. Присоединившись к нам, вы станете частью этой увлекательной схватки.
Мы предлагаем интересные и сложные задачи по анализу данных и low latency разработке для увлеченных исследователей и программистов. Гибкий график и никакой бюрократии, решения быстро принимаются и воплощаются в жизнь.
Сейчас мы ищем плюсовиков, питонистов, дата-инженеров и мл-рисерчеров.
Чудесный мир Word Embeddings: какие они бывают и зачем нужны?
Начать стоит от печки, то есть с постановки задачи. Откуда берется сама задача word embedding?
Лирическое отступление: К сожалению, русскоязычное сообщество еще не выработало единого термина для этого понятия, поэтому мы будем использовать англоязычный.
Сам по себе embedding — это сопоставление произвольной сущности (например, узла в графе или кусочка картинки) некоторому вектору.
Сегодня мы говорим про слова и стоит обсудить, как делать такое сопоставление вектора слову.
Вернемся к предмету: вот у нас есть слова и есть компьютер, который должен с этими словами как-то работать. Вопрос — как компьютер будет работать со словами? Ведь компьютер не умеет читать, и вообще устроен сильно иначе, чем человек. Самая первая идея, приходящая в голову — просто закодировать слова цифрами по порядку следования в словаре. Идея очень продуктивна в своей простоте — натуральный ряд бесконечен и можно перенумеровать все слова, не опасаясь проблем. (На секунду забудем про ограничения типов, тем более, в 64-битное слово можно запихнуть числа от 0 до 2^64 — 1, что существенно больше количества всех слов всех известных языков.)
Но у этой идеи есть и существенный недостаток: слова в словаре следуют в алфавитном порядке, и при добавлении слова нужно перенумеровывать заново большую часть слов. Но даже это не является настолько важным, а важно то, буквенное написание слова никак не связано с его смыслом (эту гипотезу еще в конце XIX века высказал известный лингвист Фердинанд де Соссюр). В самом деле слова “петух”, “курица” и “цыпленок” имеют очень мало общего между собой и стоят в словаре далеко друг от друга, хотя очевидно обозначают самца, самку и детеныша одного вида птицы. То есть мы можем выделить два вида близости слов: лексический и семантический. Как мы видим на примере с курицей, эти близости не обязательно совпадают. Можно для наглядности привести обратный пример лексически близких, но семантически далеких слов — «зола» и «золото». (Если вы никогда не задумывались, то имя Золушка происходит именно от первого.)
Чтобы получить возможность представить семантическую близость, было предложено использовать embedding, то есть сопоставить слову некий вектор, отображающий его значение в “пространстве смыслов”.
Какой самый простой способ получить вектор из слова? Кажется, что естественно будет взять вектор длины нашего словаря и поставить только одну единицу в позиции, соответствующей номеру слова в словаре. Этот подход называется one-hot encoding (OHE). OHE все еще не обладает свойствами семантической близости:
Значит нам нужно найти другой способ преобразования слов в вектора, но OHE нам еще пригодится.
Отойдем немного назад — значение одного слова нам может быть и не так важно, т.к. речь (и устная, и письменная) состоит из наборов слов, которые мы называем текстами. Так что если мы захотим как-то представить тексты, то мы возьмем OHE-вектор каждого слова в тексте и сложим вместе. Т.е. на выходе получим просто подсчет количества различных слов в тексте в одном векторе. Такой подход называется “мешок слов” (bag of words, BoW), потому что мы теряем всю информацию о взаимном расположении слов внутри текста.
Но несмотря на потерю этой информации так тексты уже можно сравнивать. Например, с помощью косинусной меры.
Мы можем пойти дальше и представить наш корпус (набор текстов) в виде матрицы “слово-документ” (term-document). Стоит отметить, что в области информационного поиска (information retrieval) эта матрица носит название «обратного индекса» (inverted index), в том смысле, что обычный/прямой индекс выглядит как «документ-слово» и очень неудобен для быстрого поиска. Но это опять же выходит за рамки нашей статьи.
Эта матрица приводит нас к тематическим моделям, где матрицу “слово-документ” пытаются представить в виде произведения двух матриц “слово-тема” и “тема-документ”. В самом простом случае мы возьмем матрицу и с помощью SVD-разложения получим представление слов через темы и документов через темы:
Здесь — слова,
— документы. Но это уже будет предметом другой статьи, а сейчас мы вернемся к нашей главной теме — векторному представлению слов.
Пусть у нас есть такой корпус:
С помощью SVD-преобразования, выделим только первые две компоненты, и нарисуем:
Что интересного на этой картинке? То, что Титан и Ио — далеко друг от друга, хотя они оба являются спутниками Сатурна, но в нашем корпусе про это ничего нет. Слова «атмосфера» и «Сатурн» очень близко друг другу, хотя не являются синонимами. В то же время «два» и «много» стоят рядом, что логично. Но общий смысл этого примера в том, что результаты, которые вы получите очень сильно зависят от корпуса, с которым вы работаете. Весь код для получения картинки выше можно посмотреть здесь.
Логика повествования выводит на следующую модификацию матрицы term-document — формулу TF-IDF. Эта аббревиатура означает «term frequency — inverse document frequency».
Давайте попробуем разобраться, что это такое. Итак, TF — это частота слова в тексте
, здесь нет ничего сложного. А вот IDF — существенно более интересная вещь: это логарифм обратной частоты распространенности слова
в корпусе
. Распространенностью называется отношение числа текстов, в которых встретилось искомое слово, к общему числу текстов в корпусе. С помощью TF-IDF тексты также можно сравнивать, и делать это можно с меньшей опаской, чем при использовании обычных частот.
Новая эпоха
Описанные выше подходы были (и остаются) хороши для времен (или областей), где количество текстов мало и словарь ограничен, хотя, как мы видели, там тоже есть свои сложности. Но с приходом в нашу жизнь интернета все стало одновременно и сложнее и проще: в доступе появилось великое множество текстов, и эти тексты с изменяющимся и расширяющимся словарем. С этим надо было что-то делать, а ранее известные модели не могли справиться с таким объемом текстов. Количество слов в английском языке очень грубо составляет миллион — матрица совместных встречаемостей только пар слов будет 10^6 x 10^6. Такая матрица даже сейчас не очень лезет в память компьютеров, а, скажем, 10 лет назад про такое можно было не мечтать. Конечно, были придуманы множество способов, упрощающих или распараллеливающих обработку таких матриц, но все это были паллиативные методы.
И тогда, как это часто бывает, был предложен выход по принципу “тот, кто нам мешает, тот нам поможет!” А именно, в 2013 году тогда мало кому известный чешский аспирант Томаш Миколов предложил свой подход к word embedding, который он назвал word2vec. Его подход основан на другой важной гипотезе, которую в науке принято называть гипотезой локальности — “слова, которые встречаются в одинаковых окружениях, имеют близкие значения”. Близость в данном случае понимается очень широко, как то, что рядом могут стоять только сочетающиеся слова. Например, для нас привычно словосочетание «заводной будильник». А сказать “заводной апельсин” мы не можем* — эти слова не сочетаются.
Основываясь на этой гипотезе Томаш Миколов предложил новый подход, который не страдал от больших объемов информации, а наоборот выигрывал [1].
Модель, предложенная Миколовым очень проста (и потому так хороша) — мы будем предсказывать вероятность слова по его окружению (контексту). То есть мы будем учить такие вектора слов, чтобы вероятность, присваиваемая моделью слову была близка к вероятности встретить это слово в этом окружении в реальном тексте.
Здесь — вектор целевого слова,
— это некоторый вектор контекста, вычисленный (например, путем усреднения) из векторов окружающих нужное слово других слов. А
— это функция, которая двум векторам сопоставляет одно число. Например, это может быть упоминавшееся выше косинусное расстояние.
Приведенная формула называется softmax, то есть “мягкий максимум”, мягкий — в смысле дифференцируемый. Это нужно для того, чтобы наша модель могла обучиться с помощью backpropagation, то есть процесса обратного распространения ошибки.
Процесс тренировки устроен следующим образом: мы берем последовательно (2k+1) слов, слово в центре является тем словом, которое должно быть предсказано. А окружающие слова являются контекстом длины по k с каждой стороны. Каждому слову в нашей модели сопоставлен уникальный вектор, который мы меняем в процессе обучения нашей модели.
В целом, этот подход называется CBOW — continuous bag of words, continuous потому, что мы скармливаем нашей модели последовательно наборы слов из текста, a BoW потому что порядок слов в контексте не важен.
Также Миколовым сразу был предложен другой подход — прямо противоположный CBOW, который он назвал skip-gram, то есть “словосочетание с пропуском”. Мы пытаемся из данного нам слова угадать его контекст (точнее вектор контекста). В остальном модель не претерпевает изменений.
Что стоит отметить: хотя в модель не заложено явно никакой семантики, а только статистические свойства корпусов текстов, оказывается, что натренированная модель word2vec может улавливать некоторые семантические свойства слов. Классический пример из работы автора:
Слово «мужчина» относится к слову «женщина» так же, как слово «дядя» к слову «тётя», что для нас совершенно естественно и понятно, но в других моделям добиться такого же соотношения векторов можно только с помощью специальных ухищрений. Здесь же — это происходит естественно из самого корпуса текстов. Кстати, помимо семантических связей, улавливаются и синтаксические, справа показано соотношение единственного и множественного числа.
Более сложные вещи
На самом деле, за прошедшее время были предложены улучшения ставшей уже также классической модели Word2Vec. Два самых распространенных будут описаны ниже. Но этот раздел может быть пропущен без ущерба для понимания статьи в целом, если покажется слишком сложным.
Negative Sampling
В стандартной модели CBoW, рассмотренной выше, мы предсказываем вероятности слов и оптимизируем их. Функцией для оптимизации (минимизации в нашем случае) служит дивергенция Кульбака-Лейблера:
Здесь — распределение вероятностей слов, которое мы берем из корпуса,
— распределение, которое порождает наша модель. Дивергенция — это буквально «расхождение», насколько одно распределение не похоже на другое. Т.к. наши распределения на словах, т.е. являются дискретными, мы можем заменить в этой формуле интеграл на сумму:
Оказалось так, что оптимизировать эту формулу достаточно сложно. Прежде всего из-за того, что рассчитывается с помощью softmax по всему словарю. (Как мы помним, в английском сейчас порядка миллиона слов.) Здесь стоит отметить, что многие слова вместе не встречаются, как мы уже отмечали выше, поэтому большая часть вычислений в softmax является избыточной. Был предложен элегантный обходной путь, который получил название Negative Sampling. Суть этого подхода заключается в том, что мы максимизируем вероятность встречи для нужного слова в типичном контексте (том, который часто встречается в нашем корпусе) и одновременно минимизируем вероятность встречи в нетипичном контексте (том, который редко или вообще не встречается). Формулой мысль выше записывается так:
Здесь — точно такой же, что и в оригинальной формуле, а вот остальное несколько отличается. Прежде всего стоит обратить внимание на то, что формуле теперь состоит из двух частей: позитивной (
) и негативной (
). Позитивная часть отвечает за типичные контексты, и
здесь — это распределение совместной встречаемости слова
и остальных слов корпуса. Негативная часть — это, пожалуй, самое интересное — это набор слов, которые с нашим целевым словом встречаются редко. Этот набор порождается из распределения
, которое на практике берется как равномерное по всем словам словаря корпуса. Было показано, что такая функция приводит при своей оптимизации к результату, аналогичному стандартному softmax [2].
Hierarchical SoftMax
Также люди зашли и с другой стороны — можно не менять исходную формулу, а попробовать посчитать сам softmax более эффективно. Например, используя бинарное дерево [3]. По всем словам в словаре строится дерево Хаффмана. В полученном дереве слов располагаются на листьях дерева.
На рисунке изображен пример такого бинарного дерева. Жирным выделен путь от корня до слова . Длину пути обозначим
, а
-ую вершину на пути к слову
обозначим через
. Можно доказать, что внутренних вершин (не листьев)
.
С помощью иерархического softmax вектора предсказывается для
внутренних вершин. А вероятность того, что слово
будет выходным словом (в зависимости от того, что мы предсказываем: слово из контекста или заданное слово по контексту) вычисляется по формуле:
где — функция softmax;
;
— левый сын вершины
;
, если используется метод skip-gram,
, то есть, усредненный вектор контекста, если используется CBOW.
Формулу можно интуитивно понять, представив, что на каждом шаге мы можем пойти налево или направо с вероятностями:
Затем на каждом шаге вероятности перемножаются ( шагов) и получается искомая формула.
При использовании простого softmax для подсчета вероятности слова, приходилось вычислять нормирующую сумму по всем словам из словаря, требовалось операций. Теперь же вероятность слова можно вычислить при помощи последовательных вычислений, которые требуют
.
Другие модели
Помимо word2vec были, само собой, предложены и другие модели word embedding. Стоит отметить модель, предложенную лабораторией компьютерной лингвистики Стенфордского университета, под названием Global Vectors (GloVe), сочетающую в себе черты SVD разложения и word2vec [4].
Также надо упомянуть о том, что т.к. изначально все описанные модели были предложены для английского языка, то там не так остро стоит проблема словоизменения, характерная для синтетических языков (это — лингвистический термин), вроде русского. Везде выше по тексту неявно предполагалось, что мы либо считаем разные формы одного слова разными словами — и тогда надеяться, что нашего корпуса будет достаточно модели, чтобы выучить их синтаксическую близость, либо используем механизмы стеммирования или лемматизации. Стеммирование — это обрезание окончания слова, оставление только основы (например, “красного яблока” превратится в “красн яблок”). А лемматизация — замена слова его начальной формой (например, “мы бежим” превратится в “я бежать”). Но мы можем и не терять эту информацию, а использовать ее — закодировав OHE в новый вектор, и сконкатинировать его с вектором для основы или леммы.
Еще стоит сказать, что то, с чем мы начинали — буквенное представление слова — тоже не кануло в Лету: предложены модели по использованию буквенного представления слова для word embedding [5].
Практическое применение
Мы поговорили о теории, пришло время посмотреть, к чему все вышеописанное применимо на практике. Ведь любая самая красивая теория без практического применения — не более чем игра ума. Рассмотрим применение Word2Vec в двух задачах:
1) Задача классификации, необходимо по последовательности посещенных сайтов определять пользователя;
2) Задача регрессии, необходимо по тексту статьи определить ее рейтинг на Хабрахабре.
Классификация
Cкачать данные для первой задачи можно со страницы соревнования «Catch Me If You Can»
Т.к. сейчас мы каждому слову сопоставили вектор, то нужно решить, что сопоставить целому предложению из слов.
Один из возможных вариантов это просто усреднить все слова в предложении и получить некоторый смысл всего предложения (если слова нет в тексте, то берем нулевой вектор).
Т.к. мы получили distributed representation, то никакое число по отдельности ничего не значит, а значит лучше всего покажут себя линейные алгоритмы. Попробуем нейронные сети, LogisticRegression и проверим нелинейный метод XGBoost.
Получили неплохой результат. Значит Word2Vec смог выявить зависимости между сессиями.
Посмотрим, что произойдет с алгоритмом XGBoost.
Видим, что алгоритм сильно подстраивается под обучающую выборку, поэтому возможно наше предположение о необходимости использовать линейные алгоритмы подтверждено.
Посмотрим, что покажет обычный LogisticRegression.
Попробуем улучшить результаты.
Теперь вместо обычного среднего, чтобы учесть частоту с которой слово встречается в тексте, возьмем взвешенное среднее. В качестве весов возьмем IDF. Учёт IDF уменьшает вес широко употребительных слов и увеличивает вес более редких слов, которые могут достаточно точно указать на то, к какому классу относится текст. В нашем случае, кому принадлежит последовательность посещенных сайтов.
Проверим изменилось ли качество LogisticRegression.
видим прирост на 0.07, значит скорее всего взвешенное среднее помогает лучше отобразить смысл всего предложения через word2vec.
Предсказание популярности
Попробуем Word2Vec уже в текстовой задаче — предсказании популярности статьи на Хабрхабре.
Испробуем силы алгоритма непосредственно на текстовых данных статей Хабра. Мы преобразовали данные в csv таблицы. Скачать их вы можете здесь: train, test.
‘Доброго хабрадня!
\r\n
\r\nПерейду сразу к сути. С недавнего времени на меня возложилась задача развития контекстной сети текстовых объявлений. Задача возможно кому-то покажется простой, но есть несколько нюансов. Страна маленькая, 90% интернет-пользователей сконцентрировано в одном городе. С одной стороны легко охватить, с другой стороны некуда развиваться.
\r\n
\r\nТак как развитие интернет-проектов у нас слабое, и недоверие клиентов к местным проектам преобладает, то привлечь рекламодателей тяжело. Но самое страшное это привлечь площадки, которые знают и Бегун и AdSense, но абсолютно не знают нас. В целом проблема такая: площадки не регистрируются, потому что нет рекламодателей с деньгами, а рекламодатели не дают объявления, потому что список площадок слаб.
\r\n
\r\nКак выходят из такого положения Хабраспециалисты?’
Будем обучать модель на всем содержании статьи. Для этого совершим некоторые преобразования над текстом.
Напишем функцию, которая будет преобразовывать тестовую статью в лист из слов необходимый для обучения Word2Vec.
Функция получает строку, в которой содержится весь текстовый документ.
1) Сначала функция будет удалять все символы кроме букв верхнего и нижнего регистра;
2) Затем преобразовывает слова к нижнему регистру;
3) После чего удаляет стоп слова из текста, т.к. они не несут никакой информации о содержании;
4) Лемматизация, процесс приведения словоформы к лемме — её нормальной (словарной) форме.
Функция возвращает лист из слов.
Лемматизация занимает много времени, поэтому ее можно убрать в целях более быстрых подсчетов.
Будем обучаться на 2015 году, а валидироваться по первым 4 месяцам 2016, т.к. в нашей тестовой выборке представлены данные за первые 4 месяца 2017 года. Более правдивую валидацию можно сделать, идя по годам, увеличивая нашу обучающую выборку и смотря качество на первых четырех месяцах следующего года
Посмотрим чему выучилась модель:
[(‘massive’, 0.6958945393562317),
(‘mining’, 0.6796239018440247),
(‘scientist’, 0.6742461919784546),
(‘visualization’, 0.6403135061264038),
(‘centers’, 0.6386666297912598),
(‘big’, 0.6237790584564209),
(‘engineering’, 0.6209672689437866),
(‘structures’, 0.609510600566864),
(‘knowledge’, 0.6094595193862915),
(‘scientists’, 0.6050446629524231)]
Модель обучилась достаточно неплохо, посмотрим на результаты алгоритмов:
Попробуем нейронные сети.
Train on 23425 samples, validate on 7556 samples
Epoch 1/20
1s — loss: 1.7292 — val_loss: 0.7336
Epoch 2/20
0s — loss: 1.2382 — val_loss: 0.6738
Epoch 3/20
0s — loss: 1.1379 — val_loss: 0.6916
Epoch 4/20
0s — loss: 1.0785 — val_loss: 0.6963
Epoch 5/20
0s — loss: 1.0362 — val_loss: 0.6256
Epoch 6/20
0s — loss: 0.9858 — val_loss: 0.6393
Epoch 7/20
0s — loss: 0.9508 — val_loss: 0.6424
Epoch 8/20
0s — loss: 0.9066 — val_loss: 0.6231
Epoch 9/20
0s — loss: 0.8819 — val_loss: 0.6207
Epoch 10/20
0s — loss: 0.8634 — val_loss: 0.5993
Epoch 11/20
1s — loss: 0.8401 — val_loss: 0.6093
Epoch 12/20
1s — loss: 0.8152 — val_loss: 0.6006
Epoch 13/20
0s — loss: 0.8005 — val_loss: 0.5931
Epoch 14/20
0s — loss: 0.7736 — val_loss: 0.6245
Epoch 15/20
0s — loss: 0.7599 — val_loss: 0.5978
Epoch 16/20
1s — loss: 0.7407 — val_loss: 0.6593
Epoch 17/20
1s — loss: 0.7339 — val_loss: 0.5906
Epoch 18/20
1s — loss: 0.7256 — val_loss: 0.5878
Epoch 19/20
1s — loss: 0.7117 — val_loss: 0.6123
Epoch 20/20
0s — loss: 0.7069 — val_loss: 0.5948
Получили более хороший результат по сравнению с гребневой регрессией.
Заключение
Word2Vec показал свою пользу на практических задачах анализа текстов, все-таки не зря на текущий момент на практике используется в основном именно он и — гораздо менее популярный — GloVe. Тем не менее, может быть в вашей конкретной задаче, вам пригодятся подходы, которым для эффективной работы не требуются такие объемы данных, как для word2vec.
Код ноутбуков с примерами можно взять здесь. Код практического применения — вот тут.
Пост написан совместно с demonzheg.
Литература
* Да, это специальная пасхалка для любителей творчества Энтони Бёрджеса.





