Часть 2 рекомендательных систем можно найти здесь

Рекомендательные системы

Большинство интернет-продуктов, которые мы используем сегодня, основаны на рекомендательных системах. Youtube, Netflix, Amazon, Pinterest и длинный список других интернет-продуктов полагаются на рекомендательные системы, чтобы фильтровать миллионы контента и давать персонализированные рекомендации своим пользователям. Рекомендательные системы хорошо изучены и доказали, что приносят огромную пользу интернет-предприятиям и их потребителям. Фактически, я был шокирован новостью о том, что в 2009 году Netflix наградил команду разработчиков призом в 1 миллион долларов за алгоритм, который повысил точность системы рекомендаций компании на 10%.

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

Подходы

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

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

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

Гибридный подход объединяет два предыдущих подхода. Большинство предприятий, вероятно, используют гибридный подход в своих производственных рекомендательных системах.

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

Давайте создадим рекомендатель фильмов

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

Данные

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

Чтобы создать рекомендатель фильмов, я выбираю Наборы данных MovieLens. Он содержит 27 753 444 рейтинга и 1 108 997 приложений тегов для 58 098 фильмов. Эти данные были созданы 283 228 пользователями в период с 9 января 1995 г. по 26 сентября 2018 г. Оценки выставлены по шкале от 1 до 5.

Мы будем использовать только два файла из наборов данных MovieLens: ratings.csv и movies.csv. Данные рейтингов предоставляют рейтинги фильмов, выставленные пользователями. В каждой строке по три поля: ['userId', 'movieId', 'rating']. Каждую строку можно рассматривать как запись взаимодействия пользователя с фильмом. Данные о фильмах содержат название фильма и жанры для каждого 'movieId' в данных рейтингов.

import os
import pandas as pd
# configure file path
data_path = os.path.join(os.environ['DATA_PATH'], 'MovieLens')
movies_filename = 'movies.csv'
ratings_filename = 'ratings.csv'
# read data
df_movies = pd.read_csv(
    os.path.join(data_path, movies_filename),
    usecols=['movieId', 'title'],
    dtype={'movieId': 'int32', 'title': 'str'})

df_ratings = pd.read_csv(
    os.path.join(data_path, ratings_filename),
    usecols=['userId', 'movieId', 'rating'],
    dtype={'userId': 'int32', 'movieId': 'int32', 'rating': 'float32'})

Давайте быстро взглянем на два набора данных: "Фильмы" и "Рейтинги".

Фильтрация данных

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

Если мы увеличим масштаб или построим график в логарифмической шкале, то сможем выяснить, что только около 13 500 из 58 098 фильмов получили оценки более чем 100 пользователей, а большинство остальных гораздо менее известны при небольшом взаимодействии с пользователем или вообще без него. Эти разреженные оценки менее предсказуемы для большинства пользователей и очень чувствительны к конкретному человеку, который любит малоизвестный фильм, что делает шаблон очень шумным.

Большинство моделей дают рекомендации на основе шаблонов оценок пользователей. Чтобы убрать шумы и избежать «ошибки памяти» из-за больших наборов данных, мы отфильтруем наш фрейм данных рейтингов только по популярным фильмам. После фильтрации в данных рейтингов остается 13 500 фильмов, которых достаточно для рекомендательной модели.

Моделирование

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

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

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

Подождите, но как нам передать данные рейтингов в модель KNN? Во-первых, нам нужно преобразовать фрейм данных рейтингов в надлежащий формат, который может использоваться моделью KNN. Мы хотим, чтобы данные были в массиве m x n, где m - количество фильмов, а n - количество пользователей. Чтобы изменить фрейм данных оценок, мы pivot переведем фрейм данных в широкий формат с фильмами в виде строк и пользователями в виде столбцов. Затем мы заполним недостающие наблюдения с помощью 0s, поскольку мы собираемся выполнять операции линейной алгебры (вычисление расстояний между векторами). Давайте назовем этот новый фрейм данных «фрейм данных функций фильма».

Наш фрейм данных характеристик фильма представляет собой чрезвычайно разреженную матрицу с формой 13,500 x 113,291. Мы определенно не хотим передавать в KNN все данные в основном с 0 в типе float32. Для более эффективных вычислений и меньшего использования памяти нам нужно преобразовать значения фрейма данных в scipy разреженную матрицу.

from scipy.sparse import csr_matrix
# pivot ratings into movie features
df_movie_features = df_ratings.pivot(
    index='movieId',
    columns='userId',
    values='rating'
).fillna(0)
# convert dataframe of movie features to scipy sparse matrix
mat_movie_features = csr_matrix(df_movie_features.values)

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

Давайте посоветуем несколько фильмов

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

from sklearn.neighbors import NearestNeighbors
model_knn = NearestNeighbors(metric='cosine', algorithm='brute', n_neighbors=20, n_jobs=-1)

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

Этот фрагмент демонстрирует наш make_recommendations метод в реализации нашего рекомендателя. Вы можете найти подробный исходный код рекомендательного приложения в моем репозитории GitHub.

Если вы перейдете на мою страницу исходного кода, вы увидите, что я собрал рекомендательную систему KNN в скрипте в виде небольшого приложения на Python. Я параметризовал свое рекомендательное приложение и предоставил пользователям две опции, movie_name и top_n. Теперь я хочу попросить своего рекомендателя предложить 10 самых похожих фильмов на Железного человека. Итак, мы можем запустить ниже bash commend внутри терминала (linux / mac): (инструкции команд можно найти здесь)

python src/knn_recommender.py --movie_name "Iron Man" --top_n 10

Ура!! Наша система рекомендаций действительно работает !! Теперь у нас есть собственный рекомендатель фильмов.

Некоторые мысли

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

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

Итак, мы просто эффективно выявили два недостатка в совместной фильтрации на основе элементов:

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

Напомним график распределения частоты рейтинга фильмов:

Лишь в очень небольшой части фильмов много взаимодействий с пользователем, в то время как у «длинных хвостов» фильмов нет. В бизнес-среде часто используемые товары, как правило, являются относительно конкурентоспособными товарами с небольшой прибылью для продавца. С другой стороны, товары с более низкой частотой имеют большую прибыль.

Как мы можем улучшить нашу систему рекомендаций по фильмам, чтобы устранить два вышеупомянутых недостатка? Мы рассмотрим более сложный метод улучшения рекомендаций по фильмам в следующем посте: Создание прототипа рекомендательной системы, шаг за шагом, часть 2: Факторизация матрицы методом альтернативных наименьших квадратов (ALS) при совместной фильтрации

Резюме

В этом посте мы кратко рассмотрели три подхода в системе рекомендаций: на основе содержимого, совместная фильтрация и гибридная. Мы узнали, как создать прототип совместной фильтрации на основе элементов в KNN, выполнив всего несколько шагов! Версия Jupyter Notebook для этого сообщения в блоге находится здесь. Если вы хотите поиграть с моим исходным кодом, вы можете найти его здесь.

В моем следующем посте мы рассмотрим более сложные темы в рекомендательных системах и используйте Spark для создания масштабируемой рекомендательной системы. Будьте на связи! А пока развлекайтесь с машинным обучением и рекомендациями!

Нравится то, что вы читаете? Ознакомьтесь с другими проектами по науке о данных / машинному обучению на моем Github: Портфолио Кевина по науке о данных