一 基于用户协同过滤简介
基于用户的协同过滤算法(user-based collaboratIve filtering)
基于用户的协同过滤算法是通过用户的历史行为数据发现用户对商品或内容的喜欢(如商品购买,收藏,内容评论或分享),并对这些喜好进行度量和打分。根据不同用户对相同商品或内容的态度和偏好程度计算用户之间的关系。在有相同喜好的用户间进行商品推荐。简单的说就是如果A,B两个用户都购买了x,y,z三本图书,并且给出了5星的好评。那么A和B就属于同一类用户。可以将A看过的图书w也推荐给用户B。
Spark MLlib的ALS
spark.ml目前支持基于模型的协作过滤,其中用户和产品由可用于预测缺失条目的一小组潜在因素来描述。spark.ml使用交替最小二乘(ALS) 算法来学习这些潜在因素。算法实现中spark.ml提供有以下参数:
注意:ALS基于DataFrame的API目前仅支持用户和项目ID为整数。用户和项目ID列支持其他数字类型,但ID必须在整数值范围内。
显式与隐式反馈
基于矩阵分解的协作过滤的标准方法将用户条目矩阵中的条目视为用户对该项目的显式偏好,例如,用户给电影的评级。
在许多真实世界的使用情况中,通常只能访问隐式反馈(例如,观看,点击,购买,喜欢,分享等)。根本上讲,这种方法不是根据用户直接评分建模,而是根据用户的行为(点击次数,停留时间),将其视为数字,代表用户对电影的可能喜欢程度。然后,这些数字与观察到的用户偏好的置信度相关,而不是与物品的显式评分。然后该模型将尝试找出可以用来预测用户对于某一项目的预期偏好的潜在因子。
正则化参数
调整的正则化参数regParam,是根据用户在更新用户因子时产生的评分数或者物品在更新物品因子时收到的评分数来解决每个最小二乘问题。这种方法被命名为“ALS-WR”,并在“Large-Scale Parallel Collaborative Filtering for the Netflix Prize ”文章中进行了讨论。它对regParam数据集规模的依赖较小,因此我们可以将从采样子集学习到的最佳参数应用于整个数据集,并期望有相似的性能。
冷启动策略
使用ALSModel进行预测时,测试数据集中的用户和/或项目在训练模型期间不存在是很常见的。这通常发生在两种情况下:
默认情况,Spark在ALSModel.transform用户和/或项目因素不存在于模型中时分配NaN预测。这在生产系统中可能是有用的,因为它表名一个新的用户或项目,因此系统可以作为预测的一个后备决定。
然而,这在交叉验证期间是不希望的,因为任何NaN预测值都将影响NaN评估度量的结果(例如,在使用时RegressionEvaluator)。这使得模型选择变得不可能。Spark允许用户将coldStartStrategy参数设置为“drop”,以便删除DataFrame包含NaN值的预测中的任何行。
注意:目前支持的冷启动策略是“nan”(上面提到的默认行为)和“drop”。未来可能会支持进一步的策略。
MovieLens电影基于用户推荐
在以下示例中,我们将从MovieLens数据集(https://grouplens.org/datasets/movielens/)中加载评分数据 ,每行由用户,电影,评分和时间戳组成。然后,我们训练一个ALS模型,默认情况下,这个模型的评分是明确的(implicitPrefs是false)。我们通过测量评级预测的均方根误差来评估推荐模型。
import org.apache.spark.ml.evaluation.RegressionEvaluator
import org.apache.spark.ml.recommendation.ALS
import org.apache.spark.sql.SparkSession
object ALSExample {
case class Rating(userId: Int, movieId: Int, rating: Float, timestamp: Long)
def parseRating(str: String): Rating = {
val fields = str.split("::")
assert(fields.size == 4)
Rating(fields(0).toInt, fields(1).toInt, fields(2).toFloat, fields(3).toLong)
}
def main(args: Array[String]) {
val spark = SparkSession
.builder
.appName("ALSExample")
.getOrCreate()
import spark.implicits._
// $example on$
val ratings = spark.read.textFile("file:///opt/modules/spark-2.2.0/data/mllib/als/sample_movielens_ratings.txt").map(parseRating).toDF()
//将数据集切分为训练集和测试集
val Array(training, test) = ratings.randomSplit(Array(0.8, 0.2))
//使用ALS在训练集数据上构建推荐模型
val als = new ALS().setMaxIter(5).setRegParam(0.01).setUserCol("userId").setItemCol("movieId").setRatingCol("rating")
val model = als.fit(training)
// 通过计算rmse(均方根误差)来评估模型
//为确保不获取到NaN评估参数,我们将冷启动策略设置为drop。
val predictions = model.transform(test)
val evaluator = new RegressionEvaluator().setMetricName("rmse").setLabelCol("rating").setPredictionCol("prediction")
val rmse = evaluator.evaluate(predictions)
println(s"Root-mean-square error = $rmse")
//每个用户推荐的前十个电影
val userRecs = model.recommendForAllUsers(10)
//每个电影推荐的十个用户
val movieRecs = model.recommendForAllItems(10)
userRecs.show()
movieRecs.show()
spark.stop()
}
}
如果评级矩阵是从另一个信息源(即它是从其他因子推断)得出,可以设置implicitPrefs以true获得更好的效果:
val als = new ALS()
.setMaxIter(5)
.setRegParam(0.01)
.setImplicitPrefs(true)
.setUserCol("userId")
.setItemCol("movieId")
.setRatingCol("rating")