用sklearn实现电影推荐系统

本节将介绍如何使用sklearn来实现一个电影推荐系统。推荐系统是一种信息过滤系统,它可以提高搜索结果的质量,并提供与搜索项更为相关或与用户的搜索历史相对应的选项。它们用于预测用户对某项商品的评价或偏好。我们将使用TMDB 5000电影数据集构建基础电影推荐系统。该kernel几乎可以作为推荐系统的基础,并为您提供一些入门知识。

用sklearn实现电影推荐系统

推荐系统基本上有三种类型:

受众特征过滤:

根据电影的受欢迎程度,它们向每个用户提供通用推荐。系统通过统计向具有相似特征的用户推荐相同的电影。由于每个用户都不相同,因此该方法被认为过于简单。该系统背后的基本思想是,更受大众欢迎和好评的电影具有更高的被普通观众喜欢的可能性。

基于内容的推荐:

根据特定物品推荐相似的物品。该系统使用物品元数据(例如电影的流派,导演,描述,演员等)来提出这些推荐建议。这些推荐系统背后的总体思想是,如果某人喜欢某个特定物品,那么他(她)也将喜欢与之相似的物品。

协同过滤:

该系统找到具有相似兴趣的人或者相似度接近的物品,并根据计算结果提供推荐建议。

import pandas as pd 
import numpy as np 
df1=pd.read_csv('/share/datasets/tmdb_5000_credits.csv')
df2=pd.read_csv('/share/datasets/tmdb_5000_movies.csv')

tmdb5000movies.csv 中共有 20 个字段,其各自释义如下:

budget:预算 
genres:分类 
homepage:主页(大量缺失值,但不重要)
id:编号 
keywords:关键词标签 
original_language:原语言 
original_title:原标题 
overview:简介 
popularity:流行度 
production_companies:制作公司 
production_countries:制作国家 
release_date:上映日期
revenue:收益 
runtime:时长 
spoken_languages:配音语言
status:状态 tagline:一句话标语
title:题目
vote_average:平均分
vote_count:参与评分人数

tmdb5000credits.csv 中共有4 个字段,其各自释义如下:

movie_id:编号
title:电影名称
cast:演员阵容 
crew:全体人员
df1.columns = ['id','tittle','cast','crew']
df2= df2.merge(df1,on='id')
df2.head(5)

特征过滤

首先我们需要如下准备:

  • 我们需要一个指标来给电影评分
  • 计算每部电影的分数
  • 按照得分排序并向用户推荐得分最高的电影

我们可以将电影的平均评分作为得分,但使用它的评分不够合理,如果电影的平均评分为8.9,但是只有3个人评分,得分评价不应该比40人评分得分为7.8的高 因此,我将使用IMDB的加权评分(wr),其给出方式为:

  • v 电影的评分人数;
  • m 系统要求最低的电影的评分人数;
  • R 电影平均评分
  • C 所有电影的平均评分

我们已经知道了v(vote_count) 和 R (vote_average) ,可以计算出C

C= df2['vote_average'].mean()
C

因此,所有电影的平均评分在10的等级上约为6。下一步是确定m的适当值,即数据表中需要列出的最低票数。 我们将使用前90%作为截止点。 换句话说,要使电影在数据表中出现,其评分人数必须超过列表中至少90%的电影。

m= df2['vote_count'].quantile(0.9)
m

筛选出符合条件的电影

q_movies = df2.copy().loc[df2['vote_count'] >= m]
q_movies.shape

我们看到有481部电影符合条件。现在,我们需要计算每个合格电影的分数。为此,我们将定义一个函数weighted_rating(),并增加一个新特征score

def weighted_rating(x, m=m, C=C):
    v = x['vote_count']
    R = x['vote_average']
    # Calculation based on the IMDB formula
    return (v/(v+m) * R) + (m/(m+v) * C)
# Define a new feature 'score' and calculate its value with `weighted_rating()`
q_movies['score'] = q_movies.apply(weighted_rating, axis=1)

最后,让我们计算电影分数并且排序,并输出标题,评分人数,平均分和加权后前10名电影的得分。

#Sort movies based on score calculated above
q_movies = q_movies.sort_values('score', ascending=False)

#Print the top 15 movies
q_movies[['title', 'vote_count', 'vote_average', 'score']].head(10)

