基于Python和Spark的推荐系统实现

177 阅读21分钟

本项目是博主基于《Python+Spark2.0+Hadoop机器学习与大数据实战》这本书中的其中一节做的一个小项目

image.png 这本书内容还是不错的,适合学习机器学习的本科生,或者刚接触机器学习的研究生去阅读。
一些书本目录be like:
image.png
因为搭建环境方面实在是太麻烦了,要Linux+Hadoop+Python+Eclipse(很离谱,作者居然要我们在虚拟机里装linxu环境再装Eclipse然后在里面配Python解释器在里面写过时的Python2,所以整个代码写法都是Python2语法),于是博主把这方面省略,直接切入正题。


缺课程设计的同学有福辣,我正好是按照课程设计的标准去写滴,所以你看见了,而且你需要的话,可以直接拿来主义!!!

忘记说了,我是关于第12章去写滴嘿嘿嘿~~~
image.png
-----------------------------------------我是分割线--------------------------------------------  

项目背景

电影产业在数字化时代迅速发展,各种在线视频平台不断涌现。为了提高用户体验和增加 用户粘性,电影推荐引擎成为在线电影平台的重要功能。通过利用大数据和机器学习技术, 电影推荐引擎可以根据用户的兴趣、历史行为以及社交网络等信息,为用户提供个性化的电 影推荐,从而提高用户的观影体验和平台的用户留存率。本项目旨在基于 Python、Spark 和 Hadoop 技术,创建一个高效、可扩展的电影推荐引擎。具体项目目标包括,第一点:通过 利用 Hadoop 集群处理大规模的电影数据集,包括电影的基本信息、用户的观影行为、社交 网络等数据,构建一个完整的电影数据仓库,为后续的推荐算法提供数据支持。第二点:使 用 Spark 进行数据预处理和特征工程,包括对电影数据的清洗、特征提取和数据转换等,以 准备用于机器学习算法的数据集。第三点:基于用户的历史观影行为和兴趣偏好,使用协同 过滤、内容推荐等推荐算法,构建个性化推荐模型,为用户生成精准的电影推荐列表。第四 点:设计和实现多种推荐策略,如基于用户的协同过滤、基于物品的协同过滤、热门电影推 荐等,以提供不同维度和场景下的推荐结果。第五点:进行推荐结果的评估和优化,不断改 进推荐算法,提高推荐引擎的性能和效果。通过本项目的实施,预期可以达到以下成果:实 现一个基于 Python、Spark 和 Hadoop 的电影推荐引擎原型,能够从大规模的电影数据中提 取有价值的信息,并为用户生成个性化的电影推荐。改进和优化推荐算法,提高推荐引擎的 推荐准确性和用户满意度。实现多种推荐策略,丰富和提升推荐结果,提高用户对电影平台 的活跃度和留存率。包括基于用户的协同过滤、基于物品的协同过滤、热门电影推荐等,以 提供不同维度和场景下的推荐结果,满足不同用户的需求。





关键词:数字化时代 推荐引擎 机器学习 协同过滤算法

项目背景1

一、项目内容3

1. 数据收集与处理:3

2. 推荐算法的实现:3

2.1 基于用户的协同过滤算法:4

2.2 基于物品的协同过滤算法:4

3. ALS *算法的优点:4

4. ALS 算法的限制:5

5. 稀疏矩阵在推荐系统引擎中的处理方法:5

6. 如何准备数据:6

6.1 配置文件读取路径6

6.2 导入ml-100k 数据:6

6.3 查看u.data 第一次项数据7

6.4 Import Rating 模块*7

6.5 读取rawUserData 前三个字段7

7. 准备ALS 训练数据8

8. 查询ratingRDD 项数9

9 查看不重复用户数9

10. 查看不重复电影数9

二、如何训练模型10

1. ALS.train 介绍 ... 10

2. 如何使用模型进行推荐 ... 10

3. 显示推荐的电影名称 ... 11

三、代码实现: ... 11

四、参考文献 ... 16

项目内容

