zoukankan      html  css  js  c++  java
  • SparkML之推荐引擎(一)---电影推荐

    本文将使用 SparkML 来构建推荐引擎。 
    推荐引擎算法大致分为 基于内容的过滤、协同过滤、矩阵分解,本文将使用基于属于矩阵分解的 最小二乘法 算法来构建推荐引擎。 
    对于推荐引擎模块这里将分为两篇文章,第一篇文章主要是以实现推荐功能为主,第二篇文章主要是对模型进行评估 
    文章将按照以下章节来进行书写: 需求分析、获取数据、提取特征、训练模型、使用模型(推荐)

    一、需求分析
    假设我们是 MovieStream 团队,专门为用户提供在线电影和电视节目的内容服务。
    现在我们有个需求::给用户推荐电影!
    就这么简单,哈哈~

    二、获取数据
    可从 http://files.grouplens.org/datasets/movielens/ml-100k.zip 下载模拟的数据集。
    对于推荐模型,主要用到了里面的三个文件:

    u.user(用户属性文件)
    u.item(电影元数据)
    u.data(用户对电影的评级)
    数据文件说明:
    1、u.user(用户属性文件)
    字段及格式说明:user id | age | gender | occupation(职业) | zip code
    样例:

    1|24|M|technician|85711
    2|53|F|other|94043
    3|23|M|writer|32067
    4|24|M|technician|43537
    5|33|F|other|15213

    2、u.item(电影信息数据)
    字段及格式说明:
    movie id | movie title | release date | video release date | IMDb URL | unknown | Action | Adventure | Animation | Children’s | Comedy | Crime | Documentary | Drama | Fantasy | Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi | Thriller | War | Western |
    样例:

    1|Toy Story (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Toy%20Story%20(1995)|0|0|0|1|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0
    2|GoldenEye (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?GoldenEye%20(1995)|0|1|1|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
    3|Four Rooms (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Four%20Rooms%20(1995)|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|1|0|0
    4|Get Shorty (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Get%20Shorty%20(1995)|0|1|0|0|0|1|0|0|1|0|0|0|0|0|0|0|0|0|0
    5|Copycat (1995)|01-Jan-1995||http://us.imdb.com/M/title-exact?Copycat%20(1995)|0|0|0|0|0|0|1|0|1|0|0|0|0|0|0|0|1|0|0
    3、u.data(用户对电影的评分)

    字段及格式说明:user_id item_id rating timestamp(注意:分隔符为 “ ”) 
    样例:

    196 242 3   881250949
    186 302 3   891717742
    22  377 1   878887116
    244 51  2   880606923
    166 346 1   886397596

    三、提取特征

    /* 生成用户评分数据的RDD,格式为:用户 电影 评分 时间戳 */
    val rawData: RDD[String] = sc.textFile("file:///E:/spark/ml-100k/u.data")
    /* 去掉时间戳的字段,格式变为:用户 电影 评分; rawRating类型为Array */
    val rawRatings = rawData.map(_.split("\t").take(3))
    /* 格式变为:Rating(用户 电影 评分),作为后续训练模型的参数 */
    val ratings = rawRatings.map{case Array(user, movie, rating) =>{
        //封装成Rating
        Rating(user.toInt, movie.toInt, rating.toDouble)
    }}

    四、训练模型
    最小二乘法的模型需要以下三个参数:

    1、rank
    对应ALS模型中的因子个数,也就是在低阶近似矩阵中的隐含特征个数。因子个数一般越多越好。但它也会接影响模型训练和保存时所需的内存开销,尤其是在用户和物品很多的时候。因此实践中该参数常作为训练效果与系统开销之间的调节参数。通常,其合理取值为10到200。
    可以简单理解为:模型因子的列的数量

    2、iterations
    对应运行时的迭代次数。ALS能确保每次迭代都能降低评级矩阵的重建误差,但一般经少数次迭代后ALS模型便已能收敛为一个比较合理的好模型。这样,大部分情况下都没必要迭代太多次(10次左右一般就挺好)。

    3、lambda
    该参数控制模型的正则化过程,从而控制模型的过拟合情况。其值越高,正则化越严厉。该参数的赋值与实际数据的大小、特征和稀疏程度有关。和其他的机器学习模型一样,正则参数应该通过用非样本的测试数据进行交叉验证来调整。

    这里将使用的 rank、iterations 和 lambda 参数的值分别为50、10和0.01
    代码如下:

    import org.apache.spark.mllib.recommendation.{Rating, ALS}
    //这就得到了推荐的模型
    val model = ALS.train(ratings, 50, 10, 0.01)

    五、使用模型(推荐)

    1、用户推荐

    为 id 为 789 的用户推荐10个电影

    //为指定的用户推荐 N 个商品
    val userID = 789
    val K = 10
    val topKRecs: Array[Rating] = model.recommendProducts(userID, K)
    println(topKRecs.mkString("
    "))

    输出为:

    Rating(789,715,5.931851273771102)
    Rating(789,12,5.582301095666215)
    Rating(789,959,5.516272981542168)
    Rating(789,42,5.458065302395629)
    Rating(789,584,5.449949837103569)
    Rating(789,750,5.348768847643657)
    Rating(789,663,5.30832117499004)
    Rating(789,134,5.278933936827717)
    Rating(789,156,5.250959077906759)
    Rating(789,432,5.169863417126231)

    2、物品推荐(作为了解)
    物品推荐可以理解为:给定一个物品,推荐 K 个与该物品相似的物品
    我们上面得到的推荐模型中没有提供物品推荐的方法,但是谋问题,我们自己可以根据余弦相似度来实现。

    科普:余弦相似度是两个两个向量在n维空间里两者夹角的度数。它的值是两个向量的点积与各向量范数(或长度)的乘积的商。该值的取值范围是 -1 到 1 之间,1表示完全相似,0表示不相关,-1表示两者不仅不相关而且还完全不同。

    ok,我们来写一个计算余弦相似度的函数,在写之前需要引入 jblas 线性代数库,该库中有一个 DoubleMatrix 类对象,向量和矩阵都用该对象来表示

    import org.jblas.DoubleMatrix
    /**
      * 用于商品推荐
      * 通过传入两个向量,返回这两个向量之间的余弦相似度
      * @param vec1
      * @param vec2
      * @return
      */
    def cosineSimilarity(vec1: DoubleMatrix, vec2: DoubleMatrix): Double = {
      vec1.dot(vec2) / (vec1.norm2() * vec2.norm2())
    }

    开始根据物品推荐:

    /**
      * 基于商品进行推荐
      */
    /*通过商品ID获得与该商品相似的商品*/
    val itemId = 567
    val itemFactor: Array[Double] = model.productFeatures.lookup(itemId).head
    val itemVector: DoubleMatrix = new DoubleMatrix(itemFactor)
    //获得每个商品与给出的商品的余弦相似度
    val sims = model.productFeatures.map{case (id, factor) => {
      val factorVector = new DoubleMatrix(factor)
      val sim = cosineSimilarity(factorVector, itemVector)
      (id, sim)
    }}
    //打印出前10的商品
    val topItem: Array[(Int, Double)] = sims.sortBy(-_._2).take(10)
    println("与567商品相似的商品:
    " + topItem.mkString("
    ") + "
    ")

    输出为:

    与567商品相似的商品:
    (567,1.0)
    (1471,0.6932331537649621)
    (670,0.6898690594544726)
    (201,0.6897964975027041)
    (343,0.6891221044611473)
    (563,0.6864214133620066)
    (294,0.6812075443259535)
    (413,0.6754663844488256)
    (184,0.6702643811753909)
    (109,0.6594872765176396)

    很正常,排名第一的最相似物品就是我们给定的物品。但是注意,因为模型的初始化是随机的,所以后面的商品可能跟你的不一样,这很正常哈~

  • 相关阅读:
    文本效果
    C# 将数据导出到Execl汇总[转帖]
    using方法的使用
    存储过程的相关记录
    Dictionary 泛型字典集合[转帖]
    JS验证
    浅谈AutoResetEvent的用法 [转帖]
    聊聊xp和scrum在实战中的应用问题
    字体下载
    [转] 前端开发工程师如何在2013年里提升自己
  • 原文地址:https://www.cnblogs.com/itboys/p/10622785.html
Copyright © 2011-2022 走看看