目前SparkMLlib支持的推荐算法只有alternating least squares (ALS)这一种,相比较Mahout中的推荐算法,SparkMLlib目前不能支持目前的业务需求;因此,参照Mahout的推荐引擎,在Spark上构建同样一套推荐算法,以支持各种业务需求。
目前SparkMLlib官方网址:
http://spark.apache.org/docs/latest/mllib-guide.html
Mahout的推荐引擎的详细介绍参照:
http://blog.fens.me/mahout-recommend-engine/
输入数据格式:
用户ID,物品ID,评分
用户输入数据模型:
defUserData (
sc:SparkContext,input:String,
split:String
):(RDD[(String,String,Double)]) = {
valuser_rdd1= sc.textFile(input,10)
valuser_rdd2=user_rdd1.map(line=> {
valfileds= line.split("split")
(fileds(0),fileds(1),fileds(2).toDouble)
})
user_rdd2
}
通过UserData可以获取用户评分RDD;
输入参数:sc是SparkContext,input就输入数据路径,split是数据分割符号。
基于物品(ItemCF)的相似度算法:
1)同现相似度
2)欧氏距离相似度
3)余弦相似度
4)秩相关系数相似度
5)曼哈顿距离相似度
6)对数似然相似度
参照:《推荐系统实践》一书中的介绍:
公式定义:
实例:
同现相似度模型:根据用户评分数据表,生成物品的相似矩阵;
输入参数:user_rdd:用户评分表;
输出参数:余弦相似矩阵:物品1,物品2,相似度值;
同现相似度矩阵模型:
def Cooccurrence (
user_rdd:RDD[(String,String,Double)]
) : (RDD[(String,String,Double)]) = {
// 0 数据做准备
valuser_rdd2=user_rdd.map(f => (f._1,f._2)).sortByKey()
user_rdd2.cache
// 1 (用户:物品)笛卡尔积 (用户:物品) =>物品:物品组合
valuser_rdd3=user_rdd2joinuser_rdd2
valuser_rdd4=user_rdd3.map(data=> (data._2,1))
// 2 物品:物品:频次
valuser_rdd5=user_rdd4.reduceByKey((x,y) => x + y)
// 3 对角矩阵
valuser_rdd6=user_rdd5.filter(f=> f._1._1 == f._1._2)
// 4 非对角矩阵
valuser_rdd7=user_rdd5.filter(f=> f._1._1 != f._1._2)
// 5 计算同现相似度(物品1,物品2,同现频次)
valuser_rdd8=user_rdd7.map(f=> (f._1._1, (f._1._1, f._1._2, f._2))).
join(user_rdd6.map(f=> (f._1._1, f._2)))
valuser_rdd9=user_rdd8.map(f=> (f._2._1._2, (f._2._1._1,
f._2._1._2, f._2._1._3, f._2._2)))
valuser_rdd10=user_rdd9.join(user_rdd6.map(f => (f._1._1, f._2)))
val user_rdd11 = user_rdd10.map(f => (f._2._1._1,f._2._1._2,f._2._1._3,f._2._1._4,f._2._2))
valuser_rdd12=user_rdd11.map(f=> (f._1, f._2, (f._3 / sqrt(f._4 * f._5)) ))
// 6结果返回
user_rdd12
}
原理:多维空间两点与所设定的点形成夹角的余弦值。
范围:[-1,1],值越大,说明夹角越大,两点相距就越远,相似度就越小。
余弦相似度模型:根据用户评分数据表,生成物品的相似矩阵;
输入参数:user_rdd:用户评分表;
输出参数:余弦相似矩阵:物品1,物品2,相似度值;
余弦相似度矩阵模型:
defCosineSimilarity (
user_rdd:RDD[(String,String,Double)]
) : (RDD[(String,String,Double)]) = {
// 0 数据做准备
valuser_rdd2=user_rdd.map(f => (f._1,(f._2,f._3))).sortByKey()
user_rdd2.cache
// 1 (用户,物品,评分)笛卡尔积 (用户,物品,评分) =>(物品1,物品2,评分1,评分2)组合
valuser_rdd3=user_rdd2joinuser_rdd2
valuser_rdd4=user_rdd3.map(f=> ((f._2._1._1, f._2._2._1),(f._2._1._2, f._2._2._2)))
// 2 (物品1,物品2,评分1,评分2)组合 => (物品1,物品2,评分1*评分2)组合并累加
valuser_rdd5=user_rdd4.map(f=> (f._1,f._2._1*f._2._2 )).reduceByKey(_+_)
// 3 对角矩阵
valuser_rdd6=user_rdd5.filter(f=> f._1._1 == f._1._2)
// 4 非对角矩阵
valuser_rdd7=user_rdd5.filter(f=> f._1._1 != f._1._2)
// 5 计算相似度
valuser_rdd8=user_rdd7.map(f=> (f._1._1, (f._1._1, f._1._2, f._2))).
join(user_rdd6.map(f=> (f._1._1, f._2)))
valuser_rdd9=user_rdd8.map(f=> (f._2._1._2, (f._2._1._1,
f._2._1._2, f._2._1._3, f._2._2)))
valuser_rdd10=user_rdd9.join(user_rdd6.map(f => (f._1._1, f._2)))
val user_rdd11 = user_rdd10.map(f => (f._2._1._1,f._2._1._2,f._2._1._3,f._2._1._4,f._2._2))
valuser_rdd12=user_rdd11.map(f=> (f._1, f._2, (f._3 / sqrt(f._4 * f._5)) ))
// 7 结果返回
user_rdd12
}
原理:利用欧式距离d定义的相似度s,s=1 /(1+d)。
改进:
num = intersect(which(M[x,]!=0),which(M[y,]!=0))
d = sum((M[x,] - M[y,])^2,na.rm = T)
s = length(num)/(1 + sqrt(d))
范围:[0,1],值越大,说明d越小,也就是距离越近,则相似度越大。
欧式相似度模型:根据用户评分数据表,生成物品的相似矩阵;
输入参数:user_rdd:用户评分表;
输出参数:余弦相似矩阵:物品1,物品2,相似度值;欧式相似度矩阵模型:
def EuclideanDistanceSimilarity (
user_rdd:RDD[(String,String,Double)]
) : (RDD[(String,String,Double)]) = {
// 0 数据做准备
valuser_rdd2=user_rdd.map(f => (f._1,(f._2,f._3))).sortByKey()
user_rdd2.cache
// 1 (用户,物品,评分)笛卡尔积 (用户,物品,评分) =>(物品1,物品2,评分1,评分2)组合
valuser_rdd3=user_rdd2joinuser_rdd2
valuser_rdd4=user_rdd3.map(f=> ((f._2._1._1, f._2._2._1),(f._2._1._2, f._2._2._2)))
// 2 (物品1,物品2,评分1,评分2)组合 => (物品1,物品2,评分1-评分2)组合并累加
valuser_rdd5=user_rdd4.map(f=> (f._1,(f._2._1-f._2._2 )*(f._2._1-f._2._2 ))).reduceByKey(_+_)
// 3 (物品1,物品2,评分1,评分2)组合 => (物品1,物品2,1)组合并累加 计算重叠数
valuser_rdd6=user_rdd4.map(f=> (f._1,1)).reduceByKey(_+_)
// 4 非对角矩阵
valuser_rdd7=user_rdd5.filter(f=> f._1._1 != f._1._2)
// 5 计算相似度
valuser_rdd8=user_rdd7.join(user_rdd6)
valuser_rdd9=user_rdd8.map(f=> (f._1._1,f._1._2,f._2._2/(1+sqrt(f._2._1))))
// 7 结果返回
user_rdd9
}
等待更新......
等待更新......
等待更新......
推荐模型:根据物品相似矩阵以及用户对物品的评分表,计算用户的推荐列表。
输入参数:items_similar物品相似矩阵,user_perf:用户评分表,r_number:推荐个数。
输出参数:用户推荐列表:用户ID,物品ID,得分。
defRecommend (
items_similar:RDD[(String,String,Double)],
user_perf:RDD[(String,String,Double)],
r_number:Int
) : (RDD[(String,String,Double)]) = {
// 1 矩阵计算——i行与j列join
valrdd_app1_R2=items_similar.map(f => (f._2, (f._1,f._3))).
join(user_perf.map(f => (f._2,(f._1,f._3))))
// 2 矩阵计算——i行与j列元素相乘
valrdd_app1_R3=rdd_app1_R2.map(f=> ((f._2._2._1,f._2._1._1),f._2._2._2*f._2._1._2))
// 3 矩阵计算——用户:元素累加求和
valrdd_app1_R4=rdd_app1_R3.reduceByKey((x,y)=> x+y).map(f => (f._1._1,(f._1._2,f._2)))
// 4 矩阵计算——用户:用户对结果排序,过滤
valrdd_app1_R5=rdd_app1_R4.groupByKey()
valrdd_app1_R6=rdd_app1_R5.map(f=> {
val i2 = f._2.toBuffer
val i2_2 = i2.sortBy(_._2)
if (i2_2.length > r_number)i2_2.remove(0,(i2_2.length-r_number))
(f._1,i2_2.toIterable)
})
valrdd_app1_R7=rdd_app1_R6.flatMap(f=> {
val id2 = f._2
for (w <-id2)yield(f._1,w._1,w._2)
})
rdd_app1_R7
}
被检索到的越多越好,这是追求“查全率”,即A/(A+B),越大越好。被检索到的,越相关的越多越好,不相关的越少越好,这是追求“查准率”,即A/(A+C),越大越好。
评估算法模型
等待更新......
转载请注明出处:
http://blog.csdn.net/sunbow0/article/details/42737541