基于SVD算法的电影推荐系统实验
推荐系统发展简单思维导图
电影推荐系统最初思路
原本是想实现一些基本的基于协同过滤思想的推荐系统设计,写完代码之后发现效果不理想。因为是要交的实验报告,所以转而使用其他算法做准确率和召回率。
程序实现的功能
- 基于关键字搜索的搜索推荐模块
- 基于新用户的推荐模块
- 基于老用户的推荐模块
- 相关数据的可视化
- 基于SVD算法的推荐系统使用的是surprise第三方推荐系统库,安装surprise需要virtual C++ 2015构建工具,可从官网下载构建工具而不用安装庞大的vs C++ 软件
- 电影数据集来自movielens中的ml-1m.zip数据集
程序整体框架
程序整体流程框架如图所示
基于关键字搜索推荐模块
设计思路如图所示:
搜索推荐模块代码
def search_name(movies_data , name , ru_mat):
title_flag = input("search by title:")
# #矩阵中用户总数 矩阵列 、行
sum_users = np.size(ru_mat,1)
sum_movies = np.size(ru_mat ,0)
#保存平均评分
aver_lis = []
for i in range(sum_movies):
movie_averpoint = sum(ru_mat[i])/sum_users
aver_lis.append(movie_averpoint)
#movies_data增加一列,最终输出 电影ID 、 电影标题 、 电影类别 、 电影平均评分
movies_data['averpoint'] = aver_lis[:3883]
#根据movies_data查找具有该类别的电影,再根据平均评分排序截取前十数据返回
#先查找电影名称、再查找电影类型,均无则返回错误告知
if title_flag == 'y' or title_flag=='Y':
reco_movies = movies_data[movies_data['title'].str.contains(pat=name)]
else :
reco_movies = movies_data[movies_data['genres'].str.contains(pat=name)]
reco_movies=reco_movies.sort_values(by = 'averpoint' , ascending =False)[:10]
return reco_movies
基于电影名称关键字的推荐模块
推荐效果如图所示:
基于电影类型搜索的推荐模块
推荐效果如图所示:
新用户电影推荐模块
设计思路如图所示:
新用户推荐模块代码
def new_userreco(users_data , loginID):
gender_map = {'F':0 , 'M':1}
users_data['gender'] = users_data['gender'].map(gender_map)
users_data = users_data.drop(['zip-code'],axis=1)
users_mat = users_data.iloc[:,:].values
users_mat=preprocessing.scale(users_mat)
#构造相似度矩阵
newuser_similar = cosine_similarity(users_mat , dense_output=True)
NU_silimar_df = pd.DataFrame(newuser_similar)
#截取该用户与其他用户的相似度
user_similar=NU_silimar_df.iloc[loginID-1]
#user_similar中的列名称即为用户ID-1 , 先获取用户ID-1
user_similar_dict = user_similar.to_dict()
#与logID用户相似的用户存放字典中,再根据字典中的value值排序取其前三
user_similar_dict = sorted(user_similar_dict.items() , key=lambda x: x[1] , reverse=True )[1:4]
#记录当下推荐的电影数
sum_recmovies = 0
#新建推荐电影ID空字典
rec_moviesID = []
for x in range(3):
#获取的是相似用户ID-1
similar_userID = user_similar_dict[x][0]
#当下第i个相似用户的相似度
simila = user_similar_dict[x][1]
#在用户评分矩阵中,该相似用户所在的行就是相似用户ID-1=similar_userID
similuser_rating = UR_mat[similar_userID]
#推荐10部电影,从当前第i个相似用户的用户评分表中找到看过的电影,并计算电影推荐得分,公式:用户相似度*0.7+电影评分*0.3
for i in range(len(similuser_rating)):
if sum_recmovies < 10:
#矩阵是从0开始,真实movieID要加1
if similuser_rating[i] != 0:
score = simila*0.7+0.3*similuser_rating[i]
rec_moviesID.append(([i+1] , score))
sum_recmovies +=1
#每次加入新推荐电影,就对字典排序,为推荐电影满10个时插入更高评分的电影准备
rec_moviesID = sorted(rec_moviesID, key=lambda x: x[1] , reverse=True )
else:
if similuser_rating[i] != 0:
score = simila*0.7+0.3*similuser_rating[i]
if rec_moviesID[9][1] < score:
#删除字典中电影得分最少的元组,加入评分更高的元组,使推荐电影的评分在所有的电影中都是最高的
rec_moviesID[9]=([i+1] , score)
rec_moviesID = sorted(rec_moviesID , key=lambda x: x[1] , reverse=True )
#以上推荐的10部电影ID获取完成
#根据推荐电影ID获取电影详细信息存储,并输出
rec_movies = []
for i in range(len(rec_moviesID)):
moviesID = rec_moviesID[i][0]
moviesID =moviesID[0]-1
rec_movie = list(movies_dat.iloc[moviesID])
rec_movies.append(rec_movie) # 电影信息
#添加电影评分
col = UR_mat[[moviesID]]
rec_movie.append( col[(0<col)].mean() )
rec_movies=rec_movies[:10]
rec_movies = sorted(rec_movies , key=lambda x: x[3] , reverse=True )
return rec_movies
新用户推荐效果
老用户电影推荐模块
最初基于协同过滤思想设计思路如图所示
基于SVD算法的程序流程图
老用户推荐模块代码
这里是自己写的协同过滤的算法,推荐效果不高,只建议参考不建议使用。
def login_reco(UR_mat,movies_dat ,loginID):
UR_mat = preprocessing.scale(UR_mat)
#构造相似度矩阵
similar_mat = cosine_similarity(UR_mat , dense_output=True)
# print(similar_mat)
silimar_df = pd.DataFrame(similar_mat)
#截取该用户与其他用户的相似度
user_similar=silimar_df.iloc[loginID-1]
#user_similar中的列名称即为用户ID-1 , 先获取用户ID-1
user_similar_dict = user_similar.to_dict()
#与logID用户相似的用户存放字典中,再根据字典中的value值排序取其前三
user_similar_dict = sorted(user_similar_dict.items() , key=lambda x: x[1] , reverse=True )[1:2]
simlar_userID = [user[0] for user in user_similar_dict ] #这里获取的只是用户所在行数,真实用户ID还要加一
alluser_watch = []
reco_movies = [ ]
for ID in simlar_userID:
user_line=UR_mat[ID]
for i in range(len(user_line)):
if user_line[i] != 0:
alluser_watch.append(i)#这里获取只是电影所在列,真实电影ID还要加一
result = Counter(alluser_watch).most_common(10) #[:10]
rec_moviesID = [x[0]+1 for x in result]
for id in rec_moviesID:
reco_movies.append(list(movies_dat.iloc[id]))
return rec_moviesID ,reco_movies #返回的是真实的电影ID
这是我参考surprise官网写的代码,推荐效果在文章末尾数据可视化中可见。
def SVD_rec( ):
# 指定文件所在路径
file_path = os.path.expanduser('ratings.csv')
# 告诉文本阅读器,文本的格式是怎么样的
reader = Reader(line_format='user item rating', sep=',')
# 加载数据
data = Dataset.load_from_file(file_path, reader=reader)
#十折交叉验证,n_splits=n 就是n折
kf = KFold(n_splits=10)
algo = SVD()
#十组测试集中每组的平均准确率
accuracy = []
recall = []
#将原始数据分成10等分,进行十折交叉验证
for trainset, testset in kf.split(data):
print('----------------------')
#对训练集使用SVD算法进行训练
algo.fit(trainset)
#使用训练好的SVD算法获得测试集的预测值
predictions = algo.test(testset)
#计算该组的所有测试集用户的准确率和召回率
precisions, recalls = precision_recall_at_k(predictions, k=5, threshold=4)
# 计算测试集的平均准确率和召回率
accuracy.append( sum(prec for prec in precisions.values()) / len(precisions) )
recall.append( sum(rec for rec in recalls.values()) / len(recalls) )
return accuracy ,recall
def precision_recall_at_k(predictions, k=10, threshold=3.5):
'''返回为每个用户推荐k个物品时的准确率和召回率。'''
# 首先将预测值映射至每个用户
user_est_true = defaultdict(list)
for uid, _, true_r, est, _ in predictions:
user_est_true[uid].append((est, true_r))
precisions = dict()
recalls = dict()
for uid, user_ratings in user_est_true.items():
# Sort user ratings by estimated value
user_ratings.sort(key=lambda x: x[0], reverse=True)
# Number of relevant items
n_rel = sum((true_r >= threshold) for (_, true_r) in user_ratings)
# Number of recommended items in top k
n_rec_k = sum((est >= threshold) for (est, _) in user_ratings[:k])
# Number of relevant and recommended items in top k
n_rel_and_rec_k = sum(((true_r >= threshold) and (est >= threshold))
for (est, true_r) in user_ratings[:k])
# Precision@K: Proportion of recommended items that are relevant
precisions[uid] = n_rel_and_rec_k / n_rec_k if n_rec_k != 0 else 1
# Recall@K: Proportion of relevant items that are recommended
recalls[uid] = n_rel_and_rec_k / n_rel if n_rel != 0 else 1
return precisions, recalls
老用户推荐效果
推荐效果如图所示:
数据可视化
用户评分相似度矩阵可视化
相似矩阵可视化代码
fig , ax = plt.subplots(1,1)
ax.set_title('相似度矩阵可视化' , fontsize = 12)
ax.matshow(UR_mat)
plt.show()
准确率与召回率可视化(SVD)
召回率可视化代码
#画图
def show_acc_recall(accuracy , recall):
#准确率和召回率折线图
plt.plot(accuracy, marker = 'o', label = '准确率')
plt.plot(recall, marker = 'x', label = '召回率')
plt.xlabel('测试集')
plt.ylabel('百分比')
plt.title('准确率和召回率折线图')
plt.legend()
plt.show()
#十折交叉验证的平均准确率
name_list = ['平均准确率' , '平均召回率']
num_list = [sum(accuracy)/len(accuracy) , sum(recall)/len(recall)]
plt.bar(range(len(num_list)), num_list,color='rgb',tick_label=name_list)
plt.show()
accuracy , recall =SVD_rec()
show_acc_recall(accuracy , recall)
平均准确率与召回率(SVD)
总结
你又在写bug啊???