Этот блог иллюстрирует систему рекомендаций на основе метаданных на Python
Перед тем, как приступить к реализации рекомендательных систем на основе метаданных в python, я рекомендую вам прочитать 4-минутное чтение этого блога, в котором описывается рекомендательная система и ее типы с точки зрения непрофессионала.
В этом блоге я покажу, как реализовать систему рекомендаций на основе метаданных в Python на наборе данных Kaggle MovieLens 100k.
Набор данных, который мы будем использовать, это набор данных MovieLens 100k на Kaggle:
Приступим к его реализации.
Постановка проблемы
Создать систему рекомендаций, которая рекомендует фильмы на основе жанра, актеров, съемочной группы и некоторых ключевых слов из ранее просмотренного фильма.
Реализация
Во-первых, давайте импортируем все необходимые библиотеки, которые мы будем использовать для создания системы рекомендаций, основанной на содержании. Давайте также импортируем необходимые файлы данных.
#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 sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import linear_kernel from ast import literal_eval from sklearn.feature_extraction.text import CountVectorizer movies = pd.read_csv(‘movies_metadata.csv’) credits = pd.read_csv(‘credits.csv’) #keywords like jealousy, fishing, etc that belongs to particular movies are also part of the metadata. #we will grab keywords from keywords.csv keywords = pd.read_csv(‘keywords.csv’) #importing necessary columns movies = movies[[‘id’,’title’,’genres’]] #clean movie_id function def clean_id(x): try: return int(x) except: return np.nan movies['id'] = movies['id'].apply(clean_id) movies = movies[movies['id'].notnull()] #converting everything into integer movies['id'] = movies['id'].astype('int') keywords['id'] = keywords['id'].astype('int') credits['id'] = credits['id'].astype('int') #merging the 3 dataframes to get all the required data on 1 datafarame movies movies = movies.merge(credits, on='id') movies = movies.merge(keywords, on='id')
После объединения независимых файлов данных credits.csv, keywords.csv и movies_metadata.csv мы получили фрейм данных фильмов:
Теперь давайте займемся необходимой обработкой данных, потому что данные кажутся запутанными в формате с большим количеством информации, которую мы не будем использовать в нашем анализе.
Столбцы жанры, состав, команда и ключевые слова относятся к объекту (или строковому типу данных). Давайте получим из этих столбцов необходимые слова, которые мы будем использовать, сначала используя literal_eval, чтобы преобразовать эти строки в объекты Python (список словарей здесь) и использовать pandas и numpy чтобы спорить с ними.
#changing the 4 columns into python objects ( list of dictionaries here) movies[‘genres’] = movies[‘genres’].apply(literal_eval) movies[‘cast’] = movies[‘cast’].apply(literal_eval) movies[‘crew’] = movies[‘crew’].apply(literal_eval) movies[‘keywords’] = movies[‘keywords’].apply(literal_eval) #grabbing the names of all the genres attached to each movie movies[‘genres’] = movies[‘genres’].apply(lambda x: [i[‘name’].lower() for i in x]) #grabbing the name of the director from all the crew members #we will only use directors from the creqw column for our purpose movies[‘crew’] = movies[‘crew’].apply(lambda x: [i[‘name’].lower() for i in x if i[‘job’]==’Director’]) #grabbing the cast and keywords from the list of dictionaries of those columns movies[‘cast’] = movies[‘cast’].apply(lambda x: [i[‘name’].lower() for i in x]) movies[‘keywords’] = movies[‘keywords’].apply(lambda x: [i[‘name’].lower() for i in x]) #taking maximum 3 cast/genre/keywords for each movie movies[‘genres’] = movies[‘genres’].apply(lambda x: x[:3] if len(x)>3 else x) movies[‘cast’] = movies[‘cast’].apply(lambda x: x[:3] if len(x)>3 else x) movies[‘keywords’] = movies[‘keywords’].apply(lambda x: x[:3] if len(x)>3 else x)
После выполнения необходимой очистки и выбора всего 3 жанров, 3 ключевых слов и приведений для иллюстрации, вот как выглядят наши данные:
Теперь у нас есть необходимые чистые данные для создания рекомендательных систем на основе метаданных. Нам просто нужно удалить пробелы между именами и фамилиями, потому что, если мы не удалим пробелы, фильмы для Тома Круза и Тома Хэнкса будут машина считает, что это то же самое, потому что имя «Том» является обычным. Давайте удалим пробелы, чтобы Том Хэнкс стал TomHanks, а Том Круз стал TomCruise.
#removing spaces movies[‘cast’] = movies[‘cast’].apply(lambda x: [i.replace(‘ ‘,’’) for i in x]) movies[‘crew’] = movies[‘crew’].apply(lambda x: [i.replace(‘ ‘,’’) for i in x]) movies[‘keywords’] = movies[‘keywords’].apply(lambda x: [i.replace(‘ ‘,’’) for i in x]) movies[‘genres’] = movies[‘genres’].apply(lambda x: [i.replace(‘ ‘,’’) for i in x])
Теперь все имена в приведении имеют 0 пробелов, что делает их уникальными.
Теперь давайте сделаем 1 столбец всех метаданных, которые у нас есть, добавив значения в столбцы жанров, актеров, экипажа и ключевых слов.
movies[‘metadata’] = movies.apply(lambda x : ‘ ‘.join(x[‘genres’]) + ‘ ‘ + ‘ ‘.join(x[‘cast’]) + ‘ ‘ + ‘ ‘.join(x[‘crew’]) + ‘ ‘ + ‘ ‘.join(x[‘keywords’]), axis = 1)
Из-за проблем с памятью в Google Colab я просто запустил первые 10000 фильмов, чтобы создать рекомендательную систему. Те же коды можно использовать для дальнейшего масштабирования.
Мы будем использовать CountVectorizer для построения числовых функций из наших метаданных. Мы не будем использовать TfIdf здесь, потому что может быть много фильмов с одними и теми же режиссерами, и мы определенно не хотим наказывать этого режиссера. Возможно, пользователь захочет, чтобы ему рекомендовали фильмы, принадлежащие этому режиссеру. Большинство слов, которые у нас есть, - это названия и жанры, которые действительно полезны для рекомендации фильмов.
Я буду использовать косинусное сходство, чтобы найти сходство между любыми двумя фильмами. Теперь давайте сделаем косинусную матрицу подобия, используя значения векторизатора счетчика, а затем создадим рекомендательную функцию.
count_vec = CountVectorizer(stop_words=’english’) count_vec_matrix = count_vec.fit_transform(movies_df[‘metadata’]) cosine_sim_matrix = cosine_similarity(count_vec_matrix, count_vec_matrix) #movies index mapping mapping = pd.Series(movies_df.index,index = movies_df[‘title’])
#recommender function to recommend movies based on metadata def recommend_movies_based_on_metadata(movie_input): movie_index = mapping[movie_input] #get similarity values with other movies similarity_score = list(enumerate(cosine_sim_matrix[movie_index])) similarity_score = sorted(similarity_score, key=lambda x: x[1], reverse=True) # Get the scores of the 15 most similar movies. Ignore the first movie. similarity_score = similarity_score[1:15] movie_indices = [i[0] for i in similarity_score] return (movies_df[‘title’].iloc[movie_indices])
Теперь, когда мы создали нашу рекомендательную функцию, давайте посмотрим, как она работает. Попробуем получить фильмы, похожие на "Слепой горизонт", используя систему рекомендаций на основе метаданных.
рекомендует_movies_based_on_metadata («Слепой горизонт»)
Для входного фильма «Слепой горизонт» нам были рекомендованы 15 фильмов на основе изучения его метаданных. Разве это не потрясающе?
Чтобы узнать о подходах, основанных на содержании и совместной фильтрации, просмотрите следующие мои блоги:
- Рекомендательные системы на основе содержания: https://medium.com/@saketgarodia/content-based-recommender-systems-in-python-2b330e01eb80?
- Рекомендательные системы, использующие совместную фильтрацию: https://medium.com/@saketgarodia/recommendation-system-using-collaborative-filtering-cc310e641fde
Спасибо
Продолжай учиться