1. 数据收集与处理:

  • 爬取电影信息数据:通过网络爬虫技术,从电影信息网站(如IMDb、豆瓣电影等)获取电影的相关信息,如电影的标题、类型、演员、导演、评分等。可以使用Python中的爬虫库(如BeautifulSoup、Scrapy等)进行网页抓取和数据提取。

  • 收集用户的历史观影行为数据:通过用户登录或注册功能,收集用户的历史观影行为数据,包括用户对电影的评分、评论、收藏等信息。可以使用数据库(如MySQL等)进行数据存储和管理,以便后续的推荐算法使用。

  • 数据清洗与转换:对爬取和收集到的数据进行清洗和转换,包括去除重复数据、处理缺失值、格式化数据等操作,使得数据适合用于机器学习算法的输入。可以使用Spark的DataFrame和SQL操作进行数据清洗和转换,例如使用Spark的API进行数据过滤、去重、填充缺失值、数据类型转换等。

 

在此项目中我们使用数据集:ml-100k。ml-100k数据集包含了以下几个文件:

  • u.data:包含了用户对电影的评分信息,每一行记录了一个用户对一个电影的评分、评分时间等信息。

  • u.item:包含了电影的详细信息,包括电影ID、电影名称、发布日期、IMDb链接等。

  • u.user:包含了用户的详细信息,包括用户ID、年龄、性别、职业等。

  • u.genre:包含了电影的类型信息,每一行记录了一个电影的类型,包括Action、Comedy、Drama等。

 

这个数据集通常用于训练和评估推荐引擎算法,例如协同过滤算法(如ALS),基于内容的推荐算法,以及混合推荐算法等。通过分析用户对电影的评分和电影的属性信息,可以构建出一个推荐引擎,为用户提供个性化的电影推荐服务。

在实际应用中,可以从GroupLens Research实验室的网站或其他公开数据集库中获取ml-100k数据集,用于构建自己的推荐引擎项目。通过对ml-100k数据集进行数据预处理、特征工程和算法实现等步骤,可以构建出一个有效的电影推荐引擎,为用户提供个性化的电影推荐服务。

2. 推荐算法的实现:

协同过滤算法是一种常用于推荐引擎的推荐算法,其基本思想是通过分析用户之间或物品之间的相似性,来为用户推荐与他们兴趣相似的其他用户或物品。协同过滤算法主要包括基于用户的协同过滤和基于物品的协同过滤两种方式。

 

2.1基于用户的协同过滤算法:

基于用户的协同过滤算法通过分析用户之间的相似性,为用户推荐和他们兴趣相似的其他用户喜欢的物品。具体步骤如下:

  1. 计算用户之间的相似性:可以使用余弦相似度、皮尔逊相关系数等方法计算用户之间的相似性。例如,可以将用户的历史观影行为数据表示为用户-物品评分矩阵,然后通过计算用户之间的相似性来衡量其兴趣的相似度。

  2. 找到相似用户:根据用户之间的相似性,找到与目标用户兴趣相似的其他用户。

  3. 推荐物品:从相似用户的历史观影行为中挑选目标用户未观看过但相似用户喜欢的物品,作为推荐结果。

2.2 基于物品的协同过滤算法:
  1. 基于物品的协同过滤算法通过分析物品之间的相似性,为用户推荐和他们喜欢的物品相似的其他物品。具体步骤如下:

  2. 计算物品之间的相似性:可以使用余弦相似度、杰卡德相似度等方法计算物品之间的相似性。例如,可以将物品的特征向量表示为物品-特征矩阵,然后通过计算物品之间的相似性来衡量其内容的相似度。

  3. 找到相似物品:根据物品之间的相似性,找到与目标物品相似的其他物品。

  4. 推荐物品:从与目标物品相似的其他物品中挑选目标用户未观看过但相似物品作为推荐结果。

 

