Комплексное тематическое исследование данных прогнозирования кликов Avazu Ad

В части-1 этого блога мы провели предварительный анализ данных о кликах по объявлениям Avazu и разработали 5 новых функций, которые были бы очень полезны для решения нашей проблемы. Теперь мы увидим, как использовать эти данные во второй части, где мы применяем различные модели машинного обучения, чтобы различать объявления, на которые будут нажимать, и объявления, на которые не будут нажимать.



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

Сопоставление бизнес-проблемы с проблемой машинного обучения:

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

Показатели эффективности

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

Цель машинного обучения

Нам нужно, чтобы наша модель имела как можно меньше потерь в тестовом бинарном журнале.

Ограничения

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

Весь код доступен в моем репозитории GitHub «https://github.com/Rohan-Thoma/Mobile_Ad_Click_Prediction».



Подготовка данных

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

Проверим распределение классов в поезде и тестовые данные.

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

Кодирование функций

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

Здесь мы определяем 2 функции, где одна функция соответствует данным поезда, а другая функция преобразует любые заданные данные.

  • Здесь мы вычисляем CTR (Click Through Rate) для каждого значения, которое принимает категориальная функция, и для новых значений, которые функция принимает в тестовых данных, вычисляется средний CTR для этой функции, а затем подставляется для этих случаев. Метод, используемый для вычисления CTR, тот же, что и при исследовательском анализе данных.
  • Здесь функция 'response_fit()' принимает имя функции и фрейм данных в качестве параметров, вычисляет CTR и сохраняет их в словаре с именем «vocab». также вычисляет средний CTR, а затем возвращает их оба.
  • Функция 'response_transform()' берет словарь словаря, средний CTR и набор данных, а затем кодирует данные, сопоставляя значения со значениями в данных поезда с помощью словарного запаса.

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

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

У нас также есть некоторые полезные функции для построения матрицы путаницы вместе с матрицами прецессии и отзыва, которые вы можете увидеть в репозитории GitHub.

Модели машинного обучения

1. Случайная модель

Теперь мы измерим производительность случайной модели, как показано ниже.

  • Здесь мы генерируем 2 случайных числа, которые представляют значения вероятности для каждого класса, которые в сумме дают 1, поэтому мы просто генерируем 2 случайных числа и делим каждое число на сумму 2 чисел, что просто нормализует их.
  • Затем мы берем значения вероятности одного из столбцов и вычисляем логарифмическую потерю, беря эти значения вместо прогнозируемой вероятности значений y класса 1 и сравнивая их с фактическими метками класса набора данных.

  • Теперь мы построим матрицы путаницы, точности и отзыва ниже.

Значения матрицы путаницы абсолютно случайны, и это то, что мы ожидаем от действительно случайной модели.

2. Логистическая регрессия с настройкой гиперпараметров

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

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

  • Мы получили наименьшую логарифмическую потерю 0,40037 для альфа = 0,00001.
  • Поскольку общее количество точек данных составляет 5 миллионов, а затем обучаем данные как 4 миллиона точек данных и 1 миллион точек данных в качестве данных проверки.

  • Тестовая логарифмическая потеря, которую мы получили для логистической регрессии с настройкой гиперпараметров, составляет 0,40037, что намного лучше, чем случайная модель, логарифмическая потеря которой составляет 0,8857. Это означает, что наши функции работают.
  • Глядя на матрицы точности и полноты, мы видим, что модель предсказывает множество точек данных класса 1 как точки данных класса 0, но это ожидается, поскольку эта проблема имеет дисбаланс классов.
  • Давайте посмотрим, сможем ли мы улучшить потери журнала с помощью деревьев решений с градиентным усилением.

3. Градиентные деревья решений (XGBoost)

Да, я знаю, что все еще использую XGboost в то время, когда текущая тенденция использует LightGBM и CatBoost, потому что XGboost превосходит их с точки зрения чистой производительности, когда мы правильно настраиваем параметры, выделяя ресурсы и время.

Здесь мы настраиваем параметры с помощью случайного поиска. Случайный поиск предполагает оценочную метрику, которая означает, что чем больше значение оценочной функции, тем лучше модель. Но потеря журнала — это метрика потерь, а не метрика оценки, поэтому нам нужно передать отрицательное значение потери журнала, что можно сделать с помощью создания пользовательской потери журнала с помощью функции make_scorer () обучения scikit.

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

  • Тестовые потери в журнале для деревьев решений с градиентным усилением с настройкой гиперпараметров составляют 0,39475, что является очень хорошим улучшением по сравнению с предыдущей потерей 0,40037, который мы получили для логистической регрессии.
  • Значение точности для класса 1 также улучшено с 0,587 до 0,609. Если мы наблюдаем это значение, это незначительное улучшение по сравнению с линейной моделью. Таким образом, это показывает, что нелинейные модели работают хорошо по сравнению с линейными моделями для заданных данных.

