ALS全称alternating least squares 交替最小二乘。在推荐算法中,是指基于ALS求解的一种协同推荐算法。ALS算法是统计分析中一种常用的逼近计算的算法,其计算结果能够最大程度逼近真实的结果。以下简单介绍下这种算法,我们还是将重点放在这个算法在协同推荐算法中的实际应用。
ALS的基础是LS——最小二乘,本质属于数学优化,也常常用在机器学习中,其原理是通过最小化误差的平方和,从而找到数据的最优函数匹配。通过LS可以便捷求得未知的数据,并且使得预测的数据与真实数据之间的误差值的平方和最小。所以,我们也常常在回归任务中看到它的身影。公式如下:
f ( x ) = a x + b f ( x ) = a x + b f(x)=ax+b
∂ = ∑ ( f ( x i ) − y i ) 2 \partial = \sum ( f ( x _i ) - y _ i ) ^ { 2 } ∂=∑(f(xi)−yi)2
f ( x ) f(x) f(x)为拟合公式,即所求目标函数, y i yi yi就是实际值,在这里我们就是希望预测与实际值间的误差最小。这个误差就是ALS的优化目标,即下文中通过 U U U和 V V V重构 W W W产生的误差
而我们所说的ALS就涉及到矩阵的知识,一个基于用户名和物品表的用户评分矩阵 W W W可以被分解为两个更小的矩阵 U U U:用户的矩阵和 V V V:物品的矩阵。ALS在MLlib中的应用如下:
(1)对 U U U或 V V V随机生成矩阵中的某个特定对象,例如是 U ( 0 ) U(0) U(0);
(2)固定随机生成的对象 U ( 0 ) U(0) U(0),求未随机生成的矩阵对象 V ( 0 ) V(0) V(0);
(3)得到求得的矩阵对象计算随机化矩阵对象;
(4)两矩阵对象相互交替计算,迭代这个过程,每次迭代都降低重构误差,求得与实际矩阵差值达到提前设定的最小阈值位置。
在MLlib中,ALS算法采用的是高效分解矩阵的计算,设计数据分区和RDD缓存来减少数据交换,这对迭代计算非常友好。对ALS的源码分析如下:
(1)object ALS:ALS伴生对象,含有train静态方法,需要设置的参数如下:
(2)class ALS:ALS类,run方法,调用NewALS类的train方法计算;
(3)矩阵分解计算
makeBlocks——创建用户和物品的Block块,是用来进行交替计算的;
initialize——初始化用户和物品的特征矩阵;
computeFactors——使用ALS求解用户和物品特征矩阵,进行迭代计算,现根据用户特征矩阵求解物品特征矩阵,再根据结果进行交替计算;
(4)MatrixFactorizationModel:矩阵分解模型,矩阵分解计算完成后生成矩阵分解模型:用户和物品特征矩阵。包括了如下方法:predict:预测用户对物品评分;recommendProducts:对用户推荐物品的列表;recommendUsers:对物品推荐用户的列表;recommendProductsForUsers:在所有用户推荐物品中选取前k个物品;recommendUsersForProducts:在所有物品推荐用户中选取前k个用户。
本次实战使用的数据集sample_movielens_ratings,样本示例如下,每行样本数据由一个用户、一部电影、该用户对电影的评分和时间戳组成。这是一种明确表示对物品喜好的行为,称作显性反馈;与此对应的,没有明确反映用户喜好的行为就是隐性反馈行为。
0::2::3::1424380312
0::3::1::1424380312
0::5::2::1424380312
0::9::4::1424380312
0::11::1::1424380312
0::12::2::1424380312
0::15::1::1424380312
0::17::1::1424380312
0::19::1::1424380312
0::21::1::1424380312
//导入需要的机器学习包
import org.apache.spark.ml.evaluation.RegressionEvaluator
import org.apache.spark.ml.recommendation.ALS
//创建读取数据的规则 Rating类型[int, int, float, long]
case class Rating(userId: Int, movieId: Int, rating: Float, timestamp: Long)
//创建函数把数据集中的数据转化为Rating类型
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)
| }
输出结果:
parseRating: (str: String)Rating
//导入需要的包文件implicits
//并取数据集
import spark.implicits._
import spark.implicits._
val ratings =
spark.sparkContext.textFile("file:///mnt/hgfs/thunder-download/MLlib_rep/data/
sample_movielens_ratings.txt").map(parseRating).toDF()
输出结果:
ratings: org.apache.spark.sql.DataFrame = [userId: int, movieId: int ... 2 more fields]
//把处理后的数据打印出来
ratings.show()
+------+-------+------+----------+
|userId|movieId|rating| timestamp|
+------+-------+------+----------+
| 0| 2| 3.0|1424380312|
| 0| 3| 1.0|1424380312|
| 0| 5| 2.0|1424380312|
| 0| 9| 4.0|1424380312|
| 0| 11| 1.0|1424380312|
| 0| 12| 2.0|1424380312|
| 0| 15| 1.0|1424380312|
| 0| 17| 1.0|1424380312|
| 0| 19| 1.0|1424380312|
| 0| 21| 1.0|1424380312|
| 0| 23| 1.0|1424380312|
| 0| 26| 3.0|1424380312|
| 0| 27| 1.0|1424380312|
| 0| 28| 1.0|1424380312|
| 0| 29| 1.0|1424380312|
| 0| 30| 1.0|1424380312|
| 0| 31| 1.0|1424380312|
| 0| 34| 1.0|1424380312|
| 0| 37| 1.0|1424380312|
| 0| 41| 2.0|1424380312|
+------+-------+------+----------+
only showing top 20 rows
//划分数据集为训练集和测试集
val Array(training, test) = ratings.randomSplit(Array(0.8, 0.2))
输出结果:
training: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [userId: int, movieId: int... 2 more fields]
test: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [userId: int, movieId: int ... 2 more fields]
//建立ALS推荐模型
//显性反馈模型
val alsExplicit = new ALS().setMaxIter(5).setRegParam(0.01).setUserCol("userId"). setItemCol("movieId").setRatingCol("rating")
输出结果:
alsExplicit: org.apache.spark.ml.recommendation.ALS = als_05fe5d78ccd4
//隐性反馈模型,setImplicitPrefs设置为true
val alsImplicit = new ALS().setMaxIter(5).setRegParam(0.01).setImplicitPrefs(true). setUserCol("userId").setItemCol("movieId").setRatingCol("rating")
输出结果:
alsImplicit: org.apache.spark.ml.recommendation.ALS = als_8f0d989bfdea
在MLlib中可以对模型参数进行如下调整:
numBlocks 是用于并行化计算的用户和商品的分块个数 默认10;
rank 是模型中隐语义因子的个数 默认10;
maxIter 是迭代的次数 默认10;
regParam 是ALS的正则化参数 默认1.0;
implicitPrefs 决定了是用显性反馈ALS的版本还是用适用隐性反馈数据集的版本(默认是false,即用显性反馈);
alpha 是一个针对于隐性反馈 ALS 版本的参数,这个参数决定了偏好行为强度的基准 默认1.0;
nonnegative 决定是否对最小二乘法使用非负的限制 默认false。
调整maxiter增加,regParam减小,均方差会减小,得到的推荐结果会更准确。
//使用推荐模型导入训练数据进行训练
//显性模型
val modelExplicit = alsExplicit.fit(training)
//隐性模型
val modelImplicit = alsImplicit.fit(training)
//使用训练好的模型进行预测,对测试数据中的用户物品预测评分,获得预测评
//分数据集
//显性模型预测
val predictionsExplicit = modelExplicit.transform(test)
输出结果:
predictionsExplicit: org.apache.spark.sql.DataFrame = [userId: int, movieId: int ... 3 more fields]
//隐性模型预测
val predictionsImplicit = modelImplicit.transform(test)
输出结果:
predictionsImplicit: org.apache.spark.sql.DataFrame = [userId: int, movieId: int ... 3 more fields]
//输出结果,对比预测与真实结果
//显性模型
predictionsExplicit.show()
输出结果:
+------+-------+------+----------+------------+
|userId|movieId|rating| timestamp| prediction|
+------+-------+------+----------+------------+
| 13| 31| 1.0|1424380312| 0.86262053|
| 5| 31| 1.0|1424380312|-0.033763513|
| 24| 31| 1.0|1424380312| 2.3084288|
| 29| 31| 1.0|1424380312| 1.9081671|
| 0| 31| 1.0|1424380312| 1.6470298|
| 28| 85| 1.0|1424380312| 5.7112412|
| 13| 85| 1.0|1424380312| 2.4970412|
| 20| 85| 2.0|1424380312| 1.9727222|
| 4| 85| 1.0|1424380312| 1.8414592|
| 8| 85| 5.0|1424380312| 3.2290685|
| 7| 85| 4.0|1424380312| 2.8074787|
| 29| 85| 1.0|1424380312| 0.7150749|
| 19| 65| 1.0|1424380312| 1.7827456|
| 4| 65| 1.0|1424380312| 2.3001173|
| 2| 65| 1.0|1424380312| 4.8762875|
| 12| 53| 1.0|1424380312| 1.5465991|
| 20| 53| 3.0|1424380312| 1.903692|
| 19| 53| 2.0|1424380312| 2.6036916|
| 8| 53| 5.0|1424380312| 3.1105173|
| 23| 53| 1.0|1424380312| 1.0042696|
+------+-------+------+----------+------------+
only showing top 20 rows
//隐性模型
predictionsImplicit.show()
输出结果:
+------+-------+------+----------+-----------+
|userId|movieId|rating| timestamp| prediction|
+------+-------+------+----------+-----------+
| 13| 31| 1.0|1424380312| 0.33150947|
| 5| 31| 1.0|1424380312|-0.24669354|
| 24| 31| 1.0|1424380312|-0.22434244|
| 29| 31| 1.0|1424380312| 0.15776125|
| 0| 31| 1.0|1424380312| 0.51940984|
| 28| 85| 1.0|1424380312| 0.88610375|
| 13| 85| 1.0|1424380312| 0.15872183|
| 20| 85| 2.0|1424380312| 0.64086926|
| 4| 85| 1.0|1424380312|-0.06314563|
| 8| 85| 5.0|1424380312| 0.2783457|
| 7| 85| 4.0|1424380312| 0.1618208|
| 29| 85| 1.0|1424380312|-0.19970453|
| 19| 65| 1.0|1424380312| 0.11606887|
| 4| 65| 1.0|1424380312|0.068018675|
| 2| 65| 1.0|1424380312| 0.28533924|
| 12| 53| 1.0|1424380312| 0.42327875|
| 20| 53| 3.0|1424380312| 0.17345423|
| 19| 53| 2.0|1424380312| 0.33321634|
| 8| 53| 5.0|1424380312| 0.10090684|
| 23| 53| 1.0|1424380312| 0.06724724|
+------+-------+------+----------+-----------+
only showing top 20 rows
//根据模型预测结果进行评估,评估标准是计算模型预测结果的均方根误差,误
//差越小,模型预测的准确度越高
val evaluator = new
RegressionEvaluator().setMetricName("rmse").setLabelCol("rating"). setPredictionCol("prediction")
//rmse:Root-mean-square error,均方根误差
//计算显性模型的rmse
val rmseExplicit = evaluator.evaluate(predictionsExplicit)
输出结果:
rmseExplicit: Double = 1.6995189118765517
//计算隐性模型的rmse
val rmseImplicit = evaluator.evaluate(predictionsImplicit)
输出结果:
rmseImplicit: Double = 1.8011620822359165