Spark Hive实现基于协同过滤的电影推荐(MovieLens数据集)

article/2025/10/12 16:39:39

  这篇文章记录一下我之前做过的通过Spark与Hive实现的基于协调过滤的电影推荐。这篇文章只能提供算法、思路和过程记录,并没有完整的代码,仅尽量全面地记录过程细节方便参考。

一、数据获取

  数据集是从下面这个地址下载的,数据集主要内容是关于用户对电影的评分、评价等。免费数据集下载(很全面)_浅笑古今的博客-CSDN博客_数据集下载网站

图1.1 数据获取

我选取的几个数据集表格如下:

图1.2 数据表格

图1.3 rating表

图1.4 movies表

图1.5 tags表

图1.6 genome-tags表

图1.7 genome-scores表

‘ratings’表是关于用户对电影的评分 24404096条

‘movies’表是关于每部电影的基本信息(电影id,名字,种类,电影的上映时间为title的后四个字符) 40110条

‘tags’表是关于用户对电影的评价(后面用tag代替) 259135条

‘genome-tags’表是每个tag的id 1128

‘genome-scores’表是每部电影与1128个tag的相关度 12040272条

图1.8 数据表总体关系展示

  上图中,未被圈起来的内容是表格名称,圈中的内容是表格的字段,表格名称被哪些字段的连线所包围,就说明该表格包括哪些字段,部分被多个表同时拥有的字段也非常恰当的表现在合适的位置从而被多个表关联。

下面也附上各个字段的详细信息介绍:

图1.9 详细字段说明

二、数据清洗与挖掘

2.1获得每部电影的平均得分

  首先,根据‘rating’表groupby处理求出每部电影的平均分,再与movies表合并。

  代码实现如下(Hive中):

drop table movies;
create table movies (movieId String,title String,genres String) row format delimited fields terminated by ','
tblproperties("skip.header.line.count"="1");load data local inpath "/usr/local/src/movies.csv" into table movies;#先把rating表group by处理 带上平均分
select movieId,avg(rating) from ratings group by movieId;
create table movie_avg_rating(movieId String,avg_rating String) 
Insert into movie_avg_rating  select movieId,avg(rating) from ratings group by movieId;#再join movies表
create table movies_detail_rating(movieId String,title String,genres String,rating String) ;
Insert into movies_detail_rating
Select movies.movieId,movies.title,movies.genres,movie_avg_rating.avg_rating from movies join movie_avg_rating on movies.movieId =movie_avg_rating.movieId;

得到如下表:

图2.1.1 添加上评分的movies表

2.2 根据tag聚类后对每部电影分类

  根据‘genome-scores’中每部电影与tags的相关度进行聚类,将聚类结果分为三类,并加入到movies表中。(这里聚类的原因是因为后面计算电影相似度时如果电影无差别两两计算,数据量会爆炸;因此提前对电影进行聚类,后面只让同类别的电影进行相似度计算,虽然会有损失,但总体上增加计算效率。)

  得到的聚类结果如下:

 图2.2.1 根据tags相关度聚类结果

  聚类代码实现如下(python):

import pandas as pd
import numpy as np
import csv
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
data = pd.read_csv('D:\MovieLens Dataset\ml-latest/genome-scores.csv')
# print(len(data))#1200w条数据
# 逐行读取csv文件
data={}
with open('D:\MovieLens Dataset\ml-latest/genome-scores.csv','r',encoding="utf-8") as csvfile:reader = csv.DictReader(csvfile)for row in reader:if data.get(row['movieId'],-1)==-1:data[row['movieId']]=[row['relevance']]else:data[row['movieId']].append(row['relevance'])
print(len(data['1']))
x = []
for k,v in data.items():# print(k)x.append(v)
X = np.array(x)
km = KMeans(n_clusters=3).fit(X)
# 标签结果
rs_labels = km.labels_
len(data.keys())
len(rs_labels)
movie_class = pd.DataFrame()
movie_class['movieId']=data.keys()
movie_class['classId']=rs_labels
movie_class.to_csv('D:\MovieLens Dataset\ml-latest/movie_class1.csv',index=False)

    数据清洗后的‘movies’表格如下。其基本信息包括电影id、title、电影种类(genres)、平均评分(rates)、类别(class)。

 图2.2.2 数据清洗后的movies表

