Неявный рекомендатель BPR (в Tensorflow)

Это краткое изложение и реализация Tensorflow концепций, изложенных в статье Штеффена Рендла, Кристофа Фройденталера, Зено Гантнера и Ларса Шмидт-Тиме BPR: персонализированное байесовское ранжирование на основе неявной обратной связи.

Содержание:

  • Введение
  • Байесовский персонализированный рейтинг
  • Модель Tensorflow
  • Набор данных
  • Хорошо, давай напишем! (код)
  • Резюме
  • Ссылки

Вступление

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

Вместо этого я сосредоточусь конкретно на том, как работает подход байесовского персонализированного ранжирования (BPR) и как его реализовать с помощью Tensorflow.

Байесовский персонализированный рейтинг

В документе предлагается то, что они называют «BRP-Opt», общий критерий оптимизации для оптимального персонализированного ранжирования. В основном это означает подход, который можно применить к различным типам моделей рекомендаций, таким как матричная факторизация или k-Nearest-Neighbor (это «общая» часть), который решает ранжирование для набора элементов для данного пользователя. В этом смысле он похож на ALS, который также использует факторизацию матрицы и пытается достичь ранжирования, используя значения предпочтений и достоверности в данных.

Неявная проблема

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

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

Это стратегия, которую BPR использует для составления рейтинга. В то время как большинство других оптимизаторов просто смотрят, взаимодействовал ли пользователь с элементом, BPR смотрит на пользователя, один элемент, с которым пользователь взаимодействовал, и один элемент, с которым пользователь не взаимодействовал (неизвестный элемент). Это дает нам тройку (u, i, j) пользователя (u), один известный элемент ( i) и один неизвестный элемент (j).

Мы можем выразить отношения между известными и неизвестными объектами следующим образом:

Здесь x̂ui обозначает оценку для пользователя u и известного элемента i и x̂uj - оценка для пользователя u и неизвестного элемента j. Вышеупомянутое условие считается выполненным, если оценка известного элемента больше, чем оценка неизвестного элемента.

ПРИМЕЧАНИЕ. Одно важное условие, которое, как мы предполагаем, является истинным, заключается в том, что все пользователи действуют независимо от всех остальных пользователей и что порядок любой пары элементов для любого данного пользователя не зависит от порядка любой другой пары элементов.

Байесовская формулировка

BPR использует байесовскую формулировку для поиска персонализированного рейтинга пользователя для всех элементов i в наборе элементов I (iI ), максимизируя его апостериорную вероятность.

Ключевое слово здесь - «байесовский». Он исходит из теоремы Байеса, которая вычисляет вероятность P того, что некоторая гипотеза H верна при некотором событии Э. Вы можете рассчитать эту вероятность, умножив априорную вероятность того, что гипотеза верна P (H) на вероятность события с учетом того, что гипотеза верна P (E | H), деленная на общую вероятность наступления события P (E) :

В этом уравнении P (H | E) вероятность того, что наша гипотеза верна для данного события, называется апостериорной вероятностью. Также можно представить себе, что апостериорная вероятность пропорциональна правдоподобию, умноженному на априорную вероятность.

Апостериорная вероятность ∝ Вероятность x Априорная вероятность

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

Более пристальный взгляд на математику

Итак, мы знаем концепцию использования троек (u, i, j), а не пар для ранжирования, и что означает байесовский вывод. Теперь давайте погрузимся немного глубже и посмотрим на особенности вывода нашего окончательного критерия оптимизации. Я опущу некоторые вещи, поэтому для исчерпывающего объяснения взгляните на бумагу.

Используя формулу Апостериорная вероятность ∝ Вероятность x Априорная вероятность, апостериорная вероятность, которую мы хотим максимизировать в этом случае, составляет:

Это читается как «вероятность Θ при заданном› u пропорциональна вероятности ›заданного, умноженной на общую вероятность». Здесь ›u представляет собой скрытую структуру предпочтений для пользователя u, а Θ представляет параметры какой-то модели, например факторизации матрицы. или кНН.

Итак, в основном мы хотим максимизировать вероятность параметров Θ с учетом скрытой структуры предпочтений для пользователя ›u. В документе показано, что произведение вероятности p (›u | Θ) равно произведению p (i‹ uj | Θ ):

Затем мы определяем вероятность того, что данный пользователь действительно предпочтет известный элемент i неизвестному элементу j как следующий:

Здесь σ - сигмовидная функция, а x̂uij (Θ) - это некая функция, которая моделирует отношения между пользователем, известным элементом и неизвестным элементом с учетом набора параметров. . BPR не определяет, какая это функция, и поэтому может использоваться с рядом различных классов моделей. В нашем случае эта функция представляет собой разницу между оценкой u и j, вычтенной из оценки u и i:

Затем мы можем переписать его, чтобы использовать сигмовидную функцию для вероятности. В документе также предлагается использовать ln sigmoid (натуральный логарифм) на основе их MLE (оценки максимального правдоподобия):

Помня правило логарифмического произведения (ln (a * b) = ln (a) + ln (b)), теперь мы можем изменить все уравнение на:

На основании этого мы приходим к окончательному критерию оптимизации нашей модели:

  • Θ: Вектор параметров нашей модели.
  • x̂uij: Связь между (u, i, j). Здесь оценка (u, j) вычитается из оценки (u, j).
  • ln : натуральный логарифм
  • σ: Логистическая сигмоида
  • λ: Лямбда, гиперпараметры регуляризации для нашей модели.
  • || Θ ||: Норма L2 параметров нашей модели

Модель Tensorflow

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

