Spark0.9分布式运行MLlib的协同过滤

1 什么是协同过滤

协同过滤推荐(Collaborative Filtering recommendation)是在信息过滤和信息系统中正迅速成为一项很受欢迎的技术。与传统的基于内容过滤直接分析内容进行推荐不同,协同过滤分析用户兴趣,在用户群中找到指定用户的相似(兴趣)用户,综合这些相似用户对某一信息的评价,形成系统对该指定用户对此信息的喜好程度预测。

1.1 与传统文本过滤相比,协同过滤有下列优点:

1.1.1 能够过滤难以进行机器自动基于内容分析的信息。如艺术品、音乐;

1.1.2 能够基于一些复杂的,难以表达的概念(信息质量、品位)进行过滤;

1.1.3 推荐的新颖性。

尽管协同过滤技术在个性化推荐系统中获得了极大的成功,但随着站点结构、内容的复杂度和用户人数的不断增加,协同过滤技术的一些缺点逐渐暴露出来。

1.2 主要有以下三点:

1.2.1 稀疏性(sparsity):

在许多推荐系统中,每个用户涉及的信息量相当有限,在一些大的系统如亚马逊网站中,用户最多不过就评估了上百万本书的1%~2%。造成评估矩阵数据相当稀疏,难以找到相似用户集,导致推荐效果大大降低。

1.2.2 扩展性(scalability):

“最近邻居”算法的计算量随着用户和项的增加而大大增加,对于上百万之巨的数目,通常的算法将遭遇到严重的扩展性问题。

1.2.3 精确性(accuracy):

通过寻找相近用户来产生推荐集,在数量较大的情况下,推荐的可信度随之降低。

2. Item-based协同过滤 和 过适[编辑]

当可以对一些项目评分的时候,比如人们可以对一些东西给出1到5星的评价的时候,协同过滤意图基于一个个体过去对某些项目的评分和(庞大的)由其他用户的评价构成的数据库,来预测该用户对未评价项目的评分。 例如: 如果一个人给披头士的评分为5(总分5)的话,我们能否预测他对席琳狄翁新专辑的评分呢?

这种情形下, item-based 协同过滤系统[5][6] 根据其它项目的评分来预测某项目的分值,一般方法为 线性回归 (). 于是,需要列出x^2个线性回归方程和2x^2个回归量,例如:当有1000个项目时,需要列多达1,000,000个线性回归方程, 以及多达2,000,000个回归量。除非我们只选择某些用户共同评价过的项目对,否则协同过滤会遇到过适[2](过拟合) 问题。

另外一种更好的方法是使用更简单一些的式子,比如 :实验证明当使用一半的回归量的时候,该式子(称为Slope One)的表现有时优于[2]线性回归方程。该简化方法也不需要那么多存储空间和延迟。

Item-based 协同过滤只是 协同过滤的一种形式.其它还有像 user-based 协同过滤一样研究用户间的联系的过滤系统。但是,考虑到其他用户数量庞大,item-based协同过滤更可行一些。

2.1电子商务中的Item-based协同过滤[编辑]

人们并不总是能给出评分,当用户只提供二进制数据(购买与否)的时候,就无法应用Slope One 和其它基于评分的算法。 二进制 item-based协同过滤应用的例子之一就是Amazon的 item-to-item 专利算法[7] ,该算法中用二进制向量表示用户-项目购买关系的矩阵,并计算二进制向量间的cosine相关系数。

有人认为Item-to-Item算法甚至比Slope One 还简单,例如:

购买统计样本

顾客

项目 1

项目 2

项目 3

John

买过

没买过

买过

Mark

没买过

买过

买过

Lucy

没买过

买过

没买过

在本例当中,项目1和项目2间的cosine相关系数为:

,

项目1和项目3间的cosine相关系数为:

,

而项目2和项目3的cosine相关系数为:

.

于是,浏览项目1的顾客会被推荐买项目3(两者相关系数最大),而浏览项目2的顾客会被推荐买项目3,浏览了项目3的会首先被推荐买项目1(再然后是项目2,因为2和3的相关系数小于1和3)。该模型只使用了每对项目间的一个参数(cosine相关系数)来产生推荐。因此,如果有n个项目,则需要计算和存储 n(n-1)/2 个cosine相关系数。