其中该项目使用的是ALS推荐算法,ALS(Alternating Minimum Quads)算法是一种协同过滤算法,用于推荐系统中的推荐任务。它通过迭代的方式,交替优化用户和物品的隐含特征向量,从而实现推荐。ALS算法的基本思想是将用户-物品之间的评分矩阵分解成两个低维的隐含特征向量矩阵:一个是用户的隐含特征向量矩阵,另一个是物品的隐含特征向量矩阵。这两个矩阵的乘积即为用户-物品之间的评分矩阵的近似值,通过将用户对未评分物品的预测评分与实际评分进行比较,不断优化这两个隐含特征向量矩阵,从而得到更准确的推荐结果。

3. ALS算法的优点:

  • 算法简单且易于实现,适用于大规模推荐系统。
  • 可以处理稀疏数据,适用于用户行为数据较为稀缺的场景。
  • 支持隐含特征的建模,能够捕捉用户和物品之间的非线性关系。

4. ALS算法的限制:

  • 需要大量的用户行为数据进行训练,对于冷启动问题和数据稀疏问题较为敏感。
  • 对于新用户或新物品,需要通过其他手段进行初始化,例如基于内容的推荐等。
  • 算法计算复杂度较高,需要进行多次迭代优化,对计算资源要求较高。

 

以下是关于ALS算法的进一步说明:

在ALS算法中,主要有两种优化策略:

  1. 交替最小二乘:交替最小二乘法,通过固定一个隐含特征向量矩阵,优化另一个隐含特征向量矩阵。这样交替地更新两个矩阵,直至收敛。
  2. 随机梯度下降:随机梯度下降法,通过随机选取一小部分样本进行梯度下降优化。
  3. 在实际项目中,可以根据数据的规模和计算资源的情况选择不同的优化策略。一般而言,ALS算法在处理大规模数据时效果较好,并且可以通过并行计算加速训练过程。

 

ALS算法的输入主要包括用户-物品的评分数据,可以是用户对物品的评分、点击、购买等行为数据。在实际项目中,需要对数据进行预处理,例如数据清洗、去除噪声、处理缺失值等,以确保模型训练的准确性。

在训练完ALS模型后,可以使用该模型生成推荐结果。例如,可以通过计算用户的隐含特征向量和物品的隐含特征向量之间的相似度,选取相似度较高的物品作为推荐结果。也可以使用模型预测用户对未评分物品的评分,将评分较高的物品作为推荐结果。需要注意的是,ALS算法是一种基于协同过滤的推荐算法,依赖于用户行为数据,因此在应用ALS算法时需要充分考虑数据的质量、量级和稀疏性等因素。同时,ALS算法也需要经过合理的参数调优和模型评估,以确保推荐结果的准确性和实用性。

5. 稀疏矩阵在推荐系统引擎中的处理方法:

矩阵分解(Matrix Factorization)是一种将一个大矩阵分解成多个小矩阵的技术,常用于协同过滤算法中的推荐引擎。在ALS算法中,矩阵分解用于将评分矩阵分解成用户和物品的隐式特征向量,从而进行推荐。

ALS算法采用交替最小二乘法的思想,通过迭代优化用户和物品的隐式特征向量。具体而言,ALS算法可以分为两个步骤,交替进行:

优化用户特征向量:固定物品特征向量,通过最小化用户对物品评分和用户特征向量的平方误差之和来更新用户特征向量。这个步骤涉及到一个线性回归问题,可以通过求解正规方程或梯度下降等方法来更新用户特征向量。优化物品特征向量:固定用户特征向量,通过最小化用户对物品评分和物品特征向量的平方误差之和来更新物品特征向量。这个步骤同样涉及到一个线性回归问题,可以通过求解正规方程或梯度下降等方法来更新物品特征向量。

在矩阵分解的过程中,用户和物品的特征向量通常初始化为随机值,并通过迭代的方式逐渐优化得到合适的特征向量。特征向量通常具有较低的维度,从而可以减少存储和计算的复杂性。在实际实现中,可以使用Python编程语言和Spark分布式计算框架来实现ALS算法的矩阵分解步骤。

总而言之,矩阵分解是ALS算法中的关键步骤,用于将评分矩阵分解成用户和物品的隐式特征向量,从而实现推荐。在实际实现中,可以使用Spark等分布式计算框架来高效地处理大规模数据和高维度特征向量的计算。

6. 如何准备数据:

