大数据进阶必修课!Spark实战ALS交替最小二乘算法

目录

  • 11.SparkMLlib ALS交替最小二乘算法
    • 11.1交替最小二乘算法
    • 11.2算法源码分析
    • 11.3应用实战
      • 11.3.1 数据说明
      • 11.3.2代码详解

11.SparkMLlib ALS交替最小二乘算法

11.1交替最小二乘算法

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)两矩阵对象相互交替计算,迭代这个过程,每次迭代都降低重构误差,求得与实际矩阵差值达到提前设定的最小阈值位置。

11.2算法源码分析

在MLlib中,ALS算法采用的是高效分解矩阵的计算,设计数据分区和RDD缓存来减少数据交换,这对迭代计算非常友好。对ALS的源码分析如下:

(1)object ALS:ALS伴生对象,含有train静态方法,需要设置的参数如下:

  • ratings——用户评分,格式RDD(userID,productID,rating);
  • rank——特征数量;
  • iterations——迭代数量;
  • lambda——正则因子,一般为0.01;
  • blocks——数据分割,用来进行并行计算;
  • seed——随机种子

(2)class ALS:ALS类,run方法,调用NewALS类的train方法计算;

(3)矩阵分解计算
makeBlocks——创建用户和物品的Block块,是用来进行交替计算的;
initialize——初始化用户和物品的特征矩阵;
computeFactors——使用ALS求解用户和物品特征矩阵,进行迭代计算,现根据用户特征矩阵求解物品特征矩阵,再根据结果进行交替计算;

(4)MatrixFactorizationModel:矩阵分解模型,矩阵分解计算完成后生成矩阵分解模型:用户和物品特征矩阵。包括了如下方法:predict:预测用户对物品评分;recommendProducts:对用户推荐物品的列表;recommendUsers:对物品推荐用户的列表;recommendProductsForUsers:在所有用户推荐物品中选取前k个物品;recommendUsersForProducts:在所有物品推荐用户中选取前k个用户。

11.3应用实战

11.3.1 数据说明

本次实战使用的数据集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

11.3.2代码详解

//导入需要的机器学习包
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

你可能感兴趣的:(硬核实战Spark机器学习库)