В этой серии публикаций я попытаюсь создать механизм рекомендаций, чтобы рекомендовать похожие фильмы по выбранному названию или рекомендовать фильмы пользователю, оценивающему пару фильмов. В этой части 0 рассказывается, как получить данные, загрузить их и выполнить базовый исследовательский анализ данных (EDA). Мы закончим этот пост созданием матрицы взаимодействия «пользователь-элемент», которая представляет рейтинги, присвоенные конкретным пользователем конкретному фильму. В следующем посте я расскажу, как реализовать:

  • Совместная фильтрация на основе пользователей (UBCF)
  • Совместная фильтрация на основе элементов (IBCF)
  • Метод альтернативных наименьших квадратов (ALS) для матричной факторизации (MF)

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

Данные

Данные для этого проекта - набор данных MovieLens. Необходимо загрузить два набора данных:

  • ml-latest-small эти данные имеют 100 000 оценок и 1,300 приложений тегов, примененных к 9000 фильмам 700 пользователями. Мы будем использовать эти данные для быстрого создания первоначального прототипа.
  • ml-latest у этих данных 26 000 000 оценок и 750 000 приложений тегов для 45 000 фильмов 270 000 пользователей. Это гораздо большие данные, которые мы будем использовать для окончательного развертывания модели.

Ссылки для загрузки обоих наборов данных представлены здесь.

complete_dataset_url = ‘http://files.grouplens.org/datasets/movielens/ml-latest.zip'
small_dataset_url = ‘http://files.grouplens.org/datasets/movielens/ml-latest-small.zip'
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.sparse as sparse
%matplotlib inline

Начнем с рейтинговых данных

ratings = pd.read_csv(“data/ml-latest-small/ratings.csv”)
ratings.head()

ratings.userId.nunique(), ratings.movieId.nunique()
> (671, 9066)

У нас 671 уникальный пользователь и 9066 уникальных предметов. Посмотрим, сколько фильмов поставил оценку каждому пользователю.

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

ratings = ratings[[“userId”, “movieId”, “rating”]]

data = ratings.groupby(“userId”, as_index=False).agg({“movieId”: ‘count’})
data.head()

data.movieId.hist()

data.movieId.describe()
> count     671.000000
  mean      149.037258
  std       231.226948
  min       20.000000
  25%       37.000000
  50%       71.000000
  75%       161.000000
  max       2391.000000
  Name: movieId, dtype: float64

В среднем пользователи оценили 149 фильмов. Интересно, что все пользователи оценили не менее 20 фильмов. Это полезно при создании обучающих и тестовых данных для оценки наших моделей.

Загрузим данные фильмов

movies = pd.read_csv(“data/ml-latest-small/movies.csv”)
movies.head()

Эти данные помогут сопоставить movieId с названием фильма.

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

users = list(np.sort(ratings.userId.unique())) # Get our unique customers
movies = list(ratings.movieId.unique()) # Get our unique products that were purchased
rating = list(ratings.rating) # All of our purchases
rows = ratings.userId.astype(‘category’, categories = users).cat.codes
# Get the associated row indices
cols = ratings.movieId.astype(‘category’, categories = movies).cat.codes
# Get the associated column indices
user_item = sparse.csr_matrix((rating, (rows, cols)), shape=(len(users), len(movies)))
matrix_size = user_item.shape[0]*user_item.shape[1] # Number of possible interactions in the matrix
num_purchases = len(user_item.nonzero()[0]) # Number of items interacted with
sparsity = 100*(1 — (1.0*num_purchases/matrix_size))
sparsity
> 98.35608583913366
user_item
> <671x9066 sparse matrix of type ‘<type ‘numpy.float64’>’
 with 100004 stored elements in Compressed Sparse Row format>

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

Я остановлюсь на этом посте. Следующий пост будет посвящен различным алгоритмам, которые мы будем тестировать. Мы увидим UBCF, IBCF, ALS для матричной факторизации и определимся с метрикой оценки. Весь код этого проекта доступен на моем Github.

Этот пост изначально был размещен в моем блоге.