Этот блог иллюстрирует систему рекомендаций на основе метаданных на 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:



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

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

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

Реализация

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

#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 фильмов на основе изучения его метаданных. Разве это не потрясающе?

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

  1. Рекомендательные системы на основе содержания: https://medium.com/@saketgarodia/content-based-recommender-systems-in-python-2b330e01eb80?
  2. Рекомендательные системы, использующие совместную фильтрацию: https://medium.com/@saketgarodia/recommendation-system-using-collaborative-filtering-cc310e641fde

Спасибо

Продолжай учиться