2.2 Slope One 协同过滤[编辑]

为了大大减少过适(过拟合)的发生,提升算法简化实现, Slope One 系列易实现的Item-based协同过滤算法被提了出来。本质上,该方法运用更简单形式的回归表达式() 和单一的自由参数,而不是一个项目评分和另一个项目评分间的线性回归 ()。 该自由参数只不过就是两个项目评分间的平均差值。甚至在某些实例当中,它比线性回归的方法更准确[2],而且该算法只需要一半(甚至更少)的存储量。

:

1.     User A 对 Item I 评分为1 对ItemJ.评分为1.5

2.     User B 对 Item I 评分为2.

3.     你认为 User B 会给 Item J 打几分?

4.     Slope One 的答案是:2.5 (1.5-1+2=2.5).


举个更实际的例子,考虑下表:

评分数据库样本

顾客

项目 1

项目 2

项目 3

John

5

3

2

Mark

3

4

未评分

Lucy

未评分

2

5

在本例中,项目2和1之间的平均评分差值为(2+(-1))/2=0.5. 因此,item1的评分平均比item2高0.5。同样的,项目3和1之间的平均评分差值为3。因此,如果我们试图根据Lucy 对项目2的评分来预测她对项目1的评分的时候,我们可以得到 2+0.5 = 2.5。同样,如果我们想要根据她对项目3的评分来预测她对项目1的评分的话,我们得到 5+3=8.

如果一个用户已经评价了一些项目,可以这样做出预测:简单地把各个项目的预测通过加权平均值结合起来。当用户两个项目都评价过的时候,权值就高。在上面的例子中,项目1和项目2都评价了的用户数为2,项目1和项目3 都评价了的用户数为1,因此权重分别为2和1. 我们可以这样预测Lucy对项目1的评价:

 于是,对“n”个项目,想要实现 Slope One,只需要计算并存储“n”对评分间的平均差值和评价数目即可。

3. 基于Spark的协同过滤的使用

MLlib目前支持基于协同过滤的模型,在这个模型里,用户和产品被一组的可以用来预测缺失的项目的潜在因子来描述。特别是,我们实现交替最小二乘(ALS)算法来学习这些潜在的因子。实现在MLlib以下参数:

numBlocks是用于并行化计算的数据分块(自动配置(设置为1))。

rank是在我们的模型中潜在因子的数量。

Iterations是迭代的数量。

Lambda是在ALS指定了正则化参数。

implicitPrefs指定是否使用显式反馈ALS变量或一个用于隐式反馈数据

α是一个参数适用于隐式反馈的ALS变量,这个变量管理着偏好观测的基线信心 

在接下来的例子中我们将要装载一个评级数据。每一行包含一个用户、一个产品和一个评级。我们使用默认ALS.train()方法,这个方法假设评级是明确的。我们通过预测评级的均方误差的来评估推荐模型评级的好坏。

import org.apache.spark.SparkContext
import org.apache.spark.mllib.recommendation.ALS
import org.apache.spark.mllib.recommendation.Rating

object SimpleApp {
  def main(args: Array[String]) {
      val sc = new SparkContext("spark://192.168.159.129:7077", "Collaborative Filtering", "/root/spark-0.9",
      List("target/scala-2.10/scala_2.10-0.1-SNAPSHOT.jar"))
      val data = sc.textFile("hdfs://master:9000/mllib/test.data")
      val ratings = data.map(_.split(',') match {
          case Array(user, item, rate) =>  Rating(user.toInt, item.toInt, rate.toDouble)
      })
      
      // Build the recommendation model using ALS
      val numIterations = 20
      val model = ALS.train(ratings, 1, 20, 0.01)
      
      // Evaluate the model on rating data
      val usersProducts = ratings.map{ case Rating(user, product, rate)  => (user, product)}
      val predictions = model.predict(usersProducts).map{
          case Rating(user, product, rate) => ((user, product), rate)
      }
      val ratesAndPreds = ratings.map{
          case Rating(user, product, rate) => ((user, product), rate)
      }.join(predictions)
      val MSE = ratesAndPreds.map{
          case ((user, product), (r1, r2)) =>  math.pow((r1- r2), 2)
      }.reduce(_ + _)/ratesAndPreds.count
      println("Mean Squared Error = " + MSE)
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值