2.3 对电影数据可视化分析与数据挖掘

  对于已有的数据集,我打算以上映时间为x轴(为了使点离散化,时间在原来基础上增加了一个(0,1)之间的小数),评分为y轴,类别class作为区分色,按电影种类划分的HTML可视化动画(‘All’代表所有种类)。(这部分的实现代码找不到了没贴出来,也不是重点。)

其中三个不同类别电影的可视化动画截图如下:

图2.3.1 Action类别电影评分预览

图2.3.2 Adventure类别电影评分预览

图2.3.3 Comedy类别电影评分预览

  通过最后画出来的可视化图像,我们可以清楚的看到每个种类电影的数目、上映时间、评分等信息。  通过观察图标我发现Comedy和Drama类别的电影很多,且评分大都中等偏上,而Child类别评分普遍偏低,其中Film-Noir类别的电影数目很少以至于样点十分稀疏。

  之前进行电影推荐时我用了tag来预测电影的class(tag和genres并不一样,是电影的不同属性),正好在可视化的过程中我把点的颜色以class的不同做了划分,来看看基于tag的class在评分上会不会有明显区别。令人惊喜的是:从图上可以看出颜色有明显的分层!说明根据用户评价的tag聚类得到的分类结果是有参考价值的,tag的不同隐约对应或影响着电影评分的分级。这是一个比较有趣的联系。

三、协同过滤算法实现

  协同过滤算法的总体思路是根据用户最喜爱的五部电影(该用户评分最高的前五部)找出与这五部电影相关度高的10部电影进行推荐。电影相关度是根据余弦相似度来计算得出的值,每部电影定义为一个向量(如下图),该向量的每一个分量即为不同用户对该部电影的评分,计算每个电影向量与其他电影向量的余弦相似度得到的结果即为电影相关度。

图3.1 电影向量

图3.2 余弦相似度计算公式参考

  同时,上面讲了,为了减少计算量,我在计算电影向量的余弦相似度之前,添加限制条件电影的class要相同,并将聚类的结果由原来的3类改为1000类(发现3类还是不太够,再进一步减少计算量),使用的聚类方法还是同上面一致,得到的新类别表为movie_class1.csv,重新导入hive中的movie_class表格。

  具体实现步骤如下:

  实现代码(hive中):

drop table ratings;
create table ratings (userId String,movieId String,rating String,time String) row format delimited fields terminated by ','
tblproperties("skip.header.line.count"="1");load data local inpath "/usr/local/src/ratings.csv" into table ratings;drop table movie_class;
create table movie_class(movieId String,classId String) row format delimited fields terminated by ',' tblproperties("skip.header.line.count"="1");load data local inpath "/usr/local/src/movie_class1.csv" into table movie_class;

  得到的movie_class预览如下:

 图3.3 movie_class

  将movie_class与rating合并:

drop table rating_class;
create table rating_class(userId String,movieId String,rating String,classId String);
#创建一个新表rating_class并把这些数据放进去 rating_class里包含用户对电影的评分和电影类别。
Insert into rating_class select ratings.userId,ratings.movieId,ratings.rating,movie_class.classId from ratings join movie_class on ratings.movieId=movie_class.movieId ;

  得到rating_class如下:

 图3.4 rating_calss

  来到spark-shell界面,引入必要的扩展包和初始化配置:

import breeze.numerics.{pow, sqrt}
import org.apache.spark.sql.SparkSession
import breeze.numerics.{pow, sqrt}
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions.{desc, row_number, udf}
import org.apache.spark.sql.{DataFrame, SparkSession}//    在driver端创建 sparksessionval spark = SparkSession.builder().master("local").appName("testRdd1").config("hive.metastore.uris","thrift://"+"192.168.77.10"+":9083").
//      config("spark.sql.warehouse.dir", "F:/recommend/spark-warehouse").enableHiveSupport().getOrCreate()

在spark中预览一下之前处理好的表格rating_class,将其读取为变量data。

 图3.5 rating_calss

  为了计算两个电影之间的相关度,新建copy表复制data的数据并将两表合并,为了减少计算量并加快计算速度,加上前提条件两个表的class相同,使数据由原来的120亿减少到3000万。因为我最后就是要基于相似的电影为用户做推荐,因此类别相似的电影提前过滤出来,不仅减少了不必要数据处理规模,也是对算法的优化。

