В этом блоге показана система рекомендаций на основе совместной фильтрации на Python

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

Https://medium.com/@saketgarodia/the-world-of-recommender-systems-e4ea504341ac?source=friends_link&sk=508a980d8391daa93530a32e9c927a87

В этом блоге я покажу, как реализовать систему рекомендаций на основе совместной фильтрации в Python на наборе данных Kaggle MovieLens 100k.

Набор данных, который мы будем использовать, это набор данных MovieLens 100k на Kaggle:



Приступим к его реализации.

Постановка проблемы

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

Реализация

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

Мы будем использовать пакет неожиданность, который имеет встроенные модели, такие как SVD, кластеризация KMean и т. Д. Для совместной фильтрации.

#importing necessary libraries
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
from surprise import Reader, Dataset, KNNBasic
from surprise.model_selection import cross_validate
from surprise import SVD
r_cols = ['user_id', 'movie_id', 'rating', 'timestamp']
ratings = pd.read_csv('u.data',  sep='\t', names=r_cols,
encoding='latin-1')
ratings.head()
i_cols = ['movie_id', 'title' ,'release date','video release date', 'IMDb URL', 'unknown', 'Action', 'Adventure',
'Animation', 'Children\'s', 'Comedy', 'Crime', 'Documentary', 'Drama', 'Fantasy',
'Film-Noir', 'Horror', 'Musical', 'Mystery', 'Romance', 'Sci-Fi', 'Thriller', 'War', 'Western']
movies = pd.read_csv('u.item',  sep='|', names=i_cols, encoding='latin-1')
movies.head()
u_cols = ['user_id', 'age', 'sex', 'occupation', 'zip_code']
users = pd.read_csv('u.user', sep='|', names=u_cols,
encoding='latin-1')
users.head()

Итак, у нас есть 1682 уникальных фильма и 100 000 общих оценок для этих уникальных фильмов от 943 пользователей.

Теперь нам нужно разделить наш фрейм данных «рейтинги» на две части - часть 1, чтобы обучить алгоритм прогнозированию оценок, и часть 2, чтобы проверить, близок ли прогнозируемый рейтинг к ожидаемому. Это поможет в оценке наших моделей.

Мы возьмем y как «user_id», чтобы гарантировать, что разделение приводит к стратифицированной выборке, и у нас есть все user_ids в обучающем наборе, чтобы сделать наш алгоритм мощным.

#Assign X as the original ratings dataframe and y as the user_id column of ratings.
X = ratings.copy()
y = ratings[‘user_id’]
#Split into training and test datasets, stratified along user_id
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, stratify=y, random_state=42)

У нас есть 75 000 оценок в обучающем наборе и 25 000 в тестовом наборе для оценки наших моделей.

df_ratings = X_train.pivot(index=’user_id’, columns=’movie_id’, values=’rating’)
Now, our df_ratings dataframe is indexed by user_ids with movie_ids belonging to different columns and the values are the ratings with most of the values as Nan as each user watches and rates only few movies. Its a sparse dataframe.

Вот как выглядит наш разреженный фрейм данных рейтинга:

Теперь мы будем использовать 2 разных метода для совместной фильтрации. В первом методе мы будем использовать средневзвешенное рейтингов, а второй метод реализуем с использованием подходов к классификации на основе моделей, таких как KNN (K ближайших соседей) и SVD (Разложение по сингулярным значениям). О КНН и СВД поговорим позже.

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

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

Метод 1. Метод взвешенного среднего

df_ratings_dummy = df_ratings.copy().fillna(0)
df_ratings_dummy.head()
#cosine similarity of the ratings
similarity_matrix = cosine_similarity(df_ratings_dummy, df_ratings_dummy)
similarity_matrix_df = pd.DataFrame(similarity_matrix, index=df_ratings.index, columns=df_ratings.index)
#calculate ratings using weighted sum of cosine similarity
#function to calculate ratings

