Построение нейронной сети с вложениями для рекомендаций фильмов

Совместная фильтрация - это инструмент, который компании все чаще используют. Netflix использует его, чтобы рекомендовать вам сериалы. Facebook использует его, чтобы порекомендовать, с кем стоит дружить. Spotify использует его, чтобы рекомендовать плейлисты и песни. Это невероятно полезно для рекомендации товаров покупателям.

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

Набор данных взят отсюда. Этот код частично основан на ноутбуке fastai.

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

import pandas as pd
ratings = pd.read_csv('ratings.csv')
movies = pd.read_csv('movies.csv')

Затем мы сделаем то же самое для идентификаторов фильмов.

u_uniq = ratings.userId.unique()
user2idx = {o:i for i,o in enumerate(u_uniq)}
ratings.userId = ratings.userId.apply(lambda x: user2idx[x])

Нам нужно будет узнать количество пользователей и количество фильмов.

n_users=int(ratings.userId.nunique())
n_movies=int(ratings.movieId.nunique())

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

Эти веса будут равномерно распределены между 0 и 0,05. Оператор _ в конце uniform_ обозначает операцию на месте.

Затем мы добавляем наши матрицы встраивания и скрытые факторы.

Мы создаем матрицу внедрения для наших идентификаторов пользователей и фильмов. Встраивание - это, по сути, поиск по массиву. Когда мы умножаем наши горячие закодированные идентификаторы пользователей на наши веса, большинство вычислений сводятся к 0 (0 * number = 0). Все, что нам осталось, - это определенная строка в весовой матрице. По сути, это просто поиск по массиву.

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

Размер этих матриц вложения будет определяться n_factors. Эти факторы определяют количество скрытых факторов в нашем наборе данных.

Скрытые факторы очень полезны в нашей сети. Они снижают потребность в проектировании функций. Например, если User_id 554 лайкает Tom cruise и Tom cruise появляется в фильме. Пользователю 554 фильм наверняка понравится. Tom cruise появление в фильме было бы скрытой особенностью. Мы не уточняли это перед тренировкой. Это просто появилось. И мы рады, что это произошло.

Наконец, нам нужно добавить нашу forward функцию.

Судя по названию этого класса, мы выполняем скалярное произведение встраиваемых матриц.

users,movies = cats[:,0],cats[:,1] дает нам мини-подборку пользователей и фильмов. Мы смотрим только на категориальные переменные для вложений. conts относится к непрерывным переменным.

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

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

self.u(users),self.m(movies) позволяет нам выполнять поиск в массиве. Этот поиск требует меньших вычислительных затрат, чем матричное умножение матрицы однократного кодирования на матрицу весов.

(u*m).sum(1).view(-1, 1) - это кросс-продукт вложений для пользователей и фильмов и возвращает одно число. Это прогнозируемый рейтинг для этого фильма.

Далее нам нужно создать объект ColumnarModelData

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

Мы также можем использовать optim.Adam. Это реализует rmsprop и импульс. В свою очередь, это приводит к адаптивной скорости обучения. Но эта статья показывает, что решения, полученные от SGD, обобщают намного лучше, чем решения, полученные от Адама. К тому же тренировка в любом случае не займет много времени, так что SGD - неплохой вариант.

Тогда мы подошли к 3 эпохам.

fit(model, data, 3, opt, F.mse_loss)

Потеря MSE - это просто потеря среднеквадратичной ошибки. Это рассчитывается автоматически.

Fastai автоматически создает нейронную сеть за кулисами. Вы можете вызвать collab_learner, который автоматически создает нейронную сеть для совместной фильтрации. У Fastai также есть варианты введения предвзятости и отсева посредством этого совместного ученика.

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

Используя fastai, мы можем легко создать ученика для совместной работы:

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

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

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

Мы можем потренироваться еще

Наконец, мы получили MSE 0.784105. Но это очень ухабистая поездка. Наши потери значительно скачут вверх и вниз. При этом 0.784105 на самом деле лучше, чем система LibRec для совместной фильтрации. Они получали 0.91**2 = 0.83 MSE.

На самом деле это также немного лучше, чем модель, которую Fastai создал в своем уроке совместной фильтрации. Они получали 0.814652 MSE.

Улучшения

  1. Мы можем настроить размер вложения, отправив словарь с именем emb_szs. Это может быть полезным параметром для настройки.
  2. Контентная рекомендация. Совместная фильтрация - это лишь один из методов построения рекомендательной системы. Другие методы могут быть полезнее. Я имею в виду систему, основанную на содержании. Это могло бы смотреть на метаданные, такие как актерский состав, съемочная группа, жанр и режиссер, чтобы давать рекомендации. Думаю, оптимальным было бы какое-то гибридное решение. Это позволит объединить систему рекомендаций на основе контента и систему совместной фильтрации.
  3. Совместная фильтрация в значительной степени подрывается проблемой холодного старта. Чтобы преодолеть это, мы потенциально можем посмотреть на метаданные пользователей. Например, мы могли бы посмотреть на такие вещи, как: пол, возраст, город, время, когда они заходили на сайт и т. Д. Просто все, что они вводили в форме регистрации. Построение модели на основе этих данных может быть непростым делом, но, если она работает хорошо, может оказаться полезным.

Первоначально опубликовано на https://spiyer99.github.io 12 июля 2020 г.