最近读到了一篇有意思的文章:如何用深度学习推荐电影,我顺着文章的思路实现了一遍,补全了原文中缺失的code,也加入了一些避免重复操作的code,亦做记录亦作分享。本项目主要实现了基于「1」协同过滤「2」DL特征提取进行电影推荐。
数据集
来源于 MovieLens 中的ml-latest-small.zip,当然也可以从本文最后我的github中找到。
本项目主要用到其中的两个csv文件:
- ml-latest-small/ratings.csvz:包含671个用户,共100004个打分
- ml-latest-small/links.csv:包含9125个电影及其IMDBid和TMDBid
分别长这样:
#ratings.csvz:
userId movieId rating timestamp
0 1 31 2.5 1260759144
1 1 1029 3.0 1260759179
2 1 1061 3.0 1260759182
3 1 1129 2.0 1260759185
4 1 1172 4.0 1260759205
......
#links.csv:
movieId imdbId tmdbId
0 1 114709 862.0
1 2 113497 8844.0
2 3 113228 15602.0
3 4 114885 31357.0
4 5 113041 11862.0
......
扒电影海报的网站:
Movie Database,网站提供API,申请不麻烦。不过你们要是能够保证不做坏事的话,可以悄悄告诉你在github里我没有删掉我的API。
理论背景
粗略地说,推荐系统有三种类型(不包括简单的评级方法):
- 基于内容的推荐
- 协同过滤
- 混合模型
“基于内容的推荐”是一个回归问题,我们把电影内容作为特征,对用户对电影的评分做预测。
“协同过滤”中,一般无法提前获得内容特征。是通过用户之间的相似度(用户们给了用一个电影相同的评级)和电影之间的相似度(有相似用户评级的电影)来学习潜在特征,同时预测用户对电影的评分。在学习了电影的特征之后,我们便可以衡量电影之间的相似度,并根据用户历史观影信息,向他/她推荐最相似的电影。
“基于内容的推荐”和“协同过滤”是10多年前最先进的技术。很显然,现在有很多模型和算法可以提高预测效果。比如,针对事先缺乏用户电影评分信息的情况,可以使用隐式矩阵分解,用偏好和置信度取代用户电影打分——比如用户对电影推荐有多少次点击,以此进行协同过滤。另外,我们还可以将“内容推荐”与“协同过滤”的方法结合起来,将内容作为侧面信息来提高预测精度。这种混合方法,可以用“学习进行排序”(”Learning to Rank” )算法来实现。
在该项目中,采用的方法是“协同过滤”。首先,用电影和用户相似度来找出相似度最高的海报,并基于相似度做电影推荐。然后,我将讨论如何Deep Learning学习潜在特征、做电影推荐。最后会谈谈如何在推荐系统中使用深度学习。
电影相似性
对于基于协同过滤的推荐系统,首先要建立评分矩阵。其中,每一行表示一个用户,每一列对应其对某一电影的打分。建立的评分矩阵如下:
#!/usr/bin/env python3
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error
rating_f = 'ml-latest-small/ratings.csv'
link_f = 'ml-latest-small/links.csv'
df = pd.read_csv(rating_f,sep=',')
df_id = pd.read_csv(link_f,sep=',')
df = pd.merge(df,df_id,on=['movieId'])
rating_matrix = np.zeros((df.userId.unique().shape[0],max(df.movieId)))
for row in df.itertuples():
rating_matrix[row[1]-1,row[2]-1] = row[3]
rating_matrix = rating_matrix[:,:9000] #get first 9000 movies
“ratings.csv”包含用户id,电影id, 评级,和时间信息,其中同一个用户id可能对多个电影进行打分;“links.csv”包含电影id, IMDB id,和TMDB id。这两个文件中包含的电影总数是不同的(rating.csv: 9066, links.csv: 9125)。我们将两个文件依movieId合并,并在此基础上得到评分矩阵rating_matrix[userId:movieId]=grade。
计算评分矩阵的稀疏性:
#Evaluate sparsity of matrix
sparsity = float(len(rating_matrix.nonzero()[0]))
sparsity /= (rating_matrix.shape[0]*rating_matrix.shape[1])
sparsity *= 100
print('Sparsity is {0}%'.format(sparsity))
可以得到sparsity=1.4%,认为是稀疏矩阵。
现在,为了训练和测试,我们将评分矩阵分解成两个矩阵,从每行(userId)评分矩阵中抠出了10个评分,将其放入测试集。
#Splite to train/test matrix
train_matrix = rating_matrix.copy()
test_matrix = np.zeros(rating_matrix.shape)
for i in range(rating_matrix.shape[0]):
rating_index = np.random.choice(rating_matrix[i].nonzero()[0],size=10,replace=True) #return a list
train_matrix[i,rating_index] = 0.0
test_matrix[i,rating_index] = rating_matrix[i,rating_index]
根据余弦相似性计算两个特征间的夹角(具体过程参见另外一篇博文:余弦相似定理和新闻分类):
即: