最近才开始接触spark的相关内容,根据老师的需要做一个关于spark streaming的协同过滤推荐。中间也遇到很多问题,也很困扰,希望有大牛能给予指导~
一.Userbased Recommdation--spark streaming
基于用户的协同过滤推荐
本次试验是使用spark streaming操作基于用户信息的去做的推荐。
试验数据1.training.txt 110M左右,包含所有用户的信息,数据内容:
Userid,itemid,ratings
(为了方面处理可以将每个相同的用户id合并,成userid@itemid:rating,itemid:rating。。当然这是另一种处理方式,可以节约内存的开销)
2.test.txt测试集包含了要处理的所有用户,数据格式:
Userid,itemid,rating
。。
这里主要使用的主要是userid,itemid。Rating的使用是在后面计算RMSE标准误差,RMSE越小则表明预测的结果越精准,本次试验的RMSE在0.6~0.7之间。。。具体我忘了。
3.TrainingTest.txt根据测试集的用户id从训练集中提取出来之后的数据,可以方便我们很快的获取到要计算的用户的评价信息。
步骤:
1.将test.txt文件作为streaming不断读取的流。(假设这个文件在不断的更新神马的。。)在调用spark给的读取本地文件流的时候老是读取为空,所以使用了一个信息的读取方式:
使用的类需要去继承:
org.apache.spark.streaming.receiver.Receiver<String>这个类
这个图怎么贴不上去。。
private void receive(BufferedReader reader) throws IOException {
String userInput="";
try {
for(int i=0;i<sendquantity;i++){
String str =reader.readLine();
// while(str== null)
// {
// System.err.println("over the test");
// System.exit(1);
// }
userInput = str;
store(userInput);}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
代码大致是这样,在git上好像有个类似的项目。
将读取的每行存储到store中。
2.然后打开receive.onstart(),利用JavaReceiverInputDStream<String> lines = jssc.receiverStream(r);去接受这个存储在store中的信息。本次试验用的是java写的,所以调用的是java 的api当然scala也是相似的。
3.逻辑阶段map:在每个子节点读取training集合,在map阶段读取。。首先我们去找这个在training中这个用户是否对指定的itemid进行了评价?如果评价了就找这个用户和测试用户评价的交集,如果有,那么计算出两个用户的相似度,这里使用Pearson相似度来算。将相似度和给Item评分与当前用户的平均分的差存到一个新的list中。
4.处理list,在处理的过程中对pearson和diffave进行处理。得出最后的得分。
注意这里不使用reduce了,可以省去shuffle的时间,不过不知道这个想法对不对。
他妹的,我用saveastextfile这个借口好像也不行,估计是脸丑。。所以用Java api进行处理存储文件
最后 jssc.start(); jssc.awaitTermination()一个是打开streaming进行处理,一个是等待处理结果结束。
大致就是这个思维。
二.ItemBasedRecommdation
基于物品的协同过滤推荐---spark streaming
其大致都和userbased的相似,所以就主要写关系算法的内部实现过程:
1.找到要评分的用户看他评价了哪些商品,将商品记录下来,然后在遍历training的过程中找到一一对应的Itemid,记录打分,
2.算两个item的相似度,就是要评价的item和这个用户打分的item的相似度。这个相似度作为一个权值,去乘以已知的打分。最终加权平均求得最后的打分。
在这我也没有使用reducebykey的方法,省去shuffle不知道能不能节约时间
那么问题来了。。。:
问题1
我存储training集合的数据结构是
Hashmap<itemid,String>
String是代表useid:rating,userid:rating。。。。形成一个String
我试过使用hashmap<itemid,hashmap<userid,rating>>这样的存储结构,但是哈希表的数据结构开辟的过大,开始使用512m的内存一直出现GC LIMIT, JAVA heap什么错误。不过这样的数据结构能节约很大一部分时间。
还有一个问题一直困扰着我,就是在spark streaming中不断的发送数据,处理之后的那些RDD是不是一直被CACHE在内存中?我的GC Limit报错出现在用
hashmap<itemid,hashmap<userid,rating>>这种结构去load 大的训练集出现的。不知道是怎么回事?
我使用的是2G,8G都出现了内存出错,应该不是内存太小的问题吧?
问题2 我在用spark去计算每个节点下用户的平均数的时候,使用hashmap<itemid,hashmap<userid,rating>>这种存储结构可以很顺利的load进比较大的数据集。但是在map阶段处理的时间实在无法忍受,用10M的文件(文本文件一共9行每行差不多50W个用户,用split去划分的)去遍历一个training的时候居然要用近一天半的时间才能处理好。。好无语。。我一直以为代码跑死了。。。