实现代码如下(scala):

val dataCopy = data.selectExpr("userId as userId1", "movieId as movieId1", "rating as rating1","classId as classId1")
val df_item_decare = dataCopy.join(data, dataCopy("userId1") === data("userId")and dataCopy("classId1") === data("classId")).filter("cast(movieId as long)!=cast(movieId1 as long)");

  结果如下:

 图3.6 电影复制表

  现在准备工作做好了,开始计算电影向量的余弦相似度即两向量的余弦值。

  首先是计算分子:

val rating_product = df_item_decare.selectExpr("movieId", "movieId1", "rating*rating1 as rating_product")
val itemSameUserRatingProductSum = rating_product.  groupBy("movieId", "movieId1"). //直接以物品对位key,这时候用户已经对好了,然后 后面的相加就是这两个物品相似度的分子了agg("rating_product" -> "sum").withColumnRenamed("sum(rating_product)", "rating_sum_product")

  得到的分子结果:

 图3.7 计算余弦相似度的分子

  然后是计算分母:

val itemSqrtRatingSum  = data.rdd.map(x => (x(1).toString(), x(2).toString)). //直接把物品对应的所有用户评分都算进去了 作为分母 多算的部分其实无所谓groupByKey().mapValues(x => sqrt(x.toArray.map(rating => pow(rating.toDouble, 2)).sum)).toDF("movieId", "sqrtRating")

  得到的分母结果:

图3.8 计算余弦相似度的分母

合并后结果:

 图3.9 计算余弦相似度的分母与分子

  接下来计算余弦相似度即电影相关度,实验代码如下:

val itemSimilar = baseData.selectExpr("movieId", "movieId1", "rating_sum_product/(sqrtRating*sqrtRatingCopy) as itemSimilar")
val itemSimilar_new = itemSimilar.sort(desc("movieId"), desc("itemSimilar")).filter("itemSimilar > 0" )

得到的结果:

 图3.10 电影相关度

现在进行推荐:

var itemSimilarCache:DataFrame=spark.sql("select * from homework.itemsimilar") ;val userLikeItem = data.withColumn("rank",row_number().over(Window.partitionBy("userId").orderBy($"rating".desc))).filter(s"rank <= 5").drop("rank")

此处对每个用户进行统计,其最喜欢的top5个电影,得到的结果如下:

 图 3.11 每个用户最喜欢的top5电影

  根据用户最喜欢的电影中每部电影的评分与该电影和其他电影的电影相关度乘积作为新的权重进行排序,选取前十部电影推荐给该用户。

  实验代码如下:

val recommand_list = userLikeItem.join(itemSimilarCache,"movieId").filter("movieId<>movieId1").selectExpr("userId","movieId1 as recall_list","rating*itemSimilar as recall_weight").withColumn("rank",row_number().over(Window.partitionBy("userId").orderBy($"recall_weight".desc))).filter(s"rank <= 10").drop("rank")

得到的结果如下:

图3.12 给用户推荐的10部电影

  最后的到的结果有2563891条,将结果存入数据库并导出到本地文件‘result.xlsx’:

recommand_list.write.mode("overwrite").saveAsTable("homework.recommand_list")Insert overwrite local directory '/usr/local/src/recommand_list' select * from recommand_list;

  最终相似度矩阵(推荐结果)result.xlsx预览如下:

图3.13 结果预览

  第一个深色列是excel自增id与数据无关,因excel显示行数有限所以只展示到104万行,从这也可以看出推荐结果条数仍不少。第二列是用户id,第三列是电影id,第四列是计算得到的用户对这个电影的预期评分,其值越大就越值得推荐,这里对每个用户都已从大到小取了前十个推荐值用以展示。

  后续可以根据这个结果表使用django或者其他简单的可视化组件制作电影推荐界面,这里比如可以通过表中的imdbid字段去爬电影的海报等信息用作可视化界面制作,就省略不写了。

四、小结

  这篇文章整理了在Spark Hive中基于协同过滤的电影推荐算法的实现,实现这些需要一步一步去做,细节较多,有些地方难免也会有没讲清楚和遗漏的,仅供参考学习和记录。


