构建基于Spark的推荐系统

一、推荐模型的分类

1,基于内容的过滤

  利用物品的内容或是属性信息以及某些相似度定义,来求出与该物品类似的物品。

2,协同过滤

  利用大量已有用户偏好来估计用户对其未接触过的物品的喜好程度。

3,矩阵分解

a,显式矩阵分解

  当要处理的数据是由用户所提供的自身的偏好数据,这些数据被称为显式偏好数据。这些数据包括如物品评级、赞、喜欢等用户对物品的评价。

b,隐式矩阵分解

  用户对物品的偏好不会直接给出,而是隐含在用户与物品的交互之中。二元数据(比如用户是否观看了某个电影或是是否购买了某个商品)和技术数据(比如用户观看某电影的次数)便是这类数据。

c,最小二乘法

  最小二乘法(Altermating Least Squares,ALS)是一种求解矩阵分解的最优化方法。
  ALS的实现原理是迭代求解一系列最小二乘回归问题。在每次迭代时,固定用用户因子矩阵或是物品因子矩阵中的一个,然后用固定的这个矩阵以及评级数据来更新另一个矩阵。之后,被更新的矩阵被固定住,再更新另外一个矩阵。如此迭代,直到模型收敛(或是迭代了预设计好的次数)。

二、提取有效特征

我们采用显式评级数据,而不是使用其他用户或物品的元数据以及“用户-物品”交互数据。

MacBook-Pro:bin xp$ spark-shell
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
17/12/11 17:28:24 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
17/12/11 17:28:29 WARN ObjectStore: Failed to get database global_temp, returning NoSuchObjectException
Spark context Web UI available at http://172.16.253.3:4040
Spark context available as 'sc' (master = local[*], app id = local-1512984505032).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.2.0
      /_/
         
Using Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131)
Type in expressions to have them evaluated.
Type :help for more information.

先查看原始信息,该数据由用户ID、影片ID、星级和时间戳等字段依次组成,各字段间用制表符分隔。

scala> val rawData=sc.textFile("/Users/xp/mlpy/ml-100k/u.data")

输出如下:

rawData: org.apache.spark.rdd.RDD[String] = /Users/xp/mlpy/ml-100k/u.data MapPartitionsRDD[1] at textFile at <console>:24

scala> rawData.first()

输出如下:

res0: String = 196 242 3 881250949

由于时间戳是不需要的,那我们简单的提取前3个字段即可:

scala> val rawRatings=rawData.map(_.split("\t").take(3))

rawRatings: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[2] at map at <console>:26

上面先对各个记录用\t分割,这会返回一个Array[String]数组。之后调用Scala的take函数来仅保留数组的前三个元素,分别对应用户ID、影片ID、星级。
rawRatings.first()命令只会将新RDD的第一条记录返回到驱动程序。通过调用它,我们可以检查一下新RDD。

scala> rawRatings.first()

res1: Array[String] = Array(196, 242, 3)

scala> import org.apache.spark.mllib.recommendation.ALS

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

scala> ALS.train

<console>:25: error: ambiguous reference to overloaded definition,
both method train in object ALS of type (ratings: org.apache.spark.rdd.RDD[org.apache.spark.mllib.recommendation.Rating], rank: Int, iterations: Int)org.apache.spark.mllib.recommendation.MatrixFactorizationModel
and method train in object ALS of type (ratings: org.apache.spark.rdd.RDD[org.apache.spark.mllib.recommendation.Rating], rank: Int, iterations: Int, lambda: Double)org.apache.spark.mllib.recommendation.MatrixFactorizationModel
match expected type ?
ALS.train
^

这个错误提示,所需提供的输入参数至少有ratings、rank和iterations。第二个函数另外还需要一个lambda参数。导入上面提到的Rating类,再类似的输入Rating()后回车,便可看到Rating对象所需的参数:

scala> import org.apache.spark.mllib.recommendation.Rating

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

scala> Rating()

<console>:26: error: not enough arguments for method apply: (user: Int, product: Int, rating: Double)org.apache.spark.mllib.recommendation.Rating in object Rating.
Unspecified value parameters user, product, rating.
Rating()
^