在使用Spark的ALS(交替最小二乘)算法进行推荐引擎的训练时,需要将训练数据转换为特定的格式,通常称为“rating RDD”。这个格式要求训练数据以RDD(弹性分布式数据集)的形式存在,并且包含用户ID、物品ID和评分三个字段。

 

下面是本项目使用的一种sc.textFile方法,将训练数据转换为rating RDD的步骤,如图(1)所示:

image.png 图(1)

6.1配置文件读取路径
global Path

if sc.master[0:5]=="local" :

Path="file:/home/data/"

else:

Path="hdfs://bigdata01:9000/num/data/"

#如果要在cluster模式运行(hadoop yarn 或Spark Stand alone),请按照书上的说明,先把文件上传到HDFS目录
6.2导入ml-100k数据:
rawUserData = sc.textFile("hdfs://bigdata01:9000/nmu/data/data/u.data")

rawUserData.count()

 

100000

从以上运行结果看共有100000项评分数据。

6.3 查看u.data第一次项数据
rawUserData.first() 

‘196\t242\t3\t881250949’

以上4个字段分别是:用户id、项目id、评价、日期时间。

6.4 Import Rating模块
导入Rating模块

from pyspark.mllib.recommendation import Rating
6.5 读取rawUserData前三个字段

按照用户、产品、用户对此产品的评价来编写rawRatings

rawRatings = rawUserData.map(lambda line: line.split(“\t”)\[:3] )

rawRatings.take(5)
[[‘196’, ‘242’, ‘3’],
 [‘186’, ‘302’, ‘3’],
 [‘22’, ‘377’, ‘1’],
 [‘244’, ‘51’, ‘2’],
 [‘166’, ‘346’, ‘1’]]
以上运行结果显示了前5项rawRatings数据。上列命令的详细说明如图(2)所示。

image.png 图(2)

7. 准备ALS训练数据

ALS训练数据格式是Rating RDD数据格式,Rating定义如下。

Rating(user,product,rating)

各字段说明如图(3)所示。

image.png

图(3)

可以使用下列指令编写ratingRDD(见图(4))

image.png

图(4)

从以上运行结果可以看到前5项数据,并且每一项含有用户、产品、用户对此产品评价。上列命令的详细说明如图(5)所示。

image.png 图(5)

8. 查询ratingRDD项数

可以使用.count()查询数据项数

numRatings = ratingsRDD.count()

numRatings

100000

以上运行结果显示共有100000项评价。

9. 查看不重复用户数

x[0]是用户字段,可以先使用.map(lambda x: x[O])转换为用户数据,再使用.distinct()筛选出不重复的数据,最后显示numUsers。

numUsers = ratingsRDD.map(lambda x: x\[0] ).distinct().count()

numUsers


943

以上运行结果显示共有943个不重复用户。

 

10. 查看不重复电影数

x[0]是电影字段,可以先使用.map(lambda x: x[1])转换为电影数据,再使用.distinct()筛选出不重复的数据。

numMovies = ratingsRDD.map(lambda x: x\[1]).distinct().count()

numMovies

 

1682

 

二、如何训练模型

如图(9)所示,我们将使用rawUserData数据以map转换为rawRatings,再改用map转换为ALS训练数据格式RDD[Rating]。然后使用ALS.train进行训练,训练完成后就会创建推荐引擎模型MatrixFactorizationModel.

image.png

图(9)

 

 

1. ALS.train介绍

  • 显式评分( Explicit Rating)训练

ALS.train(ratings, rank, iterations=5, lambda_=0.01):返回MatrixFactorizationModel

 

  • 隐式评分( lmplicit Rating )训练

ALS.trainImplicit (ratings, rank, iterations=5, lambda_=0.01):返回MatrixFactorizationModel

两种评分训练的作用都是训练数据并返回模型。当训练完成后会产生 MatrixFactorizationModel模型,训练时会执行矩阵分解(Matrix Factorization),完成后会将原本矩阵 A(m×n)分解成X(m×rank)矩阵与Y(rank×n)矩阵。

2. 如何使用模型进行推荐