http://chatgpt.dhexx.cn/article/lOZqHgSa.shtml

相关文章

基于用户的协同过滤Movielens电影推荐系统简单实例

基于用户的协同过滤Movielens电影推荐系统简单实例 一、Movielens数据集 1. MovieLens数据集的下载&#xff08;Download&#xff09; 1&#xff09; 从网站下载数据 链接: https://grouplens.org/datasets/movielens/. 有好几种版本&#xff0c;对应不同数据量&#xff0c;…

ML之GB:基于MovieLens电影评分数据集利用基于图的推荐算法(Neo4j图数据库+Cypher查询语言)实现对用户进行Top5电影推荐案例

ML之GB&#xff1a;基于MovieLens电影评分数据集利用基于图的推荐算法(Neo4j图数据库Cypher查询语言)实现对用户进行Top5电影推荐案例 目录 基于MovieLens电影评分数据集利用基于图的推荐算法(Neo4j图数据库Cypher查询语言)实现对用户进行Top5电影推荐案例 1、定义数据集 1.…

从IMDB上爬取MovieLens-1m的补充数据(电影海报和简介)

文章主要内容 本人是想做推荐算法相关的一名在校生&#xff0c;目前想做多模态融合&#xff0c;而MovieLens-1m数据集只有电影信息和用户信息&#xff0c;于是有想法能否在原有的电影推荐公开数据集中而外获取电影海报&#xff08;图片信息&#xff09;和电影简介&#xff08;…

对Movielens数据集进行评分预测

对Movielens数据集进行评分预测 实验源码&#xff1a;lab3代码.ipynb 实验环境&#xff1a;vscode colab 数据解释&#xff1a; movies.dat的数据如下 1::Toy Story (1995)::Animation|Childrens|Comedy 2::Jumanji (1995)::Adventure|Childrens|Fantasy 3::Grumpier Old…

ML之KG:基于MovieLens电影评分数据集利用基于知识图谱的推荐算法(networkx+基于路径相似度的方法)实现对用户进行Top电影推荐案例

ML之KG&#xff1a;基于MovieLens电影评分数据集利用基于知识图谱的推荐算法(networkx基于路径相似度的方法)实现对用户进行Top电影推荐案例 目录 基于MovieLens电影评分数据集利用基于知识图谱的推荐算法(networkx基于路径相似度的方法)实现对用户进行Top电影推荐案例 # 1、定…

利用pandas对MovieLens电影数据分析

掌握pandas基本语法操作「pandas基础入门中有详细语法格式」后&#xff0c;就可以利用pandas做一些简单实例的数据处理。 Movie电影数据分析 首先需要下载电影数据集MovieLens&#xff0c;这个数据集中包含用户数据&#xff1b;电影数据&#xff1b;电影评分表。电影数据中包…

MovieLens数据集处理

有一个定律&#xff0c;对于内容的访问遵循80/20原则&#xff0c;也就是20%的内容&#xff0c;会占有80%的访问量。就是zipf分布[1]。  根据MovieLens的数据集中的ratings.dat&#xff0c;我做了数据处理&#xff0c;获取得分最高的2000个条目。 ml-pro.py import os import …

推荐系统笔记(二):常用数据集Movielens学习

介绍 movielens数据集是电影推荐数据集&#xff0c;数据集有多种大小和目的使用的数据集。按照使用目的可以分为两类&#xff0c;一类数据集适用于推进最新研究的数据&#xff0c;一类数据集是用于高校研究和教育科研使用的数据集。本次介绍三个数据集的使用和处理。 数据集下…

java读取movielens数据txt

各位好&#xff0c;我是菜鸟小明哥&#xff0c;movielens数据是常见的推荐方面的开源数据集&#xff0c;另一个推荐方面的数据集是新闻MIND&#xff0c;本文将从movielens再次出发&#xff0c;做基础的推荐方法&#xff0c;比如基本的基于标题的相似性&#xff0c;word2vector&…

推荐系统数据集之MovieLens

1.概述 MovieLens其实是一个推荐系统和虚拟社区网站&#xff0c;它由美国 Minnesota 大学计算机科学与工程学院的GroupLens项目组创办&#xff0c;是一个非商业性质的、以研究为目的的实验性站点。GroupLens研究组根据MovieLens网站提供的数据制作了MovieLens数据集合&#xff…

