spark机器学习:使用ALS完成商品推荐
ALS(Alternating Least Squares)是一种广泛使用的推荐系统算法,特别用于协同过滤(Collaborative Filtering)任务。在 Apache Spark 中,ALS 被实现为
org.apache.spark.ml.recommendation.ALS
类,适用于大规模数据集,并能够有效地处理稀疏矩阵,常用于推荐引擎。
ALS 算法的基本思想
ALS 的主要思想是:
分解矩阵:ALS 通过将用户-项目评分矩阵分解为两个低秩矩阵——用户特征矩阵和项目特征矩阵。这使得可以通过用户和项目之间的特征相似度来预测评分。
交替优化:该算法交替地固定用户特征矩阵和项目特征矩阵,并通过最小化损失函数(通常是均方误差)来优化这两个矩阵。具体的优化步骤是:
- 在固定项目特征矩阵的情况下,优化用户特征矩阵。
- 然后在固定用户特征矩阵的情况下,优化项目特征矩阵。
- 重复以上步骤直到收敛。
在Spark 中使用 ALS
在 Spark 中,ALS 提供了一种简单而高效的方式来构建推荐模型。以下是使用 Spark 中的 ALS 的基本步骤:
导入必要的库:
import org.apache.spark.ml.recommendation.ALS import org.apache.spark.sql.SparkSession
创建 Spark 会话:
val spark = SparkSession.builder() .appName("ALSExample") .getOrCreate()
准备数据:
数据应包含用户 ID、项目 ID 和评分,通常以 DataFrame 格式存储。
val ratings = Seq( (0, 0, 4), (0, 1, 2), (1, 0, 5), (1, 1, 1) ).toDF("userId", "itemId", "rating")
构建 ALS 模型:
val als = new ALS() .setUserCol("userId") .setItemCol("itemId") .setRatingCol("rating") .setColdStartStrategy("drop") // 保证对待没有评分的预测结果的处理 .setRank(10) // 设定特征向量的维度 .setMaxIter(10) // 最大迭代次数 val model = als.fit(ratings)
生成推荐:
可以使用模型生成用户和项目的推荐。
val userRecs = model.recommendForAllUsers(5) // 为所有用户推荐 5 个项目 val itemRecs = model.recommendForAllItems(5) // 为所有项目推荐 5 个用户
ALS实践练习
数据展示:
三列数据分别为用户 ID、物品 ID 和评分
1,1,5.0
1,2,1.0
1,3,5.0
1,4,1.0
2,1,5.0
2,2,1.0
2,3,5.0
2,4,1.0
3,1,1.0
3,2,5.0
3,3,1.0
3,4,5.0
4,1,1.0
4,2,5.0
4,3,1.0
4,4,5.0
在hdfs文件系统,创建目录,名为mymllib5,并将linux上的myrating文件数据,上传到hdfs
hadoop fs -mkdir /mymllib5
hadoop fs -put /myrating /mymllib5
启动spark-shell
spark-shell
为了执行协同过滤操作,需要首先导入执行统计所依赖的包
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.mllib.recommendation.ALS
import org.apache.spark.mllib.recommendation.MatrixFactorizationModel
import org.apache.spark.mllib.recommendation.Rating
读取hdfs上的myrating文本文件
val data = sc.textFile("hdfs://192.168.88.161:8020/mymllib5/myrating")
把数据转化成rating类型,即[Int, Int, Double]的RDD;
val ratings = data.map(_.split(",") match {
case Array(user, item, rate) =>
Rating(user.toInt, item.toInt, rate.toDouble)
})
检查一下数据格式是否符合要求
ratings.foreach{x => println(x)}
划分训练集和测试集,比例分别是0.8和0.2。
val splits = ratings.randomSplit(Array(0.8, 0.2))
将80%作为训练数据集
val training = splits(0)
将20%作为测试数据集
val test = splits(1)
指定参数值,然后使用ALS训练数据建立推荐模型:
val rank = 10
val numIterations = 10
val model = ALS.train(training, rank, numIterations, 0.01)
使用训练好的推荐模型对用户商品进行预测评分,得到预测评分的数据集
val testUsersProducts = test.map { case Rating(user, product, rate) => (user, product) }
使用训练好的推荐模型对用户商品进行预测评分,得到预测评分的数据集:
val predictions =
model.predict(testUsersProducts).map { case Rating(user, product, rate) =>((user, product), rate)
}
将真实评分数据集与预测评分数据集进行合并。这里,Join操作类似于SQL的inner join操作,返回结果是前面和后面集合中配对成功的,过滤掉关联不上的。
val ratesAndPreds = test.map { case Rating(user, product, rate) => ((user, product), rate) }.join(predictions)
我们把结果输出,对比一下真实结果与预测结果:
ratesAndPreds.foreach(println)
查看输出效果:
比如,第一条结果记录((1,4),(1.0,2.0765385175171436))中,(1,4)分别表示1号用户和4号商品,而1.0是实际的估计分值,2.0765385175171436是经过推荐的预测分值。
当然,我们也可以针对于某一个人进行预测,对比结果。
val result = model.recommendProducts(2, 1)
result.foreach(println)
然后计算均方差,这里的r1就是真实结果,r2就是预测结果:
val MSE = ratesAndPreds.map { case ((user, product), (r1, r2)) =>
val err = (r1 - r2)
err * err
}.mean()
把输出结果打印出来:
println("Mean Squared Error = " + MSE)
输出效果:
看到打分的均方差值为1.44左右