我们可以针对每一个会员定期发送短信或E-mail,或在会员登录时向会员推荐可能会感兴趣的电影。
针对用户推荐电影,我们可以使用model.recommendProducts方法来推荐。
MatrixFactorizationModel.recommendProducts(user: Int,num: Int):输入参数user,针对此user推荐给他/她有可能感兴趣的产品。
例如:针对用户推荐电影

model.recommendProducts(196,5)

[Rating(user=196, product=695, rating=9.528713639450343),
 Rating(user=196, product=1160, rating=8.659064132610329),
 Rating(user=196, product=464, rating=7.854156158658984),
 Rating(user=196, product=320, rating=7.507801304084671),
 Rating(user=196, product=1434, rating=7.198418340100243)]

 

以上执行结果显示,第1项数据是系统针对此用户首先推荐的产品。其意义是推荐给用广ID 196,产品 ID 695,推荐评分大约为6.38。推荐评分越高,代表系统越优先推荐此产品。

3. 显示推荐的电影名称

使用MovieTitle字典,可以显示推荐的电影名称,但必须创建“电影ID与名称”的字典。

\#coding=utf-8

recommendP= model.recommendProducts(100,5)

for p in recommendP:

    print  ("对用户"+ str(p\[0]) +                    \\

               "推荐电影"+ str(movieTitle\[p\[1]]) + \\

               "推荐评分"+ str(p\[2]) )   

    

对用户100推荐电影Whole Wide World, The (1996)推荐评分6.598041662844283
对用户100推荐电影Grass Harp, The (1995)推荐评分6.493324411776026
对用户100推荐电影Grace of My Heart (1996)推荐评分6.471134682267468
对用户100推荐电影Love & Human Remains (1993)推荐评分6.197871669535257
对用户100推荐电影Contempt (M�pris, Le) (1963)推荐评分6.10865688095153

 

三、代码实现:

环境: jupyter notebook Python2.7

sc.master
'local[*]'

global Path    
if sc.master[0:5]=="local" :
   Path="file:/home/data/"
else:   
   Path="hdfs://bigdata01:9000/num/data/"
#如果要在cluster模式运行(hadoop yarn 或Spark Stand alone),请按照书上的说明,先把文件上传到HDFS目录
rawUserData = sc.textFile("hdfs://bigdata01:9000/nmu/data/data/u.data")

rawUserData.count()
100000

rawUserData.first()
'196\t242\t3\t881250949'

print (rawUserData.first())
196	242	3	881250949

rawUserData.take(5)
['196\t242\t3\t881250949',
 '186\t302\t3\t891717742',
 '22\t377\t1\t878887116',
 '244\t51\t2\t880606923',
 '166\t346\t1\t886397596']

for x in rawUserData.take(5): 
print (x)
196	242	3	881250949
186	302	3	891717742
22	377	1	878887116
244	51	2	880606923
166	346	1	886397596

from pyspark.mllib.recommendation import Rating
rawRatings = rawUserData.map(lambda line: line.split("\t")[:3] )
rawRatings.take(5)
[['196', '242', '3'],
 ['186', '302', '3'],
 ['22', '377', '1'],
 ['244', '51', '2'],
 ['166', '346', '1']]

ratingsRDD = rawRatings.map(lambda x: (x[0],x[1],x[2]))
ratingsRDD .take(5)
[('196', '242', '3'),
 ('186', '302', '3'),
 ('22', '377', '1'),
 ('244', '51', '2'),
 ('166', '346', '1')]

numRatings = ratingsRDD.count()
numRatings
100000

numUsers = ratingsRDD.map(lambda x: x[0] ).distinct().count()
numUsers
943

numMovies = ratingsRDD.map(lambda x: x[1]).distinct().count() 
numMovies
1682

numItems = ratingsRDD.map(lambda x: x[1]).distinct().count() 
numItems
1682

ratingsRDD.persist()
PythonRDD[49] at RDD at PythonRDD.scala:52

#如何训练模型
from pyspark.mllib.recommendation import ALS
model = ALS.train(ratingsRDD, 10, 10, 0.01)
print (model)
<pyspark.mllib.recommendation.MatrixFactorizationModel object at 0x7ff1aba36f60>