成功了!我们完成了一个基本的推荐系统 在这些系统的“现在趋势”标签下,我们找到了非常受欢迎的电影,可以通过按“受欢迎度”列对数据集进行排序来获得电影。

pop= df2.sort_values('popularity', ascending=False)
%matplotlib inline
import matplotlib.pyplot as plt
plt.figure(figsize=(12,4))

plt.barh(pop['title'].head(6),pop['popularity'].head(6), align='center',
        color='skyblue')
plt.gca().invert_yaxis()
plt.xlabel("Popularity")
plt.title("Popular Movies")

基于内容的推荐

在此推荐系统中,电影的内容(概述,演员,工作人员,关键字,标语等)用于计算其与其他电影的相似性。 然后,推荐最可能相似的电影。

我们将根据所有电影的情节描述计算成对相似度得分,并根据相似度得分推荐电影。

df2['overview'].head(5)

我们需要转换每个描述的词向量。现在,我们将为每个句描述计算(TF-IDF)向量。

第一步,计算词频。

第二步,计算逆文档频率。 这时,需要一个语料库(corpus),用来模拟语言的使用环境。

第三步,计算TF-IDF。

幸运的是,scikit-learn为您提供了一个内置的TfIdfVectorizer类,该类以两行代码生成TF-IDF矩阵。

#Import TfIdfVectorizer from scikit-learn
from sklearn.feature_extraction.text import TfidfVectorizer

#Define a TF-IDF Vectorizer Object. Remove all english stop words such as 'the', 'a'
tfidf = TfidfVectorizer(stop_words='english')

#Replace NaN with an empty string
df2['overview'] = df2['overview'].fillna('')

#Construct the required TF-IDF matrix by fitting and transforming the data
tfidf_matrix = tfidf.fit_transform(df2['overview'])

#Output the shape of tfidf_matrix
tfidf_matrix.shape

我们看到数据集中使用了20,000多个不同的词来描述4800部电影。

有了这个矩阵,我们现在可以计算一个相似度得分。有几个相似度计算方法,例如欧几里得,皮尔逊和余弦相似度计算。

我们将使用余弦相似度来计算表示两个电影之间相似度的数值。 我们使用余弦相似度评分,因为它与幅度无关,并且相对容易且快速地进行计算。 在数学上,它的定义如下: 由于我们使用了TF-IDF矢量化器,因此计算点积将直接为我们提供余弦相似度评分。 因此,我们将使用sklearn的 linear_kernel()代替cosine_similarities(),因为它速度更快。

# Import linear_kernel
from sklearn.metrics.pairwise import linear_kernel

# Compute the cosine similarity matrix
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)

我们将定义一个函数,该函数以电影标题作为输入并输出10个最相似电影的列表。

#Construct a reverse map of indices and movie titles
indices = pd.Series(df2.index, index=df2['title']).drop_duplicates()

现在,我们可以很好地定义推荐功能。遵循以下步骤:

  • 根据标题获得电影的索引。
  • 获取该特定电影与所有电影的余弦相似度得分列表。 将其转换为元组列表,其中第一个元素是其位置,第二个元素是相似性分数。
  • 根据相似度分数对上述元组列表进行排序。
  • 获取此列表的前10个元素。忽略第一个元素(与特定电影最相似的电影是电影本身)。
  • 返回与顶部元素索引相对应的电影标题。

In [16]:

# Function that takes in movie title as input and outputs most similar movies
def get_recommendations(title, cosine_sim=cosine_sim):
    # Get the index of the movie that matches the title
    idx = indices[title]

    # Get the pairwsie similarity scores of all movies with that movie
    sim_scores = list(enumerate(cosine_sim[idx]))

    # Sort the movies based on the similarity scores
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)

    # Get the scores of the 10 most similar movies
    sim_scores = sim_scores[1:11]

    # Get the movie indices
    movie_indices = [i[0] for i in sim_scores]

    # Return the top 10 most similar movies
    return df2['title'].iloc[movie_indices]
get_recommendations('The Dark Knight Rises')
get_recommendations('The Avengers')

虽然我们的算法在推荐具有类似情节描述的电影方面做得不错,但是推荐的质量却不是那么好。 “黑暗骑士崛起”将重现所有蝙蝠侠电影,而喜欢该电影的人们更有可能更喜欢克里斯托弗·诺兰的其他电影。这是本算法无法发现的信息。

