В этом блоге показана система рекомендаций на основе совместной фильтрации на Python
Перед тем, как приступить к реализации рекомендательных систем на основе метаданных в python, я рекомендую вам прочитать 4-минутное чтение этого блога, в котором описывается рекомендательная система и ее типы с точки зрения непрофессионала.
В этом блоге я покажу, как реализовать систему рекомендаций на основе совместной фильтрации в 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, что довольно удивительно.
Чтобы узнать о подходах, основанных на контенте и метаданных, просмотрите следующие мои блоги:
- Рекомендательные системы на основе содержания: https://medium.com/@saketgarodia/content-based-recommender-systems-in-python-2b330e01eb80?
- Рекомендательные системы на основе метаданных: https://medium.com/@saketgarodia/metadata-based-recommender-systems-in-python-c6aae213b25c
Спасибо.