一、推荐模型的分类
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章