基于荣誉,电影类型和关键字的推荐

显然,使用更好的元数据将提高我们推荐程序的质量。这正是我们在本节中要做的。我们将基于以下元数据构建推荐系统:3个顶级演员,导演,相关流派和电影情节关键字。从演员,剧组和关键字中,我们需要提取三个最重要的演员,导演和与该电影相关的关键字。

# Parse the stringified features into their corresponding python objects
from ast import literal_eval

features = ['cast', 'crew', 'keywords', 'genres']
for feature in features:
    df2[feature] = df2[feature].apply(literal_eval)

接下来,我们将编写函数以帮助我们提取所需的信息。

# Get the director's name from the crew feature. If director is not listed, return NaN
def get_director(x):
    for i in x:
        if i['job'] == 'Director':
            return i['name']
    return np.nan
# Returns the list top 3 elements or entire list; whichever is more.
def get_list(x):
    if isinstance(x, list):
        names = [i['name'] for i in x]
        #Check if more than 3 elements exist. If yes, return only first three. If no, return entire list.
        if len(names) > 3:
            names = names[:3]
        return names

    #Return empty list in case of missing/malformed data
    return []
# Define new director, cast, genres and keywords features that are in a suitable form.
df2['director'] = df2['crew'].apply(get_director)

features = ['cast', 'keywords', 'genres']
for feature in features:
    df2[feature] = df2[feature].apply(get_list)
# Print the new features of the first 3 films
df2[['title', 'cast', 'director', 'keywords', 'genres']].head(3)

下一步是将名称和关键字实例转换为小写并去除它们之间的所有空格。 这样做是为了使我们的矢量化程序不会将“ Johnny Depp”和“ Johnny Galecki”中的Johnny视为相同变量。

# Function to convert all strings to lower case and strip names of spaces
def clean_data(x):
    if isinstance(x, list):
        return [str.lower(i.replace(" ", "")) for i in x]
    else:
        #Check if director exists. If not, return empty string
        if isinstance(x, str):
            return str.lower(x.replace(" ", ""))
        else:
            return ''
# Apply clean_data function to your features.
features = ['cast', 'keywords', 'director', 'genres']

for feature in features:
    df2[feature] = df2[feature].apply(clean_data)
def create_soup(x):
    return ' '.join(x['keywords']) + ' ' + ' '.join(x['cast']) + ' ' + x['director'] + ' ' + ' '.join(x['genres'])
df2['soup'] = df2.apply(create_soup, axis=1)

后续步骤与我们对基于内容的推荐算法所做的相同。 一个重要的区别是我们使用CountVectorizer()而不是TF-IDF。 这是因为我们不希望减轻演员/导演在相对较多的电影中所扮演或导演的影响力。

# Import CountVectorizer and create the count matrix
from sklearn.feature_extraction.text import CountVectorizer

count = CountVectorizer(stop_words='english')
count_matrix = count.fit_transform(df2['soup'])
# Compute the Cosine Similarity matrix based on the count_matrix
from sklearn.metrics.pairwise import cosine_similarity

cosine_sim2 = cosine_similarity(count_matrix, count_matrix)
# Reset index of our main DataFrame and construct reverse mapping as before
df2 = df2.reset_index()
indices = pd.Series(df2.index, index=df2['title'])
get_recommendations('The Dark Knight Rises', cosine_sim2)
get_recommendations('The Godfather', cosine_sim2)

总结:

我们看到,由于更多的元数据,我们的推荐人已成功捕获了更多信息,并为我们提供了(可能是)更好的推荐。 漫威漫画或DC漫画迷更可能会喜欢同一家制作所的电影。通过本节的介绍,相信您已经可以初步理解并搭建属于自己的电影推荐系统。

移动端设备除iPad Pro外,其它移动设备仅能阅读基础的文本文字。
建议使用PC或笔记本电脑,浏览器使用Chrome或FireFox进行浏览,以开启左侧互动实验区来提升学习效率,推荐使用的分辨率为1920x1080或更高。
我们坚信最好的学习是参与其中这一理念,并致力成为中文互联网上体验更好的学练一体的IT技术学习交流平台。
您可加QQ群:575806994,一起学习交流技术,反馈网站使用中遇到问题。
内容、课程、广告等相关合作请扫描右侧二维码添加好友。

狐狸教程 Copyright 2021

进入全屏