全文共5860字,预计学习时长20分钟或更长
通过学习本教程,你将会达到下面的目标:
· 掌握什么是推荐系统,系统如何运作,以及它们之间不同的特点。
· 使用Python以及TMDB 5000部电影数据库来执行几个推荐系统。
什么是推荐系统?
推荐系统(也常被称为推荐引擎/推荐平台),旨在预测用户对可获取物品的兴趣(例如Spotify上的歌曲)并给出相应的推荐。以下是两种主要的推荐系统:
· 基于内容的过滤系统,会基于物品本身的特性给出推荐。因此,如果一名网飞用户热衷于科幻影片,网飞就能更迅速地给其推荐另一部科幻电影,而非浪漫爱情喜剧。我们接下来就将在Python上执行这一推荐系统。
· 协同过滤系统会基于用户的反应给出推荐。比如说,有两位用户都在亚马逊上购买了电子吉他,其中一位用户同时还购买了放大器。那么,亚马逊就会预测另一位用户也对该放大器感兴趣,然后将这款产品推荐给该用户。
感谢Ibtesam Ahmed为这个数据库提供的 Kaggle核函数。本文旨在用中等程序化格式遵循她的教程。
建立一个基础推荐系统
启动
和往常一样,首先要导入必需的工具包和数据库:
import pandas as pd import numpy as np # Dataset: # https://www.kaggle.com/tmdb/tmdb-movie-metadata/downloads/tmdb-5000-movie-dataset.zip/2 from google.colab import files uploaded = files.upload() credits = pd.read_csv("tmdb_5000_credits.csv") movies_incomplete = pd.read_csv("tmdb_5000_movies.csv") # Shapes of dataframes print("credits:", credits.shape) print("movies_incomplete:", movies_incomplete.shape)
这两个输入语句提供了以下输出结果:
· credits: (4803, 4)
· movies_incomplete: (4803, 20)
现在我们就用4803部电影的数据进行操作。注意数据现在会被分成两类数据帧。
遵循本教程时,始终对照此要点会让操作变得十分简单。
先从创建两个最基础的推荐系统开始,目的是要给用户推荐评分最高的电影列表和最受欢迎的电影列表。但首先要计算出每部电影平均评分(投票平均值)的加权平均值。依照Ibtesam的做法,使用IMDB(前称)的公式来计算电影的加权评级。
以下是如何得出加权平均值的例子:
V = movies_clean['vote_count'] R = movies_clean['vote_average'] C = movies_clean['vote_average'].mean() m = movies_clean['vote_count'].quantile(0.70) movies_clean['weighted_average'] = (V/(V+m) * R) + (m/(m+V) * C)
选定0.70作为 quantile()的参数,以表明我们只关注数据库中获得至少70%投票的电影。选择m的数值就比较随意,因此需要做一些实验。
推荐系统模型1:
准备开始建立第一个推荐系统。让我们一起推荐加权评分值最高的10部电影。
import matplotlib.pyplot as plt import seaborn as sns wavg = movies_ranked.sort_values('weighted_average', ascending=False) plt.figure(figsize=(16,6)) ax = sns.barplot(x=wavg['weighted_average'].head(10), y=wavg['original_title'].head(10), data=wavg, palette='deep') plt.xlim(6.75, 8.35) plt.title('"Best" Movies by TMDB Votes', weight='bold') plt.xlabel('Weighted Average Score', weight='bold') plt.ylabel('Movie Title', weight='bold') plt.savefig('best_movies.png')
接下来就能获得评分最高的电影的图表:
可以看到,运行系统推荐的大都是经典影片。但如果想要推荐在TMDB用户中流行的电影呢?
推荐系统模型2:
可以利用数据库中的流行性特点,从而基于流行度来推荐电影。
popular = movies_ranked.sort_values('popularity', ascending=False) plt.figure(figsize=(16,6)) ax = sns.barplot(x=popular['popularity'].head(10), y=popular['original_title'].head(10), data=popular, palette='deep') plt.title('"Most Popular" Movies by TMDB Votes', weight='bold') plt.xlabel('Popularity Score', weight='bold') plt.ylabel('Movie Title', weight='bold') plt.savefig('popular_movies.png')
现在就能看到基于流行度评分的推荐:
正如此前预计的一样:《小黄人大眼萌》在众多影片中脱颖而出。那么,如果想要基于加权平均值和流行度来推荐电影呢?
推荐系统模型3:
为了避免《小黄人大眼萌》超高的人气分数对评分系统产生影响,我们对加权平均值和受欢迎度的数据进行了标准化调整:加权平均值和欢迎度评分各分得50%的比例。但同时,不要害怕用这一平分比例做做试验。
# My own recommender system # half/half recommendation based on scaled weighted average & popularity score from sklearn import preprocessing min_max_scaler = preprocessing.MinMaxScaler() movies_scaled = min_max_scaler.fit_transform(movies_clean[['weighted_average', 'popularity']]) movies_norm = pd.DataFrame(movies_scaled, columns=['weighted_average', 'popularity']) movies_norm.head() movies_clean[['norm_weighted_average', 'norm_popularity']] = movies_norm movies_clean['score'] = movies_clean['norm_weighted_average'] * 0.5 + movies_clean['norm_popularity'] * 0.5 movies_scored = movies_clean.sort_values(['score'], ascending=False) movies_scored[['original_title', 'norm_weighted_average', 'norm_popularity', 'score']].head(20)
现在得到了一个新的评分系统,将电影的加权平均得分和欢迎度得分都计算在内。下面再来看看该推荐系统会推荐哪些电影:
scored = movies_clean.sort_values('score', ascending=False) plt.figure(figsize=(16,6)) ax = sns.barplot(x=scored['score'].head(10), y=scored['original_title'].head(10), data=scored, palette='deep') #plt.xlim(3.55, 5.25) plt.title('Best Rated & Most Popular Blend', weight='bold') plt.xlabel('Score', weight='bold') plt.ylabel('Movie Title', weight='bold') plt.savefig('scored_movies.png')
这就是基于各占50%比例得出的推荐影片:
以上的推荐系统都能依照设计运作,但显然还可以继续改进。现在我们就一起来看看基于内容的过滤系统。
基于内容的过滤系统
现在要利用某一电影的特点来给用户推荐其他电影。继续遵循Ibtesam的示例,现在来根据电影在概述栏给出的情节梗概来进行推荐。比方说,如果用户提供了一部电影的名字,我们的目标就是推荐其他与该电影情节梗概相似的影片。
字节矢量化和TF-IDF算法
在开始分析情节梗概之前,需要将概述栏中的文本转化为文字矢量,同时也要在概述中加入TF-IDF算法。
from sklearn.feature_extraction.text import TfidfVectorizer # Using Abhishek Thakur's arguments for TF-IDF tfv = TfidfVectorizer(min_df=3, max_features=None, strip_accents='unicode', analyzer='word',token_pattern=r'\w{1,}', ngram_range=(1, 3), use_idf=1,smooth_idf=1,sublinear_tf=1, stop_words = 'english') # Filling NaNs with empty string movies_clean['overview'] = movies_clean['overview'].fillna('') # Fitting the TF-IDF on the 'overview' text tfv_matrix = tfv.fit_transform(movies_clean['overview']) tfv_matrix.shape
这样就得到了以下输出结果:
· (4803, 10417)
我们在情节梗概中用了约10000个不同的词语来描述5000部影片(注意该数据要小于Ibtesam的数据,因为我们通过 min_df=3公式将最小词频提高到3个)。
计算相似分数
现在有了词语矩阵,就可以开始计算相似分数了。这个矩阵有助于选出与由用户提交的电影情节梗概相似的影片。Ibtesam选用了线性核函数,但本文想用sigmoid核函数核做个有趣的实验。幸运的是,我们得到了类似的结果:
from sklearn.metrics.pairwise import sigmoid_kernel # Compute the sigmoid kernel sig = sigmoid_kernel(tfv_matrix, tfv_matrix) # Reverse mapping of indices and movie titles indices = pd.Series(movies_clean.index, index=movies_clean['original_title']).drop_duplicates() # Credit to Ibtesam Ahmed for the skeleton code def give_rec(title, sig=sig): # Get the index corresponding to original_title idx = indices[title] # Get the pairwsie similarity scores sig_scores = list(enumerate(sig[idx])) # Sort the movies sig_scores = sorted(sig_scores, key=lambda x: x[1], reverse=True) # Scores of the 10 most similar movies sig_scores = sig_scores[1:11] # Movie indices movie_indices = [i[0] for i in sig_scores] # Top 10 most similar movies return movies_clean['original_title'].iloc[movie_indices]
现在我们就用经久不衰的最爱影片:《非常小特务》来测试一下已经构建的基于内容的过滤系统。
# Testing our content-based recommendation system with the seminal film Spy Kids give_rec('Spy Kids')
以下是每个基于内容的过滤系统给出的推荐:
推荐系统给出了一些和《非常小特务》相关的影片,但也出现了一些偏差,如《无法自拔》和《毒枭帝国》。
局限性
基于上述结果,可以看到基于内容的过滤系统也有一些局限:
1. 在用户搜索与《非常小特务》相似的电影时,此推荐系统可能会选出用户认为不相似的影片。为了改进此系统,可以考虑用单词计数替换TF-IDF算法,同时计算其他的相似分数。
2. 此系统只会分析每部电影的情节梗概。如果想像Ibtesam一样分析其他特征,如演员、导演、电影流派等,就需要在寻找相似影片方面做出改进。
3. 目前此系统只能基于影片特点的相关性给出推荐。因此会遗漏用户也许会感兴趣的其他流派的电影。我们需要使用协同过滤系统来解决这个问题,但现在的数据库并未包含用户信息。
留言 点赞 关注
我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”