交替最小二乘法(ALS)是统计分析中最常用的逼近计算的一种算法,其交替计算结果使得最终结果尽可能地逼近真实结果。而ALS的基础是最小二乘法(LS算法),LS算法是一种常用的机器学习算法,它通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便的求得未知的数据,并使得这些求得的数据与实际数据之间误差的平法和为最小。
最小二乘法
以一个变量为例,在二维空间中最小二乘法的原理图如下:
若干个点依次分布在向量空间中,如果希望找出一条直线和这些点达到最佳匹配,那么最简单的一个方法就是希望这些点到直线的距离最小,则可得出最小二乘法的公式如下:
这里的f(x)是直接的拟合公式,也是所求的目标函数,在这里希望各个点到直接的值最小,因此第二个公式就是求所有点到该直接的距离,我们可以微分求得其最小值。
交替最小二乘法
以用户,商品为例说明,一个基于用户名,物品表的用户评分矩阵可以被分解成2个较为小型化的矩阵,即M=U的转置矩阵*V
在这里U和V分别表示 用户和物品的矩阵,在MLlib的ALS算法中,首先会对U或者V矩阵随机生成,之后固定某一个特定的对象,去求取另一个未随机化的矩阵对象。之后利用求取的矩阵对象去求随机化矩阵对象。最后两个对象相互迭代计算,求取与实际矩阵差异达到程序设定的最小阀值位置。通俗的说就是先固定U矩阵,求取V,然后再固定V矩阵再求取U矩阵,一直这样交替迭代计算直到误差达到一定的阀值条件或者达到跌打次数的上限。具体流程步骤如下所示:
(对于ALS算法更加详细的介绍以及推导过程参考这篇文章交替最小二乘ALS)
ALS算法实战
以商品推荐为例进行,使用的数据按照MLlib中ALS算法固有的数据格式。其中源码如下:
case class Rating @Since("0.8.0") (
@Since("0.8.0") user: Int,
@Since("0.8.0") product: Int,
@Since("0.8.0") rating: Double)
这里用户和商品分别用代号表示,然后rating就是用户对于该商品的评分。因此数据集格式如下:
1 11 2
1 12 3
1 13 1
1 14 0
1 15 1
2 11 1
........
ALS算法的第二步就是数据建模,其实在MLlib算法库中有可以直接使用的训练算法,ALS.tran方法源码如下:
def train(
ratings: RDD[Rating], //需要训练的数据集
rank: Int, //模型中隐藏因子数
iterations: Int, //算法中迭代次数
lambda: Double, //ALS中的正则化参数
blocks: Int, //并行计算的block数(-1为自动配置)
alpha: Double, //ALS隐式反馈变化率用于控制每次拟合修正的幅度
seed: Long //加载矩阵的随机数
): MatrixFactorizationModel = {
new ALS(blocks, blocks, rank, iterations, lambda, true, alpha, seed).run(ratings)
}
在了解了如何建立模型之后就可以编写代码进行实战了,如下所示:
import org.apache.spark.mllib.recommendation.{ALS, Rating}
import org.apache.spark.{SparkConf, SparkContext}
object CollaborativeFilter {
def main(args: Array[String]): Unit = {
val conf=new SparkConf()
.setAppName("CollaborativeFilter")
.setMaster("local")
val sc=new SparkContext(conf)
//设置数据集
val data=sc.textFile("C:\\Users\\shinelon\\Desktop\\d.txt")
//处理数据
val ratings=data.map(_.split(" ") match {
case Array(user,item,rate) => //转化数据集
Rating(user.toInt,item.toInt,rate.toDouble) //将数据集转化为专用的Rating
})
val rank=2 //设置隐藏因子
val numIterations=2 //设置迭代次数
val model=ALS.train(ratings,rank,numIterations,0.01) //进行模型训练
val rs=model.recommendProducts(2,1) //为用户2推荐一个商品
rs.foreach(println) //打印推荐结果
val result:Double=rs(0).rating //预测的评分结果
val realilty=data.map(_.split(" ") match {
case Array(user,item,rate) =>
Rating(user.toInt,item.toInt,rate.toDouble)
}).map(num=>{
if(num.user==2&&num.product==15)
num.rating //返回实际评分结果
else
0
}).foreach(num=>{
if(num!=0)
println("对15号商品预测的准确率为:"+(1-(math.abs(result-num)/1)))
})
}
}
上面代码在建立好模型之后,然后使用recommendProducts方法为第二个用户推荐一个物品,程序结果如下所示:
Rating(2,15,3.97662718480575)
对15号商品预测的准确率为:0.9941567962014375
根据结果显示,为第二个用户优先推荐了编号为15的物品,同时预测的评分为3.97662718480575,而实际值为4,准确率为0.9941567962014375,说明推荐的效果还是不错的。
代码以及数据的下载地址如下:下载地址
参考文档:
《Spark MLlib机器学习实战》
如果你想和我们一起学习交流,共同进步,欢迎加群: