Spark(四):批量推荐用户的实现(串行化,并行化实现)

到目前为止,前面三篇文章我们已经讲过了基于物品协同过滤的原理,算法在Spark平台上的并行化实现,算法的持久化实现。前面得到的推荐结果只是根据特定的一个用户推荐相应物品,本篇要讲的是在Spark平台上实现批量推荐用户,包括串行化与并行化的实现。

本篇内容:

1.批量推荐串行化实现(略讲)

2.批量推荐并行化实现(详)

3.实现代码

4.两种方式结果对比

1.串行化实现

批量推荐,就是给一批用户,根据计算得到的相似度矩阵或模型进行批量推荐,最简单的实现方式就是串行化实现,即用一个for循环每次对一个用户推荐。

这种方式的优点是实现简单,逻辑清晰有条理;缺点是所需时间长,不能充分利用现有资源,造成资源浪费。

具体如何实现不再细讲,后面会有参考代码。

2.并行化实现

并行化实现是本文讲解的重点,通过并行化的批量推荐,可以充分利用现有资源,发挥Spark平台的优势,减少系统响应时间,更适合于实际使用。缺点是逻辑复杂,难以理解,难以实现。

接下来我会一步一步的讲解如何在Spark上实现并行化批量推荐。

1.针对一批用户,对这批用户建立用户RDD,此处我以四个用户为例:

user_id=sc.parallelize(['100','200','260','300'])

2.得到用户列表RDD后,我们要考虑的问题是如何对这批用户实现并行化推荐,而并行化的关键问题便是实现数据独立。此处我用了两次笛卡尔积以实现并行化。对于批量推荐,我们要做的主要分为几步,首先根据用户列表找到每个用户观看过的电影,然后根据每个用户观看过的电影在相似度矩阵中计算,得到相似的电影,最后根据相似度进排序进行推荐。

3.首先对用户列表RDD与用户观看过的电影user_item做笛卡尔积,然后通过filter函数进行过滤操作,过滤掉用户列表user_id与user_item中的user_id不同的项,取出相同的项,取出的RDD即为用户列表对应的用户看过的电影。取出的数据结构为:[(user_id,[item_id,item_id,.....]),(user_id,[....]),.....],此处我们已经得到每个用户观看的电影RDD。

see_list=user_id.cartesian(user_item)
print(see_list.first())
see_list=see_list.filter(lambda x:(x[0]==x[1][0])).map(lambda x:(x[1][0],x[1][1]))

4.然后用得出的用户看过的列表see_list与相似度矩阵W做笛卡尔积,得到的RDD结构为

((user_id,[item_id,item_id,......]),(item_id,[(item_id,rating),(item_id,rating),.....]))

然后通过filter函数过滤,用自己编写的find函数找到电影在用户观看列表中的电影,取出这些项。然后根据与他们相似的物品的评分高低取出N个相似的物品。即每个用户的每部看过的电影的相似的N个物品。得到RDD的数据结构为:

(user_id,[(item_id,rating),(item_id,rating),......])

def find(x,list_item):
    for i in list_item:
        if i==x:
            return True
    return False
similar=see_list.cartesian(W).filter(lambda x:find(x[1][0],x[0][1])).map(lambda x:(x[0][0],get(x[1][1],5)))

5.之后用一个groupbykey函数将相同用户id的推荐电影整合到一个RDD元素中,然后对所有相似的电影,相同的电影id相似度评分求和,排序得到前N个相似度评分最高的电影,给相应的user_id推荐这些电影。

def getall(L):
    List=[ ]
    for l in L:
        List=judge(l,List)
    List.sort(key=lambda x:x[1],reverse=True)
    return List
def judge(l,List):
    for s in List:
        if s[0]==l[0]:
            k=s[1]+l[1]
            m=s[0]
            List.remove(s)
            s=(m,k)
            List.append(s)
            return List
    List.append(l)
    return List
 sim=similar.reduceByKey(lambda x,y:x+y)
 recommend=sim.map(lambda x:(x[0],getall(x[1]))).collect()
 print("Result:")
 for r in recommend:
    print(r)

 

3.实现代码

串行化代码:

from pyspark import SparkContext
from pyspark import SparkConf
from RecommendClass import itemCF
import time
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 choose(x):
    if x in ['0','1','2','3','4','5','6','7','8','9','.']:
        return True
    return False
def prepardata(sc):
    rawUserData=sc.textFile(Path+"module.txt")
    print(rawUserData.first())
    print(rawUserData.first()[0])
    rawRatings=rawUserData.map(lambda line:line.split(","))
    print(rawRatings.first())
    ratingsRDD=rawRatings.map(lambda x:(filter(str.isdigit,x[0].encode('gbk')),filter(str.isdigit,x[1].encode('gbk')),float(filter(choose,x[2].encode('gbk')))))
    print(ratingsRDD.first())
    return(ratingsRDD)
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)
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]
if __name__=="__main__":
    start=time.time()
    sc=CreatSparkContext()
    print("prepare data")
    w=prepardata(sc)
    W=w.map(lambda x:(x[0],(x[1],x[2])))
    W=W.groupByKey().map(lambda x:(x[0],list(x[1])))
    rawUserData=sc.textFile("file:/home/hduser/pythonwork/PythonProject/data/u.data")
    rawRatings=rawUserData.map(lambda line:line.split("\t")[:2])
    ratingsRDD=rawRatings.map(lambda x:(filter(str.isdigit,x[0].encode('gbk')),filter(str.isdigit,x[1].encode('gbk'))))
    user_item=ratingsRDD.groupByKey().map(lambda x:(x[0],list(x[1])))
    user_id=sc.parallelize(['200','100','260','300'])
    see_list=user_id.cartesian(user_item)
    print(see_list.first())
    see_list=see_list.filter(lambda x:(x[0]==x[1][0])).map(lambda x:(x[1][0],x[1][1])).collect()
    for L in see_list:
        sim_item=W.filter(lambda x:find(x[0],L[1])).map(lambda x:x[1])
        recommend_item=sim_item.flatMap(lambda x:get(x,5)).reduceByKey(lambda x,y:x+y)
        recommend_item=recommend_item.sortBy(lambda x:x[1],False).take(5)
        print(L[0])
        for r in recommend_item:
            print(r)
    print("Result:")
    end=time.time()
    T=end-start
    print(T)

并行化代码:

from pyspark import SparkContext
from pyspark import SparkConf
from RecommendClass import itemCF
import math
import sys
import time
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 choose(x):
    if x in ['0','1','2','3','4','5','6','7','8','9','.']:
        return True
    return False
def prepardata(sc):
    rawUserData=sc.textFile(Path+"module.txt")
    print(rawUserData.first())
    print(rawUserData.first()[0])
    rawRatings=rawUserData.map(lambda line:line.split(","))
    print(rawRatings.first())
    ratingsRDD=rawRatings.map(lambda x:(filter(str.isdigit,x[0].encode('gbk')),filter(str.isdigit,x[1].encode('gbk')),float(filter(choose,x[2].encode('gbk')))))
    print(ratingsRDD.first())
    return(ratingsRDD)
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)
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 getall(L):
    List=[ ]
    for l in L:
        List=judge(l,List)
    List.sort(key=lambda x:x[1],reverse=True)
    return List
def judge(l,List):
    for s in List:
        if s[0]==l[0]:
            k=s[1]+l[1]
            m=s[0]
            List.remove(s)
            s=(m,k)
            List.append(s)
            return List
    List.append(l)
    return List
if __name__=="__main__":
    start=time.time()
    sc=CreatSparkContext()
    print("prepare data")
    w=prepardata(sc)
    W=w.map(lambda x:(x[0],(x[1],x[2])))
    W=W.groupByKey().map(lambda x:(x[0],list(x[1])))
    rawUserData=sc.textFile("file:/home/hduser/pythonwork/PythonProject/data/u.data")
    rawRatings=rawUserData.map(lambda line:line.split("\t")[:2])
    ratingsRDD=rawRatings.map(lambda x:(filter(str.isdigit,x[0].encode('gbk')),filter(str.isdigit,x[1].encode('gbk'))))
    user_item=ratingsRDD.groupByKey().map(lambda x:(x[0],list(x[1])))
    user_id=sc.parallelize(['100','200','260','300'])
    see_list=user_id.cartesian(user_item)
    print(see_list.first())
    see_list=see_list.filter(lambda x:(x[0]==x[1][0])).map(lambda x:(x[1][0],x[1][1]))
    similar=see_list.cartesian(W).filter(lambda x:find(x[1][0],x[0][1])).map(lambda x:(x[0][0],get(x[1][1],5)))
    #sim=similar.groupByKey().map(lambda x:(x[0],list([x[1]])))
    sim=similar.reduceByKey(lambda x,y:x+y)
    print(sim.first())
    #recommend=sim.reduceByKey(lambda x,y:x+y).sortBy(lambda x:x[1],False).take(5)
    recommend=sim.map(lambda x:(x[0],getall(x[1]))).collect()
    print("Result:")
    for r in recommend:
        print(r)
    end=time.time()
    print("time:")
    print(end-start)

4.结果对比

对于两种方式实现的批量用户推荐,我在我配置的Spark平台上分别跑了一遍,在代码中引入时间函数以计时,得到的结果如下:

并行化结果:

串行化结果:

通过结果对照我们发现,并行化得到结果所需时间远大于串行化得到结果所花时间。通过分析我们确定了以下几种原因:

1.并行化方式并未实现并行化,反而加重了计算难度与复杂性,导致时间成本大大增加。

2.并行化做两次笛卡尔积,得到的数据量呈指数级上升,相比串行的循环处理的时间增加。

3.我们只在我们配置的本地服务器上运行,只有一台服务器运算,并没有用到Spark分布式服务器运算的特点,猜想在分布式多服务器Spark平台上跑的时候会大大降低响应时间。

我们将在后续验证我们的猜想以及解决该问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值