上述输出表明ALS模型需要一个由Rating记录构成的RDD,而Rating类则是对用户ID、影片ID(这里是通称product)和实际星级这些参数封装。我们可以 调用map方法将原来的各ID和星级的数组转换为对应的Rating对象,从而创建所需的评级数据集。

scala> val ratings=rawRatings.map{case Array(user,movie,rating) => Rating(user.toInt,movie.toInt,rating.toDouble)}

ratings: org.apache.spark.rdd.RDD[org.apache.spark.mllib.recommendation.Rating] = MapPartitionsRDD[3] at map at <console>:30

scala> ratings.first()

res5: org.apache.spark.mllib.recommendation.Rating = Rating(196,242,3.0)

三、训练推荐模型

  从原始数据提取出这些简单特征后,便可训练模型。
训练模型所需的其他参数有以下几个:
rank:对应ALS模型中的因子个数。其合理值为10-200.
iterations:对应运行时的迭代次数。合理值10次左右。
lambda:该参数控制模型的正则化过程,从而控制模型的过拟合情况。

scala> val model=ALS.train(ratings,50,10,0.01)

17/12/11 17:36:04 WARN BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS
17/12/11 17:36:04 WARN BLAS: Failed to load implementation from: com.github.fommil.netlib.NativeRefBLAS
17/12/11 17:36:04 WARN LAPACK: Failed to load implementation from: com.github.fommil.netlib.NativeSystemLAPACK
17/12/11 17:36:04 WARN LAPACK: Failed to load implementation from: com.github.fommil.netlib.NativeRefLAPACK
model: org.apache.spark.mllib.recommendation.MatrixFactorizationModel = org.apache.spark.mllib.recommendation.MatrixFactorizationModel@27ee8493

scala> model.userFeatures

res6: org.apache.spark.rdd.RDD[(Int, Array[Double])] = users MapPartitionsRDD[209] at mapValues at ALS.scala:271

scala> model.userFeatures.count

res7: Long = 943

scala> model.productFeatures.count

res8: Long = 1682

四、使用推荐模型

  用户推荐具体的实现方法取决于所采用的模型。比如若采用基于用户的模型,则会利用相似用户的评级来计算某个用户的推荐。而若采用基于物品的模型,则会依靠用户接触过的物品与候选物品之间的相似度来获得推荐。

  利用矩阵分解方法时,是直接对评级数据进行建模,所以预计得分可视作相应用户因子向量和物品向量的点积。

scala> val predictedRating=model.predict(789,123)

predictedRating: Double = 3.5709657849060057

可以看到,该模型预测用户789对电影123的评级3.57。

scala> val userId=789

userId: Int = 789

scala> val K=10

K: Int = 10

scala> val topKRecs=model.recommendProducts(userId,K)

topKRecs: Array[org.apache.spark.mllib.recommendation.Rating] = Array(Rating(789,156,5.575635336185863), Rating(789,42,5.529139597902855), Rating(789,179,5.516092734457448), Rating(789,39,5.400058460147818), Rating(789,180,5.3832945184540755), Rating(789,195,5.3781917200594105), Rating(789,671,5.375058224290015), Rating(789,663,5.2987123201627355), Rating(789,92,5.278211419850456), Rating(789,647,5.165706252282592))

scala> println(topKRecs.mkString("\n"))

Rating(789,156,5.575635336185863)
Rating(789,42,5.529139597902855)
Rating(789,179,5.516092734457448)
Rating(789,39,5.400058460147818)
Rating(789,180,5.3832945184540755)
Rating(789,195,5.3781917200594105)
Rating(789,671,5.375058224290015)
Rating(789,663,5.2987123201627355)
Rating(789,92,5.278211419850456)
Rating(789,647,5.165706252282592)

这就求得了为用户789所能推荐的物品以及对应的预计得分。

详细案例查看:《Spark机器学习》第4章

参考:
1,用户兴趣模型分类以及推荐系统技术调研

2,主流推荐算法的分类及介绍

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容