def calculate_ratings(id_movie, id_user):
if id_movie in df_ratings:
cosine_scores = similarity_matrix_df[id_user] #similarity of id_user with every other user
ratings_scores = df_ratings[id_movie]      #ratings of every other user for the movie id_movie
#won't consider users who havent rated id_movie so drop similarity scores and ratings corresponsing to np.nan
index_not_rated = ratings_scores[ratings_scores.isnull()].index
ratings_scores = ratings_scores.dropna()
cosine_scores = cosine_scores.drop(index_not_rated)
#calculating rating by weighted mean of ratings and cosine scores of the users who have rated the movie
ratings_movie = np.dot(ratings_scores, cosine_scores)/cosine_scores.sum()
else:
return 2.5
return ratings_movie

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

calculate_ratings(3,150) #predicts rating for user_id 150 and movie_id 3

2.9926409218795715

Давайте создадим функцию score_on_test_set, которая оценивает нашу модель на тестовом наборе, используя root_mean_squared_error.

#evaluates on test set
def score_on_test_set():
user_movie_pairs = zip(X_test[‘movie_id’], X_test[‘user_id’])
predicted_ratings = np.array([calculate_ratings(movie, user) for (movie,user) in user_movie_pairs])
true_ratings = np.array(X_test[‘rating’])
score = np.sqrt(mean_squared_error(true_ratings, predicted_ratings))
return score
test_set_score = score_on_test_set()
print(test_set_score)

Среднеквадратичная ошибка на тестовом наборе составляет 1,0172.

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

Метод 1: подходы, основанные на моделях

В подходе, основанном на моделях, мы будем использовать 2 модели: KNN и SVD. Пакет-сюрприз имеет встроенные библиотеки с разными моделями для создания рекомендательных систем, и мы будем использовать их.

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

В методе SVD (разложение по сингулярным значениям) разреженная матрица пользовательского фильма (рейтингов) сжимается в плотную матрицу с помощью методов факторизации матрицы. Если M - матрица пользовательского фильма, SVD разбивает ее на 3 части: M = UZV, где U - матрица концепции пользователя, Z - веса различных концептов, а V - матрица концептуального фильма. «Концепт» можно интуитивно понять, представив его как надмножество подобных фильмов, например, жанр «тревожный триллер», концепт и т. Д.

Как только SVD раскладывает исходную матрицу на 3, плотная матрица напрямую используется для прогнозирования рейтинга для пары (пользователь, фильм) с использованием концепции, которой принадлежит input_movie.

# installing surprise library
!pip install surprise
#Define a Reader object
#The Reader object helps in parsing the file or dataframe containing ratings
ratings = ratings.drop(columns=’timestamp’)
reader = Reader()
#dataset creation
data = Dataset.load_from_df(ratings, reader)
#model
knn = KNNBasic()
#Evaluating the performance in terms of RMSE
cross_validate(knn, data, measures=[‘RMSE’, ‘mae’], cv = 3)

Мы видим, что ошибка root_mean_square в случае KNN еще больше уменьшилась до 0,98 по сравнению с методом взвешенного среднего. KNN определенно работает лучше, чем метод взвешенного среднего для прогнозирования рейтингов фильмов.

Теперь посмотрим, как работает СВД.

#Define the SVD algorithm object
svd = SVD()
#Evaluate the performance in terms of RMSE
cross_validate(svd, data, measures=[‘RMSE’], cv = 3)

Ошибка еще больше уменьшилась до значения «rmse» 0,948, что является лучшим результатом среди трех подходов, которые мы использовали.

trainset = data.build_full_trainset()
svd.fit(trainset)
ratings[ratings[‘user_id’] == 5]

svd.predict(1, 110)

Прогноз для user_id 1 и фильма 110 по модели SVD составляет 2,14, а фактическая оценка - 2, что довольно удивительно.

Чтобы узнать о подходах, основанных на контенте и метаданных, просмотрите следующие мои блоги:

  1. Рекомендательные системы на основе содержания: https://medium.com/@saketgarodia/content-based-recommender-systems-in-python-2b330e01eb80?
  2. Рекомендательные системы на основе метаданных: https://medium.com/@saketgarodia/metadata-based-recommender-systems-in-python-c6aae213b25c

Спасибо.