影片推荐系统思考以及用spark.mllib.ALS实现最简单的推荐

影片推荐系统思考

1、用户信息的补充和处理

背景:智能电视通过机顶盒向用户分发电视节目。事先采集的用户信息及其有限。且电视节目的用户大多以家庭为单位,用户画像也相应呈现出家庭的特征,各项属性,如年龄,性别,学历,等等特征都是不固定的。
如何扩展有限的用户特征?
方法1:利用用户在办理开通业务时候的登记号码,可以提取用户所在的区域。
方法2:另外,通过用户的历史观影(viewlog)数据,统计一天内,24小时不同时间段的用户数量,构建数据分布图,根据数据分布,将观影时段分类,建立用户观影时段属性.

2、影片之间的相似度矩阵构建
思路1:根据用户观看历史,建立用户与观看影片列表,如图:
如图,用户1观看了影片u1_m1,u1_m2。。。将他们组合在一起,形成一个句子
利用word2vec生成每个电影的词向量,利用词向量生成m-m matrix。
词向量维度可以自己定义,但一般在100-400之间,因此该方法需要有比较大的开销。

思路2:将每一个影片的导演、演员、类型等属性组成 sentence,利用编辑距离,或自定义加权编辑距离计算影片之间的距离,构建m-m matrix。

3、用户之间的相似度矩阵构建
思路1: 利用用户与观看影片列表,计算两者共同观看的电影数量占总观影数量的比例来确定user与user之间的关系。用户的点播行为是随时间变化的,且在计算时,要考虑一天中的不同时段,其观看影片的类型会有所不同(比如孩子放学的时段,播放动画片的概率会比较高。)因此user-user matrix也应该随时间更新(比如一月一次)。

4、电视节目的基于流行度的推荐
由于用户特征有限,完全的个性化推荐不太实际。首先考虑采取基于流行度的粗粒度推荐。
思路:
1、用户分类。
可根据user-user matrix 将用户分类
根据属性,比如AREA,TIMEZOOM分类
根据属性分层如:先根据AREA粗分,再根据 TIMEZOOM细分
利用上述的方法,将用户分成不同的GROUP
2、选择topk影片
根据Day1 的数据,获得每一个GROUP下TOPk 电影,并结合m-m,计算最相关的多个电影进行推荐。
这中间要解决如,编辑距离加权。和电影重复推荐,以及冷片推送的问题。

5、冷片的推荐

**用spark.mllib.ALS实现最简单的推荐 **

采用协同过滤式方法推荐影片--该方法根据用户的历史评分,分析用户的喜好,从而向用户推荐合适的产品.

数据集下载
https://grouplens.org/datasets/movielens/

下载数据解压缩:
在这里插入图片描述u.data中存放用户评分数据,分为四列,分别是use id,movieid, rating,time
在这里插入图片描述u.item 有一些字段,记录着电影的信息,分别为:movieid,name(year), pubulish date…
在这里插入图片描述导入数据


scala> val rawUserData = sc.textFile("u.data")

scala> rawUserData.take(5).foreach(println)
196	242	3	881250949
186	302	3	891717742
22	377	1	878887116
244	51	2	880606923
166	346	1	886397596

//检查数据,看看有没有缺失和异常
scala> rawUserData.map(_.split("\t")(0).toDouble).stats()
res1: org.apache.spark.util.StatCounter = (count: 100000, mean: 462.484750, stdev: 266.613087, max: 943.000000, min: 1.000000)

scala> rawUserData.map(_.split("\t")(1).toDouble).stats()
res2: org.apache.spark.util.StatCounter = (count: 100000, mean: 425.530130, stdev: 330.796702, max: 1682.000000, min: 1.000000)

scala> rawUserData.map(_.split("\t")(2).toDouble).stats()
res3: org.apache.spark.util.StatCounter = (count: 100000, mean: 3.529860, stdev: 1.125668, max: 5.000000, min: 1.000000)

scala> rawUserData.map(_.split("\t")(3).toDouble).stats()
res5: org.apache.spark.util.StatCounter = (count: 100000, mean: 883528851.488622, stdev: 5343829.470155, max: 893286638.000000, min: 874724710.000000)


训练model

scala> import org.apache.spark.mllib.recommendation.ALS
import org.apache.spark.mllib.recommendation.ALS

scala> import org.apache.spark.mllib.recommendation.Rating
import org.apache.spark.mllib.recommendation.Rating

//只需要前三个属性(userid,movieid,rating)
scala> val rawRatings = rawUserData.map(_.split("\t").take(3))
rawRatings: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[12] at map at <console>:27

