算法告诉你,如何推荐一部电影
上文,胡萝卜酱给大家讲述了协同过滤算法的原理,那么就来进行一次实战吧。数据集来源于
https://grouplens.org/datasets/movielens/
这里胡萝卜酱下载的是小数据集,当然你也可以下载大数据集来进行计算。
数据探索
数据解压会有四张表,这里小编只用到了“movies”和“ratings”两个表,同时通过movieId将两张表合成为一个数据框。
1import pandas as pd
2import numpy as np
3
4ratings = pd.read_csv('ml-latest-small/ratings.csv',index_col=None)
5movies = pd.read_csv('ml-latest-small/movies.csv',index_col=None)
6data = pd.merge(ratings,movies,on='movieId')
7print (data.head(10))需要了解的是,该数据集一共有多少用户参与了多少部电影的评分,有如下代码
1#数据透视用户评分情况
2user_rating = data.pivot_table(values = 'title',index = 'userId',columns = 'rating',aggfunc = 'count')
3print (user_rating.head())
4# 计算唯一用户和电影的数量
5n_user = data.userId.unique().shape[0]
6n_movie = data.movieId.unique().shape[0]
7print('用户数量:' + str(n_user) )
8print ('电影数量' + str(n_movie))用户数量:671,电影数量:9066
用户—评分矩阵
本文只进行基于用户的协同过滤算法的演示,所以,这里需要创建一个以用户为行,电影评分为列的二维矩阵。若是基于电影的协同过滤算法,则将矩阵进行转置即可。同时,需要将数据集分为训练集和测试集。
1# 使用scikit-learn库将数据集分割成测试和训练,本例中是0.25,
2from sklearn.model_selection import train_test_split
3train_data, test_data = train_test_split(data, test_size=0.25)
4# 第一步是创建uesr-item矩阵,此处需创建训练和测试两个UI矩阵
5movie_index = list(set(data.movieId))
6movie_index.sort()
7train_data_matrix = np.zeros((n_user, n_movie))
8#line[1]是userId,line[2]是movieId,line[3]是评分
9for line in train_data.itertuples():
10 train_data_matrix[line[1] - 1, movie_index.index(line[2])] = line[3]
11test_data_matrix = np.zeros((n_user, n_movie))
12for line in test_data.itertuples():
13 test_data_matrix[line[1] - 1, movie_index.index(line[2])] = line[3]相似度计算
本文采取的是求余弦相似度,考虑到余弦相似度只关注方向,为考虑具体得分值,同时,考虑到用户评分偏好的不同,比如有些用户喜爱的电影给5分,不喜欢的给3分,而有的用户,喜爱的电影给4分,不喜爱的给1分。所以需要求其平均值,来进行标准化。
1#用户平均评分
2def average_rating(user):
3 average = []
4 for i in range(user.shape[0]):
5 sum , num = 0 , 0
6 for j in range(user.shape[1]):
7 if user[i][j] != 0:
8 sum = sum + user[i][j]
9 num = num + 1
10 average.append(sum*1.0/num)
11 return average
12#标准化用户评分
13def standard(user,average):
14 for i in range(user.shape[0]):
15 for j in range(user.shape[1]):
16 if user[i][j] != 0:
17 user[i][j] = user[i][j] - average[i]
18 return user
19train_average = average_rating(train_data_matrix)
20train_data_matrix = standard(train_data_matrix,train_average)
21
22# 使用sklearn的pairwise_distances函数来计算余弦相似性
23from sklearn.metrics.pairwise import cosine_similarity
24
25# 计算用户相似度
26user_similarity = cosine_similarity(train_data_matrix)
27
28# 计算物品相似度
29movie_similarity = cosine_similarity(train_data_matrix.T)基于前K相似度推荐
和上文一样,本文只选取前K相似度进行推荐,并把相似度作为权重,计算推荐电影评分,并基于评分,推荐前10的电影。
1from operator import itemgetter
2def getRecommendations(userid, similarity, k, user_matrix):
3 pred={}
4 average_u_rate = train_average[userid-1]
5 sim = -np.sort(-similarity[userid-1])[1:k+1] #相似度排前k的值
6 index = np.argsort(-similarity[userid-1])[1:k+1] #前k的索引
7 sumUserSim = 0
8 for i,j in zip(index,sim):
9 for m,n in zip(range(len(user_matrix[i])),user_matrix[i]):
10 if n != 0 and user_matrix[userid-1][m] == 0:
11 pred.setdefault(m,0)
12 pred[m] += j*n
13 sumUserSim += j
14 for i, rating in pred.items():
15 pred[i] = average_u_rate + (pred[i]*1.0) / sumUserSim
16
17 # top-10 pred
18 pred = sorted(pred.items(), key=itemgetter(1), reverse=True)[0:10]
19 return pred 结果比较
假设以用户id为‘182’的用户进行推荐,并和测试集里这位用户还观看的电影进行对比,验证推荐是否有效。
1recom = getRecommendations(182, user_similarity, 20, train_data_matrix)
2name,mark = [],[]
3for i, rating in recom:
4 film_id = movie_index[i]
5 film_name = movies[movies['movieId'] == film_id].title.values
6 name.append(film_name)
7 mark.append(rating)
8 #print (' filmname:{}, rating: {}'.format( film_name, rating) )
9test = test_data[test_data['userId'] == 182]
10
11from pyecharts import Bar,Line,Overlap
12myBar = Bar("观看电影及评分")
13myBar.add("",test.title.values,test.rating.values)
14line = Line("推荐电影及评分")
15line.add('',name,mark)
16overlap = Overlap()
17overlap.add(myBar)
18overlap.add(line,is_add_xaxis=True) #是否新增一个 x 坐标轴,默认为 False
19overlap.render('movie.html')由上,我们只推荐了十部电影,这十部电影,该用户都看了。虽预测评分有偏差,但总体效果还是很不错的。
结语
这次由于采用的小数据集,所以没有考虑稀疏矩阵的问题,若矩阵过于稀疏,可以通过矩阵分解降维。
此文花费了不少功夫,赞赏、点赞、转发都是对作者的认可和支持。