4. Полевые машины факторизации (FFM)

Большинство наборов данных в прогнозе CTR содержат признаки, которые по своей природе разрежены, поэтому нам нужно извлечь значимые признаки из набора данных, и здесь в игру вступает FFM.

Оригинал исследования можно прочитать здесь. Машины факторизации — это нелинейные модели, которые можно использовать для извлечения скрытых функций в наборе данных. Когда мы их применяем, они изучают неявное векторное представление для каждой функции, и каждый скрытый вектор содержит k измерений, так что эффекты комбинаций функций моделируются как внутренние произведения между двумя скрытыми векторами. Здесь FFM представляет собой небольшую эволюцию FM, где они учитывают поле признака, которое также представлено в виде скрытых векторов. Идея FFM исходит из PITF (тензорная факторизация парного взаимодействия), которая используется в системе рекомендаций с персонализированными тегами. Об этом рассказывается в этой бумаге. Для прогнозов CTR функции, как правило, могут быть сгруппированы в поля, и FFM будут в полной мере использовать эту доступную групповую информацию и т. д. Хорошее объяснение дано в блоге ниже.



Первоначальная реализация авторов оригинальной статьи изобилует собственным набором проблем и проблем совместимости, потому что они создали эту реализацию на C++. Здесь есть альтернативная реализация на чистом питоне под названием pyffm, которая очень проста в использовании и точно основана на оригинальной статье, но на питоне.



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

Здесь мы отображаем значение логарифмических потерь для каждого значения параметра регуляризации.

  • Здесь мы получили наименьшую логарифмическую потерю 0,406, что не лучше, чем логистическая регрессия, несмотря на то, что сама по себе является нелинейной моделью.
  • Это может быть связано с тем, что нам, возможно, придется передавать разреженные закодированные функции в модель, как указано в соответствующей статье.

  • Здесь мы видим, что значение точности для точек данных класса 1 также упало до 0,487, что хуже, чем то, что мы получили с помощью логистической регрессии. , так что это также показывает важность использования значений точности и полноты в качестве метрики, которая дает нам представление о производительности в реальном мире, кроме одного значения, такого как потери журнала.

Разработка функций

Функции ГБДТ:

Функции GBDT, которые предлагаются в этом документе, в котором описывается решение-победитель по рекомендации товаров электронной коммерции от RecSys 2015. Они предложили различные дополнительные функции, такие как функции GBDT, к которым положение экземпляра на каждом из деревьев представляет его отношение. функция стоимости на различных этапах процесса градиентного спуска. Это может быть очень полезным источником информации. Мы используем его следующим образом: идентификаторы деревьев обрабатываются как различные типы функций, а индексы конечных узлов, на которые попадает экземпляр, обрабатываются как значения, которые принимают эти функции. Здесь мы можем определить количество деревьев, и это будет равно количеству дополнительных функций, которые мы генерируем.

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

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

  • Здесь мы получили 100 новых функций, а также 19 оригинальных функций и 5 ранее разработанных функций, всего 124 функции.

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

5. Логистическая регрессия с настройкой гиперпараметров с функциями GBDT

Код такой же, как мы видели выше.

  • Здесь мы получили наименьшую логарифмическую потерю 0,3966 для значений альфа = 0,01, как мы можем видеть на графике.
  • Это, безусловно, очень хорошее улучшение по сравнению с предыдущим значением потерь 0,40037 без функций GBDT. Следовательно, это показывает эффективность новых функций.

  • Мы можем видеть небольшое улучшение значения точности класса 1 с 0,59 до 0,603, хотя оно и небольшое, но все же это улучшение, которое выгодно для нас.
  • Поскольку эти функции выглядят многообещающе, потому что они дали хороший результат с логистической регрессией, мы можем ожидать еще лучших результатов, используя также нелинейные модели, такие как GBDT.

6. XG-Boost с гиперпараметрической настройкой 2 параметров

  • Здесь размерность наших данных значительно увеличилась с добавлением функций GBDT. Теперь, когда у нас есть 124-мерные данные и все функции плотные, мы столкнемся с проблемами памяти, если продолжим обучение модели с помощью предыдущего метода.
  • Нам нужно обучать модель партиями, но нет реализации пакетного обучения со случайным поиском scikitlearn, хотя официальная документация XG-Boost поддерживает пакетное обучение. Итак, нам нужно написать собственный код для настройки, как показано ниже.
  • Сначала мы настроим 2 важных параметра: количество базовых оценок и глубину деревьев в режиме поиска по сетке с 12 возможными комбинациями. Мы будем тренироваться с размером пакета 0,5 миллиона точек данных, так как у нас всего 4 миллиона точек данных для данных поезда.