print (model)
<pyspark.mllib.recommendation.MatrixFactorizationModel object at 0x7ff1aba36f60>

#如何使用模型进行推荐?
model.recommendProducts(196,5)
[Rating(user=196, product=695, rating=9.528713639450343),
 Rating(user=196, product=1160, rating=8.659064132610329),
 Rating(user=196, product=464, rating=7.854156158658984),
 Rating(user=196, product=320, rating=7.507801304084671),
 Rating(user=196, product=1434, rating=7.198418340100243)]

model.predict(196, 1172)
3.9810748936947333

model.recommendUsers(product=200,num=5)
[Rating(user=695, product=200, rating=6.582379409965513),
 Rating(user=157, product=200, rating=6.337704591073909),
 Rating(user=914, product=200, rating=6.318174712301225),
 Rating(user=310, product=200, rating=6.133916101699455),
 Rating(user=143, product=200, rating=6.123870040831552)]

#显示推荐的电影的名称
itemRDD = sc.textFile("hdfs://bigdata01:9000/nmu/data/data/u.item")
itemRDD.count()
1682

movieTitle= itemRDD.map( lambda line : line.split("|"))     \
                                   .map(lambda a: (float(a[0]),a[1]))       \
                                   .collectAsMap()
len(movieTitle)
1682

list(movieTitle.items())[:5]
[(1.0, 'Toy Story (1995)'),
 (2.0, 'GoldenEye (1995)'),
 (3.0, 'Four Rooms (1995)'),
 (4.0, 'Get Shorty (1995)'),
 (5.0, 'Copycat (1995)')]

for i in range(1,6): 
print (str(i)+":"+movieTitle[i])

1:Toy Story (1995)
2:GoldenEye (1995)
3:Four Rooms (1995)
4:Get Shorty (1995)
5:Copycat (1995)

movieTitle[5]
'Copycat (1995)'

#coding=utf-8
recommendP= model.recommendProducts(100,5) 
for p in recommendP:
    print  ("对用户"+ str(p[0]) +                    \
               "推荐电影"+ str(movieTitle[p[1]]) + \
               "推荐评分"+ str(p[2]) )  
               
 结论:              
对用户100推荐电影Whole Wide World, The (1996)推荐评分6.598041662844283
对用户100推荐电影Grass Harp, The (1995)推荐评分6.493324411776026
对用户100推荐电影Grace of My Heart (1996)推荐评分6.471134682267468
对用户100推荐电影Love & Human Remains (1993)推荐评分6.197871669535257
对用户100推荐电影Contempt (M�pris, Le) (1963)推荐评分6.10865688095153
结论:
对于用户100,推荐的电影包括《Whole Wide World, The (1996)》、《Grass Harp, The (1995)》、《Grace of My Heart (1996)》、《Love & Human Remains (1993)》和《Contempt (M�pris, Le) (1963)》。
这些电影的推荐评分较高,分别为6.5980416628442836.4933244117760266.4711346822674686.1978716695352576.10865688095153。推荐的电影可能与用户100的兴趣爱好或者历史观看记录相关,可以作为给用户100进行电影推荐的参考。

 

 

四、参考文献

[1] 林大贵.(2018). Python+Spark2.0+Hadoop 机器学习与大数据实战[J].

[2] 曹希, 张艳梅, 黄志新. (2020). 基于协同过滤算法的电影推荐系统研究[J]. 数据分析与知识发现, 4(12), 1-7.

[3] 吴建鑫, 王婷, 刘震. (2021). 基于Spark和Hadoop的电影推荐引擎设计与实现[J]. 计算机科学, 48(2), 231-236.

[4] 李斌, 张晓东, 李天一. (2016). 基于协同过滤算法的电影推荐系统设计与实现[J]. 现代计算机, (11), 110-112.

[5] 朱丹丹, 李梦颖. (2020). 基于Spark和Python的电影推荐引擎设计与实现[J]. 计算机应用研究, 37(8), 2421-2425.