问题描述:
利用ALS算法预测用户对物品的评分,同时将用户已经看过的物品(即用户对该物品已有过评分)进行过滤,并将评分较高的物品作为推荐物品推荐给用户。
但是,在模型训练的过程中,发现即使在训练参数,数据集没有做任何改变的前提下,进行两次模型训练,并分别利用两次得到的模型对同一个用户进行推荐,发现两次的推荐结果有一定的偏差。
实验数据:
随机生成1000条数据,数据格式:用户(0-50) 信息(0-50) 评分(0-1) 并对相同用户对同一条信息进行去重,保证不存在同一用户对同一信息具有不同评分。
import java.io.{File, PrintWriter}
//随机生成数据并输出到文本
object RandomTest {
def main(args: Array[String]): Unit = {
val rng = new scala.util.Random
val writer = new PrintWriter(new File("E://ALSTest.txt" ))
for(a <- 1 to 1000){
writer.println(rng.nextInt(50)+"\t"+rng.nextInt(50)+"\t"+(rng.nextInt(10)+1)/10.0)
}
writer.close()
}
}
问题分析:
ALS算法是将用户物品评分数据转换为用户物品评分矩阵,矩阵中的每一个值代表某个用户对某个物品的评分,但是在实际生活中,用户数和物品数都很大,每一个用户浏览过的物品数又是极少数的,导致用户—物品矩阵是一个大型稀疏矩阵。ALS算法中对于这样的大型稀疏矩阵主要是通过将其拆分成两个小矩阵的乘积来近似表示,最终将一个协同推荐问题通过低秩假设成功转变成一个优化问题,但是在生成两个小矩阵的过程中,是先随机生成,然后通过生成,再固定生成,一直交替生成,直到收敛。
ALS模型:
测试模型使用本地模式,设置固定参数:迭代次数20,隐藏因子3,正则项的惩罚系数0.01。并在不改动参数和训练集的情况下进行建模预测。
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.mllib.recommendation.ALS
import org.apache.spark.mllib.recommendation.Rating
object ALSTest {
def main(args: Array[String]) {
//设置环境变量
val conf=new SparkConf().setMaster("local[4]").setAppName("")
//实例化环境
val sc = new SparkContext(conf)
//设置数据集
val data =sc.textFile("E:/ALSTest.txt")
//处理数据
val ratings=data.map(_.split('\t') match{
//数据集的转换
case Array(user,item,rate) =>
//将数据集转化为专用的Rating
Rating(user.toInt,item.toInt,rate.toDouble)
})
//设置隐藏因子
val rank=3
//设置迭代次数
val numIterations=20
//进行模型训练,并设定正则项的惩罚系数为0.01
val model =ALS.train(ratings,rank,numIterations,0.01)
//为用户10推荐个商品
val rs=model.recommendProducts(1,10)
//对结果进行手动过滤
val result = rs.map(x=>(x.product.toInt,x.rating)).filter(x=>x._1!=7).filter(x=>x._1!=40).filter(x=>x._1!=12).filter(x=>x._1!=4).filter(x=>x._1!=38).filter(x=>x._1!=46).filter(x=>x._1!=36).filter(x=>x._1!=13).filter(x=>x._1!=8).filter(x=>x._1!=19)
//查看结果
result.foreach(println)
}
}
预测结果:
进行三次预测,数据格式:( 推荐商品,评分 )
结果分析:
三次结果的推荐评分顺序都不一致,这将导致每一次推荐给用户的内容都会出现偏差,而且当用户浏览的物品越多,推荐的物品评分就越高,反之就很低了。
1、用户浏览信息太少;因为信息数据量十分大,而每一位用户只浏览其中一小部分,造成这个矩阵是否稀疏,因此对每一个未知评分就有多种选择,将会造成结果的多样性。
2、ALS是利用拆分矩阵,并用最小二乘法进行最优矩阵的解析,而第一次的拆分都是随机的,虽然进过多次迭代,最终收敛到一个小区间中,但对值还是有有细微的变动,而且数据量大的情况会对结果造成一定影响。
3、用户浏览的信息越少,评分就越低,也就是当用户只浏览了少量几个信息,这个时候并不能准确的确定该用户的喜好,因此推荐的内容评分都很低,相反,如果用户浏览了大量信息,此时的推荐将更精确。
解决思路1:
研究推荐结果后发现,虽然每一次的总体推荐结果不一致,但是有些物品的出现频率还是挺高的,下面进行二十次预测训练,前十次为一组,后十次为一组,每次推荐10个结果,同时将用户已经看过的物品(即用户对该物品已有过评分)进行过滤,将得到的结果以组为单位进行统计。
根据预测十次后物品出现次数为依据,进行分析,发现两组实验中,出现频率高的,两组都高,也就是误差数据将会在多次预测中过滤掉。
数据格式(物品ID,出现次数)
1 | (15,10) | (15,5) |
2 | (21,8) | (21,6) |
3 | (2,8) | (2,5) |
4 | (39,7) | (39,5) |
5 | (0,7) | (0,7) |
6 | (16,6) | (16,5) |
7 | (37,5) | (37,8) |
8 | (6,5) | (6,7) |
9 | (10,3) | (10,6) |
10 | (41,3) | (41,3) |
代价:
每一次预测都会产生大量中转数据,需要存储空间大,且不适合用于稀疏矩阵,计算次数越多越准确,但同时消耗的时间也就变长了,也就是数据量不大的情况下可以使用该方法,但是非活跃用户的占比非常大,也就是大部分用户点击次数少,浏览间隔长,导致计算大量非活跃用户时费时费力,且准确性低,然而该方法针对活跃用户进行预测将可行。首先活跃用户占比小,使用频率高,且预测出来的评分也比较高,该方法适合针对性处理。
解决思路2:
过滤与加分
1、对数据进行过滤,也就是在推荐结果出来后存入推荐集中进行过滤,例如用户A不喜欢体育新闻,对体育新闻评分很低,那就将推荐集中包含体育的数据过滤掉。
2、对数据进行加分,如果用户A很喜欢旅游,那就对推荐集中的旅游新闻进行加分。
问题:
评分并不代表用户的喜好程度,有可能是习惯,或者是突然的兴趣导致评分很高,并不代表该用户喜欢这种文章,因此需要评分与喜欢程度挂钩才能使用该方法进行问题的解决。