Здесь мы можем уменьшить потери журнала до 0,3936, настроив всего 2 параметра, что на данный момент является лучшим результатом. Итак, мы постараемся настроить все параметры и проверить, можем ли мы еще улучшить результаты.

7. XG-Boost с гиперпараметрической настройкой 4 параметров с функциями GBDT

  • Здесь мы настроим 4 параметра: количество базовых оценок, глубину дерева, выборку столбца по дереву и подвыборку со 144 возможными комбинациями в стиле поиска по сетке. Да, это звучит безумно, но если нам нужно опробовать все комбинации параметров, чтобы получить результаты, и нам нужно оставить систему на ночь.
  • Код очень похож на приведенный выше, но с большим количеством циклов. Полный код всегда можно посмотреть в моем репозитории здесь.
  • После получения лучших параметров нам нужно обучить модель с лучшими параметрами, чтобы получить лучшую модель и получить метрики на тестовых данных.

  • Здесь мы можем получить лучшие потери журнала с функциями GBDT и с XG-Boost, и мы получили лучшие потери журнала 0,39065 до сих пор, что лучше, чем предыдущие потери журнала с настройкой только 2 функций, где мы получили 0,393065 .
  • Здесь мы можем уменьшить логарифмическую потерю на 0,003, настроив всего 4 параметра, что является значительным улучшением, это изменение также отражено в результатах матрицы путаницы, где мы можем повысить точность класса 1 до 0,607, что является улучшением с 0,587, и нет такого значительного улучшения значения полноты класса 1, которое улучшилось только с 0,072 до 0,075, это в основном связано с крайне несбалансированным характером проблемы.

Важность функции

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

  • Здесь, глядя на диаграмму важности функций, мы видим, что функции GBDT определенно сыграли очень важную роль в классификации точек данных, и наряду с ними есть некоторые из наших разработанных функций, таких как день недели и количество идентификаторов устройств, которые также сыграли значительную роль. роль наряду со многими анонимными категориальными функциями, такими как C17 и т. д.
  • Мы видим, что помимо функций GBDT, «идентификатор приложения» и «домен сайта», по-видимому, внесли наибольший вклад среди изначально заданных функций.

Конвейер данных

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

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

1. Время прогнозирования для 4,5 миллионов точек данных

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

2. Время прогнозирования для 1 точки данных

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

Модели, использованные в этом примере, выбраны с учетом отраслевых требований к низкой задержке. Итак, мы измерили время, необходимое для окончательного прогноза с помощью настроенной модели GBDT для новой точки данных, которая составляет 0,083 секунды ~ 83 миллисекунды, что очень удобно в быстро меняющемся мире, чтобы определить, будет ли данное объявление нажато или нет.

Заключительные выводы

«Здесь мы получили тестовый лог-лосс 0,39065, что является очень хорошим значением, эквивалентным 160–170-й позиции в таблице лидеров Kaggle. “

Фактический набор данных, предоставленный Kaggle, состоит из 50 миллионов точек данных, но мы взяли 5 миллионов точек данных и обучили нашу модель из-за ограничений ресурсов, а тестовые данные, предоставленные самим Kaggle, близки к 5 миллионам точек данных. поэтому мы не получим хороших прогнозов для этого набора данных, используя текущую модель, потому что будет много невидимых точек данных, для которых мы не сможем правильно прогнозировать.

  • Используя эту текущую модель на тестовых данных Kaggle, она получила показатель log-loss = 0,41, что неплохо для модели, обученной всего на 5 миллионах точки данных, когда фактический набор данных очень велик, это означает, что мы тренировались только на 10% предоставленных данных.
  • Если бы мы могли обучить модель на целых 50 миллионах точек данных, то оценка была бы намного лучше, но, принимая во внимание реальное применение решения, снижение потерь в журналах внесло бы очень небольшую пользу с точки зрения бизнеса, как мы не удалось увидеть каких-либо значительных улучшений в значениях матрицы путаницы.
  • Таким образом, мы можем сделать вывод, что, разрабатывая 6 различных типов новых функций, мы можем получить один из наименьших логарифмических потерь 0,39065 на тестовых данных, даже если мы не можем добиться значительного улучшения значений матрицы путаницы.
  • Таким образом, мы можем сделать вывод, что, разрабатывая 6 различных типов новых функций, мы можем получить один из наименьших логарифмических потерь 0,39065 на тестовых данных, даже если мы не можем добиться значительного улучшения значений матрицы путаницы, что имеет значение. в фактическом варианте использования модели для бизнес-приложения.
  • Результаты, которые мы получили от всех моделей, сведены в таблицу, как показано ниже.

Ссылки и полезные ссылки:

Приятного чтения. Вы можете найти меня в LinkedIn.