在前面的文章中,我已经大致讲解了基于物品的协同过滤算法(itemCF)的原理以及在Python上的实现,实现的机制主要使用串行化,耗时长,响应慢,难以适应现实大数据的需求。本文主要讲解itemCF算法在Spark上的并行化实现以便更适合用途以及提高性能。
本文主要内容:
1.Spark介绍。
2.Spark环境配置。
3.算法实现的基本流程(附流程图)
4.代码分步详解。
5.完整代码。
6.运行结果。
7.参考文献。
Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。Spark是UC Berkeley AMP lab (加州大学伯克利分校的AMP实验室)所开源的类Hadoop MapReduce的通用并行框架,Spark,拥有Hadoop MapReduce所具有的优点;但不同于MapReduce的是——Job中间输出结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的MapReduce的算法(百度百科)。Spark提供的高级API,使开发者可以独立于计算集群,更好的关注计算本身。Spark 是一个通用引擎,可用它来完成各种各样的运算,包括 SQL 查询、文本处理、机器学习等。计算速度快,低延迟都是Spark的特点。总结来说,使用Spark平台,我们只需要把我们要计算的数据放进去,提供相应的算法,Spark会自动的帮我们实现负载分配,并汇总结果。
实现的基本原理:
Spark Streaming:构建在Spark上处理Stream数据的框架,基本的原理是将Stream数据分成小的时间片断(几秒),以类似batch批量处理的方式来处理这小部分数据。Spark Streaming构建在Spark上,一方面是因为Spark的低延迟执行引擎(100ms+),虽然比不上专门的流式数据处理软件,也可以用于实时计算,另一方面相比基于Record的其它处理框架(如Storm),一部分窄依赖的RDD数据集可以从源数据重新计算达到容错处理目的。此外小批量处理的方式使得它可以同时兼容批量和实时数据处理的逻辑和算法。方便了一些需要历史数据和实时数据联合分析的特定应用场合。
关于Spark的详细介绍:
https://baike.baidu.com/item/SPARK/2229312?fr=aladdin
网上有很多配置Spark的教程,大家可以自行百度。此处提供师兄李赵宁写的Hadoop&Spark配置教程,大家可以参考。
链接:https://pan.baidu.com/s/1TXptFWOD9LmspaY_stohMw 密码:sttj
输入:user_id,item_id,rating
输出:前N个最感兴趣的物品(此处我们以电影推荐系统为例)
(1)计算用户对物品的喜好:对每一个用户来说,评分的基准不一样,而对物品来说,我们算法实现的是基于物品的协同过滤,而每个用户对每个物品的喜好程度不一样,因此我们需要对每个用户给物品的评分做预处理工作。我们代码实现的是一个电影网站的推荐系统,只考虑用户看过与没看过两种情况,属于隐反馈数据处理原则,所以对用户看过的每一部电影我们计分1,没看过的记为0.
(2)数据处理:因为我们要进行相似度矩阵计算(原理查看上一篇),所以我们要处理数据,得到两个矩阵,用户观看的电影矩阵(user_item)以及一部电影被哪些用户观看(item_user)。其中item_user用于计算物品相似度,user_item用与最后的推荐部分,根据user_item得到指定的用户看过哪些电影。
(3)物品好评数统计:统计每部电影喜欢的人数即被看过的次数。
(4)物品好评键值对的统计:对于两部电影,我们统计它们同时被哪些用户观看过。
(5)物品相似度计算:根据公式以及(3)(4)步的结果计算两部电影的相似性。
(6)推荐感兴趣的物品:根据相似度的计算结果向指定用户推荐前N部最感兴趣的电影。
1.设置文件路径,包括本地路径和hdfs路径
2.读取文件并初步处理,获得(user_id,item_id)键值对数据。
3.数据处理,得到用户观看电影矩阵以及电影被观看矩阵RDD,数据格式为(user_id,List(item_id1,itenm_id2,......))和(item_id,List(user_id1,user_id2,......))
4.数据并行化(重点)。要实现在Spark上并行化运算,必须实现数据并行化,最基本的要求是数据独立,我们使用的方式是笛卡尔积,使两个物品相互对应,并且独立于其他数据。
5.相似度计算。计算得出一个相似度矩阵。数据结构为(item_id_i,item_id_j,rating)
6.相似度矩阵归一化。计算得出的数据格式为(item_id_i,[(item_id_j,rating),(item_id_k,rating),.......])
即一个数据行对应一个物品以及和他相似的所有物品,以及对相似度评分。
7.物品推荐。对于指定用户u'300',通过user_item矩阵找到他看过的所有电影,得到一个看过的列表。
8.根据看过的列表在相似度矩阵中查找相似的物品。数据结构为[(item_id,rating),(item_id,rating)。
9.对于每一个看过的电影,提取出前K个与它相类似的电影,对所有提取出的数据进行扁平化处理,最后将提取出的数据中的item_id相同的数据项合并,相似度评分相加。然后输出打印前N个最相似的物品,得到结果,算法结束。
from pyspark import SparkContext
from pyspark import SparkConf
import math
import sys
def CreatSparkContext():
sparkConf=SparkConf() .setAppName("WordCounts").set("spark.ui.showConsoleProgress","false")
sc=SparkContext(conf=sparkConf)
print("master="+sc.master)
SetLogger(sc)
SetPath(sc)
return(sc)
def SetLogger(sc):
logger=sc._jvm.org.apache.log4j
logger.LogManager.getLogger("org").setLevel(logger.Level.ERROR)
logger.LogManager.getLogger("akka").setLevel(logger.Level.ERROR)
logger.LogManager.getRootLogger().setLevel(logger.Level.ERROR)
def SetPath(sc):
global Path
if sc.master[0:5]=="local":
Path="file:/home/hduser/pythonwork/PythonProject/"
else:
Path="hdfs://localhost:9000/user/hduser/"
def prepardata(sc):
rawUserData=sc.textFile(Path+"data/u.data")
rawRatings=rawUserData.map(lambda line:line.split("\t")[:2])
ratingsRDD=rawRatings.map(lambda x:(x[0],x[1]))
return(ratingsRDD)
def calculate(x1,x2):
count1=[ ]
count2=[ ]
for x in x1:
count1.append(x)
for x in x2:
count2.append(x)
count=len(count1)*len(count2)*1.0
a=set(count1)
b=set(count2)
c=a&b
bothcount=len(c)
w=bothcount/math.sqrt(count)
return w
def find(x,list_item):
for i in list_item:
if i==x:
return True
return False
def get(x,k):
x.sort(key=lambda x:x[1],reverse=True)
return x[:k]
def recommend(W,user_item,user_id,k):
user_see_item=user_item.filter(lambda x:x[0]==user_id).map(lambda x:x[1]).collect()[0]
sim_item=W.filter(lambda x:find(x[0],user_see_item)).map(lambda x:x[1])
recommend_item=sim_item.flatMap(lambda x:get(x,k)).reduceByKey(lambda x,y:x+y)
recommend_item=recommend_item.sortBy(lambda x:x[1],False)
return recommend_item.take(k)
if __name__=="__main__":
if len(sys.argv)!=3:
print("wrong input")
exit(-1)
sc=CreatSparkContext()
print("prepare data")
ratingsRDD=prepardata(sc)
user_item=ratingsRDD.groupByKey().map(lambda x:(x[0],list(x[1])))
print("got user_item")
RDD=ratingsRDD.map(lambda x:(x[1],x[0]))
item_user=RDD.groupByKey().map(lambda x:(x[0],list(x[1])))
print("got item_user")
print("item_user---item_user")
item_user=item_user.cartesian(item_user).filter(lambda x:x[0][0]!=x[1][0])
print("calculate")
w=item_user.map(lambda x:(x[0][0],x[1][0],calculate(x[0][1],x[1][1])))
W=w.map(lambda x:(x[0],(x[1],x[2])))
W=W.groupByKey().map(lambda x:(x[0],list(x[1])))
print("got similiar table")
print("recommending.......")
user_id=u'200'
recommend=recommend(W,user_item,user_id,5)
print("Result:")
for r in recommend:
print(r)
基于Spark平台推荐系统研究——杨志伟
基于物品的协同过滤推荐算法原理介绍及Python代码解读