这几天在看《推荐系统实践》,说实话看完以后我整个人的三观都崩塌了,现在只要拿起手机刷一刷B站或者酷狗,我就会去想他们是怎么做推荐的……
书中介绍了一种基于协同过滤算法的推荐系统,讲到推荐算法的话,书中提到了ItemCF和UserCF算法两种,当然还有机器学习的图运算,然后基于此对算法进行了改进和升级。
我们今天主要来尝试一下用tensorflow来给电影视频做推荐。我们采用的数据集来源自MovieLens:https://grouplens.org/datasets/movielens/
我们的目的其实就是基于用户或者是基于物品给用户推荐物品,或者是把新物品推荐给可能感兴趣的用户。用数据来量化呢,就是要去寻找两个物品/用户之间的相似度。
主要的方法有三种:
基于欧氏距离——
相似度公式就是——
也可以使用余项相似度——
或者是皮尔逊相似度——
其实无论哪种公式,都会有在位置0处值为1且为最大值。
那么有了理论基础之后,我们就要开始用机器学习的方式来尝试一下推荐算法。
1.准备数据集
首先把之前的数据集给下载了。
数据格式解析
movieLens20M使用了CSV格式存储数据列表,代替了10M和1M、100K的DAT格式,可以直接可视化分析。
文件列表:
genome_scores.csv、genome-tags.csv、links.csv、movies.csv、ratings.csv、tags.csv。
movies.csv:MovieId+title+geners。以此表示电影ID、电影名称、电影流派/种类。其中电影流派具有多个标签,即可以表示电影的多个属性。用以生成电影属性矩阵。
Rating.csv:userId+movieId+rating+timestamp。分别表示用户ID、电影ID、评分,以及截至时间戳。给出了用户对电影的评分列表。用以生成用户-电影评分矩阵。
Trgs.csv: userId+ movieId+tag+timestamp。分别表示用户ID、电影ID、用户对电影的标签、时间戳。给出了用户对电影的标签列表。用以生成用户-电影标签矩阵。
Links.csv:moviesId+imdeId+tmdbId。IMDB为互联网电影资料库。tMDB为电影数据集。给出了电影ID和两个数据标记ID的对应关系。
genome_tags.csv:电影标签 DNA标记,唯一标识符。
genome_scores.csv: movieId+tagId+relevance。分别表示电影ID、电影标签ID、官方标签相关性。给出了电影的官方标签。用以生成电影的标签相关性矩阵。
2.数据的清洗
先导入库
importpandasaspd
importnumpyasnp
importtensorflowastf
读取电影评分的ratings.csv文件
ratings_df = pd.read_csv('Desktop/ml-latest-small/ratings.csv')
我们来看一下这个表中最后几行的内容
ratings_df.tail()
同理读取电影库的.csv文件
movies_df = pd.read_csv('Desktop/ml-latest-small/movies.csv')
movies_df.tail()
可以看到最后是
增加一列名为movieRow的索引值,内容为行号:
movies_df['MovieRow'] = movies_df.index
movies_df.tail()
3.特征提取
首先提取我们需要的特征
movies_df = movies_df[['MovieRow','movieId','title']]
movies_df.tail()
然后我们将这个列表作为处理好的数据储存起来方便后面用到:
movies_df.to_csv('Desktop/ml-latest-small/moviesProcessed.csv',index=False, header =True, encoding ='utf-8')
储存好了特征提取之后的数据之后,我们将评分列表和电影列表合并,并对其movieId这一列。
ratings_df = pd.merge(ratings_df, movies_df,on='movieId')
ratings_df.head()
合并两张表之后,我们将合并好的表再来提取特征:
ratings_df = ratings_df[['userId','MovieRow','rating']]
ratings_df.head()
4.创建电影评分矩阵rating和评分记录矩阵record
特征提取完成之后,首先我们来获取评分的用户和电影的数量:
userNo = ratings_df['userId'].max()+1
movieNo = ratings_df['MovieRow'].max()+1
接下来创建一个movieNo行,userNo列,内容是0的矩阵:
rating = np.zeros((movieNo,userNo))
但是有一个错误…
内…内存不足……
计算了一下大概是一个27278x138494的零矩阵,也就大概40TB吧………玩数据挖掘的估计家里有矿吧
所以我们换了ml-latest-small…小一点吧,重新前面的操作
设置一个标签位flag,然后获取合并之后列表的列数,然后遍历rating矩阵每一个位置,将电影的评分填入矩阵中:
#标志位
flag = 0
#获取合并表中的列数
ratings_df_length = np.shape(ratings_df)[0]
#遍历矩阵,将电影的评分填入表中
for index,row in ratings_df.iterrows():
rating[int(row['MovieRow']), int(row['userId'])] = row['rating']
flag += 1
print('processed %d, %d left' %(flag,ratings_df_length-flag))
然后我们来获取一个电影是否被用户评分的列表,其中1代表该电影已经被用户评分,0是用户没有对该电影评分:
record = rating > 0
record = np.array(record, dtype = int)
record
可以得到结果矩阵:
同时我们也可以得到评分的矩阵:
5.构建模型
构建模型之前,我们将nan的地方转成数字0:
rating_norm = np.nan_to_num(rating_norm)
然后我们来定义一个标准化评分表的方法:
def normalizeRatings(rating, record):
#获取电影的数量m和用户的数量n
m,n = rating.shape
#rating_mean-电影平均分 rating_norm-标准化后的电影得分
rating_mean = np.zeros((m,1))
#rating_norm = np.zeros((m,n))
#rating_norm = np.nan_to_num(rating_norm)
rating_norm = np.nan_to_num(np.zeros((m,n)))
fori in range(m):
idx = record[i,:]!=0
rating_mean[i] = np.mean(rating[i,idx])
rating_norm[i,idx] -= rating_mean[i]
rating_mean = np.nan_to_num(rating_mean)
returnrating_norm, rating_mean
可以得到标准化后的矩阵:
接下来就是用TensorFlow来构建模型:
num_features =10
X_parameters =tf.Variable(tf.random_normal([movieNo, num_features],stddev = 0.35))
Theta_parameters= tf.Variable(tf.random_normal([userNo, num_features],stddev = 0.35))
然后又报错了
事实证明长期不用游泳,控制不当就会...咳咳咳
事实证明长期不用TF,就做不了TFboy了~
基于内容推荐算法的损失函数公式:
在这个公式中,r(x,y)是评分记录表,u是用户数量,θ(j)是j用户的喜好,y(i,j)是i用户对j电影的评分,xi表示电影的内容,n是特征数量,最后一部分是正则化项。我们后续的目的就是来最小化这个损失函数,接下来就是优化J(θ),使其最小化。
optimizer = tf.train.AdamOptimizer(1e-4)
train = optimizer.minimize(loss)
最后的步骤就是训练模型了:
结果:
#merge_all可以将所有summary全部保存到磁盘,以便tensorboard显示。
filename ='./movie_tensorborad'
writer = tf.summary.FileWriter(filename)
#指定一个文件用来保存图。
sess = tf.Session()
#定义一个session
init = tf.global_variables_initializer()
sess.run(init)
#运行session
接下来就是递归5000次,直到收敛:
for iinrange(5000):
_, movie_summary = sess.run([train, summaryMerged])
writer.add_summary(movie_summary, i)
模型训练完成之后,我们再来评估一下我们训练的模型:
Current_X_parameters, Current_Theta_parameters = sess.run([X_parameters, Theta_parameters])
#Current_X_parameters为用户内容矩阵,Current_Theta_parameters用户喜好矩阵
predicts = np.dot(Current_X_parameters,Current_Theta_parameters.T) + rating_mean
errors = np.sqrt(np.sum((predicts - rating)**2))
# sqrt(arr) ,计算各元素的平方根
Errors
userId = input('您要向哪位用户进行推荐呢?请输入用户编号:(smaller than672)')
sortedResult = predicts[:,int(userId)].argsort()[::-1]
idx =
print('为该用户推荐的评分最高的20部电影是:'.center(80,'='))
foriinsortedResult:
print('score: %.3f, movie name: %s'% (predicts[i,int(userId)], movies_df.iloc[i]['title']))
idx +=1
ifidx ==20:break
我们可以输入用户的ID,然后就可以得到如下的电影推荐输出:
好了,这样我们就可以实现基于数据的简单挖掘了~
我是垚垚,我们下期再见:)
领取专属 10元无门槛券
私享最新 技术干货