С учетом сказанного, вот некоторые из узлов, которые мы будем использовать при построении нашего графа, которые могут быть не слишком очевидны для вас, если вы раньше ничего не писали в Tensorflow:

  • Переменные: узлы, которые сохраняют свое состояние на графике при нескольких выполнениях. Это то, что мы хотим тренировать на нашем графике, чтобы минимизировать функцию потерь.
  • Заполнители: Как следует из названия, это заполнители для данных, которые мы позже вводим в график по мере его выполнения. Когда мы их создаем, они не содержат данных, а только форму данных, которые они получат позже.
  • Встраиваемый поиск: позволяет получить строку из тензора по ее индексу. Подобно индексации в массиве numpy.
  • Оптимизатор: Существуют разные типы оптимизаторов, но они рассчитывают функцию потерь, а затем обновляют значения на графике для следующего запуска, чтобы минимизировать эти потери.

Модель

Мы реализуем BPR с использованием матричной факторизации, что означает использование матрицы взаимодействия R размера (все пользователи x все элементы) и разложив его на матрицу размера U (все пользователи x скрытые функции) и V размера (скрытые функции x все элементы). Итак, когда мы закончим, захотите R ≈ U x V.

Мы будем использовать стохастический градиентный спуск (SGD), чтобы прийти к этому приближению. Он работает, инициализируя U и V с равномерно случайными значениями, а затем, используя критерий оптимизации из предыдущего, непрерывно обновляя их новыми значениями, чтобы максимизировать апостериорную вероятность, т. е. получать все более и более высокую вероятность для R = U x V.

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

Итак, мы начинаем сверху с наших полей ввода (заполнителей), которые будут идентификатором пользователя u, известным идентификатором элемента i и неизвестный идентификатор элемента j. Затем мы также случайным образом инициализируем наши матрицы U и V (переменные).

Отсюда мы создаем три внедрения: одно для пользовательских факторов, известных факторов элементов и неизвестных факторов элементов. Мы берем эти вложения и умножаем их вместе со смещением, чтобы получить x̂ui и x̂uj соответственно.

Затем мы берем x̂ui и x̂uj, вычисляем x̂uij и вводим его в отрицательный натуральный логарифм сигмоида x̂uij. Получаем - ln σ (x̂uij).

Наконец, мы добавляем L2 norm к -ln σ (x̂uij) и придем к окончательной функции потерь:

Давайте развернем это, чтобы увидеть, как это будет на самом деле реализовано в коде:

(При желании можно также пропустить первый знак минуса и вместо этого вычесть ln σ (x̂ui-x̂uj) из нормы L2. Не совсем уверен, что выглядит лучше.)

Набор данных

Как и раньше, мы будем использовать набор данных lastfm, содержащий данные о слушании 360 000 пользователей. Он содержит идентификатор пользователя, идентификатор исполнителя, имена исполнителей и количество раз, когда пользователь играл любого данного исполнителя. Также есть сведения о возрасте, поле, стране и т. Д. Пользователей, но нас это пока не интересует.

Хорошо, давай напишем!

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

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

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

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

Затем переходим к фактическому графику:

Сначала мы создаем заполнители для u, i и j, в которые позже можем вводить данные. Мы инициализируем наши матрицы U и V и создаем вложения для u_factors, i_factors, и j_factors . Мы также создаем вложения для наших предубеждений i_bias и j_bias.

Затем мы вычисляем оценку для нашего пользователя и элемента i, а также для элемента j, чтобы получить x̂ui и x̂uj , а затем вычисляем x̂uij.

Здесь мы также оцениваем AUC и добавляем его в нашу сводку Tensorflow, чтобы мы могли отслеживать его во время обучения.

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

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

Для каждой эпохи и пакета, которые мы определили в наших гиперпараметрах, мы выбираем несколько случайных индексов из нашего набора данных, а затем получаем соответствующие идентификаторы пользователей (u) и известные идентификаторы элементов (i). эти индексы. Затем мы отбираем 15 000 случайных неизвестных элементов (j).

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

Вот и все. Теперь мы можем обучить нашу модель для ряда эпох, надеясь, что наблюдая, как потери неуклонно уменьшаются до 0, а значение AUC увеличивается до 1.

Поиск похожих предметов

Теперь, когда у нас есть обученная модель, мы можем использовать ее для ранжирования некоторых элементов. Давайте начнем с поиска артистов, похожих на Бейонсе. Мы используем вспомогательную функцию get_variable, которую мы использовали ранее, чтобы получить обученные U и V векторы. Затем мы используем тот же код, что и для ALS, чтобы вычислить наиболее похожих исполнителей.

Это дает нам понять, что самыми похожими на Бейонсе артистами являются: Рианна, Мэрайя Кэри, Бритни Спирс, Black Eyed Peas, Майкл Джексон, Нелли Фуртадо, Кэти Перри, Алисия Киз и Джастин Тимберлейк. Помните, что это не обязательно сходство по жанру или стилю, а скорее в поведении слушателя. Похоже, это имеет смысл.

Повторный запуск с Бобом Диланом дает нам следующее: The Beatles, Beck, The Rolling Stones, David Bowie, Pink Floyd, Radiohead, Led Zeppelin, The Cure, REM и т. Д. После 50 эпох мы получаем значение потерь 0,051 и AUC. значение 0,997 или 99,7%.

Рекомендации

То же самое и с рекомендациями для конкретного пользователя. Как только мы получим наши матрицы U и V, мы можем использовать тот же код, что и для ALS, чтобы вернуть n исполнителей с наивысшим рейтингом для этого пользователя.

Резюме

Итак, у нас есть базовая реализация BPR с использованием Tensorflow. Одним из усовершенствований алгоритма, предложенного автором оригинальной статьи, не реализованного здесь, является изменение способа выборки наших троек (u, i, j). Вы можете прочитать больше об этом здесь".

использованная литература