【工具】Movielens数据集详细介绍

MovieLens数据集 MovieLens数据集包含多个用户对多部电影的评级数据&#xff0c;也包括电影元数据信息和用户属性信息。 下载地址 http://files.grouplens.org/datasets/movielens/ 介绍 下面以ml-100k数据集为例进行介绍&#xff1a; 最主要用的是u.data(评分) | u.item…

movielens数据集简述

一、movielens数据集 ratings数据: 文件里面的内容包含了每一个用户对于每一部电影的评分。数据格式如下: userId, movieId, rating, timestamp userId: 每个用户的id movieId: 每部电影的id rating: 用户评分,是5星制,按半颗星的规模递增(0.5 stars - 5 stars) timestam…

Movielens数据集详细介绍

MovieLens数据集包含多个用户对多部电影的评级数据&#xff0c;也包括电影元数据信息和用户属性信息。下载地址为&#xff1a;http://files.grouplens.org/datasets/movielens/ 下面以ml-100k数据集为例进行介绍&#xff1a; 最主要用的是u.data(评分) | u.item(电影信息) …

movielens数据集介绍及使用python简单处理

0 前言 个性化推荐中&#xff0c;电影推荐研究时常使用movielens上的数据集。该网站的数据集主要分两部分&#xff0c; 一是用于推进最新研究进展的数据集。当前最新的是发布于2019年12月份的25M数据集。 二是用于高校、组织科研的数据集。该类数据集按其是否带有标签、时间…

mui用ajax上拉加载更多,mui上拉加载更多下拉刷新数据的封装过程

辗转用mui做了两个项目&#xff0c;空下来把mui上拉加载更多&#xff0c;下拉刷新数据做了一个简单的封装&#xff0c;希望可以帮助到需要的朋友 demo项目的结构 直接贴代码了 index.html mui上拉刷新下拉加载都这里了&#xff0c;两个方法搞定mui上拉刷新下拉加载demo--封装 l…

如何实现上拉加载和下拉刷新

下拉刷新和上拉加载这两种交互⽅式通常出现在移动端中 本质上等同于PC⽹⻚中的分⻚&#xff0c;只是交互形式不同 开源社区也有很多优秀的解决⽅案&#xff0c;如 iscroll 、 better-scroll 、 pulltorefresh.js 库等等 这些第三⽅库使⽤起来⾮常便捷 我们通过原⽣的⽅式实现…

ComposeUI——下拉刷新+上拉加载(一、简单封装)

前言&#xff1a;ComposeUI是将来开发的趋势&#xff0c;本人也在对它进行学习&#xff0c;会把踩过的坑一一记录下来&#xff0c;希望能对大家有帮助。话不多说&#xff0c;直接开干。 目录 下拉刷新 1、引入依赖库 2、使用方法 上拉加载 1、先看用法&#xff08;结合下…

Flutter 下拉刷新、上拉加载

Flutter 下拉刷新、上拉加载有很多第三方插件&#xff0c;本文使用插件为&#xff1a;pull_to_refresh 目前pull_to_refresh在pub.dev上的使用情况&#xff1a; 刷新header的类型: ClassicHeader const ClassicHeader({Key? key,RefreshStyle refreshStyle: RefreshStyle.…

BaseQuickAdapter上拉加载功能实现

最近使用BaseQuickAdapter进行RecyclerView 的Adapter的数据绑定显示。 实现上拉加载与下拉刷新功能&#xff0c;遇到如下问题&#xff1a; 1、首先是实现下拉刷新、下拉加载的监听&#xff08;xml布局就不贴出来了&#xff09;&#xff1a; 2、现在贴出来错误的处理方式&…

android 官方上拉,手把手教你实现RecyclerView的下拉刷新和上拉加载更多

纵观多数App&#xff0c;下拉刷新和上拉加载更多是很常见的功能&#xff0c;但是谷歌官方只有一个SwipeRefreshLayout用来下拉刷新&#xff0c;上拉加载更多还要自己做。 基于RecyclerView简单封装了这两个操作&#xff0c;下拉刷新支持LinearLayoutManager、GridLayoutManager…