scala> val ratingRDD = rawRatings.map{case Array(user,movie,rating) => Rating(user.toInt,movie.toInt,rating.toDouble)}
ratingRDD: org.apache.spark.rdd.RDD[org.apache.spark.mllib.recommendation.Rating] = MapPartitionsRDD[13] at map at <console>:27

//将构造好的Rating 丢进模型训练
scala> val model = ALS.train(ratingRDD,10,10,0.01)
/*

ALS.train(ratings,rank,numIterations,lambda)参数解释:
rating:具有特定格式的 RDD

rank:首先算法将原本的u_m_matrix矩阵(user * movie)(通过ratingRDD构建--仔细思考,这个矩阵是很稀疏的)
分解为用户矩阵X(user * rank),和产品矩阵Y(movie * rank)(矩阵分解之后,矩阵不再稀疏,且维数有所约减),
然后分别构建user-user矩阵,和movie-movie矩阵,进而通过矩阵乘法可以得到movie 与所有用户的关系,也可以得到user与所有movie的关系,
rank 可以理解为分解矩阵时特征的数量,是一个超参数,需要人工调.

numIterations:训练次数
lambda:正则化参数,用来控制模型泛化性能,也是超参数,需要人工调.


*/
2019-02-28 15:48:42 WARN  BLAS:61 - Failed to load implementation from: com.github.fommil.netlib.NativeSystemBLAS
2019-02-28 15:48:42 WARN  BLAS:61 - Failed to load implementation from: com.github.fommil.netlib.NativeRefBLAS
2019-02-28 15:48:42 WARN  LAPACK:61 - Failed to load implementation from: com.github.fommil.netlib.NativeSystemLAPACK
2019-02-28 15:48:42 WARN  LAPACK:61 - Failed to load implementation from: com.github.fommil.netlib.NativeRefLAPACK
model: org.apache.spark.mllib.recommendation.MatrixFactorizationModel = org.apache.spark.mllib.recommendation.MatrixFactorizationModel@23bc053c



预测

//推荐用户454喜欢的movie
scala> model.recommendProducts(454,5).mkString("\n")
res8: String =
Rating(454,1141,4.473476952668481)
Rating(454,34,4.465395838192516)
Rating(454,1192,4.129537334173054)
Rating(454,1410,4.125483999697048)
Rating(454,867,4.001905086743346)

//推荐用户相关的用户
scala> model.recommendUsers(464,5).mkString("\n")
res9: String =
Rating(219,464,13.07212541336996)
Rating(726,464,11.970157628357022)
Rating(418,464,11.162018183746392)
Rating(98,464,10.768677532641455)
Rating(820,464,9.919882868872023)

//预测用户对movie 的评分
scala> model.predict(196,442)
//这个人可能蛮不喜欢这个产品
res10: Double = -0.20212121637734182

将推荐的影片id跟名称对应起来


scala> val itemRDD = sc.textFile("u.item")
itemRDD: org.apache.spark.rdd.RDD[String] = u.item MapPartitionsRDD[226] at textFile at <console>:26
//读取对应关系,构建collectAsMap()
scala> val movieTitle = itemRDD.map(_.split("\\|").take(2)).map(array=>(array(0).toInt,array(1))).collectAsMap()

scala> movieTitle.take(5).foreach(println)
(146,Unhook the Stars (1996))
(1205,Secret Agent, The (1996))
(550,Die Hard: With a Vengeance (1995))
(891,Bent (1997))
(137,Big Night (1996))

//每一次推荐,返回的是Rating组成的Array
scala> model.recommendProducts(196,5)
res14: Array[org.apache.spark.mllib.recommendation.Rating] = Array(Rating(196,634,9.521015571592464), Rating(196,394,9.006035614225851), Rating(196,1242,8.888490353715557), Rating(196,1066,8.876096458148403), Rating(196,1172,8.239287750500973))

//Rating.product读出movie id,再用movieTitle对应表找出name
scala> model.recommendProducts(196,5).map(rating=>(rating.product,movieTitle(rating.product),rating.rating)).foreach(println)

(634,Microcosmos: Le peuple de l'herbe (1996),9.521015571592464)
(394,Radioland Murders (1994),9.006035614225851)
(1242,Old Lady Who Walked in the Sea, The (Vieille qui marchait dans la mer, La) (1991),8.888490353715557)
(1066,Balto (1995),8.876096458148403)
(1172,Women, The (1939),8.239287750500973)


电影推荐demo :https://www.jianshu.com/p/be5daf1fd1be

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半九拾

援助一个bug吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值